audrey 0.15.0 → 0.16.1
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 -21
- package/README.md +808 -681
- package/mcp-server/config.js +80 -76
- package/mcp-server/index.js +728 -456
- package/package.json +76 -77
- package/src/adaptive.js +53 -53
- package/src/affect.js +64 -64
- package/src/audrey.js +604 -647
- package/src/causal.js +95 -95
- package/src/confidence.js +120 -120
- package/src/consolidate.js +265 -242
- package/src/context.js +15 -15
- package/src/db.js +370 -333
- package/src/decay.js +84 -84
- package/src/embedding.js +256 -256
- package/src/encode.js +63 -85
- package/src/export.js +67 -61
- package/src/forget.js +111 -111
- package/src/import.js +245 -123
- package/src/index.js +27 -20
- package/src/interference.js +51 -51
- package/src/introspect.js +48 -48
- package/src/llm.js +246 -240
- package/src/migrate.js +58 -58
- package/src/prompts.js +223 -223
- package/src/recall.js +352 -329
- package/src/rollback.js +42 -42
- package/src/ulid.js +18 -18
- package/src/utils.js +38 -38
- package/src/validate.js +172 -172
package/package.json
CHANGED
|
@@ -1,77 +1,76 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "audrey",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Biological memory architecture for AI agents — encode, consolidate, and recall memories with confidence decay, contradiction detection, and causal graphs",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "src/index.js",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": "./src/index.js",
|
|
9
|
-
"./mcp": "./mcp-server/index.js"
|
|
10
|
-
},
|
|
11
|
-
"bin": {
|
|
12
|
-
"audrey": "mcp-server/index.js",
|
|
13
|
-
"audrey-mcp": "mcp-server/index.js"
|
|
14
|
-
},
|
|
15
|
-
"files": [
|
|
16
|
-
"src/",
|
|
17
|
-
"mcp-server/",
|
|
18
|
-
"README.md",
|
|
19
|
-
"LICENSE"
|
|
20
|
-
],
|
|
21
|
-
"scripts": {
|
|
22
|
-
"test": "vitest run",
|
|
23
|
-
"test:watch": "vitest"
|
|
24
|
-
},
|
|
25
|
-
"keywords": [
|
|
26
|
-
"ai",
|
|
27
|
-
"memory",
|
|
28
|
-
"agents",
|
|
29
|
-
"llm",
|
|
30
|
-
"cognitive",
|
|
31
|
-
"sqlite",
|
|
32
|
-
"embedding",
|
|
33
|
-
"mcp",
|
|
34
|
-
"biological-memory",
|
|
35
|
-
"episodic-memory",
|
|
36
|
-
"semantic-memory",
|
|
37
|
-
"forgetting-curve",
|
|
38
|
-
"vector-search",
|
|
39
|
-
"knowledge-graph",
|
|
40
|
-
"causal-graph",
|
|
41
|
-
"ai-agent",
|
|
42
|
-
"model-context-protocol",
|
|
43
|
-
"recall",
|
|
44
|
-
"consolidation",
|
|
45
|
-
"confidence",
|
|
46
|
-
"long-term-memory",
|
|
47
|
-
"persistent-memory",
|
|
48
|
-
"rag",
|
|
49
|
-
"claude",
|
|
50
|
-
"agent-framework"
|
|
51
|
-
],
|
|
52
|
-
"repository": {
|
|
53
|
-
"type": "git",
|
|
54
|
-
"url": "git+https://github.com/Evilander/Audrey.git"
|
|
55
|
-
},
|
|
56
|
-
"homepage": "https://github.com/Evilander/Audrey",
|
|
57
|
-
"bugs": {
|
|
58
|
-
"url": "https://github.com/Evilander/Audrey/issues"
|
|
59
|
-
},
|
|
60
|
-
"author": "Tyler Eveland <j.tyler.eveland@gmail.com>",
|
|
61
|
-
"engines": {
|
|
62
|
-
"node": ">=18"
|
|
63
|
-
},
|
|
64
|
-
"license": "MIT",
|
|
65
|
-
"dependencies": {
|
|
66
|
-
"@huggingface/transformers": "^3.8.1",
|
|
67
|
-
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "audrey",
|
|
3
|
+
"version": "0.16.1",
|
|
4
|
+
"description": "Biological memory architecture for AI agents — encode, consolidate, and recall memories with confidence decay, contradiction detection, and causal graphs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js",
|
|
9
|
+
"./mcp": "./mcp-server/index.js"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"audrey": "mcp-server/index.js",
|
|
13
|
+
"audrey-mcp": "mcp-server/index.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src/",
|
|
17
|
+
"mcp-server/",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"ai",
|
|
27
|
+
"memory",
|
|
28
|
+
"agents",
|
|
29
|
+
"llm",
|
|
30
|
+
"cognitive",
|
|
31
|
+
"sqlite",
|
|
32
|
+
"embedding",
|
|
33
|
+
"mcp",
|
|
34
|
+
"biological-memory",
|
|
35
|
+
"episodic-memory",
|
|
36
|
+
"semantic-memory",
|
|
37
|
+
"forgetting-curve",
|
|
38
|
+
"vector-search",
|
|
39
|
+
"knowledge-graph",
|
|
40
|
+
"causal-graph",
|
|
41
|
+
"ai-agent",
|
|
42
|
+
"model-context-protocol",
|
|
43
|
+
"recall",
|
|
44
|
+
"consolidation",
|
|
45
|
+
"confidence",
|
|
46
|
+
"long-term-memory",
|
|
47
|
+
"persistent-memory",
|
|
48
|
+
"rag",
|
|
49
|
+
"claude",
|
|
50
|
+
"agent-framework"
|
|
51
|
+
],
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/Evilander/Audrey.git"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://github.com/Evilander/Audrey",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/Evilander/Audrey/issues"
|
|
59
|
+
},
|
|
60
|
+
"author": "Tyler Eveland <j.tyler.eveland@gmail.com>",
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=18"
|
|
63
|
+
},
|
|
64
|
+
"license": "MIT",
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@huggingface/transformers": "^3.8.1",
|
|
67
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
68
|
+
"better-sqlite3": "^12.6.2",
|
|
69
|
+
"sqlite-vec": "^0.1.7-alpha.2",
|
|
70
|
+
"ulid": "^3.0.2",
|
|
71
|
+
"zod": "^4.3.6"
|
|
72
|
+
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"vitest": "^4.0.18"
|
|
75
|
+
}
|
|
76
|
+
}
|
package/src/adaptive.js
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
export function suggestConsolidationParams(db) {
|
|
2
|
-
const runs = db.prepare(`
|
|
3
|
-
SELECT min_cluster_size, similarity_threshold, clusters_found, principles_extracted, episodes_evaluated
|
|
4
|
-
FROM consolidation_metrics
|
|
5
|
-
ORDER BY created_at DESC
|
|
6
|
-
LIMIT 20
|
|
7
|
-
`).all();
|
|
8
|
-
|
|
9
|
-
if (runs.length === 0) {
|
|
10
|
-
return {
|
|
11
|
-
minClusterSize: 3,
|
|
12
|
-
similarityThreshold: 0.85,
|
|
13
|
-
confidence: 'no_data',
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const paramScores = new Map();
|
|
18
|
-
for (const run of runs) {
|
|
19
|
-
if (run.episodes_evaluated === 0) continue;
|
|
20
|
-
const key = `${run.min_cluster_size}:${run.similarity_threshold}`;
|
|
21
|
-
if (!paramScores.has(key)) {
|
|
22
|
-
paramScores.set(key, {
|
|
23
|
-
minClusterSize: run.min_cluster_size,
|
|
24
|
-
similarityThreshold: run.similarity_threshold,
|
|
25
|
-
yields: [],
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
paramScores.get(key).yields.push(run.principles_extracted / run.episodes_evaluated);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
let bestKey = null;
|
|
32
|
-
let bestAvgYield = -1;
|
|
33
|
-
for (const [key, data] of paramScores) {
|
|
34
|
-
const avg = data.yields.reduce((a, b) => a + b, 0) / data.yields.length;
|
|
35
|
-
if (avg > bestAvgYield) {
|
|
36
|
-
bestAvgYield = avg;
|
|
37
|
-
bestKey = key;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (!bestKey) {
|
|
42
|
-
return { minClusterSize: 3, similarityThreshold: 0.85, confidence: 'no_data' };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const best = paramScores.get(bestKey);
|
|
46
|
-
const confidence = runs.length >= 5 ? 'high' : runs.length >= 2 ? 'medium' : 'low';
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
minClusterSize: best.minClusterSize,
|
|
50
|
-
similarityThreshold: best.similarityThreshold,
|
|
51
|
-
confidence,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
1
|
+
export function suggestConsolidationParams(db) {
|
|
2
|
+
const runs = db.prepare(`
|
|
3
|
+
SELECT min_cluster_size, similarity_threshold, clusters_found, principles_extracted, episodes_evaluated
|
|
4
|
+
FROM consolidation_metrics
|
|
5
|
+
ORDER BY created_at DESC
|
|
6
|
+
LIMIT 20
|
|
7
|
+
`).all();
|
|
8
|
+
|
|
9
|
+
if (runs.length === 0) {
|
|
10
|
+
return {
|
|
11
|
+
minClusterSize: 3,
|
|
12
|
+
similarityThreshold: 0.85,
|
|
13
|
+
confidence: 'no_data',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const paramScores = new Map();
|
|
18
|
+
for (const run of runs) {
|
|
19
|
+
if (run.episodes_evaluated === 0) continue;
|
|
20
|
+
const key = `${run.min_cluster_size}:${run.similarity_threshold}`;
|
|
21
|
+
if (!paramScores.has(key)) {
|
|
22
|
+
paramScores.set(key, {
|
|
23
|
+
minClusterSize: run.min_cluster_size,
|
|
24
|
+
similarityThreshold: run.similarity_threshold,
|
|
25
|
+
yields: [],
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
paramScores.get(key).yields.push(run.principles_extracted / run.episodes_evaluated);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let bestKey = null;
|
|
32
|
+
let bestAvgYield = -1;
|
|
33
|
+
for (const [key, data] of paramScores) {
|
|
34
|
+
const avg = data.yields.reduce((a, b) => a + b, 0) / data.yields.length;
|
|
35
|
+
if (avg > bestAvgYield) {
|
|
36
|
+
bestAvgYield = avg;
|
|
37
|
+
bestKey = key;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!bestKey) {
|
|
42
|
+
return { minClusterSize: 3, similarityThreshold: 0.85, confidence: 'no_data' };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const best = paramScores.get(bestKey);
|
|
46
|
+
const confidence = runs.length >= 5 ? 'high' : runs.length >= 2 ? 'medium' : 'low';
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
minClusterSize: best.minClusterSize,
|
|
50
|
+
similarityThreshold: best.similarityThreshold,
|
|
51
|
+
confidence,
|
|
52
|
+
};
|
|
53
|
+
}
|
package/src/affect.js
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
export function arousalSalienceBoost(arousal) {
|
|
2
|
-
if (arousal === undefined || arousal === null) return 0;
|
|
3
|
-
// Inverted-U (Yerkes-Dodson): peaks at 0.7, Gaussian sigma=0.3
|
|
4
|
-
return Math.exp(-Math.pow(arousal - 0.7, 2) / (2 * 0.3 * 0.3));
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function affectSimilarity(a, b) {
|
|
8
|
-
if (!a || !b) return 0;
|
|
9
|
-
if (a.valence === undefined || b.valence === undefined) return 0;
|
|
10
|
-
const valenceDist = Math.abs(a.valence - b.valence);
|
|
11
|
-
const valenceSim = 1.0 - (valenceDist / 2.0);
|
|
12
|
-
if (a.arousal === undefined || b.arousal === undefined) return valenceSim;
|
|
13
|
-
const arousalSim = 1.0 - Math.abs(a.arousal - b.arousal);
|
|
14
|
-
// Valence is primary (70%), arousal secondary (30%) per Bower 1981
|
|
15
|
-
return 0.7 * valenceSim + 0.3 * arousalSim;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function moodCongruenceModifier(encodingAffect, retrievalMood, weight = 0.2) {
|
|
19
|
-
if (!encodingAffect || !retrievalMood) return 1.0;
|
|
20
|
-
const similarity = affectSimilarity(encodingAffect, retrievalMood);
|
|
21
|
-
if (similarity === 0) return 1.0;
|
|
22
|
-
return 1.0 + (weight * similarity);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export async function detectResonance(db, embeddingProvider, episodeId, { content, affect }, config = {}) {
|
|
26
|
-
const { enabled = true, k = 5, threshold = 0.5, affectThreshold = 0.6 } = config;
|
|
27
|
-
if (!enabled || !affect || affect.valence === undefined) return [];
|
|
28
|
-
|
|
29
|
-
const vector = await embeddingProvider.embed(content);
|
|
30
|
-
const buffer = embeddingProvider.vectorToBuffer(vector);
|
|
31
|
-
|
|
32
|
-
const matches = db.prepare(`
|
|
33
|
-
SELECT e.*, (1.0 - v.distance) AS similarity
|
|
34
|
-
FROM vec_episodes v
|
|
35
|
-
JOIN episodes e ON e.id = v.id
|
|
36
|
-
WHERE v.embedding MATCH ?
|
|
37
|
-
AND k = ?
|
|
38
|
-
AND e.id != ?
|
|
39
|
-
AND e.superseded_by IS NULL
|
|
40
|
-
`).all(buffer, k, episodeId);
|
|
41
|
-
|
|
42
|
-
const resonances = [];
|
|
43
|
-
for (const match of matches) {
|
|
44
|
-
if (match.similarity < threshold) continue;
|
|
45
|
-
let priorAffect;
|
|
46
|
-
try { priorAffect = JSON.parse(match.affect || '{}'); } catch { continue; }
|
|
47
|
-
if (priorAffect.valence === undefined) continue;
|
|
48
|
-
|
|
49
|
-
const emotionalSimilarity = affectSimilarity(affect, priorAffect);
|
|
50
|
-
if (emotionalSimilarity < affectThreshold) continue;
|
|
51
|
-
|
|
52
|
-
resonances.push({
|
|
53
|
-
priorEpisodeId: match.id,
|
|
54
|
-
priorContent: match.content,
|
|
55
|
-
priorAffect,
|
|
56
|
-
semanticSimilarity: match.similarity,
|
|
57
|
-
emotionalSimilarity,
|
|
58
|
-
timeDeltaDays: Math.floor((Date.now() - new Date(match.created_at).getTime()) / 86400000),
|
|
59
|
-
priorCreatedAt: match.created_at,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return resonances;
|
|
64
|
-
}
|
|
1
|
+
export function arousalSalienceBoost(arousal) {
|
|
2
|
+
if (arousal === undefined || arousal === null) return 0;
|
|
3
|
+
// Inverted-U (Yerkes-Dodson): peaks at 0.7, Gaussian sigma=0.3
|
|
4
|
+
return Math.exp(-Math.pow(arousal - 0.7, 2) / (2 * 0.3 * 0.3));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function affectSimilarity(a, b) {
|
|
8
|
+
if (!a || !b) return 0;
|
|
9
|
+
if (a.valence === undefined || b.valence === undefined) return 0;
|
|
10
|
+
const valenceDist = Math.abs(a.valence - b.valence);
|
|
11
|
+
const valenceSim = 1.0 - (valenceDist / 2.0);
|
|
12
|
+
if (a.arousal === undefined || b.arousal === undefined) return valenceSim;
|
|
13
|
+
const arousalSim = 1.0 - Math.abs(a.arousal - b.arousal);
|
|
14
|
+
// Valence is primary (70%), arousal secondary (30%) per Bower 1981
|
|
15
|
+
return 0.7 * valenceSim + 0.3 * arousalSim;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function moodCongruenceModifier(encodingAffect, retrievalMood, weight = 0.2) {
|
|
19
|
+
if (!encodingAffect || !retrievalMood) return 1.0;
|
|
20
|
+
const similarity = affectSimilarity(encodingAffect, retrievalMood);
|
|
21
|
+
if (similarity === 0) return 1.0;
|
|
22
|
+
return 1.0 + (weight * similarity);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function detectResonance(db, embeddingProvider, episodeId, { content, affect }, config = {}) {
|
|
26
|
+
const { enabled = true, k = 5, threshold = 0.5, affectThreshold = 0.6 } = config;
|
|
27
|
+
if (!enabled || !affect || affect.valence === undefined) return [];
|
|
28
|
+
|
|
29
|
+
const vector = await embeddingProvider.embed(content);
|
|
30
|
+
const buffer = embeddingProvider.vectorToBuffer(vector);
|
|
31
|
+
|
|
32
|
+
const matches = db.prepare(`
|
|
33
|
+
SELECT e.*, (1.0 - v.distance) AS similarity
|
|
34
|
+
FROM vec_episodes v
|
|
35
|
+
JOIN episodes e ON e.id = v.id
|
|
36
|
+
WHERE v.embedding MATCH ?
|
|
37
|
+
AND k = ?
|
|
38
|
+
AND e.id != ?
|
|
39
|
+
AND e.superseded_by IS NULL
|
|
40
|
+
`).all(buffer, k, episodeId);
|
|
41
|
+
|
|
42
|
+
const resonances = [];
|
|
43
|
+
for (const match of matches) {
|
|
44
|
+
if (match.similarity < threshold) continue;
|
|
45
|
+
let priorAffect;
|
|
46
|
+
try { priorAffect = JSON.parse(match.affect || '{}'); } catch { continue; }
|
|
47
|
+
if (priorAffect.valence === undefined) continue;
|
|
48
|
+
|
|
49
|
+
const emotionalSimilarity = affectSimilarity(affect, priorAffect);
|
|
50
|
+
if (emotionalSimilarity < affectThreshold) continue;
|
|
51
|
+
|
|
52
|
+
resonances.push({
|
|
53
|
+
priorEpisodeId: match.id,
|
|
54
|
+
priorContent: match.content,
|
|
55
|
+
priorAffect,
|
|
56
|
+
semanticSimilarity: match.similarity,
|
|
57
|
+
emotionalSimilarity,
|
|
58
|
+
timeDeltaDays: Math.floor((Date.now() - new Date(match.created_at).getTime()) / 86400000),
|
|
59
|
+
priorCreatedAt: match.created_at,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return resonances;
|
|
64
|
+
}
|