audrey 0.17.0 → 0.20.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 (191) hide show
  1. package/README.md +129 -374
  2. package/dist/mcp-server/config.d.ts +20 -0
  3. package/dist/mcp-server/config.d.ts.map +1 -0
  4. package/dist/mcp-server/config.js +125 -0
  5. package/dist/mcp-server/config.js.map +1 -0
  6. package/dist/mcp-server/index.d.ts +100 -0
  7. package/dist/mcp-server/index.d.ts.map +1 -0
  8. package/dist/mcp-server/index.js +1113 -0
  9. package/dist/mcp-server/index.js.map +1 -0
  10. package/dist/src/adaptive.d.ts +7 -0
  11. package/dist/src/adaptive.d.ts.map +1 -0
  12. package/dist/src/adaptive.js +49 -0
  13. package/dist/src/adaptive.js.map +1 -0
  14. package/dist/src/affect.d.ts +19 -0
  15. package/dist/src/affect.d.ts.map +1 -0
  16. package/dist/src/affect.js +72 -0
  17. package/dist/src/affect.js.map +1 -0
  18. package/dist/src/audrey.d.ts +140 -0
  19. package/dist/src/audrey.d.ts.map +1 -0
  20. package/dist/src/audrey.js +564 -0
  21. package/dist/src/audrey.js.map +1 -0
  22. package/dist/src/capsule.d.ts +68 -0
  23. package/dist/src/capsule.d.ts.map +1 -0
  24. package/dist/src/capsule.js +311 -0
  25. package/dist/src/capsule.js.map +1 -0
  26. package/dist/src/causal.d.ts +28 -0
  27. package/dist/src/causal.d.ts.map +1 -0
  28. package/dist/src/causal.js +65 -0
  29. package/dist/src/causal.js.map +1 -0
  30. package/dist/src/confidence.d.ts +12 -0
  31. package/dist/src/confidence.d.ts.map +1 -0
  32. package/dist/src/confidence.js +63 -0
  33. package/dist/src/confidence.js.map +1 -0
  34. package/dist/src/consolidate.d.ts +8 -0
  35. package/dist/src/consolidate.d.ts.map +1 -0
  36. package/dist/src/consolidate.js +218 -0
  37. package/dist/src/consolidate.js.map +1 -0
  38. package/dist/src/context.d.ts +3 -0
  39. package/dist/src/context.d.ts.map +1 -0
  40. package/dist/src/context.js +19 -0
  41. package/dist/src/context.js.map +1 -0
  42. package/dist/src/db.d.ts +12 -0
  43. package/dist/src/db.d.ts.map +1 -0
  44. package/dist/src/db.js +380 -0
  45. package/dist/src/db.js.map +1 -0
  46. package/dist/src/decay.d.ts +7 -0
  47. package/dist/src/decay.d.ts.map +1 -0
  48. package/dist/src/decay.js +68 -0
  49. package/dist/src/decay.js.map +1 -0
  50. package/dist/src/embedding.d.ts +57 -0
  51. package/dist/src/embedding.d.ts.map +1 -0
  52. package/dist/src/embedding.js +254 -0
  53. package/dist/src/embedding.js.map +1 -0
  54. package/dist/src/encode.d.ts +15 -0
  55. package/dist/src/encode.d.ts.map +1 -0
  56. package/dist/src/encode.js +36 -0
  57. package/dist/src/encode.js.map +1 -0
  58. package/dist/src/events.d.ts +69 -0
  59. package/dist/src/events.d.ts.map +1 -0
  60. package/dist/src/events.js +149 -0
  61. package/dist/src/events.js.map +1 -0
  62. package/dist/src/export.d.ts +3 -0
  63. package/dist/src/export.d.ts.map +1 -0
  64. package/dist/src/export.js +46 -0
  65. package/dist/src/export.js.map +1 -0
  66. package/dist/src/forget.d.ts +11 -0
  67. package/dist/src/forget.d.ts.map +1 -0
  68. package/dist/src/forget.js +105 -0
  69. package/dist/src/forget.js.map +1 -0
  70. package/dist/src/fts.d.ts +34 -0
  71. package/dist/src/fts.d.ts.map +1 -0
  72. package/dist/src/fts.js +117 -0
  73. package/dist/src/fts.js.map +1 -0
  74. package/dist/src/hybrid-recall.d.ts +37 -0
  75. package/dist/src/hybrid-recall.d.ts.map +1 -0
  76. package/dist/src/hybrid-recall.js +213 -0
  77. package/dist/src/hybrid-recall.js.map +1 -0
  78. package/dist/src/import.d.ts +4 -0
  79. package/dist/src/import.d.ts.map +1 -0
  80. package/dist/src/import.js +127 -0
  81. package/dist/src/import.js.map +1 -0
  82. package/dist/src/index.d.ts +22 -0
  83. package/dist/src/index.d.ts.map +1 -0
  84. package/{src → dist/src}/index.js +5 -13
  85. package/dist/src/index.js.map +1 -0
  86. package/dist/src/interference.d.ts +13 -0
  87. package/dist/src/interference.d.ts.map +1 -0
  88. package/dist/src/interference.js +45 -0
  89. package/dist/src/interference.js.map +1 -0
  90. package/dist/src/introspect.d.ts +4 -0
  91. package/dist/src/introspect.d.ts.map +1 -0
  92. package/dist/src/introspect.js +40 -0
  93. package/dist/src/introspect.js.map +1 -0
  94. package/dist/src/llm.d.ts +38 -0
  95. package/dist/src/llm.d.ts.map +1 -0
  96. package/dist/src/llm.js +167 -0
  97. package/dist/src/llm.js.map +1 -0
  98. package/dist/src/migrate.d.ts +6 -0
  99. package/dist/src/migrate.d.ts.map +1 -0
  100. package/dist/src/migrate.js +51 -0
  101. package/dist/src/migrate.js.map +1 -0
  102. package/dist/src/promote.d.ts +40 -0
  103. package/dist/src/promote.d.ts.map +1 -0
  104. package/dist/src/promote.js +200 -0
  105. package/dist/src/promote.js.map +1 -0
  106. package/dist/src/prompts.d.ts +16 -0
  107. package/dist/src/prompts.d.ts.map +1 -0
  108. package/{src → dist/src}/prompts.js +172 -203
  109. package/dist/src/prompts.js.map +1 -0
  110. package/dist/src/recall.d.ts +9 -0
  111. package/dist/src/recall.d.ts.map +1 -0
  112. package/dist/src/recall.js +432 -0
  113. package/dist/src/recall.js.map +1 -0
  114. package/dist/src/redact.d.ts +27 -0
  115. package/dist/src/redact.d.ts.map +1 -0
  116. package/dist/src/redact.js +228 -0
  117. package/dist/src/redact.js.map +1 -0
  118. package/dist/src/rollback.d.ts +8 -0
  119. package/dist/src/rollback.d.ts.map +1 -0
  120. package/dist/src/rollback.js +33 -0
  121. package/dist/src/rollback.js.map +1 -0
  122. package/dist/src/routes.d.ts +7 -0
  123. package/dist/src/routes.d.ts.map +1 -0
  124. package/dist/src/routes.js +226 -0
  125. package/dist/src/routes.js.map +1 -0
  126. package/dist/src/rules-compiler.d.ts +20 -0
  127. package/dist/src/rules-compiler.d.ts.map +1 -0
  128. package/dist/src/rules-compiler.js +143 -0
  129. package/dist/src/rules-compiler.js.map +1 -0
  130. package/dist/src/server.d.ts +12 -0
  131. package/dist/src/server.d.ts.map +1 -0
  132. package/dist/src/server.js +22 -0
  133. package/dist/src/server.js.map +1 -0
  134. package/dist/src/tool-trace.d.ts +37 -0
  135. package/dist/src/tool-trace.d.ts.map +1 -0
  136. package/dist/src/tool-trace.js +142 -0
  137. package/dist/src/tool-trace.js.map +1 -0
  138. package/dist/src/types.d.ts +446 -0
  139. package/dist/src/types.d.ts.map +1 -0
  140. package/dist/src/types.js +6 -0
  141. package/dist/src/types.js.map +1 -0
  142. package/dist/src/ulid.d.ts +3 -0
  143. package/dist/src/ulid.d.ts.map +1 -0
  144. package/dist/src/ulid.js +11 -0
  145. package/dist/src/ulid.js.map +1 -0
  146. package/dist/src/utils.d.ts +10 -0
  147. package/dist/src/utils.d.ts.map +1 -0
  148. package/dist/src/utils.js +41 -0
  149. package/dist/src/utils.js.map +1 -0
  150. package/dist/src/validate.d.ts +22 -0
  151. package/dist/src/validate.d.ts.map +1 -0
  152. package/dist/src/validate.js +109 -0
  153. package/dist/src/validate.js.map +1 -0
  154. package/docs/production-readiness.md +28 -0
  155. package/examples/fintech-ops-demo.js +1 -1
  156. package/examples/healthcare-ops-demo.js +1 -1
  157. package/examples/stripe-demo.js +1 -1
  158. package/package.json +34 -13
  159. package/benchmarks/baselines.js +0 -169
  160. package/benchmarks/cases.js +0 -421
  161. package/benchmarks/reference-results.js +0 -70
  162. package/benchmarks/report.js +0 -255
  163. package/benchmarks/run.js +0 -514
  164. package/mcp-server/config.js +0 -133
  165. package/mcp-server/index.js +0 -1265
  166. package/mcp-server/serve.js +0 -482
  167. package/src/adaptive.js +0 -53
  168. package/src/affect.js +0 -64
  169. package/src/audrey.js +0 -642
  170. package/src/causal.js +0 -95
  171. package/src/confidence.js +0 -120
  172. package/src/consolidate.js +0 -281
  173. package/src/context.js +0 -15
  174. package/src/db.js +0 -391
  175. package/src/decay.js +0 -84
  176. package/src/embedding.js +0 -260
  177. package/src/encode.js +0 -69
  178. package/src/export.js +0 -67
  179. package/src/forget.js +0 -111
  180. package/src/fts.js +0 -134
  181. package/src/import.js +0 -273
  182. package/src/interference.js +0 -51
  183. package/src/introspect.js +0 -48
  184. package/src/llm.js +0 -249
  185. package/src/migrate.js +0 -58
  186. package/src/recall.js +0 -573
  187. package/src/rollback.js +0 -42
  188. package/src/ulid.js +0 -18
  189. package/src/utils.js +0 -63
  190. package/src/validate.js +0 -172
  191. package/types/index.d.ts +0 -434
@@ -1,1265 +0,0 @@
1
- #!/usr/bin/env node
2
- import { z } from 'zod';
3
- import { homedir } from 'node:os';
4
- import { join, resolve } from 'node:path';
5
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
6
- import { execFileSync } from 'node:child_process';
7
- import { fileURLToPath } from 'node:url';
8
- import { Audrey } from '../src/index.js';
9
- import { readStoredDimensions } from '../src/db.js';
10
- import {
11
- VERSION,
12
- SERVER_NAME,
13
- buildAudreyConfig,
14
- buildInstallArgs,
15
- resolveDataDir,
16
- resolveEmbeddingProvider,
17
- resolveLLMProvider,
18
- } from './config.js';
19
-
20
- const VALID_SOURCES = ['direct-observation', 'told-by-user', 'tool-result', 'inference', 'model-generated'];
21
- const VALID_TYPES = ['episodic', 'semantic', 'procedural'];
22
-
23
- export const MAX_MEMORY_CONTENT_LENGTH = 50_000;
24
-
25
- const subcommand = process.argv[2];
26
-
27
- function isNonEmptyText(value) {
28
- return typeof value === 'string' && value.trim().length > 0;
29
- }
30
-
31
- export function validateMemoryContent(content) {
32
- if (!isNonEmptyText(content)) {
33
- throw new Error('content must be a non-empty string');
34
- }
35
- if (content.length > MAX_MEMORY_CONTENT_LENGTH) {
36
- throw new Error(`content exceeds maximum length of ${MAX_MEMORY_CONTENT_LENGTH} characters`);
37
- }
38
- }
39
-
40
- export function validateForgetSelection(id, query) {
41
- if ((id && query) || (!id && !query)) {
42
- throw new Error('Provide exactly one of id or query');
43
- }
44
- }
45
-
46
- export async function initializeEmbeddingProvider(provider) {
47
- if (provider && typeof provider.ready === 'function') {
48
- await provider.ready();
49
- }
50
- }
51
-
52
- async function closeAudreyGracefully(audrey) {
53
- if (audrey && typeof audrey.waitForIdle === 'function') {
54
- await audrey.waitForIdle();
55
- }
56
- audrey?.close();
57
- }
58
-
59
- export const memoryEncodeToolSchema = {
60
- content: z.string()
61
- .max(MAX_MEMORY_CONTENT_LENGTH)
62
- .refine(isNonEmptyText, 'Content must not be empty')
63
- .describe('The memory content to encode'),
64
- source: z.enum(VALID_SOURCES).describe('Source type of the memory'),
65
- tags: z.array(z.string()).optional().describe('Optional tags for categorization'),
66
- salience: z.number().min(0).max(1).optional().describe('Importance weight 0-1'),
67
- context: z.record(z.string()).optional().describe('Situational context as key-value pairs (e.g., {task: "debugging", domain: "payments"})'),
68
- affect: z.object({
69
- valence: z.number().min(-1).max(1).describe('Emotional valence: -1 (very negative) to 1 (very positive)'),
70
- arousal: z.number().min(0).max(1).optional().describe('Emotional arousal: 0 (calm) to 1 (highly activated)'),
71
- label: z.string().optional().describe('Human-readable emotion label (e.g., "curiosity", "frustration", "relief")'),
72
- }).optional().describe('Emotional affect - how this memory feels'),
73
- private: z.boolean().optional().describe('If true, memory is only visible to the AI and excluded from public recall results'),
74
- };
75
-
76
- export const memoryRecallToolSchema = {
77
- query: z.string().describe('Search query to match against memories'),
78
- limit: z.number().min(1).max(50).optional().describe('Max results (default 10)'),
79
- types: z.array(z.enum(VALID_TYPES)).optional().describe('Memory types to search'),
80
- min_confidence: z.number().min(0).max(1).optional().describe('Minimum confidence threshold'),
81
- tags: z.array(z.string()).optional().describe('Only return episodic memories with these tags'),
82
- sources: z.array(z.enum(VALID_SOURCES)).optional().describe('Only return episodic memories from these sources'),
83
- after: z.string().optional().describe('Only return memories created after this ISO date'),
84
- before: z.string().optional().describe('Only return memories created before this ISO date'),
85
- context: z.record(z.string()).optional().describe('Retrieval context - memories encoded in matching context get boosted'),
86
- mood: z.object({
87
- valence: z.number().min(-1).max(1).describe('Current emotional valence: -1 (negative) to 1 (positive)'),
88
- arousal: z.number().min(0).max(1).optional().describe('Current arousal: 0 (calm) to 1 (activated)'),
89
- }).optional().describe('Current mood - boosts recall of memories encoded in similar emotional state'),
90
- };
91
-
92
- export const memoryImportToolSchema = {
93
- snapshot: z.object({
94
- version: z.string(),
95
- episodes: z.array(z.any()),
96
- semantics: z.array(z.any()).optional(),
97
- procedures: z.array(z.any()).optional(),
98
- causalLinks: z.array(z.any()).optional(),
99
- contradictions: z.array(z.any()).optional(),
100
- consolidationRuns: z.array(z.any()).optional(),
101
- consolidationMetrics: z.array(z.any()).optional(),
102
- config: z.record(z.string()).optional(),
103
- }).passthrough().describe('A snapshot from memory_export'),
104
- };
105
-
106
- export const memoryForgetToolSchema = {
107
- id: z.string().optional().describe('ID of the memory to forget'),
108
- query: z.string().optional().describe('Semantic query to find and forget the closest matching memory'),
109
- min_similarity: z.number().min(0).max(1).optional().describe('Minimum similarity for query-based forget (default 0.9)'),
110
- purge: z.boolean().optional().describe('Hard-delete the memory permanently (default false, soft-delete)'),
111
- };
112
-
113
- async function reembed() {
114
- const dataDir = resolveDataDir(process.env);
115
- const explicit = process.env.AUDREY_EMBEDDING_PROVIDER;
116
- const embedding = resolveEmbeddingProvider(process.env, explicit);
117
- const storedDims = readStoredDimensions(dataDir);
118
- const dimensionsChanged = storedDims !== null && storedDims !== embedding.dimensions;
119
-
120
- console.log(`Re-embedding with ${embedding.provider} (${embedding.dimensions}d)...`);
121
- if (dimensionsChanged) {
122
- console.log(`Dimension change: ${storedDims}d -> ${embedding.dimensions}d (will drop and recreate vec tables)`);
123
- }
124
-
125
- const audrey = new Audrey({ dataDir, agent: 'reembed', embedding });
126
- try {
127
- await initializeEmbeddingProvider(audrey.embeddingProvider);
128
- const { reembedAll } = await import('../src/migrate.js');
129
- const counts = await reembedAll(audrey.db, audrey.embeddingProvider, { dropAndRecreate: dimensionsChanged });
130
- console.log(`Done. Re-embedded: ${counts.episodes} episodes, ${counts.semantics} semantics, ${counts.procedures} procedures`);
131
- } finally {
132
- await closeAudreyGracefully(audrey);
133
- }
134
- }
135
-
136
- async function dream() {
137
- const dataDir = resolveDataDir(process.env);
138
- const explicit = process.env.AUDREY_EMBEDDING_PROVIDER;
139
- const embedding = resolveEmbeddingProvider(process.env, explicit);
140
- const storedDims = readStoredDimensions(dataDir);
141
-
142
- const config = {
143
- dataDir,
144
- agent: 'dream',
145
- embedding,
146
- };
147
-
148
- const llm = resolveLLMProvider(process.env, process.env.AUDREY_LLM_PROVIDER);
149
- if (llm) config.llm = llm;
150
-
151
- const audrey = new Audrey(config);
152
- try {
153
- await initializeEmbeddingProvider(audrey.embeddingProvider);
154
-
155
- const embeddingLabel = storedDims !== null && storedDims !== embedding.dimensions
156
- ? `${embedding.provider} (${embedding.dimensions}d; stored ${storedDims}d)`
157
- : `${embedding.provider} (${embedding.dimensions}d)`;
158
-
159
- console.log('[audrey] Starting dream cycle...');
160
- console.log(`[audrey] Embedding: ${embeddingLabel}`);
161
-
162
- const result = await audrey.dream();
163
- const health = audrey.memoryStatus();
164
-
165
- console.log(
166
- `[audrey] Consolidation: evaluated ${result.consolidation.episodesEvaluated} episodes, `
167
- + `found ${result.consolidation.clustersFound} clusters, extracted ${result.consolidation.principlesExtracted} principles `
168
- + `(${result.consolidation.semanticsCreated ?? 0} semantic, ${result.consolidation.proceduresCreated ?? 0} procedural)`
169
- );
170
- console.log(
171
- `[audrey] Decay: evaluated ${result.decay.totalEvaluated} memories, `
172
- + `${result.decay.transitionedToDormant} transitioned to dormant`
173
- );
174
- console.log(
175
- `[audrey] Final: ${result.stats.episodic} episodic, ${result.stats.semantic} semantic, ${result.stats.procedural} procedural `
176
- + `| ${health.healthy ? 'healthy' : 'unhealthy'}`
177
- );
178
- console.log('[audrey] Dream complete.');
179
- } finally {
180
- await closeAudreyGracefully(audrey);
181
- }
182
- }
183
-
184
- async function greeting() {
185
- const dataDir = resolveDataDir(process.env);
186
- const contextArg = process.argv[3] || undefined;
187
-
188
- if (!existsSync(dataDir)) {
189
- console.log('[audrey] No data yet - fresh start.');
190
- return;
191
- }
192
-
193
- const storedDimensions = readStoredDimensions(dataDir);
194
- const resolvedEmbedding = resolveEmbeddingProvider(process.env, process.env.AUDREY_EMBEDDING_PROVIDER);
195
- const canUseResolvedEmbedding = Boolean(contextArg)
196
- && storedDimensions !== null
197
- && storedDimensions === resolvedEmbedding.dimensions;
198
- const dimensions = storedDimensions || resolvedEmbedding.dimensions || 8;
199
- const audrey = new Audrey({
200
- dataDir,
201
- agent: 'greeting',
202
- embedding: canUseResolvedEmbedding
203
- ? resolvedEmbedding
204
- : { provider: 'mock', dimensions },
205
- });
206
-
207
- try {
208
- if (canUseResolvedEmbedding) {
209
- await initializeEmbeddingProvider(audrey.embeddingProvider);
210
- }
211
- const result = await audrey.greeting({ context: canUseResolvedEmbedding ? contextArg : undefined });
212
- const health = audrey.memoryStatus();
213
-
214
- const lines = [];
215
- lines.push(`[Audrey v${VERSION}] Memory briefing`);
216
- lines.push('');
217
-
218
- if (contextArg && !canUseResolvedEmbedding) {
219
- lines.push(
220
- `Context recall skipped: stored index is ${storedDimensions ?? 'unknown'}d `
221
- + `but current embedding config resolves to ${resolvedEmbedding.dimensions}d.`
222
- );
223
- lines.push('');
224
- }
225
-
226
- // Mood
227
- if (result.mood && result.mood.samples > 0) {
228
- const v = result.mood.valence;
229
- const moodWord = v > 0.3 ? 'positive' : v < -0.3 ? 'negative' : 'neutral';
230
- lines.push(`Mood: ${moodWord} (valence=${v.toFixed(2)}, arousal=${result.mood.arousal.toFixed(2)}, from ${result.mood.samples} recent memories)`);
231
- }
232
-
233
- // Health
234
- const stats = audrey.introspect();
235
- lines.push(`Memory: ${stats.episodic} episodic, ${stats.semantic} semantic, ${stats.procedural} procedural | ${health.healthy ? 'healthy' : 'needs attention'}`);
236
- lines.push('');
237
-
238
- // Principles (semantic memories)
239
- if (result.principles?.length > 0) {
240
- lines.push('Learned principles:');
241
- for (const p of result.principles) {
242
- lines.push(` - ${p.content}`);
243
- }
244
- lines.push('');
245
- }
246
-
247
- // Identity (private memories)
248
- if (result.identity?.length > 0) {
249
- lines.push('Identity:');
250
- for (const m of result.identity) {
251
- lines.push(` - ${m.content}`);
252
- }
253
- lines.push('');
254
- }
255
-
256
- // Recent memories
257
- if (result.recent?.length > 0) {
258
- lines.push('Recent memories:');
259
- for (const r of result.recent) {
260
- const age = timeSince(r.created_at);
261
- lines.push(` - [${age}] ${r.content.slice(0, 200)}`);
262
- }
263
- lines.push('');
264
- }
265
-
266
- // Unresolved
267
- if (result.unresolved?.length > 0) {
268
- lines.push('Unresolved threads:');
269
- for (const u of result.unresolved) {
270
- lines.push(` - ${u.content.slice(0, 150)}`);
271
- }
272
- lines.push('');
273
- }
274
-
275
- // Contextual recall
276
- if (result.contextual?.length > 0) {
277
- lines.push(`Context-relevant memories (query: "${contextArg}"):`);
278
- for (const c of result.contextual) {
279
- lines.push(` - [${c.type}] ${c.content.slice(0, 200)}`);
280
- }
281
- lines.push('');
282
- }
283
-
284
- console.log(lines.join('\n'));
285
- } finally {
286
- await closeAudreyGracefully(audrey);
287
- }
288
- }
289
-
290
- function timeSince(isoDate) {
291
- const ms = Date.now() - new Date(isoDate).getTime();
292
- const mins = Math.floor(ms / 60000);
293
- if (mins < 60) return `${mins}m ago`;
294
- const hours = Math.floor(mins / 60);
295
- if (hours < 24) return `${hours}h ago`;
296
- const days = Math.floor(hours / 24);
297
- return `${days}d ago`;
298
- }
299
-
300
- async function reflect() {
301
- const dataDir = resolveDataDir(process.env);
302
- const explicit = process.env.AUDREY_EMBEDDING_PROVIDER;
303
- const embedding = resolveEmbeddingProvider(process.env, explicit);
304
-
305
- const config = {
306
- dataDir,
307
- agent: 'reflect',
308
- embedding,
309
- };
310
-
311
- const llm = resolveLLMProvider(process.env, process.env.AUDREY_LLM_PROVIDER);
312
- if (llm) config.llm = llm;
313
-
314
- const audrey = new Audrey(config);
315
- try {
316
- await initializeEmbeddingProvider(audrey.embeddingProvider);
317
-
318
- // Read conversation turns from stdin if available
319
- let turns = null;
320
- if (!process.stdin.isTTY) {
321
- const chunks = [];
322
- for await (const chunk of process.stdin) {
323
- chunks.push(chunk);
324
- }
325
- const raw = Buffer.concat(chunks).toString('utf-8').trim();
326
- if (raw) {
327
- try {
328
- turns = JSON.parse(raw);
329
- } catch {
330
- console.error('[audrey] Could not parse stdin as JSON turns, skipping reflect.');
331
- }
332
- }
333
- }
334
-
335
- if (turns && Array.isArray(turns) && turns.length > 0) {
336
- console.log(`[audrey] Reflecting on ${turns.length} conversation turns...`);
337
- const reflectResult = await audrey.reflect(turns);
338
- if (reflectResult.skipped) {
339
- console.log(`[audrey] Reflect skipped: ${reflectResult.skipped}`);
340
- } else {
341
- console.log(`[audrey] Reflected: encoded ${reflectResult.encoded} lasting memories.`);
342
- }
343
- }
344
-
345
- // Always run dream cycle after reflect
346
- console.log('[audrey] Starting dream cycle...');
347
- const result = await audrey.dream();
348
- console.log(
349
- `[audrey] Consolidation: ${result.consolidation.episodesEvaluated} episodes evaluated, `
350
- + `${result.consolidation.clustersFound} clusters, ${result.consolidation.principlesExtracted} principles`
351
- );
352
- console.log(
353
- `[audrey] Decay: ${result.decay.totalEvaluated} evaluated, `
354
- + `${result.decay.transitionedToDormant} dormant`
355
- );
356
- console.log(
357
- `[audrey] Status: ${result.stats.episodic} episodic, ${result.stats.semantic} semantic, `
358
- + `${result.stats.procedural} procedural`
359
- );
360
- console.log('[audrey] Dream complete.');
361
- } finally {
362
- await closeAudreyGracefully(audrey);
363
- }
364
- }
365
-
366
- async function recall() {
367
- const dataDir = resolveDataDir(process.env);
368
-
369
- if (!existsSync(dataDir)) {
370
- // No data yet — nothing to recall
371
- process.exit(0);
372
- }
373
-
374
- // Read hook JSON from stdin
375
- let hookInput = null;
376
- if (!process.stdin.isTTY) {
377
- const chunks = [];
378
- for await (const chunk of process.stdin) {
379
- chunks.push(chunk);
380
- }
381
- const raw = Buffer.concat(chunks).toString('utf-8').trim();
382
- if (raw) {
383
- try {
384
- hookInput = JSON.parse(raw);
385
- } catch {
386
- console.error('[audrey] Could not parse stdin as JSON');
387
- process.exit(0);
388
- }
389
- }
390
- }
391
-
392
- // Extract query from hook input or CLI arg
393
- const query = hookInput?.prompt // UserPromptSubmit hook
394
- || hookInput?.query // direct query field
395
- || process.argv[3]; // CLI argument
396
-
397
- if (!query || typeof query !== 'string' || !query.trim()) {
398
- process.exit(0);
399
- }
400
-
401
- const storedDimensions = readStoredDimensions(dataDir);
402
- const resolvedEmbedding = resolveEmbeddingProvider(process.env, process.env.AUDREY_EMBEDDING_PROVIDER);
403
- const canEmbed = storedDimensions !== null && storedDimensions === resolvedEmbedding.dimensions;
404
-
405
- if (!canEmbed) {
406
- // Dimension mismatch — skip recall silently
407
- process.exit(0);
408
- }
409
-
410
- const audrey = new Audrey({
411
- dataDir,
412
- agent: 'recall-hook',
413
- embedding: resolvedEmbedding,
414
- });
415
-
416
- try {
417
- await initializeEmbeddingProvider(audrey.embeddingProvider);
418
-
419
- const limit = parseInt(process.argv[4], 10) || 5;
420
- const results = await audrey.recall(query.trim(), {
421
- limit,
422
- includePrivate: false,
423
- });
424
-
425
- if (!results || results.length === 0) {
426
- process.exit(0);
427
- }
428
-
429
- const lines = results.map(r => {
430
- const type = r.type === 'semantic' ? 'principle' : r.type === 'procedural' ? 'procedure' : 'memory';
431
- return `[${type}] ${r.content}`;
432
- });
433
-
434
- const output = {
435
- additionalContext: `Relevant memories from Audrey:\n\n${lines.join('\n\n')}`,
436
- };
437
-
438
- console.log(JSON.stringify(output));
439
- } finally {
440
- await closeAudreyGracefully(audrey);
441
- }
442
- }
443
-
444
- export function buildHooksConfig({ scope = 'user' } = {}) {
445
- const audreyBin = 'npx audrey';
446
-
447
- return {
448
- SessionStart: [
449
- {
450
- matcher: 'startup|resume',
451
- hooks: [
452
- {
453
- type: 'command',
454
- command: `${audreyBin} greeting`,
455
- timeout: 30,
456
- },
457
- ],
458
- },
459
- ],
460
- UserPromptSubmit: [
461
- {
462
- matcher: '',
463
- hooks: [
464
- {
465
- type: 'command',
466
- command: `${audreyBin} recall`,
467
- timeout: 15,
468
- },
469
- ],
470
- },
471
- ],
472
- Stop: [
473
- {
474
- matcher: '',
475
- hooks: [
476
- {
477
- type: 'command',
478
- command: `${audreyBin} reflect`,
479
- timeout: 120,
480
- },
481
- ],
482
- },
483
- ],
484
- PostCompact: [
485
- {
486
- matcher: '',
487
- hooks: [
488
- {
489
- type: 'command',
490
- command: `${audreyBin} greeting`,
491
- timeout: 30,
492
- },
493
- ],
494
- },
495
- ],
496
- };
497
- }
498
-
499
- function hooksInstall() {
500
- const settingsPath = join(homedir(), '.claude', 'settings.json');
501
- const settingsDir = join(homedir(), '.claude');
502
-
503
- let settings = {};
504
- if (existsSync(settingsPath)) {
505
- try {
506
- settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
507
- } catch {
508
- console.error(`[audrey] Could not parse ${settingsPath}. Please fix it manually.`);
509
- process.exit(1);
510
- }
511
- }
512
-
513
- const audreyHooks = buildHooksConfig();
514
-
515
- if (!settings.hooks) {
516
- settings.hooks = {};
517
- }
518
-
519
- // Merge Audrey hooks with existing hooks, preserving user's existing hooks
520
- for (const [event, audreyEntries] of Object.entries(audreyHooks)) {
521
- if (!settings.hooks[event]) {
522
- settings.hooks[event] = [];
523
- }
524
-
525
- // Remove any previously-installed Audrey hooks (by command match)
526
- settings.hooks[event] = settings.hooks[event].filter(entry => {
527
- if (!entry.hooks) return true;
528
- return !entry.hooks.some(h => h.command && h.command.includes('npx audrey'));
529
- });
530
-
531
- // Add Audrey hooks
532
- settings.hooks[event].push(...audreyEntries);
533
- }
534
-
535
- mkdirSync(settingsDir, { recursive: true });
536
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
537
-
538
- console.log(`[audrey] Hooks installed in ${settingsPath}
539
-
540
- Hooks configured:
541
- SessionStart → npx audrey greeting (load identity, principles, mood)
542
- UserPromptSubmit → npx audrey recall (semantic memory search per prompt)
543
- Stop → npx audrey reflect (consolidate learnings + dream cycle)
544
- PostCompact → npx audrey greeting (re-inject memories after compaction)
545
-
546
- Verify: Open ${settingsPath} or run claude /hooks
547
- `);
548
- }
549
-
550
- function hooksUninstall() {
551
- const settingsPath = join(homedir(), '.claude', 'settings.json');
552
-
553
- if (!existsSync(settingsPath)) {
554
- console.log('[audrey] No settings.json found. Nothing to remove.');
555
- return;
556
- }
557
-
558
- let settings;
559
- try {
560
- settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
561
- } catch {
562
- console.error(`[audrey] Could not parse ${settingsPath}.`);
563
- process.exit(1);
564
- }
565
-
566
- if (!settings.hooks) {
567
- console.log('[audrey] No hooks configured. Nothing to remove.');
568
- return;
569
- }
570
-
571
- let removed = 0;
572
- for (const event of Object.keys(settings.hooks)) {
573
- const before = settings.hooks[event].length;
574
- settings.hooks[event] = settings.hooks[event].filter(entry => {
575
- if (!entry.hooks) return true;
576
- return !entry.hooks.some(h => h.command && h.command.includes('npx audrey'));
577
- });
578
- removed += before - settings.hooks[event].length;
579
-
580
- // Clean up empty arrays
581
- if (settings.hooks[event].length === 0) {
582
- delete settings.hooks[event];
583
- }
584
- }
585
-
586
- // Clean up empty hooks object
587
- if (Object.keys(settings.hooks).length === 0) {
588
- delete settings.hooks;
589
- }
590
-
591
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
592
- console.log(`[audrey] Removed ${removed} hook(s) from ${settingsPath}`);
593
- }
594
-
595
- export function resolveSnapshotPath(outputArg, dataDir) {
596
- if (outputArg) return resolve(outputArg);
597
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '_').slice(0, 19);
598
- return resolve(dataDir, '..', `audrey-snapshot-${timestamp}.json`);
599
- }
600
-
601
- async function snapshot() {
602
- const dataDir = resolveDataDir(process.env);
603
-
604
- if (!existsSync(dataDir)) {
605
- console.error('[audrey] No data directory found. Nothing to snapshot.');
606
- process.exit(1);
607
- }
608
-
609
- const storedDimensions = readStoredDimensions(dataDir);
610
- const dimensions = storedDimensions || 8;
611
- const audrey = new Audrey({
612
- dataDir,
613
- agent: 'snapshot',
614
- embedding: { provider: 'mock', dimensions },
615
- });
616
-
617
- try {
618
- const data = audrey.export();
619
- const stats = audrey.introspect();
620
-
621
- const outputPath = resolveSnapshotPath(process.argv[3], dataDir);
622
-
623
- writeFileSync(outputPath, JSON.stringify(data, null, 2) + '\n');
624
-
625
- console.log(`[audrey] Snapshot saved to ${outputPath}`);
626
- console.log(` ${stats.episodic} episodes, ${stats.semantic} semantics, ${stats.procedural} procedures`);
627
- console.log(` ${data.contradictions?.length || 0} contradictions, ${data.causalLinks?.length || 0} causal links`);
628
- console.log(` Version: ${data.version}, exported at: ${data.exportedAt}`);
629
- console.log('');
630
- console.log('To restore: npx audrey restore ' + outputPath);
631
- } finally {
632
- await closeAudreyGracefully(audrey);
633
- }
634
- }
635
-
636
- async function restore() {
637
- const snapshotPath = process.argv[3];
638
- if (!snapshotPath) {
639
- console.error('Usage: npx audrey restore <snapshot-file>');
640
- console.error(' e.g.: npx audrey restore audrey-snapshot-2026-03-24.json');
641
- process.exit(1);
642
- }
643
-
644
- const resolvedPath = resolve(snapshotPath);
645
- if (!existsSync(resolvedPath)) {
646
- console.error(`[audrey] Snapshot file not found: ${resolvedPath}`);
647
- process.exit(1);
648
- }
649
-
650
- let data;
651
- try {
652
- data = JSON.parse(readFileSync(resolvedPath, 'utf-8'));
653
- } catch {
654
- console.error(`[audrey] Could not parse snapshot file: ${resolvedPath}`);
655
- process.exit(1);
656
- }
657
-
658
- if (!data.version || !data.episodes) {
659
- console.error('[audrey] Invalid snapshot: missing version or episodes field.');
660
- process.exit(1);
661
- }
662
-
663
- const dataDir = resolveDataDir(process.env);
664
- const explicit = process.env.AUDREY_EMBEDDING_PROVIDER;
665
- const embedding = resolveEmbeddingProvider(process.env, explicit);
666
-
667
- const audrey = new Audrey({ dataDir, agent: 'restore', embedding });
668
-
669
- try {
670
- await initializeEmbeddingProvider(audrey.embeddingProvider);
671
-
672
- const stats = audrey.introspect();
673
- const isEmpty = stats.episodic === 0 && stats.semantic === 0 && stats.procedural === 0;
674
-
675
- if (!isEmpty) {
676
- const force = process.argv.includes('--force');
677
- if (!force) {
678
- console.error('[audrey] Database is not empty. Use --force to purge and restore.');
679
- console.error(` Current: ${stats.episodic} episodes, ${stats.semantic} semantics, ${stats.procedural} procedures`);
680
- process.exit(1);
681
- }
682
- console.log('[audrey] --force: purging existing memories before restore...');
683
- audrey.purge();
684
- }
685
-
686
- console.log(`[audrey] Restoring from snapshot v${data.version} (${data.exportedAt || 'unknown date'})...`);
687
- console.log(`[audrey] Re-embedding with ${embedding.provider} (${embedding.dimensions}d)...`);
688
-
689
- await audrey.import(data);
690
-
691
- const restored = audrey.introspect();
692
- console.log(`[audrey] Restored: ${restored.episodic} episodes, ${restored.semantic} semantics, ${restored.procedural} procedures`);
693
- console.log('[audrey] Restore complete.');
694
- } finally {
695
- await closeAudreyGracefully(audrey);
696
- }
697
- }
698
-
699
- function install() {
700
- try {
701
- execFileSync('claude', ['--version'], { stdio: 'ignore' });
702
- } catch {
703
- console.error('Error: claude CLI not found. Install Claude Code first: https://docs.anthropic.com/en/docs/claude-code');
704
- process.exit(1);
705
- }
706
-
707
- const dataDir = resolveDataDir(process.env);
708
- const resolvedEmbedding = resolveEmbeddingProvider(process.env, process.env.AUDREY_EMBEDDING_PROVIDER);
709
- const resolvedLlm = resolveLLMProvider(process.env, process.env.AUDREY_LLM_PROVIDER);
710
- if (resolvedEmbedding.provider === 'gemini') {
711
- console.log('Using Gemini embeddings (3072d)');
712
- } else if (resolvedEmbedding.provider === 'local') {
713
- console.log(`Using local embeddings (384d, device=${resolvedEmbedding.device || 'gpu'})`);
714
- } else if (resolvedEmbedding.provider === 'openai') {
715
- console.log('Using OpenAI embeddings (1536d)');
716
- } else if (resolvedEmbedding.provider === 'mock') {
717
- console.log('Using mock embeddings');
718
- }
719
-
720
- if (resolvedLlm?.provider === 'anthropic') {
721
- console.log('Using Anthropic for LLM-powered consolidation, contradiction detection, and reflection');
722
- } else if (resolvedLlm?.provider === 'openai') {
723
- console.log('Using OpenAI for LLM-powered consolidation, contradiction detection, and reflection');
724
- } else if (resolvedLlm?.provider === 'mock') {
725
- console.log('Using mock LLM provider');
726
- } else {
727
- console.log('No LLM provider configured - consolidation and contradiction detection will use heuristics');
728
- }
729
-
730
- try {
731
- execFileSync('claude', ['mcp', 'remove', SERVER_NAME], { stdio: 'ignore' });
732
- } catch {
733
- // Not registered yet.
734
- }
735
-
736
- const args = buildInstallArgs(process.env);
737
- try {
738
- execFileSync('claude', args, { stdio: 'inherit' });
739
- } catch {
740
- console.error('Failed to register MCP server. Is Claude Code installed and on your PATH?');
741
- process.exit(1);
742
- }
743
-
744
- console.log(`
745
- Audrey registered as "${SERVER_NAME}" with Claude Code.
746
-
747
- 13 MCP tools available in every session:
748
- memory_encode - Store observations, facts, preferences
749
- memory_recall - Search memories by semantic similarity
750
- memory_consolidate - Extract principles from accumulated episodes
751
- memory_dream - Full sleep cycle: consolidate + decay + stats
752
- memory_introspect - Check memory system health
753
- memory_resolve_truth - Resolve contradictions between claims
754
- memory_export - Export all memories as JSON snapshot
755
- memory_import - Import a snapshot into a fresh database
756
- memory_forget - Forget a specific memory by ID or query
757
- memory_decay - Apply forgetting curves, transition low-confidence to dormant
758
- memory_status - Check brain health (episode/vec sync, dimensions)
759
- memory_reflect - Form lasting memories from a conversation
760
- memory_greeting - Wake up as yourself: load identity, context, mood
761
-
762
- CLI subcommands:
763
- npx audrey install - Register MCP server with Claude Code
764
- npx audrey uninstall - Remove MCP server registration
765
- npx audrey status - Show memory store health and stats
766
- npx audrey status --json - Emit machine-readable health output
767
- npx audrey status --json --fail-on-unhealthy - Exit non-zero on unhealthy status
768
- npx audrey greeting - Output session briefing (for hooks)
769
- npx audrey recall - Semantic recall for hook context injection
770
- npx audrey reflect - Reflect on conversation + dream cycle (for hooks)
771
- npx audrey dream - Run consolidation + decay cycle
772
- npx audrey reembed - Re-embed all memories with current provider
773
-
774
- Versioning (git-friendly memory snapshots):
775
- npx audrey snapshot [file] - Export memories to a JSON snapshot file
776
- npx audrey restore <file> - Restore memories from a snapshot (--force to overwrite)
777
-
778
- Hooks integration (automatic memory in every session):
779
- npx audrey hooks install - Add Audrey hooks to ~/.claude/settings.json
780
- npx audrey hooks uninstall - Remove Audrey hooks from settings
781
-
782
- REST API server (any language, any framework):
783
- npx audrey serve [port] - Start HTTP server (default: 3487)
784
- AUDREY_API_KEY=secret npx audrey serve - Start with Bearer token auth
785
- npx audrey dashboard - Start server and open memory dashboard
786
-
787
- Data stored in: ${dataDir}
788
- Verify: claude mcp list
789
- `);
790
- }
791
-
792
- function uninstall() {
793
- try {
794
- execFileSync('claude', ['--version'], { stdio: 'ignore' });
795
- } catch {
796
- console.error('Error: claude CLI not found.');
797
- process.exit(1);
798
- }
799
-
800
- try {
801
- execFileSync('claude', ['mcp', 'remove', SERVER_NAME], { stdio: 'inherit' });
802
- console.log(`Removed "${SERVER_NAME}" from Claude Code.`);
803
- } catch {
804
- console.error(`Failed to remove "${SERVER_NAME}". It may not be registered.`);
805
- process.exit(1);
806
- }
807
- }
808
-
809
- function cliHasFlag(flag, argv = process.argv) {
810
- return Array.isArray(argv) && argv.includes(flag);
811
- }
812
-
813
- export function buildStatusReport({
814
- dataDir = resolveDataDir(process.env),
815
- claudeJsonPath = join(homedir(), '.claude.json'),
816
- } = {}) {
817
- let registered = false;
818
- try {
819
- const claudeConfig = JSON.parse(readFileSync(claudeJsonPath, 'utf-8'));
820
- registered = SERVER_NAME in (claudeConfig.mcpServers || {});
821
- } catch {
822
- // Ignore unreadable config.
823
- }
824
-
825
- const report = {
826
- generatedAt: new Date().toISOString(),
827
- registered,
828
- dataDir,
829
- exists: existsSync(dataDir),
830
- storedDimensions: null,
831
- stats: null,
832
- health: null,
833
- lastConsolidation: null,
834
- error: null,
835
- };
836
-
837
- if (!report.exists) {
838
- return report;
839
- }
840
-
841
- try {
842
- report.storedDimensions = readStoredDimensions(dataDir);
843
- const dimensions = report.storedDimensions || 8;
844
- const audrey = new Audrey({
845
- dataDir,
846
- agent: 'status-check',
847
- embedding: { provider: 'mock', dimensions },
848
- });
849
- report.stats = audrey.introspect();
850
- report.health = audrey.memoryStatus();
851
- report.lastConsolidation = audrey.db.prepare(`
852
- SELECT completed_at FROM consolidation_runs
853
- WHERE status = 'completed'
854
- ORDER BY completed_at DESC
855
- LIMIT 1
856
- `).get()?.completed_at || 'never';
857
- audrey.close();
858
- } catch (err) {
859
- report.error = err.message || String(err);
860
- }
861
-
862
- return report;
863
- }
864
-
865
- export function formatStatusReport(report) {
866
- const lines = [];
867
- lines.push(`Registration: ${report.registered ? 'active' : 'not registered'}`);
868
-
869
- if (!report.exists) {
870
- lines.push(`Data directory: ${report.dataDir} (not yet created - will be created on first use)`);
871
- return lines.join('\n');
872
- }
873
-
874
- if (report.error) {
875
- lines.push(`Data directory: ${report.dataDir} (exists but could not read: ${report.error})`);
876
- return lines.join('\n');
877
- }
878
-
879
- lines.push(`Data directory: ${report.dataDir}`);
880
- lines.push(`Stored dimensions: ${report.storedDimensions ?? 'unknown'}`);
881
- lines.push(
882
- `Memories: ${report.stats.episodic} episodic, ${report.stats.semantic} semantic, ${report.stats.procedural} procedural`
883
- );
884
- lines.push(
885
- `Index sync: ${report.health.vec_episodes}/${report.health.searchable_episodes} episodic, `
886
- + `${report.health.vec_semantics}/${report.health.searchable_semantics} semantic, `
887
- + `${report.health.vec_procedures}/${report.health.searchable_procedures} procedural`
888
- );
889
- lines.push(
890
- `Health: ${report.health.healthy ? 'healthy' : 'unhealthy'}`
891
- + `${report.health.reembed_recommended ? ' (re-embed recommended)' : ''}`
892
- );
893
- lines.push(`Dormant: ${report.stats.dormant}`);
894
- lines.push(`Causal links: ${report.stats.causalLinks}`);
895
- lines.push(`Contradictions: ${report.stats.contradictions.open} open, ${report.stats.contradictions.resolved} resolved`);
896
- lines.push(`Consolidation runs: ${report.stats.totalConsolidationRuns}`);
897
- lines.push(`Last consolidation: ${report.lastConsolidation}`);
898
-
899
- return lines.join('\n');
900
- }
901
-
902
- export function runStatusCommand({
903
- argv = process.argv,
904
- dataDir = resolveDataDir(process.env),
905
- claudeJsonPath = join(homedir(), '.claude.json'),
906
- out = console.log,
907
- } = {}) {
908
- const report = buildStatusReport({ dataDir, claudeJsonPath });
909
- if (cliHasFlag('--json', argv)) {
910
- out(JSON.stringify(report, null, 2));
911
- } else {
912
- out(formatStatusReport(report));
913
- }
914
-
915
- const exitCode = report.error
916
- || (cliHasFlag('--fail-on-unhealthy', argv) && report.exists && report.health && !report.health.healthy)
917
- ? 1
918
- : 0;
919
-
920
- return { report, exitCode };
921
- }
922
-
923
- function status() {
924
- const { exitCode } = runStatusCommand();
925
- if (exitCode !== 0) {
926
- process.exitCode = exitCode;
927
- }
928
- }
929
-
930
- function toolResult(data) {
931
- return { content: [{ type: 'text', text: JSON.stringify(data) }] };
932
- }
933
-
934
- function toolError(err) {
935
- return { isError: true, content: [{ type: 'text', text: `Error: ${err.message || String(err)}` }] };
936
- }
937
-
938
- export function registerShutdownHandlers(processRef, audrey, logger = console.error) {
939
- let closed = false;
940
-
941
- const shutdown = (message, exitCode = 0) => {
942
- if (message) {
943
- logger(message);
944
- }
945
- if (!closed) {
946
- closed = true;
947
- if (typeof audrey?.waitForIdle === 'function') {
948
- Promise.resolve(audrey.waitForIdle())
949
- .catch(err => {
950
- logger(`[audrey-mcp] shutdown wait error: ${err.message || String(err)}`);
951
- exitCode = exitCode === 0 ? 1 : exitCode;
952
- })
953
- .finally(() => {
954
- try {
955
- audrey.close();
956
- } catch (err) {
957
- logger(`[audrey-mcp] shutdown error: ${err.message || String(err)}`);
958
- exitCode = exitCode === 0 ? 1 : exitCode;
959
- }
960
- if (typeof processRef.exit === 'function') {
961
- processRef.exit(exitCode);
962
- }
963
- });
964
- return;
965
- }
966
- try {
967
- audrey.close();
968
- } catch (err) {
969
- logger(`[audrey-mcp] shutdown error: ${err.message || String(err)}`);
970
- exitCode = exitCode === 0 ? 1 : exitCode;
971
- }
972
- }
973
- if (typeof processRef.exit === 'function') {
974
- processRef.exit(exitCode);
975
- }
976
- };
977
-
978
- processRef.once('SIGINT', () => shutdown('[audrey-mcp] received SIGINT, shutting down'));
979
- processRef.once('SIGTERM', () => shutdown('[audrey-mcp] received SIGTERM, shutting down'));
980
- processRef.once('SIGHUP', () => shutdown('[audrey-mcp] received SIGHUP, shutting down'));
981
- processRef.once('uncaughtException', err => {
982
- logger('[audrey-mcp] uncaught exception:', err);
983
- shutdown(null, 1);
984
- });
985
- processRef.once('unhandledRejection', reason => {
986
- logger('[audrey-mcp] unhandled rejection:', reason);
987
- shutdown(null, 1);
988
- });
989
-
990
- return shutdown;
991
- }
992
-
993
- export function registerDreamTool(server, audrey) {
994
- server.tool(
995
- 'memory_dream',
996
- {
997
- min_cluster_size: z.number().optional().describe('Minimum episodes per cluster (default 3)'),
998
- similarity_threshold: z.number().optional().describe('Similarity threshold for clustering (default 0.85)'),
999
- dormant_threshold: z.number().min(0).max(1).optional().describe('Confidence below which memories go dormant (default 0.1)'),
1000
- },
1001
- async ({ min_cluster_size, similarity_threshold, dormant_threshold }) => {
1002
- try {
1003
- const result = await audrey.dream({
1004
- minClusterSize: min_cluster_size,
1005
- similarityThreshold: similarity_threshold,
1006
- dormantThreshold: dormant_threshold,
1007
- });
1008
- return toolResult(result);
1009
- } catch (err) {
1010
- return toolError(err);
1011
- }
1012
- },
1013
- );
1014
- }
1015
-
1016
- async function main() {
1017
- const { McpServer } = await import('@modelcontextprotocol/sdk/server/mcp.js');
1018
- const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
1019
- const config = buildAudreyConfig();
1020
- const audrey = new Audrey(config);
1021
-
1022
- const embLabel = config.embedding.provider === 'mock'
1023
- ? 'mock embeddings - set OPENAI_API_KEY for real semantic search'
1024
- : `${config.embedding.provider} embeddings (${config.embedding.dimensions}d)`;
1025
- console.error(`[audrey-mcp] v${VERSION} started - agent=${config.agent} dataDir=${config.dataDir} (${embLabel})`);
1026
-
1027
- const server = new McpServer({
1028
- name: SERVER_NAME,
1029
- version: VERSION,
1030
- });
1031
-
1032
- server.tool('memory_encode', memoryEncodeToolSchema, async ({ content, source, tags, salience, private: isPrivate, context, affect }) => {
1033
- try {
1034
- validateMemoryContent(content);
1035
- const id = await audrey.encode({ content, source, tags, salience, private: isPrivate, context, affect });
1036
- return toolResult({ id, content, source, private: isPrivate ?? false });
1037
- } catch (err) {
1038
- return toolError(err);
1039
- }
1040
- });
1041
-
1042
- server.tool('memory_recall', memoryRecallToolSchema, async ({ query, limit, types, min_confidence, tags, sources, after, before, context, mood }) => {
1043
- try {
1044
- const results = await audrey.recall(query, {
1045
- limit: limit ?? 10,
1046
- types,
1047
- minConfidence: min_confidence,
1048
- tags,
1049
- sources,
1050
- after,
1051
- before,
1052
- context,
1053
- mood,
1054
- });
1055
- return toolResult(results);
1056
- } catch (err) {
1057
- return toolError(err);
1058
- }
1059
- });
1060
-
1061
- server.tool('memory_consolidate', {
1062
- min_cluster_size: z.number().optional().describe('Minimum episodes per cluster'),
1063
- similarity_threshold: z.number().optional().describe('Similarity threshold for clustering'),
1064
- }, async ({ min_cluster_size, similarity_threshold }) => {
1065
- try {
1066
- const consolidation = await audrey.consolidate({
1067
- minClusterSize: min_cluster_size,
1068
- similarityThreshold: similarity_threshold,
1069
- });
1070
- return toolResult(consolidation);
1071
- } catch (err) {
1072
- return toolError(err);
1073
- }
1074
- });
1075
-
1076
- server.tool('memory_introspect', {}, async () => {
1077
- try {
1078
- return toolResult(audrey.introspect());
1079
- } catch (err) {
1080
- return toolError(err);
1081
- }
1082
- });
1083
-
1084
- server.tool('memory_resolve_truth', {
1085
- contradiction_id: z.string().describe('ID of the contradiction to resolve'),
1086
- }, async ({ contradiction_id }) => {
1087
- try {
1088
- return toolResult(await audrey.resolveTruth(contradiction_id));
1089
- } catch (err) {
1090
- return toolError(err);
1091
- }
1092
- });
1093
-
1094
- server.tool('memory_export', {}, async () => {
1095
- try {
1096
- return toolResult(audrey.export());
1097
- } catch (err) {
1098
- return toolError(err);
1099
- }
1100
- });
1101
-
1102
- server.tool('memory_import', memoryImportToolSchema, async ({ snapshot }) => {
1103
- try {
1104
- await audrey.import(snapshot);
1105
- return toolResult({ imported: true, stats: audrey.introspect() });
1106
- } catch (err) {
1107
- return toolError(err);
1108
- }
1109
- });
1110
-
1111
- server.tool('memory_forget', memoryForgetToolSchema, async ({ id, query, min_similarity, purge }) => {
1112
- try {
1113
- validateForgetSelection(id, query);
1114
- let result;
1115
- if (id) {
1116
- result = audrey.forget(id, { purge: purge ?? false });
1117
- } else {
1118
- result = await audrey.forgetByQuery(query, {
1119
- minSimilarity: min_similarity ?? 0.9,
1120
- purge: purge ?? false,
1121
- });
1122
- if (!result) {
1123
- return toolResult({ forgotten: false, reason: 'No memory found above similarity threshold' });
1124
- }
1125
- }
1126
- return toolResult({ forgotten: true, ...result });
1127
- } catch (err) {
1128
- return toolError(err);
1129
- }
1130
- });
1131
-
1132
- server.tool('memory_decay', {
1133
- dormant_threshold: z.number().min(0).max(1).optional().describe('Confidence below which memories go dormant (default 0.1)'),
1134
- }, async ({ dormant_threshold }) => {
1135
- try {
1136
- return toolResult(audrey.decay({ dormantThreshold: dormant_threshold }));
1137
- } catch (err) {
1138
- return toolError(err);
1139
- }
1140
- });
1141
-
1142
- server.tool('memory_status', {}, async () => {
1143
- try {
1144
- return toolResult(audrey.memoryStatus());
1145
- } catch (err) {
1146
- return toolError(err);
1147
- }
1148
- });
1149
-
1150
- server.tool('memory_reflect', {
1151
- turns: z.array(z.object({
1152
- role: z.string().describe('Message role: user or assistant'),
1153
- content: z.string().describe('Message content'),
1154
- })).describe('Conversation turns to reflect on. Call at end of meaningful conversations to form lasting memories.'),
1155
- }, async ({ turns }) => {
1156
- try {
1157
- return toolResult(await audrey.reflect(turns));
1158
- } catch (err) {
1159
- return toolError(err);
1160
- }
1161
- });
1162
-
1163
- registerDreamTool(server, audrey);
1164
-
1165
- server.tool('memory_greeting', {
1166
- context: z.string().optional().describe('Optional hint about this session (e.g. "working on authentication feature"). If provided, also returns semantically relevant memories.'),
1167
- }, async ({ context }) => {
1168
- try {
1169
- return toolResult(await audrey.greeting({ context }));
1170
- } catch (err) {
1171
- return toolError(err);
1172
- }
1173
- });
1174
-
1175
- const transport = new StdioServerTransport();
1176
- await server.connect(transport);
1177
- console.error('[audrey-mcp] connected via stdio');
1178
- registerShutdownHandlers(process, audrey);
1179
- }
1180
-
1181
- const isDirectRun = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
1182
-
1183
- if (isDirectRun) {
1184
- if (subcommand === 'install') {
1185
- install();
1186
- } else if (subcommand === 'uninstall') {
1187
- uninstall();
1188
- } else if (subcommand === 'reembed') {
1189
- reembed().catch(err => {
1190
- console.error('[audrey] reembed failed:', err);
1191
- process.exit(1);
1192
- });
1193
- } else if (subcommand === 'dream') {
1194
- dream().catch(err => {
1195
- console.error('[audrey] dream failed:', err);
1196
- process.exit(1);
1197
- });
1198
- } else if (subcommand === 'greeting') {
1199
- greeting().catch(err => {
1200
- console.error('[audrey] greeting failed:', err);
1201
- process.exit(1);
1202
- });
1203
- } else if (subcommand === 'reflect') {
1204
- reflect().catch(err => {
1205
- console.error('[audrey] reflect failed:', err);
1206
- process.exit(1);
1207
- });
1208
- } else if (subcommand === 'recall') {
1209
- recall().catch(err => {
1210
- console.error('[audrey] recall failed:', err);
1211
- process.exit(1);
1212
- });
1213
- } else if (subcommand === 'hooks') {
1214
- const hooksAction = process.argv[3];
1215
- if (hooksAction === 'install') {
1216
- hooksInstall();
1217
- } else if (hooksAction === 'uninstall') {
1218
- hooksUninstall();
1219
- } else {
1220
- console.error('Usage: npx audrey hooks [install|uninstall]');
1221
- process.exit(1);
1222
- }
1223
- } else if (subcommand === 'snapshot') {
1224
- snapshot().catch(err => {
1225
- console.error('[audrey] snapshot failed:', err);
1226
- process.exit(1);
1227
- });
1228
- } else if (subcommand === 'restore') {
1229
- restore().catch(err => {
1230
- console.error('[audrey] restore failed:', err);
1231
- process.exit(1);
1232
- });
1233
- } else if (subcommand === 'serve') {
1234
- import('./serve.js').then(({ startServer }) => {
1235
- const port = process.argv[3] ? parseInt(process.argv[3], 10) : undefined;
1236
- return startServer({ port });
1237
- }).catch(err => {
1238
- console.error('[audrey] serve failed:', err);
1239
- process.exit(1);
1240
- });
1241
- } else if (subcommand === 'dashboard') {
1242
- import('./serve.js').then(({ startServer }) => {
1243
- const port = process.argv[3] ? parseInt(process.argv[3], 10) : undefined;
1244
- return startServer({ port }).then(({ server }) => {
1245
- const addr = server.address();
1246
- const url = `http://localhost:${addr.port}/dashboard`;
1247
- console.log(`[audrey] Opening dashboard: ${url}`);
1248
- import('node:child_process').then(({ exec: execCmd }) => {
1249
- const cmd = process.platform === 'win32' ? `start ${url}` : process.platform === 'darwin' ? `open ${url}` : `xdg-open ${url}`;
1250
- execCmd(cmd);
1251
- });
1252
- });
1253
- }).catch(err => {
1254
- console.error('[audrey] dashboard failed:', err);
1255
- process.exit(1);
1256
- });
1257
- } else if (subcommand === 'status') {
1258
- status();
1259
- } else {
1260
- main().catch(err => {
1261
- console.error('[audrey-mcp] fatal:', err);
1262
- process.exit(1);
1263
- });
1264
- }
1265
- }