audrey 0.9.0 → 0.11.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/mcp-server/config.js +30 -16
- package/mcp-server/index.js +33 -5
- package/package.json +2 -1
- package/src/affect.js +1 -1
- package/src/audrey.js +49 -1
- package/src/db.js +321 -282
- package/src/embedding.js +90 -53
- package/src/encode.js +63 -61
- package/src/export.js +5 -3
- package/src/import.js +15 -8
- package/src/migrate.js +27 -9
- package/src/prompts.js +43 -0
- package/src/recall.js +13 -14
package/mcp-server/config.js
CHANGED
|
@@ -1,26 +1,40 @@
|
|
|
1
1
|
import { homedir } from 'node:os';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
|
|
4
|
-
export const VERSION = '0.
|
|
4
|
+
export const VERSION = '0.11.0';
|
|
5
5
|
export const SERVER_NAME = 'audrey-memory';
|
|
6
6
|
export const DEFAULT_DATA_DIR = join(homedir(), '.audrey', 'data');
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Resolves which embedding provider to use.
|
|
10
|
+
* Priority: explicit config -> gemini (if GOOGLE_API_KEY exists) -> local
|
|
11
|
+
* OpenAI is NEVER auto-selected -- must be set explicitly via AUDREY_EMBEDDING_PROVIDER=openai.
|
|
12
|
+
*/
|
|
13
|
+
export function resolveEmbeddingProvider(env, explicit) {
|
|
14
|
+
if (explicit && explicit !== 'auto') {
|
|
15
|
+
const dims = explicit === 'openai' ? 1536 : explicit === 'gemini' ? 768 : 384;
|
|
16
|
+
const apiKey = explicit === 'gemini'
|
|
17
|
+
? (env.GOOGLE_API_KEY || env.GEMINI_API_KEY)
|
|
18
|
+
: explicit === 'openai'
|
|
19
|
+
? env.OPENAI_API_KEY
|
|
20
|
+
: undefined;
|
|
21
|
+
return { provider: explicit, apiKey, dimensions: dims };
|
|
22
|
+
}
|
|
23
|
+
if (env.GOOGLE_API_KEY || env.GEMINI_API_KEY) {
|
|
24
|
+
return { provider: 'gemini', apiKey: env.GOOGLE_API_KEY || env.GEMINI_API_KEY, dimensions: 768 };
|
|
25
|
+
}
|
|
26
|
+
return { provider: 'local', dimensions: 384 };
|
|
27
|
+
}
|
|
28
|
+
|
|
8
29
|
export function buildAudreyConfig() {
|
|
9
30
|
const dataDir = process.env.AUDREY_DATA_DIR || DEFAULT_DATA_DIR;
|
|
10
31
|
const agent = process.env.AUDREY_AGENT || 'claude-code';
|
|
11
|
-
const
|
|
12
|
-
const embDimensions = parseInt(process.env.AUDREY_EMBEDDING_DIMENSIONS || '8', 10);
|
|
32
|
+
const explicitProvider = process.env.AUDREY_EMBEDDING_PROVIDER;
|
|
13
33
|
const llmProvider = process.env.AUDREY_LLM_PROVIDER;
|
|
14
34
|
|
|
15
|
-
const
|
|
16
|
-
dataDir,
|
|
17
|
-
agent,
|
|
18
|
-
embedding: { provider: embProvider, dimensions: embDimensions },
|
|
19
|
-
};
|
|
35
|
+
const embedding = resolveEmbeddingProvider(process.env, explicitProvider);
|
|
20
36
|
|
|
21
|
-
|
|
22
|
-
config.embedding.apiKey = process.env.OPENAI_API_KEY;
|
|
23
|
-
}
|
|
37
|
+
const config = { dataDir, agent, embedding };
|
|
24
38
|
|
|
25
39
|
if (llmProvider === 'anthropic') {
|
|
26
40
|
config.llm = { provider: 'anthropic', apiKey: process.env.ANTHROPIC_API_KEY };
|
|
@@ -36,13 +50,13 @@ export function buildAudreyConfig() {
|
|
|
36
50
|
export function buildInstallArgs(env = process.env) {
|
|
37
51
|
const envPairs = [`AUDREY_DATA_DIR=${DEFAULT_DATA_DIR}`];
|
|
38
52
|
|
|
39
|
-
|
|
53
|
+
const embedding = resolveEmbeddingProvider(env);
|
|
54
|
+
if (embedding.provider === 'gemini') {
|
|
55
|
+
envPairs.push('AUDREY_EMBEDDING_PROVIDER=gemini');
|
|
56
|
+
envPairs.push(`GOOGLE_API_KEY=${embedding.apiKey}`);
|
|
57
|
+
} else if (embedding.provider === 'openai') {
|
|
40
58
|
envPairs.push('AUDREY_EMBEDDING_PROVIDER=openai');
|
|
41
|
-
envPairs.push('AUDREY_EMBEDDING_DIMENSIONS=1536');
|
|
42
59
|
envPairs.push(`OPENAI_API_KEY=${env.OPENAI_API_KEY}`);
|
|
43
|
-
} else {
|
|
44
|
-
envPairs.push('AUDREY_EMBEDDING_PROVIDER=mock');
|
|
45
|
-
envPairs.push('AUDREY_EMBEDDING_DIMENSIONS=8');
|
|
46
60
|
}
|
|
47
61
|
|
|
48
62
|
if (env.ANTHROPIC_API_KEY) {
|
package/mcp-server/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { z } from 'zod';
|
|
@@ -8,7 +8,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
8
8
|
import { execFileSync } from 'node:child_process';
|
|
9
9
|
import { Audrey } from '../src/index.js';
|
|
10
10
|
import { readStoredDimensions } from '../src/db.js';
|
|
11
|
-
import { VERSION, SERVER_NAME, DEFAULT_DATA_DIR, buildAudreyConfig, buildInstallArgs } from './config.js';
|
|
11
|
+
import { VERSION, SERVER_NAME, DEFAULT_DATA_DIR, buildAudreyConfig, buildInstallArgs, resolveEmbeddingProvider } from './config.js';
|
|
12
12
|
|
|
13
13
|
const VALID_SOURCES = ['direct-observation', 'told-by-user', 'tool-result', 'inference', 'model-generated'];
|
|
14
14
|
const VALID_TYPES = ['episodic', 'semantic', 'procedural'];
|
|
@@ -19,6 +19,11 @@ if (subcommand === 'install') {
|
|
|
19
19
|
install();
|
|
20
20
|
} else if (subcommand === 'uninstall') {
|
|
21
21
|
uninstall();
|
|
22
|
+
} else if (subcommand === 'reembed') {
|
|
23
|
+
reembed().catch(err => {
|
|
24
|
+
console.error('[audrey] reembed failed:', err);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
});
|
|
22
27
|
} else if (subcommand === 'status') {
|
|
23
28
|
status();
|
|
24
29
|
} else {
|
|
@@ -28,6 +33,28 @@ if (subcommand === 'install') {
|
|
|
28
33
|
});
|
|
29
34
|
}
|
|
30
35
|
|
|
36
|
+
|
|
37
|
+
async function reembed() {
|
|
38
|
+
const dataDir = process.env.AUDREY_DATA_DIR || DEFAULT_DATA_DIR;
|
|
39
|
+
const explicit = process.env.AUDREY_EMBEDDING_PROVIDER;
|
|
40
|
+
const embedding = resolveEmbeddingProvider(process.env, explicit);
|
|
41
|
+
|
|
42
|
+
const storedDims = readStoredDimensions(dataDir);
|
|
43
|
+
const dimensionsChanged = storedDims !== null && storedDims !== embedding.dimensions;
|
|
44
|
+
|
|
45
|
+
console.log(`Re-embedding with ${embedding.provider} (${embedding.dimensions}d)...`);
|
|
46
|
+
if (dimensionsChanged) {
|
|
47
|
+
console.log(`Dimension change: ${storedDims}d -> ${embedding.dimensions}d (will drop and recreate vec tables)`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const audrey = new Audrey({ dataDir, agent: 'reembed', embedding });
|
|
51
|
+
const { reembedAll } = await import('../src/migrate.js');
|
|
52
|
+
const counts = await reembedAll(audrey.db, audrey.embeddingProvider, { dropAndRecreate: dimensionsChanged });
|
|
53
|
+
audrey.close();
|
|
54
|
+
|
|
55
|
+
console.log(`Done. Re-embedded: ${counts.episodes} episodes, ${counts.semantics} semantics, ${counts.procedures} procedures`);
|
|
56
|
+
}
|
|
57
|
+
|
|
31
58
|
function install() {
|
|
32
59
|
try {
|
|
33
60
|
execFileSync('claude', ['--version'], { stdio: 'ignore' });
|
|
@@ -169,11 +196,12 @@ async function main() {
|
|
|
169
196
|
arousal: z.number().min(0).max(1).optional().describe('Emotional arousal: 0 (calm) to 1 (highly activated)'),
|
|
170
197
|
label: z.string().optional().describe('Human-readable emotion label (e.g., "curiosity", "frustration", "relief")'),
|
|
171
198
|
}).optional().describe('Emotional affect — how this memory feels'),
|
|
199
|
+
private: z.boolean().optional().describe('If true, memory is only visible to the AI � excluded from public recall results'),
|
|
172
200
|
},
|
|
173
|
-
async ({ content, source, tags, salience, context, affect }) => {
|
|
201
|
+
async ({ content, source, tags, salience, private: isPrivate, context, affect }) => {
|
|
174
202
|
try {
|
|
175
|
-
const id = await audrey.encode({ content, source, tags, salience, context, affect });
|
|
176
|
-
return toolResult({ id, content, source });
|
|
203
|
+
const id = await audrey.encode({ content, source, tags, salience, private: isPrivate, context, affect });
|
|
204
|
+
return toolResult({ id, content, source, private: isPrivate ?? false });
|
|
177
205
|
} catch (err) {
|
|
178
206
|
return toolError(err);
|
|
179
207
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "audrey",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Biological memory architecture for AI agents — encode, consolidate, and recall memories with confidence decay, contradiction detection, and causal graphs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
},
|
|
64
64
|
"license": "MIT",
|
|
65
65
|
"dependencies": {
|
|
66
|
+
"@huggingface/transformers": "^3.8.1",
|
|
66
67
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
67
68
|
"better-sqlite3": "^12.6.2",
|
|
68
69
|
"sqlite-vec": "^0.1.7-alpha.2",
|
package/src/affect.js
CHANGED
|
@@ -55,7 +55,7 @@ export async function detectResonance(db, embeddingProvider, episodeId, { conten
|
|
|
55
55
|
priorAffect,
|
|
56
56
|
semanticSimilarity: match.similarity,
|
|
57
57
|
emotionalSimilarity,
|
|
58
|
-
|
|
58
|
+
timeDeltaDays: Math.floor((Date.now() - new Date(match.created_at).getTime()) / 86400000),
|
|
59
59
|
priorCreatedAt: match.created_at,
|
|
60
60
|
});
|
|
61
61
|
}
|
package/src/audrey.js
CHANGED
|
@@ -10,7 +10,7 @@ import { applyDecay } from './decay.js';
|
|
|
10
10
|
import { rollbackConsolidation, getConsolidationHistory } from './rollback.js';
|
|
11
11
|
import { forgetMemory, forgetByQuery as forgetByQueryFn, purgeMemories } from './forget.js';
|
|
12
12
|
import { introspect as introspectFn } from './introspect.js';
|
|
13
|
-
import { buildContextResolutionPrompt } from './prompts.js';
|
|
13
|
+
import { buildContextResolutionPrompt, buildReflectionPrompt } from './prompts.js';
|
|
14
14
|
import { exportMemories } from './export.js';
|
|
15
15
|
import { importMemories } from './import.js';
|
|
16
16
|
import { suggestConsolidationParams as suggestParamsFn } from './adaptive.js';
|
|
@@ -29,6 +29,8 @@ import { detectResonance } from './affect.js';
|
|
|
29
29
|
* @property {{ trigger?: string, consequence?: string }} [causal]
|
|
30
30
|
* @property {string[]} [tags]
|
|
31
31
|
* @property {string} [supersedes]
|
|
32
|
+
* @property {Record<string, string>} [context]
|
|
33
|
+
* @property {{ valence?: number, arousal?: number, label?: string }} [affect]
|
|
32
34
|
*
|
|
33
35
|
* @typedef {Object} RecallOptions
|
|
34
36
|
* @property {number} [minConfidence]
|
|
@@ -40,6 +42,8 @@ import { detectResonance } from './affect.js';
|
|
|
40
42
|
* @property {string[]} [sources]
|
|
41
43
|
* @property {string} [after]
|
|
42
44
|
* @property {string} [before]
|
|
45
|
+
* @property {Record<string, string>} [context]
|
|
46
|
+
* @property {{ valence?: number, arousal?: number }} [mood]
|
|
43
47
|
*
|
|
44
48
|
* @typedef {Object} RecallResult
|
|
45
49
|
* @property {string} id
|
|
@@ -94,6 +98,7 @@ export class Audrey extends EventEmitter {
|
|
|
94
98
|
interference = {},
|
|
95
99
|
context = {},
|
|
96
100
|
affect = {},
|
|
101
|
+
autoReflect = false,
|
|
97
102
|
} = {}) {
|
|
98
103
|
super();
|
|
99
104
|
|
|
@@ -148,6 +153,7 @@ export class Audrey extends EventEmitter {
|
|
|
148
153
|
affectThreshold: affect.resonance?.affectThreshold ?? 0.6,
|
|
149
154
|
},
|
|
150
155
|
};
|
|
156
|
+
this.autoReflect = autoReflect;
|
|
151
157
|
}
|
|
152
158
|
|
|
153
159
|
async _ensureMigrated() {
|
|
@@ -212,6 +218,48 @@ export class Audrey extends EventEmitter {
|
|
|
212
218
|
return id;
|
|
213
219
|
}
|
|
214
220
|
|
|
221
|
+
|
|
222
|
+
async reflect(turns) {
|
|
223
|
+
if (!this.llmProvider) return { encoded: 0, memories: [], skipped: 'no llm provider' };
|
|
224
|
+
|
|
225
|
+
const prompt = buildReflectionPrompt(turns);
|
|
226
|
+
let raw;
|
|
227
|
+
try {
|
|
228
|
+
raw = await this.llmProvider.chat(prompt);
|
|
229
|
+
} catch (err) {
|
|
230
|
+
this.emit('error', err);
|
|
231
|
+
return { encoded: 0, memories: [], skipped: 'llm error' };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let parsed;
|
|
235
|
+
try {
|
|
236
|
+
parsed = JSON.parse(raw);
|
|
237
|
+
} catch {
|
|
238
|
+
return { encoded: 0, memories: [], skipped: 'invalid llm response' };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const memories = parsed.memories ?? [];
|
|
242
|
+
let encoded = 0;
|
|
243
|
+
for (const mem of memories) {
|
|
244
|
+
if (!mem.content || !mem.source) continue;
|
|
245
|
+
try {
|
|
246
|
+
await this.encode({
|
|
247
|
+
content: mem.content,
|
|
248
|
+
source: mem.source,
|
|
249
|
+
salience: mem.salience,
|
|
250
|
+
tags: mem.tags,
|
|
251
|
+
private: mem.private ?? false,
|
|
252
|
+
affect: mem.affect ?? undefined,
|
|
253
|
+
});
|
|
254
|
+
encoded++;
|
|
255
|
+
} catch (err) {
|
|
256
|
+
this.emit('error', err);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return { encoded, memories };
|
|
261
|
+
}
|
|
262
|
+
|
|
215
263
|
/**
|
|
216
264
|
* @param {EncodeParams[]} paramsList
|
|
217
265
|
* @returns {Promise<string[]>}
|