audrey 0.21.0 → 1.0.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 (346) hide show
  1. package/CHANGELOG.md +238 -0
  2. package/LICENSE +21 -21
  3. package/README.md +281 -33
  4. package/SECURITY.md +30 -0
  5. package/benchmarks/adapter-kit.mjs +20 -0
  6. package/benchmarks/adapter-self-test.mjs +166 -0
  7. package/benchmarks/adapters/example-allow.mjs +28 -0
  8. package/benchmarks/adapters/mem0-platform.mjs +267 -0
  9. package/benchmarks/adapters/registry.json +51 -0
  10. package/benchmarks/adapters/zep-cloud.mjs +280 -0
  11. package/benchmarks/baselines.js +169 -0
  12. package/benchmarks/build-leaderboard.mjs +170 -0
  13. package/benchmarks/cases.js +537 -0
  14. package/benchmarks/create-conformance-card.mjs +139 -0
  15. package/benchmarks/create-submission-bundle.mjs +176 -0
  16. package/benchmarks/dry-run-external-adapters.mjs +165 -0
  17. package/benchmarks/guardbench.js +1035 -0
  18. package/benchmarks/output/adapter-self-test/guardbench-adapter-self-test.json +50 -0
  19. package/benchmarks/output/external/guardbench-external-dry-run.json +69 -0
  20. package/benchmarks/output/external/guardbench-external-evidence.json +56 -0
  21. package/benchmarks/output/guardbench-conformance-card.json +63 -0
  22. package/benchmarks/output/guardbench-manifest.json +414 -0
  23. package/benchmarks/output/guardbench-raw.json +1171 -0
  24. package/benchmarks/output/guardbench-summary.json +1981 -0
  25. package/benchmarks/output/leaderboard/guardbench-leaderboard.json +93 -0
  26. package/benchmarks/output/leaderboard/guardbench-leaderboard.md +7 -0
  27. package/benchmarks/output/submission-bundle/guardbench-conformance-card.json +63 -0
  28. package/benchmarks/output/submission-bundle/guardbench-manifest.json +414 -0
  29. package/benchmarks/output/submission-bundle/guardbench-raw.json +1171 -0
  30. package/benchmarks/output/submission-bundle/guardbench-summary.json +1981 -0
  31. package/benchmarks/output/submission-bundle/schemas/guardbench-adapter-registry.schema.json +69 -0
  32. package/benchmarks/output/submission-bundle/schemas/guardbench-adapter-self-test.schema.json +156 -0
  33. package/benchmarks/output/submission-bundle/schemas/guardbench-conformance-card.schema.json +184 -0
  34. package/benchmarks/output/submission-bundle/schemas/guardbench-external-dry-run.schema.json +74 -0
  35. package/benchmarks/output/submission-bundle/schemas/guardbench-external-evidence.schema.json +108 -0
  36. package/benchmarks/output/submission-bundle/schemas/guardbench-external-run.schema.json +160 -0
  37. package/benchmarks/output/submission-bundle/schemas/guardbench-leaderboard.schema.json +179 -0
  38. package/benchmarks/output/submission-bundle/schemas/guardbench-manifest.schema.json +213 -0
  39. package/benchmarks/output/submission-bundle/schemas/guardbench-publication-verification.schema.json +47 -0
  40. package/benchmarks/output/submission-bundle/schemas/guardbench-raw.schema.json +164 -0
  41. package/benchmarks/output/submission-bundle/schemas/guardbench-submission-manifest.schema.json +151 -0
  42. package/benchmarks/output/submission-bundle/schemas/guardbench-summary.schema.json +228 -0
  43. package/benchmarks/output/submission-bundle/submission-manifest.json +131 -0
  44. package/benchmarks/output/submission-bundle/validation-report.json +31 -0
  45. package/benchmarks/output/summary.json +2354 -0
  46. package/benchmarks/perf-snapshot.js +304 -0
  47. package/benchmarks/perf.bench.js +161 -0
  48. package/benchmarks/public-paths.mjs +78 -0
  49. package/benchmarks/reference-results.js +70 -0
  50. package/benchmarks/report.js +259 -0
  51. package/benchmarks/run-external-guardbench.mjs +281 -0
  52. package/benchmarks/run.js +682 -0
  53. package/benchmarks/schemas/guardbench-adapter-registry.schema.json +69 -0
  54. package/benchmarks/schemas/guardbench-adapter-self-test.schema.json +156 -0
  55. package/benchmarks/schemas/guardbench-conformance-card.schema.json +184 -0
  56. package/benchmarks/schemas/guardbench-external-dry-run.schema.json +74 -0
  57. package/benchmarks/schemas/guardbench-external-evidence.schema.json +108 -0
  58. package/benchmarks/schemas/guardbench-external-run.schema.json +160 -0
  59. package/benchmarks/schemas/guardbench-leaderboard.schema.json +179 -0
  60. package/benchmarks/schemas/guardbench-manifest.schema.json +213 -0
  61. package/benchmarks/schemas/guardbench-publication-verification.schema.json +47 -0
  62. package/benchmarks/schemas/guardbench-raw.schema.json +164 -0
  63. package/benchmarks/schemas/guardbench-submission-manifest.schema.json +151 -0
  64. package/benchmarks/schemas/guardbench-summary.schema.json +228 -0
  65. package/benchmarks/snapshots/perf-0.22.2.json +123 -0
  66. package/benchmarks/snapshots/perf-0.23.0.json +123 -0
  67. package/benchmarks/validate-adapter-module.mjs +104 -0
  68. package/benchmarks/validate-adapter-registry.mjs +134 -0
  69. package/benchmarks/validate-adapter-self-test.mjs +96 -0
  70. package/benchmarks/validate-guardbench-artifacts.mjs +343 -0
  71. package/benchmarks/verify-external-evidence.mjs +296 -0
  72. package/benchmarks/verify-publication-artifacts.mjs +286 -0
  73. package/benchmarks/verify-submission-bundle.mjs +167 -0
  74. package/dist/mcp-server/config.d.ts +5 -4
  75. package/dist/mcp-server/config.d.ts.map +1 -1
  76. package/dist/mcp-server/config.js +6 -8
  77. package/dist/mcp-server/config.js.map +1 -1
  78. package/dist/mcp-server/index.d.ts +281 -23
  79. package/dist/mcp-server/index.d.ts.map +1 -1
  80. package/dist/mcp-server/index.js +1186 -82
  81. package/dist/mcp-server/index.js.map +1 -1
  82. package/dist/src/action-key.d.ts +9 -0
  83. package/dist/src/action-key.d.ts.map +1 -0
  84. package/dist/src/action-key.js +49 -0
  85. package/dist/src/action-key.js.map +1 -0
  86. package/dist/src/adaptive.d.ts.map +1 -1
  87. package/dist/src/adaptive.js +8 -6
  88. package/dist/src/adaptive.js.map +1 -1
  89. package/dist/src/affect.d.ts +4 -1
  90. package/dist/src/affect.d.ts.map +1 -1
  91. package/dist/src/affect.js +14 -12
  92. package/dist/src/affect.js.map +1 -1
  93. package/dist/src/audrey.d.ts +57 -4
  94. package/dist/src/audrey.d.ts.map +1 -1
  95. package/dist/src/audrey.js +512 -65
  96. package/dist/src/audrey.js.map +1 -1
  97. package/dist/src/capsule.d.ts +2 -1
  98. package/dist/src/capsule.d.ts.map +1 -1
  99. package/dist/src/capsule.js +18 -8
  100. package/dist/src/capsule.js.map +1 -1
  101. package/dist/src/causal.d.ts.map +1 -1
  102. package/dist/src/causal.js +23 -5
  103. package/dist/src/causal.js.map +1 -1
  104. package/dist/src/confidence.d.ts.map +1 -1
  105. package/dist/src/confidence.js +3 -0
  106. package/dist/src/confidence.js.map +1 -1
  107. package/dist/src/consolidate.d.ts +1 -0
  108. package/dist/src/consolidate.d.ts.map +1 -1
  109. package/dist/src/consolidate.js +70 -54
  110. package/dist/src/consolidate.js.map +1 -1
  111. package/dist/src/controller.d.ts +94 -0
  112. package/dist/src/controller.d.ts.map +1 -0
  113. package/dist/src/controller.js +350 -0
  114. package/dist/src/controller.js.map +1 -0
  115. package/dist/src/db.d.ts.map +1 -1
  116. package/dist/src/db.js +181 -169
  117. package/dist/src/db.js.map +1 -1
  118. package/dist/src/decay.d.ts.map +1 -1
  119. package/dist/src/decay.js +62 -55
  120. package/dist/src/decay.js.map +1 -1
  121. package/dist/src/embedding.d.ts +2 -1
  122. package/dist/src/embedding.d.ts.map +1 -1
  123. package/dist/src/embedding.js +60 -22
  124. package/dist/src/embedding.js.map +1 -1
  125. package/dist/src/encode.d.ts +9 -2
  126. package/dist/src/encode.d.ts.map +1 -1
  127. package/dist/src/encode.js +25 -12
  128. package/dist/src/encode.js.map +1 -1
  129. package/dist/src/export.d.ts.map +1 -1
  130. package/dist/src/export.js +5 -3
  131. package/dist/src/export.js.map +1 -1
  132. package/dist/src/feedback.d.ts +35 -0
  133. package/dist/src/feedback.d.ts.map +1 -0
  134. package/dist/src/feedback.js +129 -0
  135. package/dist/src/feedback.js.map +1 -0
  136. package/dist/src/forget.d.ts.map +1 -1
  137. package/dist/src/forget.js +68 -60
  138. package/dist/src/forget.js.map +1 -1
  139. package/dist/src/fts.js +1 -1
  140. package/dist/src/fts.js.map +1 -1
  141. package/dist/src/hybrid-recall.d.ts +2 -1
  142. package/dist/src/hybrid-recall.d.ts.map +1 -1
  143. package/dist/src/hybrid-recall.js +41 -32
  144. package/dist/src/hybrid-recall.js.map +1 -1
  145. package/dist/src/impact.d.ts +47 -0
  146. package/dist/src/impact.d.ts.map +1 -0
  147. package/dist/src/impact.js +146 -0
  148. package/dist/src/impact.js.map +1 -0
  149. package/dist/src/import.d.ts +177 -1
  150. package/dist/src/import.d.ts.map +1 -1
  151. package/dist/src/import.js +235 -46
  152. package/dist/src/import.js.map +1 -1
  153. package/dist/src/index.d.ts +5 -1
  154. package/dist/src/index.d.ts.map +1 -1
  155. package/dist/src/index.js +3 -1
  156. package/dist/src/index.js.map +1 -1
  157. package/dist/src/interference.d.ts +5 -2
  158. package/dist/src/interference.d.ts.map +1 -1
  159. package/dist/src/interference.js +39 -32
  160. package/dist/src/interference.js.map +1 -1
  161. package/dist/src/introspect.js +18 -18
  162. package/dist/src/llm.d.ts.map +1 -1
  163. package/dist/src/llm.js +1 -0
  164. package/dist/src/llm.js.map +1 -1
  165. package/dist/src/migrate.d.ts.map +1 -1
  166. package/dist/src/migrate.js +21 -9
  167. package/dist/src/migrate.js.map +1 -1
  168. package/dist/src/preflight.d.ts +2 -1
  169. package/dist/src/preflight.d.ts.map +1 -1
  170. package/dist/src/preflight.js +66 -5
  171. package/dist/src/preflight.js.map +1 -1
  172. package/dist/src/profile.d.ts +23 -0
  173. package/dist/src/profile.d.ts.map +1 -0
  174. package/dist/src/profile.js +51 -0
  175. package/dist/src/profile.js.map +1 -0
  176. package/dist/src/promote.d.ts.map +1 -1
  177. package/dist/src/promote.js +8 -9
  178. package/dist/src/promote.js.map +1 -1
  179. package/dist/src/prompts.d.ts.map +1 -1
  180. package/dist/src/prompts.js +165 -136
  181. package/dist/src/prompts.js.map +1 -1
  182. package/dist/src/recall.d.ts +9 -6
  183. package/dist/src/recall.d.ts.map +1 -1
  184. package/dist/src/recall.js +204 -62
  185. package/dist/src/recall.js.map +1 -1
  186. package/dist/src/redact.d.ts +7 -1
  187. package/dist/src/redact.d.ts.map +1 -1
  188. package/dist/src/redact.js +94 -11
  189. package/dist/src/redact.js.map +1 -1
  190. package/dist/src/reflexes.d.ts +1 -0
  191. package/dist/src/reflexes.d.ts.map +1 -1
  192. package/dist/src/reflexes.js +3 -0
  193. package/dist/src/reflexes.js.map +1 -1
  194. package/dist/src/rollback.d.ts.map +1 -1
  195. package/dist/src/rollback.js +13 -8
  196. package/dist/src/rollback.js.map +1 -1
  197. package/dist/src/routes.d.ts +1 -0
  198. package/dist/src/routes.d.ts.map +1 -1
  199. package/dist/src/routes.js +251 -6
  200. package/dist/src/routes.js.map +1 -1
  201. package/dist/src/rules-compiler.d.ts.map +1 -1
  202. package/dist/src/rules-compiler.js +36 -6
  203. package/dist/src/rules-compiler.js.map +1 -1
  204. package/dist/src/server.d.ts +2 -1
  205. package/dist/src/server.d.ts.map +1 -1
  206. package/dist/src/server.js +42 -4
  207. package/dist/src/server.js.map +1 -1
  208. package/dist/src/tool-trace.d.ts.map +1 -1
  209. package/dist/src/tool-trace.js +42 -29
  210. package/dist/src/tool-trace.js.map +1 -1
  211. package/dist/src/types.d.ts +28 -1
  212. package/dist/src/types.d.ts.map +1 -1
  213. package/dist/src/ulid.d.ts.map +1 -1
  214. package/dist/src/ulid.js +52 -2
  215. package/dist/src/ulid.js.map +1 -1
  216. package/dist/src/utils.d.ts.map +1 -1
  217. package/dist/src/utils.js +8 -1
  218. package/dist/src/utils.js.map +1 -1
  219. package/dist/src/validate.d.ts +2 -0
  220. package/dist/src/validate.d.ts.map +1 -1
  221. package/dist/src/validate.js +77 -46
  222. package/dist/src/validate.js.map +1 -1
  223. package/docs/AUDREY_PAPER_OUTLINE.md +175 -0
  224. package/docs/MEMORY_BENCHMARKING.md +59 -0
  225. package/docs/PRODUCTION_BACKLOG.md +304 -0
  226. package/docs/paper/00-master.md +48 -0
  227. package/docs/paper/01-introduction.md +27 -0
  228. package/docs/paper/02-related-work.md +47 -0
  229. package/docs/paper/03-problem-definition.md +108 -0
  230. package/docs/paper/04-design.md +164 -0
  231. package/docs/paper/05-guardbench-spec.md +412 -0
  232. package/docs/paper/06-implementation.md +113 -0
  233. package/docs/paper/07-evaluation.md +168 -0
  234. package/docs/paper/08-discussion-limitations.md +61 -0
  235. package/docs/paper/09-conclusion.md +11 -0
  236. package/docs/paper/SUBMISSION_README.md +162 -0
  237. package/docs/paper/appendix-a-demo-transcript.md +114 -0
  238. package/docs/paper/arxiv-compile-report.schema.json +116 -0
  239. package/docs/paper/arxiv-source.schema.json +61 -0
  240. package/docs/paper/audrey-paper-v1.md +1106 -0
  241. package/docs/paper/browser-launch-plan.json +209 -0
  242. package/docs/paper/browser-launch-plan.schema.json +100 -0
  243. package/docs/paper/browser-launch-results.json +86 -0
  244. package/docs/paper/browser-launch-results.schema.json +66 -0
  245. package/docs/paper/claim-register.json +138 -0
  246. package/docs/paper/claim-register.schema.json +81 -0
  247. package/docs/paper/evidence-ledger.md +103 -0
  248. package/docs/paper/output/arxiv/README-arxiv.txt +8 -0
  249. package/docs/paper/output/arxiv/arxiv-manifest.json +41 -0
  250. package/docs/paper/output/arxiv/main.tex +949 -0
  251. package/docs/paper/output/arxiv/references.bib +222 -0
  252. package/docs/paper/output/arxiv-compile-report.json +24 -0
  253. package/docs/paper/output/submission-bundle/LICENSE +21 -0
  254. package/docs/paper/output/submission-bundle/README.md +533 -0
  255. package/docs/paper/output/submission-bundle/benchmarks/output/adapter-self-test/guardbench-adapter-self-test.json +50 -0
  256. package/docs/paper/output/submission-bundle/benchmarks/output/external/guardbench-external-dry-run.json +69 -0
  257. package/docs/paper/output/submission-bundle/benchmarks/output/external/guardbench-external-evidence.json +56 -0
  258. package/docs/paper/output/submission-bundle/benchmarks/output/guardbench-conformance-card.json +63 -0
  259. package/docs/paper/output/submission-bundle/benchmarks/output/guardbench-manifest.json +414 -0
  260. package/docs/paper/output/submission-bundle/benchmarks/output/guardbench-raw.json +1171 -0
  261. package/docs/paper/output/submission-bundle/benchmarks/output/guardbench-summary.json +1981 -0
  262. package/docs/paper/output/submission-bundle/benchmarks/output/leaderboard/guardbench-leaderboard.json +93 -0
  263. package/docs/paper/output/submission-bundle/benchmarks/output/leaderboard/guardbench-leaderboard.md +7 -0
  264. package/docs/paper/output/submission-bundle/benchmarks/output/submission-bundle/submission-manifest.json +131 -0
  265. package/docs/paper/output/submission-bundle/benchmarks/output/submission-bundle/validation-report.json +31 -0
  266. package/docs/paper/output/submission-bundle/benchmarks/output/summary.json +2354 -0
  267. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-adapter-registry.schema.json +69 -0
  268. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-adapter-self-test.schema.json +156 -0
  269. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-conformance-card.schema.json +184 -0
  270. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-external-dry-run.schema.json +74 -0
  271. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-external-evidence.schema.json +108 -0
  272. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-external-run.schema.json +160 -0
  273. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-leaderboard.schema.json +179 -0
  274. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-manifest.schema.json +213 -0
  275. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-publication-verification.schema.json +47 -0
  276. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-raw.schema.json +164 -0
  277. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-submission-manifest.schema.json +151 -0
  278. package/docs/paper/output/submission-bundle/benchmarks/schemas/guardbench-summary.schema.json +228 -0
  279. package/docs/paper/output/submission-bundle/docs/AUDREY_PAPER_OUTLINE.md +175 -0
  280. package/docs/paper/output/submission-bundle/docs/paper/00-master.md +48 -0
  281. package/docs/paper/output/submission-bundle/docs/paper/01-introduction.md +27 -0
  282. package/docs/paper/output/submission-bundle/docs/paper/02-related-work.md +47 -0
  283. package/docs/paper/output/submission-bundle/docs/paper/03-problem-definition.md +108 -0
  284. package/docs/paper/output/submission-bundle/docs/paper/04-design.md +164 -0
  285. package/docs/paper/output/submission-bundle/docs/paper/05-guardbench-spec.md +412 -0
  286. package/docs/paper/output/submission-bundle/docs/paper/06-implementation.md +113 -0
  287. package/docs/paper/output/submission-bundle/docs/paper/07-evaluation.md +168 -0
  288. package/docs/paper/output/submission-bundle/docs/paper/08-discussion-limitations.md +61 -0
  289. package/docs/paper/output/submission-bundle/docs/paper/09-conclusion.md +11 -0
  290. package/docs/paper/output/submission-bundle/docs/paper/SUBMISSION_README.md +162 -0
  291. package/docs/paper/output/submission-bundle/docs/paper/appendix-a-demo-transcript.md +114 -0
  292. package/docs/paper/output/submission-bundle/docs/paper/arxiv-compile-report.schema.json +116 -0
  293. package/docs/paper/output/submission-bundle/docs/paper/arxiv-source.schema.json +61 -0
  294. package/docs/paper/output/submission-bundle/docs/paper/audrey-paper-v1.md +1106 -0
  295. package/docs/paper/output/submission-bundle/docs/paper/browser-launch-plan.json +209 -0
  296. package/docs/paper/output/submission-bundle/docs/paper/browser-launch-plan.schema.json +100 -0
  297. package/docs/paper/output/submission-bundle/docs/paper/browser-launch-results.json +86 -0
  298. package/docs/paper/output/submission-bundle/docs/paper/browser-launch-results.schema.json +66 -0
  299. package/docs/paper/output/submission-bundle/docs/paper/claim-register.json +138 -0
  300. package/docs/paper/output/submission-bundle/docs/paper/claim-register.schema.json +81 -0
  301. package/docs/paper/output/submission-bundle/docs/paper/evidence-ledger.md +103 -0
  302. package/docs/paper/output/submission-bundle/docs/paper/output/arxiv/README-arxiv.txt +8 -0
  303. package/docs/paper/output/submission-bundle/docs/paper/output/arxiv/arxiv-manifest.json +41 -0
  304. package/docs/paper/output/submission-bundle/docs/paper/output/arxiv/main.tex +949 -0
  305. package/docs/paper/output/submission-bundle/docs/paper/output/arxiv/references.bib +222 -0
  306. package/docs/paper/output/submission-bundle/docs/paper/output/arxiv-compile-report.json +24 -0
  307. package/docs/paper/output/submission-bundle/docs/paper/paper-submission-bundle.schema.json +70 -0
  308. package/docs/paper/output/submission-bundle/docs/paper/publication-pack.json +81 -0
  309. package/docs/paper/output/submission-bundle/docs/paper/publication-pack.schema.json +60 -0
  310. package/docs/paper/output/submission-bundle/docs/paper/references.bib +222 -0
  311. package/docs/paper/output/submission-bundle/package.json +212 -0
  312. package/docs/paper/output/submission-bundle/paper-submission-manifest.json +379 -0
  313. package/docs/paper/paper-submission-bundle.schema.json +70 -0
  314. package/docs/paper/publication-pack.json +81 -0
  315. package/docs/paper/publication-pack.schema.json +60 -0
  316. package/docs/paper/references.bib +222 -0
  317. package/package.json +103 -26
  318. package/scripts/audit-release-completion.mjs +362 -0
  319. package/scripts/create-arxiv-source.mjs +362 -0
  320. package/scripts/create-paper-submission-bundle.mjs +210 -0
  321. package/scripts/finalize-release.mjs +526 -0
  322. package/scripts/prepare-release-cut.mjs +269 -0
  323. package/scripts/publish-release-bundle.mjs +209 -0
  324. package/scripts/publish-release-github-api.mjs +429 -0
  325. package/scripts/run-vitest.mjs +34 -0
  326. package/scripts/smoke-cli.js +72 -0
  327. package/scripts/sync-paper-artifacts.mjs +109 -0
  328. package/scripts/verify-arxiv-compile.mjs +440 -0
  329. package/scripts/verify-arxiv-source.mjs +194 -0
  330. package/scripts/verify-browser-launch-plan.mjs +237 -0
  331. package/scripts/verify-browser-launch-results.mjs +285 -0
  332. package/scripts/verify-paper-artifacts.mjs +338 -0
  333. package/scripts/verify-paper-claims.mjs +226 -0
  334. package/scripts/verify-paper-submission-bundle.mjs +207 -0
  335. package/scripts/verify-publication-pack.mjs +196 -0
  336. package/scripts/verify-python-package.py +201 -0
  337. package/scripts/verify-release-readiness.mjs +741 -0
  338. package/docs/assets/benchmarks/local-benchmark.svg +0 -45
  339. package/docs/assets/benchmarks/operations-benchmark.svg +0 -45
  340. package/docs/assets/benchmarks/published-memory-standards.svg +0 -50
  341. package/docs/audrey-for-dummies.md +0 -670
  342. package/docs/benchmarking.md +0 -151
  343. package/docs/future-of-llm-memory.md +0 -452
  344. package/docs/mcp-hosts.md +0 -206
  345. package/docs/ollama-local-agents.md +0 -128
  346. package/docs/production-readiness.md +0 -128
@@ -9,6 +9,8 @@ import { runConsolidation } from './consolidate.js';
9
9
  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
+ import { applyFeedback } from './feedback.js';
13
+ import { buildImpactReport } from './impact.js';
12
14
  import { introspect as introspectFn } from './introspect.js';
13
15
  import { buildContextResolutionPrompt, buildReflectionPrompt } from './prompts.js';
14
16
  import { exportMemories } from './export.js';
@@ -22,11 +24,100 @@ import { listEvents, countEvents, recentFailures, } from './events.js';
22
24
  import { buildCapsule } from './capsule.js';
23
25
  import { buildPreflight } from './preflight.js';
24
26
  import { buildReflexReport } from './reflexes.js';
27
+ import { beforeAction as guardBeforeAction, afterAction as guardAfterAction, } from './controller.js';
25
28
  import { findPromotionCandidates, } from './promote.js';
26
29
  import { renderAllRules } from './rules-compiler.js';
27
30
  import { insertEvent } from './events.js';
28
31
  import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
29
- import { dirname, join, resolve as pathResolve } from 'node:path';
32
+ import { dirname, join, resolve as pathResolve, relative, isAbsolute as pathIsAbsolute } from 'node:path';
33
+ import { ProfileRecorder } from './profile.js';
34
+ import { performance } from 'node:perf_hooks';
35
+ function roundMs(value) {
36
+ return Math.round(value * 1000) / 1000;
37
+ }
38
+ function validateEncodeParams(params) {
39
+ if (!params.content || typeof params.content !== 'string') {
40
+ throw new Error('content must be a non-empty string');
41
+ }
42
+ if (params.salience !== undefined && (params.salience < 0 || params.salience > 1)) {
43
+ throw new Error('salience must be between 0 and 1');
44
+ }
45
+ if (params.tags !== undefined && !Array.isArray(params.tags)) {
46
+ throw new Error('tags must be an array');
47
+ }
48
+ }
49
+ const REFLECTION_SOURCES = new Set([
50
+ 'direct-observation',
51
+ 'told-by-user',
52
+ 'inference',
53
+ ]);
54
+ function boundedString(value, maxLength) {
55
+ if (typeof value !== 'string')
56
+ return undefined;
57
+ const trimmed = value.trim();
58
+ if (!trimmed)
59
+ return undefined;
60
+ return trimmed.slice(0, maxLength);
61
+ }
62
+ function boundedNumber(value, min, max) {
63
+ if (typeof value !== 'number' || !Number.isFinite(value))
64
+ return undefined;
65
+ return Math.max(min, Math.min(max, value));
66
+ }
67
+ function normalizeReflectionAffect(raw) {
68
+ if (!raw || typeof raw !== 'object')
69
+ return undefined;
70
+ const record = raw;
71
+ const valence = boundedNumber(record.valence, -1, 1);
72
+ const arousal = boundedNumber(record.arousal, 0, 1);
73
+ if (valence === undefined && arousal === undefined)
74
+ return undefined;
75
+ const affect = {};
76
+ if (valence !== undefined)
77
+ affect.valence = valence;
78
+ if (arousal !== undefined)
79
+ affect.arousal = arousal;
80
+ const label = boundedString(record.label, 64);
81
+ if (label)
82
+ affect.label = label;
83
+ return affect;
84
+ }
85
+ function normalizeReflectionMemory(raw) {
86
+ if (!raw || typeof raw !== 'object')
87
+ return null;
88
+ const record = raw;
89
+ const content = boundedString(record.content, 5000);
90
+ if (!content)
91
+ return null;
92
+ const source = record.source;
93
+ if (typeof source !== 'string' || !REFLECTION_SOURCES.has(source)) {
94
+ return null;
95
+ }
96
+ const memory = {
97
+ content,
98
+ source: source,
99
+ };
100
+ const salience = boundedNumber(record.salience, 0, 1);
101
+ if (salience !== undefined)
102
+ memory.salience = salience;
103
+ if (Array.isArray(record.tags)) {
104
+ const tags = record.tags
105
+ .map(tag => boundedString(tag, 64))
106
+ .filter((tag) => Boolean(tag))
107
+ .slice(0, 20);
108
+ if (tags.length > 0)
109
+ memory.tags = tags;
110
+ }
111
+ if (typeof record.private === 'boolean')
112
+ memory.private = record.private;
113
+ const affect = normalizeReflectionAffect(record.affect);
114
+ if (affect)
115
+ memory.affect = affect;
116
+ return memory;
117
+ }
118
+ function messagesToLegacyPrompt(messages) {
119
+ return messages.map(message => `${message.role.toUpperCase()}:\n${message.content}`).join('\n\n');
120
+ }
30
121
  export class Audrey extends EventEmitter {
31
122
  agent;
32
123
  dataDir;
@@ -39,10 +130,18 @@ export class Audrey extends EventEmitter {
39
130
  interferenceConfig;
40
131
  contextConfig;
41
132
  affectConfig;
133
+ defaultRetrievalMode;
42
134
  autoReflect;
43
135
  _migrationPending;
44
136
  _autoConsolidateTimer;
45
137
  _closed;
138
+ _postEncodeQueue;
139
+ _pendingPostEncodeIds;
140
+ _embeddingWarm;
141
+ _embeddingWarmupPromise;
142
+ _warmupDurationMs;
143
+ _lastRecallCheckAt;
144
+ _lastRecallErrors;
46
145
  constructor({ dataDir = './audrey-data', agent = 'default', embedding = { provider: 'mock', dimensions: 64 }, llm, confidence = {}, consolidation = {}, decay = {}, interference = {}, context = {}, affect = {}, autoReflect = false, } = {}) {
47
146
  super();
48
147
  const dormantThreshold = decay.dormantThreshold ?? 0.1;
@@ -69,9 +168,9 @@ export class Audrey extends EventEmitter {
69
168
  affectWeight: affect.weight ?? 0.2,
70
169
  };
71
170
  this.consolidationConfig = {
72
- minEpisodes: consolidation.minEpisodes || 3,
171
+ minEpisodes,
73
172
  };
74
- this.decayConfig = { dormantThreshold: decay.dormantThreshold || 0.1 };
173
+ this.decayConfig = { dormantThreshold };
75
174
  this._autoConsolidateTimer = null;
76
175
  this._closed = false;
77
176
  this.interferenceConfig = {
@@ -95,7 +194,15 @@ export class Audrey extends EventEmitter {
95
194
  affectThreshold: affect.resonance?.affectThreshold ?? 0.6,
96
195
  },
97
196
  };
197
+ this.defaultRetrievalMode = 'hybrid';
98
198
  this.autoReflect = autoReflect;
199
+ this._postEncodeQueue = Promise.resolve();
200
+ this._pendingPostEncodeIds = new Set();
201
+ this._embeddingWarm = false;
202
+ this._embeddingWarmupPromise = null;
203
+ this._warmupDurationMs = null;
204
+ this._lastRecallCheckAt = null;
205
+ this._lastRecallErrors = [];
99
206
  }
100
207
  async _ensureMigrated() {
101
208
  if (!this._migrationPending)
@@ -104,54 +211,207 @@ export class Audrey extends EventEmitter {
104
211
  this._migrationPending = false;
105
212
  this.emit('migration', counts);
106
213
  }
107
- _emitValidation(id, params) {
108
- validateMemory(this.db, this.embeddingProvider, { id, ...params }, {
109
- llmProvider: this.llmProvider,
214
+ startEmbeddingWarmup(text = 'warmup') {
215
+ if (this._embeddingWarm)
216
+ return Promise.resolve();
217
+ if (this._embeddingWarmupPromise)
218
+ return this._embeddingWarmupPromise;
219
+ const startedAt = performance.now();
220
+ this._embeddingWarmupPromise = (async () => {
221
+ if (typeof this.embeddingProvider.ready === 'function') {
222
+ await this.embeddingProvider.ready();
223
+ }
224
+ await this.embeddingProvider.embed(text);
225
+ this._embeddingWarm = true;
226
+ })()
227
+ .catch(err => {
228
+ this._emitQueueError(err);
229
+ throw err;
110
230
  })
111
- .then(validation => {
112
- if (validation.action === 'reinforced') {
113
- this.emit('reinforcement', {
114
- episodeId: id,
115
- targetId: validation.semanticId,
116
- similarity: validation.similarity,
117
- });
231
+ .finally(() => {
232
+ this._warmupDurationMs = roundMs(performance.now() - startedAt);
233
+ });
234
+ return this._embeddingWarmupPromise;
235
+ }
236
+ async _waitForEmbeddingWarmup(profile, spanName = 'embedding.wait_for_warmup') {
237
+ if (!this._embeddingWarmupPromise || this._embeddingWarm)
238
+ return;
239
+ const wait = async () => {
240
+ try {
241
+ await this._embeddingWarmupPromise;
118
242
  }
119
- else if (validation.action === 'contradiction') {
120
- this.emit('contradiction', {
121
- episodeId: id,
122
- contradictionId: validation.contradictionId,
123
- semanticId: validation.semanticId,
124
- similarity: validation.similarity,
125
- resolution: validation.resolution,
126
- });
243
+ catch {
244
+ // Warmup failure should not poison the foreground call; the foreground
245
+ // embed path will surface provider errors if the provider is truly broken.
127
246
  }
128
- })
129
- .catch(err => this.emit('error', err));
247
+ };
248
+ if (profile)
249
+ await profile.measure(spanName, wait);
250
+ else
251
+ await wait();
130
252
  }
131
- async encode(params) {
132
- await this._ensureMigrated();
133
- const encodeParams = { ...params, arousalWeight: this.affectConfig.arousalWeight };
134
- const id = await encodeEpisode(this.db, this.embeddingProvider, encodeParams);
135
- this.emit('encode', { id, ...params });
253
+ async _validateEncodedMemory(id, params, embedding) {
254
+ const validation = await validateMemory(this.db, this.embeddingProvider, { id, ...params }, {
255
+ llmProvider: this.llmProvider,
256
+ embeddingVector: embedding?.vector,
257
+ embeddingBuffer: embedding?.buffer,
258
+ });
259
+ if (validation.action === 'reinforced') {
260
+ this.emit('reinforcement', {
261
+ episodeId: id,
262
+ targetId: validation.semanticId,
263
+ similarity: validation.similarity,
264
+ });
265
+ }
266
+ else if (validation.action === 'contradiction') {
267
+ this.emit('contradiction', {
268
+ episodeId: id,
269
+ contradictionId: validation.contradictionId,
270
+ semanticId: validation.semanticId,
271
+ similarity: validation.similarity,
272
+ resolution: validation.resolution,
273
+ });
274
+ }
275
+ }
276
+ async _runPostEncodeStage(name, run) {
277
+ try {
278
+ await run();
279
+ }
280
+ catch (err) {
281
+ this._emitQueueError(Object.assign(err instanceof Error ? err : new Error(String(err)), {
282
+ stage: name,
283
+ }));
284
+ }
285
+ }
286
+ async _runPostEncode(id, params, embedding) {
136
287
  if (this.interferenceConfig.enabled) {
137
- applyInterference(this.db, this.embeddingProvider, id, params, this.interferenceConfig)
138
- .then(affected => {
288
+ await this._runPostEncodeStage('interference', async () => {
289
+ const affected = await applyInterference(this.db, this.embeddingProvider, id, params, this.interferenceConfig, embedding);
139
290
  if (affected.length > 0) {
140
291
  this.emit('interference', { episodeId: id, affected });
141
292
  }
142
- })
143
- .catch(err => this.emit('error', err));
293
+ });
144
294
  }
145
295
  if (this.affectConfig.enabled && this.affectConfig.resonance.enabled && params.affect?.valence !== undefined) {
146
- detectResonance(this.db, this.embeddingProvider, id, params, this.affectConfig.resonance)
147
- .then(echoes => {
296
+ await this._runPostEncodeStage('resonance', async () => {
297
+ const echoes = await detectResonance(this.db, this.embeddingProvider, id, params, this.affectConfig.resonance, embedding);
148
298
  if (echoes.length > 0) {
149
299
  this.emit('resonance', { episodeId: id, affect: params.affect, echoes });
150
300
  }
151
- })
152
- .catch(err => this.emit('error', err));
301
+ });
302
+ }
303
+ await this._runPostEncodeStage('validation', async () => {
304
+ await this._validateEncodedMemory(id, params, embedding);
305
+ });
306
+ }
307
+ _enqueuePostEncode(id, params, embedding) {
308
+ const enqueuedAt = performance.now();
309
+ this._pendingPostEncodeIds.add(id);
310
+ const run = async () => {
311
+ const startedAt = performance.now();
312
+ try {
313
+ if (!this._closed) {
314
+ await this._runPostEncode(id, params, embedding);
315
+ }
316
+ }
317
+ finally {
318
+ const finishedAt = performance.now();
319
+ this._pendingPostEncodeIds.delete(id);
320
+ this.emit('post-encode-complete', {
321
+ episodeId: id,
322
+ queued_ms: roundMs(startedAt - enqueuedAt),
323
+ processing_ms: roundMs(finishedAt - startedAt),
324
+ total_ms: roundMs(finishedAt - enqueuedAt),
325
+ pending_consolidation_count: this._pendingPostEncodeIds.size,
326
+ });
327
+ }
328
+ };
329
+ const task = this._postEncodeQueue.then(run, run);
330
+ this._postEncodeQueue = task.catch(err => {
331
+ this._emitQueueError(err);
332
+ });
333
+ return task;
334
+ }
335
+ _emitQueueError(err) {
336
+ if (this.listenerCount('error') > 0) {
337
+ // Caller has opted into error handling; let them route logging.
338
+ this.emit('error', err);
339
+ return;
340
+ }
341
+ // Standard EventEmitter idiom: log only when nobody is listening, so we
342
+ // surface failures by default but don't double-log for apps with structured
343
+ // error pipelines. The MCP server registers a logger listener at startup.
344
+ const stage = err?.stage;
345
+ const prefix = stage ? `[audrey:post-encode:${stage}]` : '[audrey:post-encode]';
346
+ const message = err instanceof Error ? (err.stack ?? err.message) : String(err);
347
+ console.error(`${prefix} ${message}`);
348
+ }
349
+ pendingConsolidationIds() {
350
+ return [...this._pendingPostEncodeIds];
351
+ }
352
+ async drainPostEncodeQueue(timeoutMs = 5000) {
353
+ if (this._pendingPostEncodeIds.size === 0) {
354
+ return { drained: true, pendingIds: [] };
355
+ }
356
+ let timeout;
357
+ const timedOut = Symbol('timed-out');
358
+ const timeoutPromise = new Promise(resolve => {
359
+ timeout = setTimeout(() => resolve(timedOut), timeoutMs);
360
+ });
361
+ const result = await Promise.race([
362
+ this._postEncodeQueue.then(() => true),
363
+ timeoutPromise,
364
+ ]);
365
+ if (timeout)
366
+ clearTimeout(timeout);
367
+ const drained = result === true && this._pendingPostEncodeIds.size === 0;
368
+ return {
369
+ drained,
370
+ pendingIds: this.pendingConsolidationIds(),
371
+ };
372
+ }
373
+ async encode(params) {
374
+ return this._encodeInternal(params);
375
+ }
376
+ async encodeWithDiagnostics(params) {
377
+ const profile = new ProfileRecorder('memory_encode');
378
+ const id = await this._encodeInternal(params, profile);
379
+ return { id, diagnostics: profile.finish() };
380
+ }
381
+ async _encodeInternal(params, profile) {
382
+ await this._waitForEmbeddingWarmup(profile, 'encode.wait_for_warmup');
383
+ if (profile)
384
+ await profile.measure('encode.ensure_migrated', () => this._ensureMigrated());
385
+ else
386
+ await this._ensureMigrated();
387
+ const encodeParams = { ...params, agent: params.agent ?? this.agent, arousalWeight: this.affectConfig.arousalWeight };
388
+ let encodedVector;
389
+ let encodedBuffer;
390
+ const id = profile
391
+ ? await profile.measure('encode.episode', () => encodeEpisode(this.db, this.embeddingProvider, encodeParams, {
392
+ profile,
393
+ onVector: (vector, buffer) => {
394
+ encodedVector = vector;
395
+ encodedBuffer = buffer;
396
+ },
397
+ }))
398
+ : await encodeEpisode(this.db, this.embeddingProvider, encodeParams, {
399
+ onVector: (vector, buffer) => {
400
+ encodedVector = vector;
401
+ encodedBuffer = buffer;
402
+ },
403
+ });
404
+ const encodedEmbedding = { vector: encodedVector, buffer: encodedBuffer };
405
+ this.emit('encode', { id, ...params });
406
+ const postEncodeTask = profile
407
+ ? profile.measureSync('encode.enqueue_background', () => this._enqueuePostEncode(id, params, encodedEmbedding))
408
+ : this._enqueuePostEncode(id, params, encodedEmbedding);
409
+ if (params.waitForConsolidation) {
410
+ if (profile)
411
+ await profile.measure('encode.wait_for_consolidation', () => postEncodeTask);
412
+ else
413
+ await postEncodeTask;
153
414
  }
154
- this._emitValidation(id, params);
155
415
  return id;
156
416
  }
157
417
  async reflect(turns) {
@@ -160,7 +420,15 @@ export class Audrey extends EventEmitter {
160
420
  const prompt = buildReflectionPrompt(turns);
161
421
  let raw;
162
422
  try {
163
- raw = await this.llmProvider.chat(prompt);
423
+ if (typeof this.llmProvider.complete === 'function') {
424
+ raw = (await this.llmProvider.complete(prompt)).content;
425
+ }
426
+ else if (typeof this.llmProvider.chat === 'function') {
427
+ raw = await this.llmProvider.chat(messagesToLegacyPrompt(prompt));
428
+ }
429
+ else {
430
+ return { encoded: 0, memories: [], skipped: 'llm provider missing completion method' };
431
+ }
164
432
  }
165
433
  catch (err) {
166
434
  this.emit('error', err);
@@ -173,11 +441,11 @@ export class Audrey extends EventEmitter {
173
441
  catch {
174
442
  return { encoded: 0, memories: [], skipped: 'invalid llm response' };
175
443
  }
176
- const memories = parsed.memories ?? [];
444
+ const memories = Array.isArray(parsed.memories)
445
+ ? parsed.memories.map(normalizeReflectionMemory).filter((mem) => mem !== null).slice(0, 50)
446
+ : [];
177
447
  let encoded = 0;
178
448
  for (const mem of memories) {
179
- if (!mem.content || !mem.source)
180
- continue;
181
449
  try {
182
450
  await this.encode({
183
451
  content: mem.content,
@@ -193,32 +461,80 @@ export class Audrey extends EventEmitter {
193
461
  this.emit('error', err);
194
462
  }
195
463
  }
196
- return { encoded, memories: memories };
464
+ return { encoded, memories };
197
465
  }
198
466
  async encodeBatch(paramsList) {
467
+ await this._waitForEmbeddingWarmup();
199
468
  await this._ensureMigrated();
200
- const ids = [];
469
+ if (paramsList.length === 0)
470
+ return [];
201
471
  for (const params of paramsList) {
202
- const id = await encodeEpisode(this.db, this.embeddingProvider, params);
472
+ validateEncodeParams(params);
473
+ }
474
+ const normalized = paramsList.map(params => ({
475
+ ...params,
476
+ agent: params.agent ?? this.agent,
477
+ arousalWeight: this.affectConfig.arousalWeight,
478
+ }));
479
+ const vectors = await this.embeddingProvider.embedBatch(normalized.map(params => params.content));
480
+ if (vectors.length !== normalized.length) {
481
+ throw new Error(`embedBatch returned ${vectors.length} vectors for ${normalized.length} inputs`);
482
+ }
483
+ const ids = [];
484
+ const tasks = [];
485
+ for (let i = 0; i < normalized.length; i++) {
486
+ const encodeParams = normalized[i];
487
+ let encodedVector;
488
+ let encodedBuffer;
489
+ const id = await encodeEpisode(this.db, this.embeddingProvider, encodeParams, {
490
+ vector: vectors[i],
491
+ onVector: (vector, buffer) => {
492
+ encodedVector = vector;
493
+ encodedBuffer = buffer;
494
+ },
495
+ });
203
496
  ids.push(id);
204
- this.emit('encode', { id, ...params });
497
+ this.emit('encode', { id, ...paramsList[i] });
498
+ const encodedEmbedding = { vector: encodedVector, buffer: encodedBuffer };
499
+ tasks.push(this._enqueuePostEncode(id, paramsList[i], encodedEmbedding));
205
500
  }
206
- for (let i = 0; i < ids.length; i++) {
207
- this._emitValidation(ids[i], paramsList[i]);
501
+ if (paramsList.some(p => p.waitForConsolidation)) {
502
+ await Promise.all(tasks);
208
503
  }
209
504
  return ids;
210
505
  }
211
506
  async recall(query, options = {}) {
212
- await this._ensureMigrated();
213
- return recallFn(this.db, this.embeddingProvider, query, {
507
+ return this._recallInternal(query, options);
508
+ }
509
+ async recallWithDiagnostics(query, options = {}) {
510
+ const profile = new ProfileRecorder('memory_recall');
511
+ const results = await this._recallInternal(query, options, profile);
512
+ return { results, diagnostics: profile.finish() };
513
+ }
514
+ async _recallInternal(query, options = {}, profile) {
515
+ await this._waitForEmbeddingWarmup(profile, 'recall.wait_for_warmup');
516
+ if (profile)
517
+ await profile.measure('recall.ensure_migrated', () => this._ensureMigrated());
518
+ else
519
+ await this._ensureMigrated();
520
+ const results = await recallFn(this.db, this.embeddingProvider, query, {
214
521
  ...options,
522
+ agent: options.agent ?? this.agent,
523
+ retrieval: options.retrieval ?? this.defaultRetrievalMode,
215
524
  confidenceConfig: this._recallConfig(options),
525
+ profile,
216
526
  });
527
+ this._lastRecallCheckAt = new Date().toISOString();
528
+ this._lastRecallErrors = results.errors ?? [];
529
+ return results;
217
530
  }
218
531
  async *recallStream(query, options = {}) {
532
+ await this._waitForEmbeddingWarmup();
219
533
  await this._ensureMigrated();
220
534
  yield* recallStreamFn(this.db, this.embeddingProvider, query, {
221
535
  ...options,
536
+ agent: options.agent ?? this.agent,
537
+ retrieval: options.retrieval ?? this.defaultRetrievalMode,
222
538
  confidenceConfig: this._recallConfig(options),
223
539
  });
224
540
  }
@@ -234,20 +550,22 @@ export class Audrey extends EventEmitter {
234
550
  }
235
551
  async consolidate(options = {}) {
236
552
  await this._ensureMigrated();
553
+ // Use ?? throughout so 0 / '' are not silently replaced with the default.
237
554
  const result = await runConsolidation(this.db, this.embeddingProvider, {
238
- minClusterSize: options.minClusterSize || this.consolidationConfig.minEpisodes,
239
- similarityThreshold: options.similarityThreshold || 0.80,
555
+ minClusterSize: options.minClusterSize ?? this.consolidationConfig.minEpisodes,
556
+ similarityThreshold: options.similarityThreshold ?? 0.80,
557
+ agent: options.agent ?? this.agent,
240
558
  extractPrinciple: options.extractPrinciple,
241
- llmProvider: options.llmProvider || this.llmProvider || undefined,
559
+ llmProvider: options.llmProvider ?? this.llmProvider ?? undefined,
242
560
  });
243
561
  const run = db_prepare_get_status(this.db, result.runId);
244
- const output = { ...result, status: run?.status || 'completed' };
562
+ const output = { ...result, status: run?.status ?? 'completed' };
245
563
  this.emit('consolidation', output);
246
564
  return output;
247
565
  }
248
566
  decay(options = {}) {
249
567
  const result = applyDecay(this.db, {
250
- dormantThreshold: options.dormantThreshold || this.decayConfig.dormantThreshold,
568
+ dormantThreshold: options.dormantThreshold ?? this.decayConfig.dormantThreshold,
251
569
  halfLives: options.halfLives ?? this.confidenceConfig.halfLives,
252
570
  });
253
571
  this.emit('decay', result);
@@ -271,9 +589,9 @@ export class Audrey extends EventEmitter {
271
589
  const result = await this.llmProvider.json(messages);
272
590
  const now = new Date().toISOString();
273
591
  const newState = result.resolution === 'context_dependent' ? 'context_dependent' : 'resolved';
274
- this.db.prepare(`
275
- UPDATE contradictions SET state = ?, resolution = ?, resolved_at = ?
276
- WHERE id = ?
592
+ this.db.prepare(`
593
+ UPDATE contradictions SET state = ?, resolution = ?, resolved_at = ?
594
+ WHERE id = ?
277
595
  `).run(newState, JSON.stringify(result), now, contradictionId);
278
596
  if (result.resolution === 'a_wins' && contradiction.claim_a_type === 'semantic') {
279
597
  this.db.prepare("UPDATE semantics SET state = 'active' WHERE id = ?").run(contradiction.claim_a_id);
@@ -354,14 +672,23 @@ export class Audrey extends EventEmitter {
354
672
  device: device ?? null,
355
673
  healthy,
356
674
  reembed_recommended: reembedRecommended,
675
+ pending_consolidation_count: this._pendingPostEncodeIds.size,
676
+ embedding_warm: this._embeddingWarm,
677
+ warmup_duration_ms: this._warmupDurationMs,
678
+ default_retrieval_mode: this.defaultRetrievalMode,
679
+ recall_degraded: this._lastRecallErrors.length > 0,
680
+ last_recall_check_at: this._lastRecallCheckAt,
681
+ last_recall_errors: this._lastRecallErrors,
357
682
  };
358
683
  }
359
- async greeting({ context, recentLimit = 10, principleLimit = 5, identityLimit = 5 } = {}) {
360
- const recent = this.db.prepare('SELECT id, content, source, tags, salience, created_at FROM episodes WHERE "private" = 0 ORDER BY created_at DESC LIMIT ?').all(recentLimit);
361
- const principles = this.db.prepare('SELECT id, content, salience, created_at FROM semantics WHERE state = ? ORDER BY salience DESC LIMIT ?').all('active', principleLimit);
362
- const identity = this.db.prepare('SELECT id, content, tags, salience, created_at FROM episodes WHERE "private" = 1 ORDER BY created_at DESC LIMIT ?').all(identityLimit);
363
- const unresolved = this.db.prepare("SELECT id, content, tags, salience, created_at FROM episodes WHERE tags LIKE '%unresolved%' AND salience > 0.3 ORDER BY created_at DESC LIMIT 10").all();
364
- const rawAffectRows = this.db.prepare("SELECT affect FROM episodes WHERE affect IS NOT NULL AND affect != '{}' ORDER BY created_at DESC LIMIT 20").all();
684
+ async greeting({ context, recentLimit = 10, principleLimit = 5, identityLimit = 5, scope = 'agent' } = {}) {
685
+ const agentClause = scope === 'agent' ? 'AND agent = ?' : '';
686
+ const agentParam = scope === 'agent' ? [this.agent] : [];
687
+ const recent = this.db.prepare(`SELECT id, content, source, tags, salience, created_at FROM episodes WHERE "private" = 0 ${agentClause} ORDER BY created_at DESC LIMIT ?`).all(...agentParam, recentLimit);
688
+ const principles = this.db.prepare(`SELECT id, content, salience, created_at FROM semantics WHERE state = ? ${agentClause} ORDER BY salience DESC LIMIT ?`).all('active', ...agentParam, principleLimit);
689
+ const identity = this.db.prepare(`SELECT id, content, tags, salience, created_at FROM episodes WHERE "private" = 1 ${agentClause} ORDER BY created_at DESC LIMIT ?`).all(...agentParam, identityLimit);
690
+ const unresolved = this.db.prepare(`SELECT id, content, tags, salience, created_at FROM episodes WHERE tags LIKE '%unresolved%' AND salience > 0.3 ${agentClause} ORDER BY created_at DESC LIMIT 10`).all(...agentParam);
691
+ const rawAffectRows = this.db.prepare(`SELECT affect FROM episodes WHERE affect IS NOT NULL AND affect != '{}' ${agentClause} ORDER BY created_at DESC LIMIT 20`).all(...agentParam);
365
692
  const affectParsed = rawAffectRows
366
693
  .map(r => { try {
367
694
  return JSON.parse(r.affect);
@@ -385,7 +712,7 @@ export class Audrey extends EventEmitter {
385
712
  }
386
713
  const result = { recent, principles, mood, unresolved, identity };
387
714
  if (context) {
388
- result.contextual = await this.recall(context, { limit: 5, includePrivate: true });
715
+ result.contextual = await this.recall(context, { limit: 5, includePrivate: true, scope });
389
716
  }
390
717
  return result;
391
718
  }
@@ -436,6 +763,76 @@ export class Audrey extends EventEmitter {
436
763
  suggestConsolidationParams() {
437
764
  return suggestParamsFn(this.db);
438
765
  }
766
+ validate(input) {
767
+ let preflightMetadata = null;
768
+ if (input.preflightEventId) {
769
+ const preflightEvent = this.db.prepare("SELECT event_type, metadata FROM memory_events WHERE id = ?").get(input.preflightEventId);
770
+ if (!preflightEvent) {
771
+ throw new Error(`preflight_event_id ${input.preflightEventId} was not found`);
772
+ }
773
+ if (preflightEvent.event_type !== 'PreToolUse') {
774
+ throw new Error(`preflight_event_id ${input.preflightEventId} is not a PreToolUse event`);
775
+ }
776
+ if (preflightEvent.metadata) {
777
+ try {
778
+ preflightMetadata = JSON.parse(preflightEvent.metadata);
779
+ }
780
+ catch {
781
+ preflightMetadata = null;
782
+ }
783
+ }
784
+ const preflightEvidenceIds = Array.isArray(preflightMetadata?.preflight_evidence_ids)
785
+ ? preflightMetadata.preflight_evidence_ids.filter((id) => typeof id === 'string')
786
+ : [];
787
+ if (preflightEvidenceIds.length > 0 && !preflightEvidenceIds.includes(input.id)) {
788
+ throw new Error(`memory id ${input.id} was not evidence for preflight_event_id ${input.preflightEventId}`);
789
+ }
790
+ const preflightActionKey = typeof preflightMetadata?.audrey_guard_action_key === 'string'
791
+ ? preflightMetadata.audrey_guard_action_key
792
+ : undefined;
793
+ if (input.actionKey && preflightActionKey && input.actionKey !== preflightActionKey) {
794
+ throw new Error('action_key does not match the preflight event action key');
795
+ }
796
+ if (!input.actionKey && preflightActionKey) {
797
+ input = { ...input, actionKey: preflightActionKey };
798
+ }
799
+ if (!input.evidenceIds && preflightEvidenceIds.length > 0) {
800
+ input = { ...input, evidenceIds: preflightEvidenceIds };
801
+ }
802
+ }
803
+ const result = applyFeedback(this.db, input);
804
+ if (result) {
805
+ // Audit row in memory_events so audrey impact can show
806
+ // helpful-vs-wrong breakdown over a window. Outcome is mapped onto the
807
+ // events-table enum: helpful → succeeded, wrong → failed, used → unknown.
808
+ // The original outcome string is preserved in metadata.
809
+ const eventOutcome = input.outcome === 'helpful' ? 'succeeded'
810
+ : input.outcome === 'wrong' ? 'failed'
811
+ : 'unknown';
812
+ insertEvent(this.db, {
813
+ eventType: 'Validate',
814
+ source: 'memory_validate',
815
+ actorAgent: this.agent,
816
+ outcome: eventOutcome,
817
+ redactionState: 'clean',
818
+ metadata: {
819
+ memory_id: result.id,
820
+ memory_type: result.type,
821
+ outcome: input.outcome,
822
+ salience_after: result.salience,
823
+ usage_count_after: result.usageCount,
824
+ ...(input.preflightEventId ? { preflight_event_id: input.preflightEventId } : {}),
825
+ ...(input.actionKey ? { audrey_guard_action_key: input.actionKey } : {}),
826
+ ...(input.evidenceIds ? { preflight_evidence_ids: input.evidenceIds } : {}),
827
+ },
828
+ });
829
+ this.emit('validate', result);
830
+ }
831
+ return result;
832
+ }
833
+ impact(options = {}) {
834
+ return buildImpactReport(this.db, options.windowDays ?? 7, options.limit ?? 5);
835
+ }
439
836
  forget(id, options = {}) {
440
837
  const result = forgetMemory(this.db, id, options);
441
838
  this.emit('forget', result);
@@ -458,10 +855,28 @@ export class Audrey extends EventEmitter {
458
855
  return;
459
856
  this._closed = true;
460
857
  this.stopAutoConsolidate();
858
+ if (this._pendingPostEncodeIds.size > 0) {
859
+ // Sync close() can't await; emit a clear signal so callers can spot data loss.
860
+ // Use closeAsync() (preferred) or call drainPostEncodeQueue() before close() to avoid this.
861
+ console.error(`[audrey] close() called with ${this._pendingPostEncodeIds.size} pending post-encode tasks ` +
862
+ `(use closeAsync() or await drainPostEncodeQueue() first to avoid losing consolidation work)`);
863
+ }
461
864
  closeDatabase(this.db);
462
865
  }
866
+ async closeAsync(timeoutMs = 5000) {
867
+ if (this._closed)
868
+ return undefined;
869
+ let result;
870
+ if (this._pendingPostEncodeIds.size > 0) {
871
+ result = await this.drainPostEncodeQueue(timeoutMs);
872
+ }
873
+ this._closed = true;
874
+ this.stopAutoConsolidate();
875
+ closeDatabase(this.db);
876
+ return result;
877
+ }
463
878
  async waitForIdle() {
464
- return Promise.resolve();
879
+ await this._postEncodeQueue;
465
880
  }
466
881
  observeTool(input) {
467
882
  const result = observeTool(this.db, {
@@ -490,6 +905,16 @@ export class Audrey extends EventEmitter {
490
905
  this.emit('preflight', preflight);
491
906
  return preflight;
492
907
  }
908
+ async beforeAction(action, options = {}) {
909
+ const decision = await guardBeforeAction(this, action, options);
910
+ this.emit('guard-before', decision);
911
+ return decision;
912
+ }
913
+ afterAction(input) {
914
+ const outcome = guardAfterAction(this, input);
915
+ this.emit('guard-after', outcome);
916
+ return outcome;
917
+ }
493
918
  async reflexes(action, options = {}) {
494
919
  const report = await buildReflexReport(this, action, options);
495
920
  this.emit('reflexes', report);
@@ -511,6 +936,28 @@ export class Audrey extends EventEmitter {
511
936
  });
512
937
  const dryRun = options.dryRun ?? !options.yes;
513
938
  const projectDir = pathResolve(options.projectDir ?? process.cwd());
939
+ // Guard against malicious project_dir from MCP/HTTP callers writing
940
+ // .claude/rules/*.md to arbitrary locations — those files are read by
941
+ // Claude Code on the next session, making this a persistent
942
+ // prompt-injection vector. By default the path must be under cwd or one
943
+ // of the explicit AUDREY_PROMOTE_ROOTS entries.
944
+ if (!dryRun) {
945
+ const allowedRoots = [pathResolve(process.cwd())];
946
+ const extra = process.env.AUDREY_PROMOTE_ROOTS;
947
+ if (extra) {
948
+ for (const root of extra.split(/[:;]/).map(s => s.trim()).filter(Boolean)) {
949
+ allowedRoots.push(pathResolve(root));
950
+ }
951
+ }
952
+ const isUnderAllowedRoot = allowedRoots.some(root => {
953
+ const rel = relative(root, projectDir);
954
+ return rel === '' || (!rel.startsWith('..') && !pathIsAbsolute(rel));
955
+ });
956
+ if (!isUnderAllowedRoot) {
957
+ throw new Error(`promote: refusing to write to ${projectDir} — path is outside cwd and AUDREY_PROMOTE_ROOTS. ` +
958
+ `Set AUDREY_PROMOTE_ROOTS=<path1>:<path2> to allow additional locations.`);
959
+ }
960
+ }
514
961
  const promotedAt = new Date().toISOString();
515
962
  const docs = renderAllRules(candidates, promotedAt);
516
963
  const applied = [];