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
package/src/audrey.js DELETED
@@ -1,642 +0,0 @@
1
- import { EventEmitter } from 'node:events';
2
- import { createDatabase, closeDatabase } from './db.js';
3
- import { createEmbeddingProvider } from './embedding.js';
4
- import { createLLMProvider } from './llm.js';
5
- import { encodeEpisode } from './encode.js';
6
- import { recall as recallFn, recallStream as recallStreamFn } from './recall.js';
7
- import { validateMemory } from './validate.js';
8
- import { runConsolidation } from './consolidate.js';
9
- import { applyDecay } from './decay.js';
10
- import { rollbackConsolidation, getConsolidationHistory } from './rollback.js';
11
- import { forgetMemory, forgetByQuery as forgetByQueryFn, purgeMemories } from './forget.js';
12
- import { introspect as introspectFn } from './introspect.js';
13
- import { buildContextResolutionPrompt, buildReflectionPrompt } from './prompts.js';
14
- import { exportMemories } from './export.js';
15
- import { importMemories } from './import.js';
16
- import { suggestConsolidationParams as suggestParamsFn } from './adaptive.js';
17
- import { reembedAll } from './migrate.js';
18
- import { applyInterference } from './interference.js';
19
- import { detectResonance } from './affect.js';
20
-
21
- /**
22
- * @typedef {'direct-observation' | 'told-by-user' | 'tool-result' | 'inference' | 'model-generated'} SourceType
23
- * @typedef {'episodic' | 'semantic' | 'procedural'} MemoryType
24
- *
25
- * @typedef {Object} EncodeParams
26
- * @property {string} content
27
- * @property {SourceType} source
28
- * @property {number} [salience]
29
- * @property {{ trigger?: string, consequence?: string }} [causal]
30
- * @property {string[]} [tags]
31
- * @property {string} [supersedes]
32
- * @property {Record<string, string>} [context]
33
- * @property {{ valence?: number, arousal?: number, label?: string }} [affect]
34
- *
35
- * @typedef {Object} RecallOptions
36
- * @property {number} [minConfidence]
37
- * @property {MemoryType[]} [types]
38
- * @property {number} [limit]
39
- * @property {boolean} [includeProvenance]
40
- * @property {boolean} [includeDormant]
41
- * @property {string[]} [tags]
42
- * @property {string[]} [sources]
43
- * @property {string} [after]
44
- * @property {string} [before]
45
- * @property {Record<string, string>} [context]
46
- * @property {{ valence?: number, arousal?: number }} [mood]
47
- * @property {'hybrid' | 'vector' | 'keyword'} [retrieval]
48
- *
49
- * @typedef {Object} RecallResult
50
- * @property {string} id
51
- * @property {string} content
52
- * @property {MemoryType} type
53
- * @property {number} confidence
54
- * @property {number} score
55
- * @property {string} source
56
- * @property {string} createdAt
57
- *
58
- * @typedef {Object} ConsolidationResult
59
- * @property {string} runId
60
- * @property {number} episodesEvaluated
61
- * @property {number} clustersFound
62
- * @property {number} principlesExtracted
63
- * @property {string} status
64
- *
65
- * @typedef {Object} IntrospectResult
66
- * @property {number} episodic
67
- * @property {number} semantic
68
- * @property {number} procedural
69
- * @property {number} causalLinks
70
- * @property {number} dormant
71
- * @property {{ open: number, resolved: number, context_dependent: number, reopened: number }} contradictions
72
- * @property {string | null} lastConsolidation
73
- * @property {number} totalConsolidationRuns
74
- *
75
- * @typedef {Object} TruthResolution
76
- * @property {'a_wins' | 'b_wins' | 'context_dependent'} resolution
77
- * @property {Object} [conditions]
78
- * @property {string} explanation
79
- *
80
- * @typedef {Object} AudreyConfig
81
- * @property {string} [dataDir]
82
- * @property {string} [agent]
83
- * @property {{ provider: 'mock' | 'openai', dimensions?: number, apiKey?: string }} [embedding]
84
- * @property {{ provider: 'mock' | 'anthropic' | 'openai', apiKey?: string, model?: string }} [llm]
85
- * @property {{ minEpisodes?: number }} [consolidation]
86
- * @property {{ dormantThreshold?: number }} [decay]
87
- */
88
-
89
- export class Audrey extends EventEmitter {
90
- /** @param {AudreyConfig} [config] */
91
- constructor({
92
- dataDir = './audrey-data',
93
- agent = 'default',
94
- embedding = { provider: 'mock', dimensions: 64 },
95
- llm,
96
- confidence = {},
97
- consolidation = {},
98
- decay = {},
99
- interference = {},
100
- context = {},
101
- affect = {},
102
- autoReflect = false,
103
- } = {}) {
104
- super();
105
-
106
- const dormantThreshold = decay.dormantThreshold ?? 0.1;
107
- if (dormantThreshold < 0 || dormantThreshold > 1) {
108
- throw new Error(`dormantThreshold must be between 0 and 1, got: ${dormantThreshold}`);
109
- }
110
-
111
- const minEpisodes = consolidation.minEpisodes ?? 3;
112
- if (!Number.isInteger(minEpisodes) || minEpisodes < 1) {
113
- throw new Error(`minEpisodes must be a positive integer, got: ${minEpisodes}`);
114
- }
115
-
116
- this.agent = agent;
117
- this.dataDir = dataDir;
118
- this.embeddingProvider = createEmbeddingProvider(embedding);
119
- const { db, migrated } = createDatabase(dataDir, { dimensions: this.embeddingProvider.dimensions });
120
- this.db = db;
121
- this._migrationPending = migrated;
122
- this._pending = new Set();
123
- this.llmProvider = llm ? createLLMProvider(llm) : null;
124
- this.confidenceConfig = {
125
- weights: confidence.weights,
126
- halfLives: confidence.halfLives,
127
- sourceReliability: confidence.sourceReliability,
128
- interferenceWeight: interference.weight ?? 0.1,
129
- contextWeight: context.weight ?? 0.3,
130
- affectWeight: affect.weight ?? 0.2,
131
- };
132
- this.consolidationConfig = {
133
- minEpisodes: consolidation.minEpisodes ?? 3,
134
- };
135
- this.decayConfig = { dormantThreshold: decay.dormantThreshold ?? 0.1 };
136
- this._autoConsolidateTimer = null;
137
- this._closed = false;
138
- this.interferenceConfig = {
139
- enabled: interference.enabled ?? true,
140
- k: interference.k ?? 5,
141
- threshold: interference.threshold ?? 0.6,
142
- weight: interference.weight ?? 0.1,
143
- };
144
- this.contextConfig = {
145
- enabled: context.enabled ?? true,
146
- weight: context.weight ?? 0.3,
147
- };
148
- this.affectConfig = {
149
- enabled: affect.enabled ?? true,
150
- weight: affect.weight ?? 0.2,
151
- arousalWeight: affect.arousalWeight ?? 0.3,
152
- resonance: {
153
- enabled: affect.resonance?.enabled ?? true,
154
- k: affect.resonance?.k ?? 5,
155
- threshold: affect.resonance?.threshold ?? 0.5,
156
- affectThreshold: affect.resonance?.affectThreshold ?? 0.6,
157
- },
158
- };
159
- this.autoReflect = autoReflect;
160
- }
161
-
162
- async _ensureMigrated() {
163
- if (!this._migrationPending) return;
164
- const counts = await reembedAll(this.db, this.embeddingProvider);
165
- this._migrationPending = false;
166
- this.emit('migration', counts);
167
- }
168
-
169
- _trackAsync(promise) {
170
- this._pending.add(promise);
171
- promise.finally(() => this._pending.delete(promise));
172
- }
173
-
174
- async waitForIdle() {
175
- while (this._pending.size > 0) {
176
- await Promise.allSettled([...this._pending]);
177
- }
178
- }
179
-
180
- _emitValidation(id, params) {
181
- const p = validateMemory(this.db, this.embeddingProvider, { id, ...params }, {
182
- llmProvider: this.llmProvider,
183
- })
184
- .then(validation => {
185
- if (validation.action === 'reinforced') {
186
- this.emit('reinforcement', {
187
- episodeId: id,
188
- targetId: validation.semanticId,
189
- similarity: validation.similarity,
190
- });
191
- } else if (validation.action === 'contradiction') {
192
- this.emit('contradiction', {
193
- episodeId: id,
194
- contradictionId: validation.contradictionId,
195
- semanticId: validation.semanticId,
196
- similarity: validation.similarity,
197
- resolution: validation.resolution,
198
- });
199
- }
200
- })
201
- .catch(err => { if (!this._closed) this.emit('error', err); });
202
- this._trackAsync(p);
203
- }
204
-
205
- /**
206
- * @param {EncodeParams} params
207
- * @returns {Promise<string>}
208
- */
209
- async encode(params) {
210
- await this._ensureMigrated();
211
- const encodeParams = { agent: this.agent, ...params, arousalWeight: this.affectConfig.arousalWeight };
212
- const id = await encodeEpisode(this.db, this.embeddingProvider, encodeParams);
213
- this.emit('encode', { id, ...params });
214
- if (this.interferenceConfig.enabled) {
215
- const p = applyInterference(this.db, this.embeddingProvider, id, params, this.interferenceConfig)
216
- .then(affected => {
217
- if (affected.length > 0) {
218
- this.emit('interference', { episodeId: id, affected });
219
- }
220
- })
221
- .catch(err => { if (!this._closed) this.emit('error', err); });
222
- this._trackAsync(p);
223
- }
224
- if (this.affectConfig.enabled && this.affectConfig.resonance.enabled && params.affect?.valence !== undefined) {
225
- const p = detectResonance(this.db, this.embeddingProvider, id, params, this.affectConfig.resonance)
226
- .then(echoes => {
227
- if (echoes.length > 0) {
228
- this.emit('resonance', { episodeId: id, affect: params.affect, echoes });
229
- }
230
- })
231
- .catch(err => { if (!this._closed) this.emit('error', err); });
232
- this._trackAsync(p);
233
- }
234
- this._emitValidation(id, params);
235
- return id;
236
- }
237
-
238
-
239
- async reflect(turns) {
240
- if (!this.llmProvider) return { encoded: 0, memories: [], skipped: 'no llm provider' };
241
-
242
- const prompt = buildReflectionPrompt(turns);
243
- let raw;
244
- try {
245
- raw = await this.llmProvider.chat(prompt);
246
- } catch (err) {
247
- this.emit('error', err);
248
- return { encoded: 0, memories: [], skipped: 'llm error' };
249
- }
250
-
251
- let parsed;
252
- try {
253
- parsed = JSON.parse(raw);
254
- } catch {
255
- return { encoded: 0, memories: [], skipped: 'invalid llm response' };
256
- }
257
-
258
- const memories = parsed.memories ?? [];
259
- let encoded = 0;
260
- for (const mem of memories) {
261
- if (!mem.content || !mem.source) continue;
262
- try {
263
- await this.encode({
264
- content: mem.content,
265
- source: mem.source,
266
- salience: mem.salience,
267
- tags: mem.tags,
268
- private: mem.private ?? false,
269
- affect: mem.affect ?? undefined,
270
- });
271
- encoded++;
272
- } catch (err) {
273
- this.emit('error', err);
274
- }
275
- }
276
-
277
- return { encoded, memories };
278
- }
279
-
280
- /**
281
- * @param {EncodeParams[]} paramsList
282
- * @returns {Promise<string[]>}
283
- */
284
- async encodeBatch(paramsList) {
285
- await this._ensureMigrated();
286
- const ids = [];
287
- for (const params of paramsList) {
288
- const id = await encodeEpisode(this.db, this.embeddingProvider, params);
289
- ids.push(id);
290
- this.emit('encode', { id, ...params });
291
- }
292
-
293
- for (let i = 0; i < ids.length; i++) {
294
- this._emitValidation(ids[i], paramsList[i]);
295
- }
296
-
297
- return ids;
298
- }
299
-
300
- /**
301
- * @param {string} query
302
- * @param {RecallOptions} [options]
303
- * @returns {Promise<RecallResult[]>}
304
- */
305
- async recall(query, options = {}) {
306
- await this._ensureMigrated();
307
- return recallFn(this.db, this.embeddingProvider, query, {
308
- agent: this.agent,
309
- ...options,
310
- confidenceConfig: this._recallConfig(options),
311
- });
312
- }
313
-
314
- /**
315
- * @param {string} query
316
- * @param {RecallOptions} [options]
317
- * @returns {AsyncGenerator<RecallResult>}
318
- */
319
- async *recallStream(query, options = {}) {
320
- await this._ensureMigrated();
321
- yield* recallStreamFn(this.db, this.embeddingProvider, query, {
322
- agent: this.agent,
323
- ...options,
324
- confidenceConfig: this._recallConfig(options),
325
- });
326
- }
327
-
328
- _recallConfig(options) {
329
- let config = options.confidenceConfig ?? this.confidenceConfig;
330
- if (this.contextConfig.enabled && options.context) {
331
- config = { ...config, retrievalContext: options.context };
332
- }
333
- if (this.affectConfig.enabled && options.mood) {
334
- config = { ...config, retrievalMood: options.mood };
335
- }
336
- return config;
337
- }
338
-
339
- /**
340
- * @param {{ minClusterSize?: number, similarityThreshold?: number, extractPrinciple?: Function, llmProvider?: import('./llm.js').LLMProvider }} [options]
341
- * @returns {Promise<ConsolidationResult>}
342
- */
343
- async consolidate(options = {}) {
344
- await this._ensureMigrated();
345
- const result = await runConsolidation(this.db, this.embeddingProvider, {
346
- minClusterSize: options.minClusterSize ?? this.consolidationConfig.minEpisodes,
347
- similarityThreshold: options.similarityThreshold ?? 0.80,
348
- extractPrinciple: options.extractPrinciple,
349
- llmProvider: options.llmProvider || this.llmProvider,
350
- });
351
- const run = this.db.prepare('SELECT status FROM consolidation_runs WHERE id = ?').get(result.runId);
352
- const output = { ...result, status: run?.status || 'completed' };
353
- this.emit('consolidation', output);
354
- return output;
355
- }
356
-
357
- /**
358
- * @param {{ dormantThreshold?: number }} [options]
359
- * @returns {{ totalEvaluated: number, transitionedToDormant: number, timestamp: string }}
360
- */
361
- decay(options = {}) {
362
- const result = applyDecay(this.db, {
363
- dormantThreshold: options.dormantThreshold ?? this.decayConfig.dormantThreshold,
364
- halfLives: options.halfLives ?? this.confidenceConfig.halfLives,
365
- });
366
- this.emit('decay', result);
367
- return result;
368
- }
369
-
370
- /**
371
- * @param {string} runId
372
- * @returns {{ rolledBackMemories: number, restoredEpisodes: number }}
373
- */
374
- rollback(runId) {
375
- const result = rollbackConsolidation(this.db, runId);
376
- this.emit('rollback', { runId, ...result });
377
- return result;
378
- }
379
-
380
- /**
381
- * @param {string} contradictionId
382
- * @returns {Promise<TruthResolution>}
383
- */
384
- async resolveTruth(contradictionId) {
385
- if (!this.llmProvider) {
386
- throw new Error('resolveTruth requires an LLM provider');
387
- }
388
-
389
- const contradiction = this.db.prepare(
390
- 'SELECT * FROM contradictions WHERE id = ?'
391
- ).get(contradictionId);
392
- if (!contradiction) throw new Error(`Contradiction not found: ${contradictionId}`);
393
-
394
- const claimA = this._loadClaimContent(contradiction.claim_a_id, contradiction.claim_a_type);
395
- const claimB = this._loadClaimContent(contradiction.claim_b_id, contradiction.claim_b_type);
396
-
397
- const messages = buildContextResolutionPrompt(claimA, claimB);
398
- const result = await this.llmProvider.json(messages);
399
-
400
- const now = new Date().toISOString();
401
- const newState = result.resolution === 'context_dependent' ? 'context_dependent' : 'resolved';
402
- this.db.prepare(`
403
- UPDATE contradictions SET state = ?, resolution = ?, resolved_at = ?
404
- WHERE id = ?
405
- `).run(newState, JSON.stringify(result), now, contradictionId);
406
-
407
- if (result.resolution === 'a_wins' && contradiction.claim_a_type === 'semantic') {
408
- this.db.prepare("UPDATE semantics SET state = 'active' WHERE id = ?").run(contradiction.claim_a_id);
409
- }
410
- if (result.resolution === 'b_wins' && contradiction.claim_b_type === 'semantic') {
411
- this.db.prepare("UPDATE semantics SET state = 'active' WHERE id = ?").run(contradiction.claim_b_id);
412
- }
413
- if (result.resolution === 'context_dependent') {
414
- if (contradiction.claim_a_type === 'semantic' && result.conditions) {
415
- this.db.prepare("UPDATE semantics SET state = 'context_dependent', conditions = ? WHERE id = ?")
416
- .run(JSON.stringify(result.conditions), contradiction.claim_a_id);
417
- }
418
- }
419
-
420
- return result;
421
- }
422
-
423
- _loadClaimContent(claimId, claimType) {
424
- if (claimType === 'semantic') {
425
- const row = this.db.prepare('SELECT content FROM semantics WHERE id = ?').get(claimId);
426
- if (!row) throw new Error(`Semantic memory not found: ${claimId}`);
427
- return row.content;
428
- } else if (claimType === 'episodic') {
429
- const row = this.db.prepare('SELECT content FROM episodes WHERE id = ?').get(claimId);
430
- if (!row) throw new Error(`Episode not found: ${claimId}`);
431
- return row.content;
432
- }
433
- throw new Error(`Unknown claim type: ${claimType}`);
434
- }
435
-
436
- /** @returns {Array<{ id: string, input_episode_ids: string, output_memory_ids: string, started_at: string, completed_at: string, status: string }>} */
437
- consolidationHistory() {
438
- return getConsolidationHistory(this.db);
439
- }
440
-
441
- /** @returns {IntrospectResult} */
442
- introspect() {
443
- return introspectFn(this.db);
444
- }
445
-
446
- memoryStatus() {
447
- const episodes = this.db.prepare('SELECT COUNT(*) as c FROM episodes').get().c;
448
- const semantics = this.db.prepare('SELECT COUNT(*) as c FROM semantics').get().c;
449
- const procedures = this.db.prepare('SELECT COUNT(*) as c FROM procedures').get().c;
450
- const searchableEpisodes = this.db.prepare('SELECT COUNT(*) as c FROM episodes WHERE embedding IS NOT NULL').get().c;
451
- const searchableSemantics = this.db.prepare('SELECT COUNT(*) as c FROM semantics WHERE embedding IS NOT NULL').get().c;
452
- const searchableProcedures = this.db.prepare('SELECT COUNT(*) as c FROM procedures WHERE embedding IS NOT NULL').get().c;
453
-
454
- let vecEpisodes = 0, vecSemantics = 0, vecProcedures = 0;
455
- try {
456
- vecEpisodes = this.db.prepare('SELECT COUNT(*) as c FROM vec_episodes').get().c;
457
- vecSemantics = this.db.prepare('SELECT COUNT(*) as c FROM vec_semantics').get().c;
458
- vecProcedures = this.db.prepare('SELECT COUNT(*) as c FROM vec_procedures').get().c;
459
- } catch {
460
- // vec tables may not exist if no dimensions configured
461
- }
462
-
463
- const dimsRow = this.db.prepare("SELECT value FROM audrey_config WHERE key = 'dimensions'").get();
464
- const dimensions = dimsRow ? parseInt(dimsRow.value, 10) : null;
465
- const versionRow = this.db.prepare("SELECT value FROM audrey_config WHERE key = 'schema_version'").get();
466
- const schemaVersion = versionRow ? parseInt(versionRow.value, 10) : 0;
467
-
468
- const device = this.embeddingProvider._actualDevice
469
- ?? this.embeddingProvider.device
470
- ?? null;
471
-
472
- const healthy = episodes === vecEpisodes
473
- && semantics === vecSemantics
474
- && procedures === vecProcedures;
475
- const reembedRecommended = searchableEpisodes !== vecEpisodes
476
- || searchableSemantics !== vecSemantics
477
- || searchableProcedures !== vecProcedures;
478
-
479
- return {
480
- episodes,
481
- vec_episodes: vecEpisodes,
482
- semantics,
483
- vec_semantics: vecSemantics,
484
- procedures,
485
- vec_procedures: vecProcedures,
486
- searchable_episodes: searchableEpisodes,
487
- searchable_semantics: searchableSemantics,
488
- searchable_procedures: searchableProcedures,
489
- dimensions,
490
- schema_version: schemaVersion,
491
- device,
492
- healthy,
493
- reembed_recommended: reembedRecommended,
494
- };
495
- }
496
-
497
- async greeting({ context, recentLimit = 10, principleLimit = 5, identityLimit = 5 } = {}) {
498
- const recent = this.db.prepare(
499
- 'SELECT id, content, source, tags, salience, created_at FROM episodes WHERE "private" = 0 ORDER BY created_at DESC LIMIT ?'
500
- ).all(recentLimit);
501
-
502
- const principles = this.db.prepare(
503
- 'SELECT id, content, salience, created_at FROM semantics WHERE state = ? ORDER BY salience DESC LIMIT ?'
504
- ).all('active', principleLimit);
505
-
506
- const identity = this.db.prepare(
507
- 'SELECT id, content, tags, salience, created_at FROM episodes WHERE "private" = 1 ORDER BY created_at DESC LIMIT ?'
508
- ).all(identityLimit);
509
-
510
- const unresolved = this.db.prepare(
511
- "SELECT id, content, tags, salience, created_at FROM episodes WHERE tags LIKE '%unresolved%' AND salience > 0.3 ORDER BY created_at DESC LIMIT 10"
512
- ).all();
513
-
514
- const rawAffectRows = this.db.prepare(
515
- "SELECT affect FROM episodes WHERE affect IS NOT NULL AND affect != '{}' ORDER BY created_at DESC LIMIT 20"
516
- ).all();
517
-
518
- const affectParsed = rawAffectRows
519
- .map(r => { try { return JSON.parse(r.affect); } catch { return null; } })
520
- .filter(a => a && a.valence !== undefined);
521
-
522
- let mood;
523
- if (affectParsed.length === 0) {
524
- mood = { valence: 0, arousal: 0, samples: 0 };
525
- } else {
526
- const sumV = affectParsed.reduce((s, a) => s + a.valence, 0);
527
- const sumA = affectParsed.reduce((s, a) => s + (a.arousal ?? 0), 0);
528
- mood = {
529
- valence: sumV / affectParsed.length,
530
- arousal: sumA / affectParsed.length,
531
- samples: affectParsed.length,
532
- };
533
- }
534
-
535
- const result = { recent, principles, mood, unresolved, identity };
536
-
537
- if (context) {
538
- result.contextual = await this.recall(context, { limit: 5, includePrivate: true });
539
- }
540
-
541
- return result;
542
- }
543
-
544
- async dream(options = {}) {
545
- await this._ensureMigrated();
546
-
547
- const consolidation = await this.consolidate({
548
- minClusterSize: options.minClusterSize,
549
- similarityThreshold: options.similarityThreshold,
550
- });
551
-
552
- const decay = this.decay({
553
- dormantThreshold: options.dormantThreshold,
554
- });
555
-
556
- const stats = this.introspect();
557
-
558
- const result = {
559
- consolidation,
560
- decay,
561
- stats,
562
- };
563
-
564
- this.emit('dream', result);
565
- return result;
566
- }
567
-
568
- export() {
569
- return exportMemories(this.db);
570
- }
571
-
572
- async import(snapshot) {
573
- return importMemories(this.db, this.embeddingProvider, snapshot);
574
- }
575
-
576
- startAutoConsolidate(intervalMs, options = {}) {
577
- if (intervalMs < 1000) {
578
- throw new Error('Auto-consolidation interval must be at least 1000ms');
579
- }
580
- if (this._autoConsolidateTimer) {
581
- throw new Error('Auto-consolidation is already running');
582
- }
583
- this._autoConsolidateTimer = setInterval(() => {
584
- this.consolidate(options).catch(err => this.emit('error', err));
585
- }, intervalMs);
586
- if (typeof this._autoConsolidateTimer.unref === 'function') {
587
- this._autoConsolidateTimer.unref();
588
- }
589
- }
590
-
591
- stopAutoConsolidate() {
592
- if (this._autoConsolidateTimer) {
593
- clearInterval(this._autoConsolidateTimer);
594
- this._autoConsolidateTimer = null;
595
- }
596
- }
597
-
598
- suggestConsolidationParams() {
599
- return suggestParamsFn(this.db);
600
- }
601
-
602
- forget(id, options = {}) {
603
- const result = forgetMemory(this.db, id, options);
604
- this.emit('forget', result);
605
- return result;
606
- }
607
-
608
- markUsed(id) {
609
- const now = new Date().toISOString();
610
- const tables = ['episodes', 'semantics', 'procedures'];
611
- for (const table of tables) {
612
- const result = this.db.prepare(
613
- `UPDATE ${table} SET usage_count = usage_count + 1, last_used_at = ? WHERE id = ?`
614
- ).run(now, id);
615
- if (result.changes > 0) {
616
- this.emit('used', { id, table, usageCount: result.changes });
617
- return;
618
- }
619
- }
620
- }
621
-
622
- async forgetByQuery(query, options = {}) {
623
- await this._ensureMigrated();
624
- const result = await forgetByQueryFn(this.db, this.embeddingProvider, query, options);
625
- if (result) this.emit('forget', result);
626
- return result;
627
- }
628
-
629
- purge() {
630
- const result = purgeMemories(this.db);
631
- this.emit('purge', result);
632
- return result;
633
- }
634
-
635
- close() {
636
- if (this._closed) return;
637
- this._closed = true;
638
- this.stopAutoConsolidate();
639
- this._pending.clear();
640
- closeDatabase(this.db);
641
- }
642
- }