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.
@@ -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.9.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 embProvider = process.env.AUDREY_EMBEDDING_PROVIDER || 'mock';
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 config = {
16
- dataDir,
17
- agent,
18
- embedding: { provider: embProvider, dimensions: embDimensions },
19
- };
35
+ const embedding = resolveEmbeddingProvider(process.env, explicitProvider);
20
36
 
21
- if (embProvider === 'openai') {
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
- if (env.OPENAI_API_KEY) {
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) {
@@ -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.9.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
- timeDelta: Date.now() - new Date(match.created_at).getTime(),
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[]>}