hippo-memory 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +305 -0
- package/dist/autolearn.d.ts +34 -0
- package/dist/autolearn.d.ts.map +1 -0
- package/dist/autolearn.js +119 -0
- package/dist/autolearn.js.map +1 -0
- package/dist/cli.d.ts +21 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +956 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +35 -0
- package/dist/config.js.map +1 -0
- package/dist/consolidate.d.ts +24 -0
- package/dist/consolidate.d.ts.map +1 -0
- package/dist/consolidate.js +133 -0
- package/dist/consolidate.js.map +1 -0
- package/dist/embeddings.d.ts +39 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +184 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/memory.d.ts +59 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +118 -0
- package/dist/memory.js.map +1 -0
- package/dist/search.d.ts +51 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +239 -0
- package/dist/search.js.map +1 -0
- package/dist/shared.d.ts +38 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +111 -0
- package/dist/shared.js.map +1 -0
- package/dist/store.d.ts +70 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +244 -0
- package/dist/store.js.map +1 -0
- package/dist/yaml.d.ts +14 -0
- package/dist/yaml.d.ts.map +1 -0
- package/dist/yaml.js +81 -0
- package/dist/yaml.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,oBAAY,KAAK;IACf,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,QAAQ,aAAa;CACtB;AAED,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;AAEhF,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,iBAAiB,EAAE,gBAAgB,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAUD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAmBpF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,MAAM,CAmBhF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,GAAG,WAAW,CAO3E;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,GAAE,MAAc,GAAG,MAAM,CAGzD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,iBAAiB,CAAC,EAAE,gBAAgB,CAAC;IACrC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACZ,GACL,WAAW,CA+Bb"}
|
package/dist/memory.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core data model for Hippo memory entries.
|
|
3
|
+
* Based on the strength formula from PLAN.md.
|
|
4
|
+
*/
|
|
5
|
+
export var Layer;
|
|
6
|
+
(function (Layer) {
|
|
7
|
+
Layer["Buffer"] = "buffer";
|
|
8
|
+
Layer["Episodic"] = "episodic";
|
|
9
|
+
Layer["Semantic"] = "semantic";
|
|
10
|
+
})(Layer || (Layer = {}));
|
|
11
|
+
// Emotional multipliers from PLAN.md
|
|
12
|
+
const EMOTIONAL_MULTIPLIERS = {
|
|
13
|
+
neutral: 1.0,
|
|
14
|
+
positive: 1.3,
|
|
15
|
+
negative: 1.5, // error-tagged
|
|
16
|
+
critical: 2.0,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Calculate current strength at a given time.
|
|
20
|
+
* strength(t) = base_strength * decay * retrieval_boost * emotional_multiplier
|
|
21
|
+
*
|
|
22
|
+
* Pinned memories always return 1.0 (no decay).
|
|
23
|
+
*/
|
|
24
|
+
export function calculateStrength(entry, now = new Date()) {
|
|
25
|
+
if (entry.pinned)
|
|
26
|
+
return 1.0;
|
|
27
|
+
const lastRetrieved = new Date(entry.last_retrieved);
|
|
28
|
+
const daysSince = (now.getTime() - lastRetrieved.getTime()) / (1000 * 60 * 60 * 24);
|
|
29
|
+
// Exponential decay: base * (0.5 ^ (days / half_life))
|
|
30
|
+
const decay = Math.pow(0.5, daysSince / entry.half_life_days);
|
|
31
|
+
// Retrieval boost: 1 + 0.1 * log2(retrieval_count + 1)
|
|
32
|
+
const retrievalBoost = 1 + 0.1 * Math.log2(entry.retrieval_count + 1);
|
|
33
|
+
// Emotional multiplier
|
|
34
|
+
const emotionalMultiplier = EMOTIONAL_MULTIPLIERS[entry.emotional_valence] ?? 1.0;
|
|
35
|
+
const raw = decay * retrievalBoost * emotionalMultiplier;
|
|
36
|
+
// Clamp to [0, 1]
|
|
37
|
+
return Math.min(1.0, Math.max(0.0, raw));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Derive half-life based on signals, as per PLAN.md table.
|
|
41
|
+
*/
|
|
42
|
+
export function deriveHalfLife(base, entry) {
|
|
43
|
+
let hl = base;
|
|
44
|
+
// Error-tagged: 2x half-life
|
|
45
|
+
if (entry.tags?.includes('error')) {
|
|
46
|
+
hl *= 2;
|
|
47
|
+
}
|
|
48
|
+
// High schema fit: consolidates faster (1.5x)
|
|
49
|
+
if (entry.schema_fit !== undefined && entry.schema_fit > 0.7) {
|
|
50
|
+
hl *= 1.5;
|
|
51
|
+
}
|
|
52
|
+
// Low schema fit: decay faster (0.5x)
|
|
53
|
+
if (entry.schema_fit !== undefined && entry.schema_fit < 0.3) {
|
|
54
|
+
hl *= 0.5;
|
|
55
|
+
}
|
|
56
|
+
return hl;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Apply outcome feedback to a memory entry.
|
|
60
|
+
* Positive: +5 days half-life. Negative: -3 days.
|
|
61
|
+
*/
|
|
62
|
+
export function applyOutcome(entry, good) {
|
|
63
|
+
const delta = good ? 5 : -3;
|
|
64
|
+
const newHalfLife = Math.max(1, entry.half_life_days + delta);
|
|
65
|
+
const newOutcome = good ? 1 : -1;
|
|
66
|
+
const updated = { ...entry, half_life_days: newHalfLife, outcome_score: newOutcome };
|
|
67
|
+
updated.strength = calculateStrength(updated);
|
|
68
|
+
return updated;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Generate a random memory ID.
|
|
72
|
+
*/
|
|
73
|
+
export function generateId(prefix = 'mem') {
|
|
74
|
+
const hex = Math.random().toString(16).slice(2, 8) + Math.random().toString(16).slice(2, 8);
|
|
75
|
+
return `${prefix}_${hex.slice(0, 8)}`;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Create a new memory entry with defaults.
|
|
79
|
+
*/
|
|
80
|
+
export function createMemory(content, options = {}) {
|
|
81
|
+
const now = new Date().toISOString();
|
|
82
|
+
const layer = options.layer ?? Layer.Episodic;
|
|
83
|
+
const tags = options.tags ?? [];
|
|
84
|
+
const emotional_valence = options.emotional_valence ?? inferValence(tags);
|
|
85
|
+
const schema_fit = options.schema_fit ?? 0.5;
|
|
86
|
+
const partial = { tags, schema_fit };
|
|
87
|
+
const half_life_days = deriveHalfLife(7, partial);
|
|
88
|
+
const entry = {
|
|
89
|
+
id: generateId(layer === Layer.Semantic ? 'sem' : 'mem'),
|
|
90
|
+
created: now,
|
|
91
|
+
last_retrieved: now,
|
|
92
|
+
retrieval_count: 0,
|
|
93
|
+
strength: 1.0,
|
|
94
|
+
half_life_days,
|
|
95
|
+
layer,
|
|
96
|
+
tags,
|
|
97
|
+
emotional_valence,
|
|
98
|
+
schema_fit,
|
|
99
|
+
source: options.source ?? 'cli',
|
|
100
|
+
outcome_score: null,
|
|
101
|
+
conflicts_with: [],
|
|
102
|
+
pinned: options.pinned ?? false,
|
|
103
|
+
content,
|
|
104
|
+
};
|
|
105
|
+
// Recalculate strength with the emotional multiplier applied
|
|
106
|
+
entry.strength = calculateStrength(entry);
|
|
107
|
+
return entry;
|
|
108
|
+
}
|
|
109
|
+
function inferValence(tags) {
|
|
110
|
+
if (tags.includes('critical'))
|
|
111
|
+
return 'critical';
|
|
112
|
+
if (tags.includes('error'))
|
|
113
|
+
return 'negative';
|
|
114
|
+
if (tags.includes('success') || tags.includes('win'))
|
|
115
|
+
return 'positive';
|
|
116
|
+
return 'neutral';
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"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"}
|
package/dist/search.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BM25 search + optional embedding hybrid search for Hippo.
|
|
3
|
+
* Zero external dependencies when embeddings are not available.
|
|
4
|
+
*/
|
|
5
|
+
import { MemoryEntry } from './memory.js';
|
|
6
|
+
/**
|
|
7
|
+
* Rough token estimate: characters / 4 (works well for English text).
|
|
8
|
+
*/
|
|
9
|
+
export declare function estimateTokens(text: string): number;
|
|
10
|
+
export interface SearchResult {
|
|
11
|
+
entry: MemoryEntry;
|
|
12
|
+
score: number;
|
|
13
|
+
bm25: number;
|
|
14
|
+
tokens: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Hybrid search: BM25 + cosine similarity (when embeddings are available).
|
|
18
|
+
* score = 0.4 * bm25_norm + 0.6 * cosine_sim (with embeddings)
|
|
19
|
+
* score = bm25_norm * strength * recency (BM25-only fallback)
|
|
20
|
+
*
|
|
21
|
+
* embeddingWeight: weight for the cosine similarity component (0.0 to 1.0).
|
|
22
|
+
*/
|
|
23
|
+
export declare function hybridSearch(query: string, entries: MemoryEntry[], options?: {
|
|
24
|
+
budget?: number;
|
|
25
|
+
now?: Date;
|
|
26
|
+
hippoRoot?: string;
|
|
27
|
+
embeddingWeight?: number;
|
|
28
|
+
}): Promise<SearchResult[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Search entries using BM25 + strength + recency composite score.
|
|
31
|
+
* When embeddings are available and hippoRoot is provided, uses hybrid scoring.
|
|
32
|
+
* Returns results sorted by score, capped at token budget.
|
|
33
|
+
*
|
|
34
|
+
* Also updates retrieval metadata on returned entries (side effect: caller
|
|
35
|
+
* must persist the updated entries).
|
|
36
|
+
*/
|
|
37
|
+
export declare function search(query: string, entries: MemoryEntry[], options?: {
|
|
38
|
+
budget?: number;
|
|
39
|
+
now?: Date;
|
|
40
|
+
hippoRoot?: string;
|
|
41
|
+
}): SearchResult[];
|
|
42
|
+
/**
|
|
43
|
+
* Update retrieval metadata on entries that were returned by a search.
|
|
44
|
+
* Returns the mutated copies (caller must persist to disk).
|
|
45
|
+
*/
|
|
46
|
+
export declare function markRetrieved(entries: MemoryEntry[], now?: Date): MemoryEntry[];
|
|
47
|
+
/**
|
|
48
|
+
* Compute text overlap ratio between two strings (Jaccard on token sets).
|
|
49
|
+
*/
|
|
50
|
+
export declare function textOverlap(a: string, b: string): number;
|
|
51
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAqB,MAAM,aAAa,CAAC;AAkF7D;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAiBD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,WAAW,EAAE,EACtB,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;CACrB,GACL,OAAO,CAAC,YAAY,EAAE,CAAC,CAqFzB;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CACpB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,WAAW,EAAE,EACtB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,IAAI,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAChE,YAAY,EAAE,CA+ChB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,GAAG,GAAE,IAAiB,GAAG,WAAW,EAAE,CAY3F;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAaxD"}
|
package/dist/search.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BM25 search + optional embedding hybrid search for Hippo.
|
|
3
|
+
* Zero external dependencies when embeddings are not available.
|
|
4
|
+
*/
|
|
5
|
+
import { calculateStrength } from './memory.js';
|
|
6
|
+
import { isEmbeddingAvailable, getEmbedding, cosineSimilarity, loadEmbeddingIndex, } from './embeddings.js';
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Tokenizer
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
function tokenize(text) {
|
|
11
|
+
return text
|
|
12
|
+
.toLowerCase()
|
|
13
|
+
.replace(/[^\w\s]/g, ' ')
|
|
14
|
+
.split(/\s+/)
|
|
15
|
+
.filter((t) => t.length > 1);
|
|
16
|
+
}
|
|
17
|
+
const BM25_K1 = 1.5;
|
|
18
|
+
const BM25_B = 0.75;
|
|
19
|
+
function buildCorpus(texts) {
|
|
20
|
+
const docs = texts.map(tokenize);
|
|
21
|
+
const N = docs.length;
|
|
22
|
+
const df = new Map();
|
|
23
|
+
let totalLen = 0;
|
|
24
|
+
for (const doc of docs) {
|
|
25
|
+
totalLen += doc.length;
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
for (const term of doc) {
|
|
28
|
+
if (!seen.has(term)) {
|
|
29
|
+
df.set(term, (df.get(term) ?? 0) + 1);
|
|
30
|
+
seen.add(term);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const avgLen = N > 0 ? totalLen / N : 1;
|
|
35
|
+
return { docs, avgLen, df, N };
|
|
36
|
+
}
|
|
37
|
+
function bm25Score(corpus, docIdx, queryTerms) {
|
|
38
|
+
const doc = corpus.docs[docIdx];
|
|
39
|
+
const docLen = doc.length;
|
|
40
|
+
let score = 0;
|
|
41
|
+
// Term frequency map for this doc
|
|
42
|
+
const tf = new Map();
|
|
43
|
+
for (const t of doc)
|
|
44
|
+
tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
45
|
+
for (const term of queryTerms) {
|
|
46
|
+
const f = tf.get(term) ?? 0;
|
|
47
|
+
if (f === 0)
|
|
48
|
+
continue;
|
|
49
|
+
const df = corpus.df.get(term) ?? 0;
|
|
50
|
+
const idf = Math.log((corpus.N - df + 0.5) / (df + 0.5) + 1);
|
|
51
|
+
const numerator = f * (BM25_K1 + 1);
|
|
52
|
+
const denominator = f + BM25_K1 * (1 - BM25_B + BM25_B * (docLen / corpus.avgLen));
|
|
53
|
+
score += idf * (numerator / denominator);
|
|
54
|
+
}
|
|
55
|
+
return score;
|
|
56
|
+
}
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Token budget estimation
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
/**
|
|
61
|
+
* Rough token estimate: characters / 4 (works well for English text).
|
|
62
|
+
*/
|
|
63
|
+
export function estimateTokens(text) {
|
|
64
|
+
return Math.ceil(text.length / 4);
|
|
65
|
+
}
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Recency boost
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
function recencyBoost(entry, now) {
|
|
70
|
+
const created = new Date(entry.created);
|
|
71
|
+
const ageDays = (now.getTime() - created.getTime()) / (1000 * 60 * 60 * 24);
|
|
72
|
+
// Exponential decay: memories < 1 day get boost ~1.0, older get less
|
|
73
|
+
return Math.exp(-ageDays / 30);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Hybrid search: BM25 + cosine similarity (when embeddings are available).
|
|
77
|
+
* score = 0.4 * bm25_norm + 0.6 * cosine_sim (with embeddings)
|
|
78
|
+
* score = bm25_norm * strength * recency (BM25-only fallback)
|
|
79
|
+
*
|
|
80
|
+
* embeddingWeight: weight for the cosine similarity component (0.0 to 1.0).
|
|
81
|
+
*/
|
|
82
|
+
export async function hybridSearch(query, entries, options = {}) {
|
|
83
|
+
const now = options.now ?? new Date();
|
|
84
|
+
const budget = options.budget ?? 4000;
|
|
85
|
+
const embeddingWeight = options.embeddingWeight ?? 0.6;
|
|
86
|
+
const bm25Weight = 1 - embeddingWeight;
|
|
87
|
+
if (entries.length === 0)
|
|
88
|
+
return [];
|
|
89
|
+
const queryTerms = tokenize(query);
|
|
90
|
+
if (queryTerms.length === 0)
|
|
91
|
+
return [];
|
|
92
|
+
// Build BM25 corpus
|
|
93
|
+
const texts = entries.map((e) => `${e.content} ${e.tags.join(' ')}`);
|
|
94
|
+
const corpus = buildCorpus(texts);
|
|
95
|
+
// Score all entries with BM25
|
|
96
|
+
const bm25Scores = entries.map((_, i) => bm25Score(corpus, i, queryTerms));
|
|
97
|
+
const maxBm25 = Math.max(...bm25Scores, 1e-9);
|
|
98
|
+
// Try to get embedding scores if available
|
|
99
|
+
let useEmbeddings = false;
|
|
100
|
+
let embeddingIndex = {};
|
|
101
|
+
let queryVector = [];
|
|
102
|
+
if (isEmbeddingAvailable() && options.hippoRoot) {
|
|
103
|
+
try {
|
|
104
|
+
queryVector = await getEmbedding(query);
|
|
105
|
+
if (queryVector.length > 0) {
|
|
106
|
+
embeddingIndex = loadEmbeddingIndex(options.hippoRoot);
|
|
107
|
+
useEmbeddings = true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// Fall through to BM25-only
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Score each entry
|
|
115
|
+
const scored = [];
|
|
116
|
+
for (let i = 0; i < entries.length; i++) {
|
|
117
|
+
const rawBm25 = bm25Scores[i];
|
|
118
|
+
if (!useEmbeddings && rawBm25 <= 0)
|
|
119
|
+
continue;
|
|
120
|
+
const normBm25 = rawBm25 / maxBm25;
|
|
121
|
+
const strength = calculateStrength(entries[i], now);
|
|
122
|
+
const recency = recencyBoost(entries[i], now);
|
|
123
|
+
let compositeScore;
|
|
124
|
+
if (useEmbeddings) {
|
|
125
|
+
const cached = embeddingIndex[entries[i].id];
|
|
126
|
+
const cosine = cached && queryVector.length > 0
|
|
127
|
+
? cosineSimilarity(queryVector, cached)
|
|
128
|
+
: 0;
|
|
129
|
+
// Hybrid: weighted blend, then modulated by strength and recency
|
|
130
|
+
const hybrid = bm25Weight * normBm25 + embeddingWeight * Math.max(0, cosine);
|
|
131
|
+
compositeScore = hybrid * (0.5 + 0.5 * strength) * (0.8 + 0.2 * recency);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// Pure BM25 path: identical to original behavior
|
|
135
|
+
const normQ = queryTerms.length > 0 ? rawBm25 / queryTerms.length : rawBm25;
|
|
136
|
+
compositeScore = normQ * (0.5 + 0.5 * strength) * (0.8 + 0.2 * recency);
|
|
137
|
+
}
|
|
138
|
+
if (compositeScore <= 0)
|
|
139
|
+
continue;
|
|
140
|
+
const tokens = estimateTokens(entries[i].content);
|
|
141
|
+
scored.push({ entry: entries[i], score: compositeScore, bm25: rawBm25, tokens });
|
|
142
|
+
}
|
|
143
|
+
// Sort by composite score descending
|
|
144
|
+
scored.sort((a, b) => b.score - a.score);
|
|
145
|
+
// Apply token budget
|
|
146
|
+
const results = [];
|
|
147
|
+
let usedTokens = 0;
|
|
148
|
+
for (const result of scored) {
|
|
149
|
+
if (usedTokens + result.tokens > budget)
|
|
150
|
+
continue;
|
|
151
|
+
results.push(result);
|
|
152
|
+
usedTokens += result.tokens;
|
|
153
|
+
}
|
|
154
|
+
return results;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Search entries using BM25 + strength + recency composite score.
|
|
158
|
+
* When embeddings are available and hippoRoot is provided, uses hybrid scoring.
|
|
159
|
+
* Returns results sorted by score, capped at token budget.
|
|
160
|
+
*
|
|
161
|
+
* Also updates retrieval metadata on returned entries (side effect: caller
|
|
162
|
+
* must persist the updated entries).
|
|
163
|
+
*/
|
|
164
|
+
export function search(query, entries, options = {}) {
|
|
165
|
+
// Synchronous path: BM25 only (no async hybrid)
|
|
166
|
+
const now = options.now ?? new Date();
|
|
167
|
+
const budget = options.budget ?? 4000;
|
|
168
|
+
if (entries.length === 0)
|
|
169
|
+
return [];
|
|
170
|
+
const queryTerms = tokenize(query);
|
|
171
|
+
if (queryTerms.length === 0)
|
|
172
|
+
return [];
|
|
173
|
+
// Build corpus from all entries (content + tags joined)
|
|
174
|
+
const texts = entries.map((e) => `${e.content} ${e.tags.join(' ')}`);
|
|
175
|
+
const corpus = buildCorpus(texts);
|
|
176
|
+
// Score each entry
|
|
177
|
+
const scored = [];
|
|
178
|
+
for (let i = 0; i < entries.length; i++) {
|
|
179
|
+
const bm25 = bm25Score(corpus, i, queryTerms);
|
|
180
|
+
if (bm25 <= 0)
|
|
181
|
+
continue;
|
|
182
|
+
const strength = calculateStrength(entries[i], now);
|
|
183
|
+
const recency = recencyBoost(entries[i], now);
|
|
184
|
+
// Composite: BM25 relevance * strength * recency
|
|
185
|
+
// Normalise BM25 against query term count to keep scale consistent
|
|
186
|
+
const normBm25 = queryTerms.length > 0 ? bm25 / queryTerms.length : bm25;
|
|
187
|
+
const composite = normBm25 * (0.5 + 0.5 * strength) * (0.8 + 0.2 * recency);
|
|
188
|
+
const tokens = estimateTokens(entries[i].content);
|
|
189
|
+
scored.push({ entry: entries[i], score: composite, bm25, tokens });
|
|
190
|
+
}
|
|
191
|
+
// Sort by composite score descending
|
|
192
|
+
scored.sort((a, b) => b.score - a.score);
|
|
193
|
+
// Apply token budget
|
|
194
|
+
const results = [];
|
|
195
|
+
let usedTokens = 0;
|
|
196
|
+
for (const result of scored) {
|
|
197
|
+
if (usedTokens + result.tokens > budget)
|
|
198
|
+
continue;
|
|
199
|
+
results.push(result);
|
|
200
|
+
usedTokens += result.tokens;
|
|
201
|
+
}
|
|
202
|
+
return results;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Update retrieval metadata on entries that were returned by a search.
|
|
206
|
+
* Returns the mutated copies (caller must persist to disk).
|
|
207
|
+
*/
|
|
208
|
+
export function markRetrieved(entries, now = new Date()) {
|
|
209
|
+
return entries.map((e) => {
|
|
210
|
+
const updated = {
|
|
211
|
+
...e,
|
|
212
|
+
retrieval_count: e.retrieval_count + 1,
|
|
213
|
+
last_retrieved: now.toISOString(),
|
|
214
|
+
// Extend half-life by +2 days per retrieval (PLAN.md)
|
|
215
|
+
half_life_days: e.half_life_days + 2,
|
|
216
|
+
};
|
|
217
|
+
updated.strength = calculateStrength(updated, now);
|
|
218
|
+
return updated;
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Compute text overlap ratio between two strings (Jaccard on token sets).
|
|
223
|
+
*/
|
|
224
|
+
export function textOverlap(a, b) {
|
|
225
|
+
const setA = new Set(tokenize(a));
|
|
226
|
+
const setB = new Set(tokenize(b));
|
|
227
|
+
if (setA.size === 0 && setB.size === 0)
|
|
228
|
+
return 1;
|
|
229
|
+
if (setA.size === 0 || setB.size === 0)
|
|
230
|
+
return 0;
|
|
231
|
+
let intersection = 0;
|
|
232
|
+
for (const t of setA) {
|
|
233
|
+
if (setB.has(t))
|
|
234
|
+
intersection++;
|
|
235
|
+
}
|
|
236
|
+
const union = setA.size + setB.size - intersection;
|
|
237
|
+
return intersection / union;
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"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"}
|
package/dist/shared.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-agent shared memory for Hippo.
|
|
3
|
+
* Global store at ~/.hippo/ is shared across all projects.
|
|
4
|
+
* Local .hippo/ stores are per-project.
|
|
5
|
+
*/
|
|
6
|
+
import { MemoryEntry } from './memory.js';
|
|
7
|
+
import { SearchResult } from './search.js';
|
|
8
|
+
/**
|
|
9
|
+
* Returns the path to the global Hippo store: ~/.hippo/
|
|
10
|
+
*/
|
|
11
|
+
export declare function getGlobalRoot(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Ensure the global store exists.
|
|
14
|
+
*/
|
|
15
|
+
export declare function initGlobal(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Copy a local memory entry to the global store.
|
|
18
|
+
* Assigns a new ID (prefixed with 'g_') to avoid collisions.
|
|
19
|
+
* Returns the new global entry.
|
|
20
|
+
*/
|
|
21
|
+
export declare function promoteToGlobal(localRoot: string, id: string): MemoryEntry;
|
|
22
|
+
export interface SearchOptions {
|
|
23
|
+
budget?: number;
|
|
24
|
+
now?: Date;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Search across both local and global stores, merging results.
|
|
28
|
+
* Local results are boosted by 1.2x to prefer project-specific context.
|
|
29
|
+
* Returns results sorted by adjusted score, within combined token budget.
|
|
30
|
+
*/
|
|
31
|
+
export declare function searchBoth(query: string, localRoot: string, globalRoot: string, options?: SearchOptions): SearchResult[];
|
|
32
|
+
/**
|
|
33
|
+
* Copy all global memories into the local store.
|
|
34
|
+
* Skips entries that already exist locally (by ID or by near-identical content).
|
|
35
|
+
* Returns the count of newly copied entries.
|
|
36
|
+
*/
|
|
37
|
+
export declare function syncGlobalToLocal(localRoot: string, globalRoot: string): number;
|
|
38
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,WAAW,EAAc,MAAM,aAAa,CAAC;AAQtD,OAAO,EAAU,YAAY,EAAE,MAAM,aAAa,CAAC;AAEnD;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAQjC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,CAgB1E;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,aAAkB,GAC1B,YAAY,EAAE,CAwChB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAgB/E"}
|
package/dist/shared.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-agent shared memory for Hippo.
|
|
3
|
+
* Global store at ~/.hippo/ is shared across all projects.
|
|
4
|
+
* Local .hippo/ stores are per-project.
|
|
5
|
+
*/
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import * as os from 'os';
|
|
9
|
+
import { generateId } from './memory.js';
|
|
10
|
+
import { initStore, loadAllEntries, loadIndex, writeEntry, readEntry, } from './store.js';
|
|
11
|
+
import { search } from './search.js';
|
|
12
|
+
/**
|
|
13
|
+
* Returns the path to the global Hippo store: ~/.hippo/
|
|
14
|
+
*/
|
|
15
|
+
export function getGlobalRoot() {
|
|
16
|
+
return path.join(os.homedir(), '.hippo');
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Ensure the global store exists.
|
|
20
|
+
*/
|
|
21
|
+
export function initGlobal() {
|
|
22
|
+
const globalRoot = getGlobalRoot();
|
|
23
|
+
if (!fs.existsSync(globalRoot)) {
|
|
24
|
+
initStore(globalRoot);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// Ensure subdirectories exist in case partially initialized
|
|
28
|
+
initStore(globalRoot);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Copy a local memory entry to the global store.
|
|
33
|
+
* Assigns a new ID (prefixed with 'g_') to avoid collisions.
|
|
34
|
+
* Returns the new global entry.
|
|
35
|
+
*/
|
|
36
|
+
export function promoteToGlobal(localRoot, id) {
|
|
37
|
+
const entry = readEntry(localRoot, id);
|
|
38
|
+
if (!entry)
|
|
39
|
+
throw new Error(`Memory not found: ${id}`);
|
|
40
|
+
initGlobal();
|
|
41
|
+
const globalRoot = getGlobalRoot();
|
|
42
|
+
// Mint a new ID for the global store
|
|
43
|
+
const globalEntry = {
|
|
44
|
+
...entry,
|
|
45
|
+
id: generateId('g'),
|
|
46
|
+
source: `promoted:${localRoot}`,
|
|
47
|
+
};
|
|
48
|
+
writeEntry(globalRoot, globalEntry);
|
|
49
|
+
return globalEntry;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Search across both local and global stores, merging results.
|
|
53
|
+
* Local results are boosted by 1.2x to prefer project-specific context.
|
|
54
|
+
* Returns results sorted by adjusted score, within combined token budget.
|
|
55
|
+
*/
|
|
56
|
+
export function searchBoth(query, localRoot, globalRoot, options = {}) {
|
|
57
|
+
const { budget = 4000, now = new Date() } = options;
|
|
58
|
+
const localEntries = fs.existsSync(localRoot) ? loadAllEntries(localRoot) : [];
|
|
59
|
+
const globalEntries = fs.existsSync(globalRoot) ? loadAllEntries(globalRoot) : [];
|
|
60
|
+
if (localEntries.length === 0 && globalEntries.length === 0)
|
|
61
|
+
return [];
|
|
62
|
+
// Search each store with full budget, then blend
|
|
63
|
+
const localResults = search(query, localEntries, { budget, now });
|
|
64
|
+
const globalResults = search(query, globalEntries, { budget, now });
|
|
65
|
+
// Tag global results
|
|
66
|
+
const tagged = [
|
|
67
|
+
...localResults.map((r) => ({ ...r, isGlobal: false, score: r.score * 1.2 })),
|
|
68
|
+
...globalResults.map((r) => ({ ...r, isGlobal: true })),
|
|
69
|
+
];
|
|
70
|
+
// Remove duplicates: if same ID appears in both, keep the local (higher weight) one
|
|
71
|
+
const seen = new Set();
|
|
72
|
+
const deduped = tagged.filter((r) => {
|
|
73
|
+
if (seen.has(r.entry.id))
|
|
74
|
+
return false;
|
|
75
|
+
seen.add(r.entry.id);
|
|
76
|
+
return true;
|
|
77
|
+
});
|
|
78
|
+
// Sort by adjusted score descending
|
|
79
|
+
deduped.sort((a, b) => b.score - a.score);
|
|
80
|
+
// Apply combined token budget
|
|
81
|
+
const results = [];
|
|
82
|
+
let usedTokens = 0;
|
|
83
|
+
for (const r of deduped) {
|
|
84
|
+
if (usedTokens + r.tokens > budget)
|
|
85
|
+
continue;
|
|
86
|
+
results.push(r);
|
|
87
|
+
usedTokens += r.tokens;
|
|
88
|
+
}
|
|
89
|
+
return results;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Copy all global memories into the local store.
|
|
93
|
+
* Skips entries that already exist locally (by ID or by near-identical content).
|
|
94
|
+
* Returns the count of newly copied entries.
|
|
95
|
+
*/
|
|
96
|
+
export function syncGlobalToLocal(localRoot, globalRoot) {
|
|
97
|
+
if (!fs.existsSync(globalRoot))
|
|
98
|
+
return 0;
|
|
99
|
+
const globalEntries = loadAllEntries(globalRoot);
|
|
100
|
+
const localIndex = loadIndex(localRoot);
|
|
101
|
+
let count = 0;
|
|
102
|
+
for (const entry of globalEntries) {
|
|
103
|
+
// Skip if already present by ID
|
|
104
|
+
if (localIndex.entries[entry.id])
|
|
105
|
+
continue;
|
|
106
|
+
writeEntry(localRoot, entry);
|
|
107
|
+
count++;
|
|
108
|
+
}
|
|
109
|
+
return count;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"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"}
|