mem-os-python 1.6.27__py3-none-any.whl

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 (253) hide show
  1. mem_os_python-1.6.27.dist-info/METADATA +1032 -0
  2. mem_os_python-1.6.27.dist-info/RECORD +253 -0
  3. mem_os_python-1.6.27.dist-info/WHEEL +4 -0
  4. mem_os_python-1.6.27.dist-info/entry_points.txt +2 -0
  5. mem_os_python-1.6.27.dist-info/licenses/LICENSE +21 -0
  6. memos/__init__.py +16 -0
  7. memos/_js/adapters/anthropic.d.ts +279 -0
  8. memos/_js/adapters/anthropic.d.ts.map +1 -0
  9. memos/_js/adapters/anthropic.js +348 -0
  10. memos/_js/adapters/anthropic.js.map +1 -0
  11. memos/_js/adapters/base.d.ts +10 -0
  12. memos/_js/adapters/base.d.ts.map +1 -0
  13. memos/_js/adapters/base.js +2 -0
  14. memos/_js/adapters/base.js.map +1 -0
  15. memos/_js/adapters/openai.d.ts +264 -0
  16. memos/_js/adapters/openai.d.ts.map +1 -0
  17. memos/_js/adapters/openai.js +342 -0
  18. memos/_js/adapters/openai.js.map +1 -0
  19. memos/_js/apps/explorer.d.ts +244 -0
  20. memos/_js/apps/explorer.d.ts.map +1 -0
  21. memos/_js/apps/explorer.js +738 -0
  22. memos/_js/apps/explorer.js.map +1 -0
  23. memos/_js/citations.d.ts +65 -0
  24. memos/_js/citations.d.ts.map +1 -0
  25. memos/_js/citations.js +143 -0
  26. memos/_js/citations.js.map +1 -0
  27. memos/_js/cli-browse.d.ts +3 -0
  28. memos/_js/cli-browse.d.ts.map +1 -0
  29. memos/_js/cli-browse.js +129 -0
  30. memos/_js/cli-browse.js.map +1 -0
  31. memos/_js/cli-completion.d.ts +31 -0
  32. memos/_js/cli-completion.d.ts.map +1 -0
  33. memos/_js/cli-completion.js +340 -0
  34. memos/_js/cli-completion.js.map +1 -0
  35. memos/_js/cli-help.d.ts +18 -0
  36. memos/_js/cli-help.d.ts.map +1 -0
  37. memos/_js/cli-help.js +357 -0
  38. memos/_js/cli-help.js.map +1 -0
  39. memos/_js/cli.d.ts +28 -0
  40. memos/_js/cli.d.ts.map +1 -0
  41. memos/_js/cli.js +2880 -0
  42. memos/_js/cli.js.map +1 -0
  43. memos/_js/compression.d.ts +25 -0
  44. memos/_js/compression.d.ts.map +1 -0
  45. memos/_js/compression.js +319 -0
  46. memos/_js/compression.js.map +1 -0
  47. memos/_js/confidence-machine.d.ts +99 -0
  48. memos/_js/confidence-machine.d.ts.map +1 -0
  49. memos/_js/confidence-machine.js +222 -0
  50. memos/_js/confidence-machine.js.map +1 -0
  51. memos/_js/context-pack.d.ts +506 -0
  52. memos/_js/context-pack.d.ts.map +1 -0
  53. memos/_js/context-pack.js +1435 -0
  54. memos/_js/context-pack.js.map +1 -0
  55. memos/_js/contradictions.d.ts +160 -0
  56. memos/_js/contradictions.d.ts.map +1 -0
  57. memos/_js/contradictions.js +255 -0
  58. memos/_js/contradictions.js.map +1 -0
  59. memos/_js/dashboard.d.ts +30 -0
  60. memos/_js/dashboard.d.ts.map +1 -0
  61. memos/_js/dashboard.js +302 -0
  62. memos/_js/dashboard.js.map +1 -0
  63. memos/_js/ebbinghaus.d.ts +29 -0
  64. memos/_js/ebbinghaus.d.ts.map +1 -0
  65. memos/_js/ebbinghaus.js +32 -0
  66. memos/_js/ebbinghaus.js.map +1 -0
  67. memos/_js/embedding-queue.d.ts +190 -0
  68. memos/_js/embedding-queue.d.ts.map +1 -0
  69. memos/_js/embedding-queue.js +316 -0
  70. memos/_js/embedding-queue.js.map +1 -0
  71. memos/_js/embeddings.d.ts +187 -0
  72. memos/_js/embeddings.d.ts.map +1 -0
  73. memos/_js/embeddings.js +633 -0
  74. memos/_js/embeddings.js.map +1 -0
  75. memos/_js/entity-extraction.d.ts +54 -0
  76. memos/_js/entity-extraction.d.ts.map +1 -0
  77. memos/_js/entity-extraction.js +325 -0
  78. memos/_js/entity-extraction.js.map +1 -0
  79. memos/_js/entity-index.d.ts +93 -0
  80. memos/_js/entity-index.d.ts.map +1 -0
  81. memos/_js/entity-index.js +165 -0
  82. memos/_js/entity-index.js.map +1 -0
  83. memos/_js/event-memory.d.ts +147 -0
  84. memos/_js/event-memory.d.ts.map +1 -0
  85. memos/_js/event-memory.js +290 -0
  86. memos/_js/event-memory.js.map +1 -0
  87. memos/_js/external-import.d.ts +98 -0
  88. memos/_js/external-import.d.ts.map +1 -0
  89. memos/_js/external-import.js +669 -0
  90. memos/_js/external-import.js.map +1 -0
  91. memos/_js/fidelity.d.ts +367 -0
  92. memos/_js/fidelity.d.ts.map +1 -0
  93. memos/_js/fidelity.js +911 -0
  94. memos/_js/fidelity.js.map +1 -0
  95. memos/_js/graph-expansion.d.ts +119 -0
  96. memos/_js/graph-expansion.d.ts.map +1 -0
  97. memos/_js/graph-expansion.js +213 -0
  98. memos/_js/graph-expansion.js.map +1 -0
  99. memos/_js/graph-mermaid.d.ts +13 -0
  100. memos/_js/graph-mermaid.d.ts.map +1 -0
  101. memos/_js/graph-mermaid.js +32 -0
  102. memos/_js/graph-mermaid.js.map +1 -0
  103. memos/_js/graph.d.ts +170 -0
  104. memos/_js/graph.d.ts.map +1 -0
  105. memos/_js/graph.js +482 -0
  106. memos/_js/graph.js.map +1 -0
  107. memos/_js/harness.d.ts +63 -0
  108. memos/_js/harness.d.ts.map +1 -0
  109. memos/_js/harness.js +93 -0
  110. memos/_js/harness.js.map +1 -0
  111. memos/_js/importance.d.ts +118 -0
  112. memos/_js/importance.d.ts.map +1 -0
  113. memos/_js/importance.js +144 -0
  114. memos/_js/importance.js.map +1 -0
  115. memos/_js/index.d.ts +51 -0
  116. memos/_js/index.d.ts.map +1 -0
  117. memos/_js/index.js +53 -0
  118. memos/_js/index.js.map +1 -0
  119. memos/_js/mcp.d.ts +58 -0
  120. memos/_js/mcp.d.ts.map +1 -0
  121. memos/_js/mcp.js +1853 -0
  122. memos/_js/mcp.js.map +1 -0
  123. memos/_js/memory.d.ts +1762 -0
  124. memos/_js/memory.d.ts.map +1 -0
  125. memos/_js/memory.js +6283 -0
  126. memos/_js/memory.js.map +1 -0
  127. memos/_js/procedural.d.ts +74 -0
  128. memos/_js/procedural.d.ts.map +1 -0
  129. memos/_js/procedural.js +125 -0
  130. memos/_js/procedural.js.map +1 -0
  131. memos/_js/project-config.d.ts +68 -0
  132. memos/_js/project-config.d.ts.map +1 -0
  133. memos/_js/project-config.js +59 -0
  134. memos/_js/project-config.js.map +1 -0
  135. memos/_js/proof-of-forgetting.d.ts +57 -0
  136. memos/_js/proof-of-forgetting.d.ts.map +1 -0
  137. memos/_js/proof-of-forgetting.js +80 -0
  138. memos/_js/proof-of-forgetting.js.map +1 -0
  139. memos/_js/provenance.d.ts +122 -0
  140. memos/_js/provenance.d.ts.map +1 -0
  141. memos/_js/provenance.js +182 -0
  142. memos/_js/provenance.js.map +1 -0
  143. memos/_js/quarantine-commands.d.ts +23 -0
  144. memos/_js/quarantine-commands.d.ts.map +1 -0
  145. memos/_js/quarantine-commands.js +42 -0
  146. memos/_js/quarantine-commands.js.map +1 -0
  147. memos/_js/quarantine.d.ts +120 -0
  148. memos/_js/quarantine.d.ts.map +1 -0
  149. memos/_js/quarantine.js +873 -0
  150. memos/_js/quarantine.js.map +1 -0
  151. memos/_js/redact.d.ts +12 -0
  152. memos/_js/redact.d.ts.map +1 -0
  153. memos/_js/redact.js +26 -0
  154. memos/_js/redact.js.map +1 -0
  155. memos/_js/repl.d.ts +57 -0
  156. memos/_js/repl.d.ts.map +1 -0
  157. memos/_js/repl.js +295 -0
  158. memos/_js/repl.js.map +1 -0
  159. memos/_js/rerank-local.d.ts +61 -0
  160. memos/_js/rerank-local.d.ts.map +1 -0
  161. memos/_js/rerank-local.js +210 -0
  162. memos/_js/rerank-local.js.map +1 -0
  163. memos/_js/retain-filter.d.ts +98 -0
  164. memos/_js/retain-filter.d.ts.map +1 -0
  165. memos/_js/retain-filter.js +232 -0
  166. memos/_js/retain-filter.js.map +1 -0
  167. memos/_js/retrieval.d.ts +265 -0
  168. memos/_js/retrieval.d.ts.map +1 -0
  169. memos/_js/retrieval.js +1018 -0
  170. memos/_js/retrieval.js.map +1 -0
  171. memos/_js/revert.d.ts +77 -0
  172. memos/_js/revert.d.ts.map +1 -0
  173. memos/_js/revert.js +252 -0
  174. memos/_js/revert.js.map +1 -0
  175. memos/_js/scope.d.ts +30 -0
  176. memos/_js/scope.d.ts.map +1 -0
  177. memos/_js/scope.js +71 -0
  178. memos/_js/scope.js.map +1 -0
  179. memos/_js/semantic-screen.d.ts +152 -0
  180. memos/_js/semantic-screen.d.ts.map +1 -0
  181. memos/_js/semantic-screen.js +290 -0
  182. memos/_js/semantic-screen.js.map +1 -0
  183. memos/_js/session-hooks.d.ts +127 -0
  184. memos/_js/session-hooks.d.ts.map +1 -0
  185. memos/_js/session-hooks.js +482 -0
  186. memos/_js/session-hooks.js.map +1 -0
  187. memos/_js/storage/sqlite.d.ts +427 -0
  188. memos/_js/storage/sqlite.d.ts.map +1 -0
  189. memos/_js/storage/sqlite.js +2428 -0
  190. memos/_js/storage/sqlite.js.map +1 -0
  191. memos/_js/sync.d.ts +213 -0
  192. memos/_js/sync.d.ts.map +1 -0
  193. memos/_js/sync.js +389 -0
  194. memos/_js/sync.js.map +1 -0
  195. memos/_js/tamper-log.d.ts +98 -0
  196. memos/_js/tamper-log.d.ts.map +1 -0
  197. memos/_js/tamper-log.js +327 -0
  198. memos/_js/tamper-log.js.map +1 -0
  199. memos/_js/temporal.d.ts +92 -0
  200. memos/_js/temporal.d.ts.map +1 -0
  201. memos/_js/temporal.js +536 -0
  202. memos/_js/temporal.js.map +1 -0
  203. memos/_js/time-travel.d.ts +38 -0
  204. memos/_js/time-travel.d.ts.map +1 -0
  205. memos/_js/time-travel.js +46 -0
  206. memos/_js/time-travel.js.map +1 -0
  207. memos/_js/timestamp-format.d.ts +18 -0
  208. memos/_js/timestamp-format.d.ts.map +1 -0
  209. memos/_js/timestamp-format.js +52 -0
  210. memos/_js/timestamp-format.js.map +1 -0
  211. memos/_js/triple-extraction.d.ts +31 -0
  212. memos/_js/triple-extraction.d.ts.map +1 -0
  213. memos/_js/triple-extraction.js +116 -0
  214. memos/_js/triple-extraction.js.map +1 -0
  215. memos/_js/types.d.ts +2474 -0
  216. memos/_js/types.d.ts.map +1 -0
  217. memos/_js/types.js +20 -0
  218. memos/_js/types.js.map +1 -0
  219. memos/_js/updater.d.ts +95 -0
  220. memos/_js/updater.d.ts.map +1 -0
  221. memos/_js/updater.js +268 -0
  222. memos/_js/updater.js.map +1 -0
  223. memos/_js/version.d.ts +17 -0
  224. memos/_js/version.d.ts.map +1 -0
  225. memos/_js/version.js +34 -0
  226. memos/_js/version.js.map +1 -0
  227. memos/_js/vision/clip-query-cache.d.ts +58 -0
  228. memos/_js/vision/clip-query-cache.d.ts.map +1 -0
  229. memos/_js/vision/clip-query-cache.js +79 -0
  230. memos/_js/vision/clip-query-cache.js.map +1 -0
  231. memos/_js/vision/clip.d.ts +113 -0
  232. memos/_js/vision/clip.d.ts.map +1 -0
  233. memos/_js/vision/clip.js +224 -0
  234. memos/_js/vision/clip.js.map +1 -0
  235. memos/_js/vision/fake-clip.d.ts +55 -0
  236. memos/_js/vision/fake-clip.d.ts.map +1 -0
  237. memos/_js/vision/fake-clip.js +122 -0
  238. memos/_js/vision/fake-clip.js.map +1 -0
  239. memos/_js/vision/index.d.ts +15 -0
  240. memos/_js/vision/index.d.ts.map +1 -0
  241. memos/_js/vision/index.js +13 -0
  242. memos/_js/vision/index.js.map +1 -0
  243. memos/_js/vision/store.d.ts +137 -0
  244. memos/_js/vision/store.d.ts.map +1 -0
  245. memos/_js/vision/store.js +383 -0
  246. memos/_js/vision/store.js.map +1 -0
  247. memos/adapters/__init__.py +0 -0
  248. memos/adapters/crewai.py +385 -0
  249. memos/adapters/langchain.py +301 -0
  250. memos/adapters/ollama.py +396 -0
  251. memos/server/__init__.py +0 -0
  252. memos/server/main.py +547 -0
  253. memos/server/routes.py +360 -0
memos/_js/memory.d.ts ADDED
@@ -0,0 +1,1762 @@
1
+ /**
2
+ * MemoryManager — the public API surface of MemOS.
3
+ *
4
+ * Orchestrates graph operations, persistence, auto-linking, and
5
+ * extractive summarisation. This is the class that application
6
+ * code interacts with.
7
+ *
8
+ * @module @memos/memory
9
+ */
10
+ import { type DetectedExportSource, type ExternalImportSource } from "./external-import.js";
11
+ import { type CitationResolution } from "./citations.js";
12
+ import type { ContextPack } from "./context-pack.js";
13
+ import type { DeletionCertificate } from "./proof-of-forgetting.js";
14
+ import type { CompactOptions, CompactResult, FidelityLevel, FidelityRecallOptions, FidelityRecallResult } from "./fidelity.js";
15
+ import type { TamperVerifyResult } from "./tamper-log.js";
16
+ import type { TamperLogEntry } from "./types.js";
17
+ import type { RelevanceGateResult, MemoryNode, MemoryEdge, MemoryPool, MemoryScope, MemoryHistory, HistoryOptions, CreateMemoryInput, UpdateMemoryInput, ScoredMemory, SearchFilter, SemanticSearchOptions, GraphSnapshot, MemOSConfig, MemOSEvent, MemOSEventListener, ExportOptions, ExportResult, ImportOptions, ImportResult, ContextPackCompressionOptions, ContextPackWireFormat, EmbeddingRuntimeInfo, MemoryType, MemorySource, EmbeddingNodeStatusInfo, EmbeddingQueueStatus, DedupeOptions, DedupeResult, DecayForgettingOptions, DecayForgetResult, ArchiveOptions, ArchiveResult, ConsolidateOptions, ConsolidateResult, SummarizeClusterOptions, RevertOptions, RevertResult, RevertTargetResolution, RevertScopeInput, SummarizeClusterResult, ConversationMessage, ExtractFactsOptions, ExtractFactsResult, DiagnosticsResult, LessonOutcome, ProceduralLesson, ProvenanceTier, HarnessCount, HarnessMergeOptions, HarnessMergeResult } from "./types.js";
18
+ import type { QuarantineVerdict } from "./quarantine.js";
19
+ import type { AddImageMemoryOptions, AddImageMemoryResult, ImageInput, ImageSearchResult, ListImagesOptions, SearchImagesOptions } from "./types.js";
20
+ /**
21
+ * Thrown by `store({ filterRetain: true })` when the Hermes-style retain
22
+ * pre-filter decides the content is too low-signal to store (v1.6.26).
23
+ * Catch this to silently skip noise instead of treating it as an error.
24
+ *
25
+ * @example
26
+ * try {
27
+ * await memos.store("ok got it", { filterRetain: true });
28
+ * } catch (e) {
29
+ * if (e instanceof MemorySkippedError) return; // expected — low signal
30
+ * throw e;
31
+ * }
32
+ */
33
+ export declare class MemorySkippedError extends Error {
34
+ readonly score: number;
35
+ constructor(reason: string, score: number);
36
+ }
37
+ /**
38
+ * Core MemOS instance. This is the primary entry point for all
39
+ * memory operations.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * import { MemOS } from "@mem-os/sdk";
44
+ *
45
+ * const memos = new MemOS({ dbPath: "./my-app.db" });
46
+ * await memos.init();
47
+ *
48
+ * const { node } = await memos.store("User prefers dark mode", { type: "preference" });
49
+ * const results = await memos.search("dark mode");
50
+ * ```
51
+ */
52
+ export declare class MemOS {
53
+ private graph;
54
+ private storage;
55
+ /** In-memory snapshots for time-travel (experimental). */
56
+ private snapshots;
57
+ private config;
58
+ private experimental;
59
+ private embeddingProvider;
60
+ /**
61
+ * In-process LRU cache of query embedding vectors, keyed by
62
+ * `${provider.id}:${provider.model}:${query}`.
63
+ *
64
+ * Round 4 (perf): the semantic leg embeds the query on every search,
65
+ * and `multiStreamSearch` fans out to three pool streams — previously
66
+ * three identical ONNX inferences per search. Embedding is a pure
67
+ * function of (provider, model, query), so caching the vector is
68
+ * provably recall-neutral: a cache hit returns the byte-identical
69
+ * vector the provider would have produced. Bounded (LRU eviction) so
70
+ * long-lived processes cannot grow it without limit.
71
+ */
72
+ private queryEmbeddingCache;
73
+ private static readonly QUERY_EMBEDDING_CACHE_MAX;
74
+ /**
75
+ * Tier-2 semantic screen for the provenance-trust write gate (intent
76
+ * level, fastembed embeddings — no LLM). Lazily constructed: the
77
+ * anchor embeddings are computed on first use and cached for the
78
+ * process lifetime.
79
+ */
80
+ private semanticScreen;
81
+ private listeners;
82
+ private initialised;
83
+ private sweepTimer;
84
+ /** Background embedding queue. Null when no provider is configured. */
85
+ private embeddingQueue;
86
+ /**
87
+ * Per-node embedding status cache. Populated as the queue emits status
88
+ * callbacks and as nodes are reaped into the persisted embedding.
89
+ */
90
+ private nodeEmbeddingStatus;
91
+ /**
92
+ * LRU cache for recent search results. Prevents re-running hybrid
93
+ * search (which embeds the query + scans the embedding table) when
94
+ * the same query is issued repeatedly within a short window.
95
+ * Key: `${query}::${namespace}::${limit}`. Value: { results, expiry }.
96
+ */
97
+ private searchCache;
98
+ private searchCacheMaxEntries;
99
+ private searchCacheTtlMs;
100
+ /**
101
+ * Vision (image memory) runtime state. All three fields stay inert
102
+ * until `vision.enabled` is set, so a bare `new MemOS()` pays nothing
103
+ * and behaves exactly as it did before image memory existed.
104
+ *
105
+ * - `blobRoot` is derived once from the db path so every image write
106
+ * resolves the same directory.
107
+ * - `clipProvider` is the Track B encoder (explicit from config, or
108
+ * the lazily-loaded default). Null when Track B is off.
109
+ * - `clipQueryCache` memoizes CLIP query encodings; it is deliberately
110
+ * separate from `queryEmbeddingCache` so a CLIP vector can never be
111
+ * served to a text-semantic query.
112
+ */
113
+ private imageBlobRoot;
114
+ private clipProvider;
115
+ private clipQueryCache;
116
+ /** In-flight image embeds, drained by `flushEmbeddings`. */
117
+ private pendingImageEmbeds;
118
+ /**
119
+ * Idempotency guard for the ranking-modulation chain (effective
120
+ * importance + triple boost + Ebbinghaus reinforcement).
121
+ *
122
+ * `applyRankingModulators` is additive, so running it twice would
123
+ * double-count. It now lives inside `hybridSearch` (so `contextPack`,
124
+ * `multiStreamSearch`, and the store-time evidence pass rank like
125
+ * `search`), while `postSortResults` — the historical owner — is still
126
+ * reached by `search()` on both the hybrid and the keyword-only path.
127
+ * `postSortResults` therefore skips the chain when it recognizes the
128
+ * very objects `applyRankingModulators` produced: `hybridSearch` hands
129
+ * back that same array, so `results[0]` is still the marked entry.
130
+ */
131
+ private readonly rankingModulated;
132
+ /** Warn only once when the rerank endpoint fails (graceful degradation). */
133
+ private rerankFailureWarned;
134
+ /**
135
+ * Last timestamp handed out by `store()`. Fast successive stores can land
136
+ * in the same millisecond, which would make createdAt ties ambiguous for
137
+ * temporal ordering, version timelines, and recency tie-breaks — so
138
+ * store timestamps are kept strictly monotonic per instance.
139
+ */
140
+ private lastStoreTime;
141
+ /**
142
+ * Node ids written by `store()` whose embeddings have not been scanned
143
+ * for contradiction candidates yet. The write-time scan runs when the
144
+ * embedding is persisted (see `onEmbeddingPersisted`) — the gate keeps
145
+ * the scan off the startup backfill path and off re-embeddings from
146
+ * `update()`, so detection stays a bounded per-write cost.
147
+ */
148
+ private contradictionScanPending;
149
+ /**
150
+ * Append one entry to the tamper-evident mutation log (see
151
+ * `src/tamper-log.ts`). Fail-open: logging never blocks the mutation
152
+ * — a logging failure is swallowed so the write path always wins.
153
+ */
154
+ private logMutation;
155
+ /**
156
+ * `storage.updateNode` plus tamper-log bookkeeping. Use for every
157
+ * node mutation that goes through `updateNode` so the log stays
158
+ * complete — a mutation invisible to the log is indistinguishable
159
+ * from tampering at verify time.
160
+ */
161
+ private updateNodeLogged;
162
+ /**
163
+ * Lifetime token-savings telemetry for context packs (in-process —
164
+ * resets when the MemOS instance is recreated). `packTokens` counts
165
+ * what was actually injected; `naiveBaselineTokens` counts what
166
+ * dumping the same candidates as raw JSON nodes would have cost.
167
+ */
168
+ private usage;
169
+ /**
170
+ * Create a new MemOS instance.
171
+ *
172
+ * @param config — Configuration options. All fields are optional.
173
+ */
174
+ constructor(config?: MemOSConfig);
175
+ /**
176
+ * Initialise the storage backend and hydrate the in-memory graph.
177
+ * Must be called before any other method.
178
+ */
179
+ init(): Promise<void>;
180
+ /**
181
+ * Store a new memory.
182
+ *
183
+ * Automatically generates a summary (if not provided) and attempts
184
+ * to link the new node to existing nodes based on text similarity.
185
+ *
186
+ * @param content — Text content to remember.
187
+ * @param opts — Optional metadata, type, summary, importance, ttl, tags.
188
+ * @returns The created node and any auto-created edges.
189
+ */
190
+ store(content: string, opts?: Omit<CreateMemoryInput, "content">): Promise<{
191
+ node: MemoryNode;
192
+ links: MemoryEdge[];
193
+ }>;
194
+ /**
195
+ * Import a record with its original identity (ID and timestamps).
196
+ *
197
+ * Used by encrypted sync restore (`src/sync.ts`). Unlike `store()`,
198
+ * this bypasses the write pipeline — no retain filter, no quarantine
199
+ * screen, no event-update resolution, no auto-linking — because the
200
+ * record already went through all of that on the exporting machine.
201
+ * The node is still queued for (re-)embedding under the local
202
+ * provider and registered for the contradiction scan.
203
+ */
204
+ importRecord(record: {
205
+ id: string;
206
+ content: string;
207
+ summary?: string;
208
+ type: MemoryType;
209
+ tags?: string[];
210
+ importance?: number;
211
+ createdAt: number;
212
+ updatedAt: number;
213
+ namespace?: string;
214
+ metadata?: Record<string, unknown>;
215
+ source?: MemorySource;
216
+ provenance?: ProvenanceTier;
217
+ pool?: MemoryPool;
218
+ expiresAt?: number | null;
219
+ validFrom?: number | null;
220
+ validTo?: number | null;
221
+ harness?: string;
222
+ }): Promise<MemoryNode>;
223
+ /**
224
+ * Inner write path. `skipEventUpdate` is true only for the event-update
225
+ * machinery below (prevents the replacement write from re-triggering
226
+ * update detection on its own text — infinite recursion otherwise).
227
+ */
228
+ private storeInner;
229
+ /**
230
+ * Resolve a natural-language event update/cancel against the store.
231
+ *
232
+ * Cross-harness rule: the target is resolved from the CURRENT rows in
233
+ * the shared DB file (same deterministic rules in every harness), so
234
+ * model B can update an event stored by model A with no shared
235
+ * conversation state.
236
+ *
237
+ * Deterministic tiebreaks:
238
+ * - kind named ("that meeting") → newest open event whose kind matches
239
+ * `metadata.event.kind`, or whose label contains the kind;
240
+ * createdAt desc, id asc.
241
+ * - kind unnamed ("it got moved") → the single open event if exactly
242
+ * one exists; zero or ambiguous → null (fail safe, never guess).
243
+ *
244
+ * @returns The resolved replacement write, or null when nothing
245
+ * resolves (the caller falls through to a normal write).
246
+ */
247
+ private applyEventUpdate;
248
+ /**
249
+ * Find the currently-open scheduled event an update/cancel refers to.
250
+ * See `applyEventUpdate` for the deterministic tiebreak rules.
251
+ */
252
+ private resolveOpenEventTarget;
253
+ /**
254
+ * Retrieve a single memory by ID.
255
+ *
256
+ * @param id — Memory node ID.
257
+ * @returns The node, or `null` if not found.
258
+ */
259
+ retrieve(id: string): Promise<MemoryNode | null>;
260
+ /**
261
+ * Store an image as a durable memory.
262
+ *
263
+ * An image memory is an ORDINARY memory node plus two side artifacts:
264
+ * a caption stored as the node's `content` (so every existing text
265
+ * retrieval path finds it), and the image bytes in a content-addressed
266
+ * blob store next to the database. The node shape does not change and
267
+ * no new `MemoryType` member is introduced — `metadata.image` is the
268
+ * discriminator, exactly like `metadata.context`.
269
+ *
270
+ * Because it routes through `storeInner`, the write goes through the
271
+ * same pipeline as every text memory: retain triage, quarantine
272
+ * screening, provenance resolution, auto-linking, contradiction scan,
273
+ * tamper log. That is the difference between ~250 lines and ~800.
274
+ *
275
+ * Two embedding tracks, independent of each other:
276
+ * - **Track A (always on):** the caption is embedded by the normal
277
+ * TEXT provider, so the image is found by keyword and text-semantic
278
+ * search on every install. Zero extra model downloads.
279
+ * - **Track B (opt-in via `vision.clip.enabled`):** the image itself
280
+ * is encoded by CLIP into a separate vector space, so a query can
281
+ * match the PIXELS when its wording differs from the caption's.
282
+ * Degrades to Track A when the transformers package is missing —
283
+ * it never throws and never substitutes a text vector.
284
+ *
285
+ * @param input — A filesystem path, raw bytes, `{ data, mime }`
286
+ * (base64), `{ url }`, or a `data:` URL.
287
+ * @param opts — Caption, kind, tags, and the usual store options.
288
+ * @returns The created node, its image attachment, and the CLIP status.
289
+ *
290
+ * @example
291
+ * ```ts
292
+ * const { node, image } = await memos.addImageMemory("./diagram.png", {
293
+ * caption: "gateway → auth → orders → postgres, retry loop on orders",
294
+ * kind: "diagram",
295
+ * tags: ["arch"],
296
+ * });
297
+ * ```
298
+ */
299
+ addImageMemory(input: ImageInput, opts?: AddImageMemoryOptions): Promise<AddImageMemoryResult>;
300
+ /**
301
+ * Resolve an image reference (or node id) to its bytes.
302
+ *
303
+ * Takes the REF, not the node id, so a host model that only saw
304
+ * `[img:…]` in a context pack can resolve it without any other
305
+ * context. Also accepts a node id for convenience.
306
+ *
307
+ * @returns The blob bytes and MIME type, or null when missing.
308
+ */
309
+ imageMemory(refOrId: string): Promise<{
310
+ buffer: Buffer;
311
+ mime: string;
312
+ ref: string;
313
+ } | null>;
314
+ /**
315
+ * Search image memories by text query.
316
+ *
317
+ * Runs the caption leg (keyword + text-semantic — the same legs every
318
+ * other memory uses) and, when Track B is enabled, the CLIP leg, then
319
+ * fuses them. The CLIP leg is what makes a query match the PIXELS
320
+ * when its wording shares nothing with the caption.
321
+ *
322
+ * Only nodes carrying `metadata.image` are returned.
323
+ */
324
+ searchImages(query: string, opts?: SearchImagesOptions): Promise<ImageSearchResult[]>;
325
+ /**
326
+ * All image memories in a namespace, newest first.
327
+ *
328
+ * Requires vision to be enabled, so an operator who has not turned
329
+ * the feature on gets told how rather than an empty list that looks
330
+ * like "no images stored".
331
+ */
332
+ listImages(opts?: ListImagesOptions): Promise<ImageSearchResult[]>;
333
+ /**
334
+ * Delete an image memory: the node, its blob, and its CLIP vector.
335
+ * The embedding row cascades with the node (ON DELETE CASCADE).
336
+ */
337
+ deleteImageMemory(nodeId: string): Promise<boolean>;
338
+ /**
339
+ * Delete blobs no live node references any more.
340
+ *
341
+ * Explicit rather than scheduled: eviction paths (`forgetByDecay`,
342
+ * `archive`) delete nodes, so their blobs become unreachable garbage
343
+ * that only an explicit sweep removes.
344
+ */
345
+ purgeOrphanImages(): Promise<number>;
346
+ /**
347
+ * The CLIP retrieval leg for a hybrid search, or [] when it cannot
348
+ * contribute.
349
+ *
350
+ * Returns an empty list — with zero storage reads — whenever any of
351
+ * these holds:
352
+ * - the query is empty (a structured-only filter search),
353
+ * - `vision.clip.enabled` is false (the default),
354
+ * - the provider cannot encode text (`embedImageText` missing), or
355
+ * - the storage adapter has no `querySimilarImages`.
356
+ *
357
+ * That is what makes the leg free for a text-only install: not one
358
+ * extra query, not one changed byte. When it IS active, the query is
359
+ * encoded through the dedicated CLIP LRU (never `embedQueryCached`,
360
+ * whose keys belong to the text provider) and scanned against
361
+ * `image_embeddings` with the caller's own filter mirrored — so an
362
+ * image in another namespace can never leak into this scope.
363
+ *
364
+ * A CLIP failure degrades to [] rather than propagating: Track B is a
365
+ * recall upgrade, and losing it must never break a text search.
366
+ */
367
+ private fetchImageLeg;
368
+ /** Resolve (and memoize) the blob-store root for this instance. */
369
+ private resolveImageBlobRoot;
370
+ /**
371
+ * Resolve the Track B image encoder.
372
+ *
373
+ * Order: an explicitly injected provider, then the lazily-constructed
374
+ * default. Null whenever Track B is off — which is the common case, so
375
+ * the default `ClipEmbeddingProvider` is never even allocated.
376
+ */
377
+ private resolveClipProvider;
378
+ private resolveClipQueryCache;
379
+ /**
380
+ * The image payload carried by a node, or null when it is not an
381
+ * image memory. `metadata.image` — not `type`, not a new MemoryType
382
+ * member — is the discriminator, so MemoryType exhaustiveness stays
383
+ * intact everywhere (sync allowlist, fidelity labels, adapters, MCP
384
+ * schema).
385
+ */
386
+ private imageAttachmentOf;
387
+ /** The CLIP bookkeeping recorded in `metadata.clip`. */
388
+ private clipMetaOf;
389
+ private clipStatusOf;
390
+ /**
391
+ * Schedule the CLIP embed for a freshly written image node.
392
+ *
393
+ * Deliberately NOT routed through `EmbeddingQueue`: that queue's
394
+ * batch-and-persist contract writes to the TEXT `embeddings` table,
395
+ * and CLIP vectors belong in `image_embeddings`. This is a small
396
+ * in-flight set drained by `flushEmbeddings`, with retry-free
397
+ * fail-soft semantics — a CLIP failure records
398
+ * `metadata.clip.status = "failed"` and leaves Track A intact.
399
+ */
400
+ private scheduleImageEmbedding;
401
+ /** Persist `metadata.clip` on the node. */
402
+ private recordClipStatus;
403
+ /** Find an image node with the same content digest in one namespace. */
404
+ private findImageNodeByDigest;
405
+ /** Find an image node by its blob ref. */
406
+ private findImageNodeByRef;
407
+ /** Count image nodes sharing one digest (blob refcount). */
408
+ private countImageNodesByDigest;
409
+ /** Every ref referenced by a live image node. */
410
+ private collectLiveImageRefs;
411
+ /**
412
+ * Fuse the caption leg and the CLIP leg by reciprocal rank.
413
+ *
414
+ * RRF rather than score blending: the two legs live in different
415
+ * vector spaces, so their raw scores are not comparable — only their
416
+ * RANKS are. An image found by both legs outranks one found by
417
+ * either alone, which is exactly the intended behaviour.
418
+ */
419
+ private fuseImageLegs;
420
+ /**
421
+ * Search memories by text query and/or structured filters.
422
+ *
423
+ * @param queryOrFilter — A plain text query string, or a full SearchFilter object.
424
+ * @returns Array of scored memories, ordered by relevance.
425
+ */
426
+ search(queryOrFilter: string | SearchFilter): Promise<ScoredMemory[]>;
427
+ /**
428
+ * Whether a text query should take the hybrid (multi-leg) path.
429
+ *
430
+ * Historically this was just "is a text embedding provider
431
+ * configured". The CLIP image leg is a second, INDEPENDENT reason to
432
+ * fuse: an install with `embeddings.enabled: false` but
433
+ * `vision.clip.enabled: true` still has a vector leg to run, and
434
+ * gating on the text provider alone would leave Track B silently
435
+ * dead on exactly the installs that opted into it.
436
+ *
437
+ * Cheap: the check is a boolean on resolved config, not a probe.
438
+ */
439
+ private canRunHybrid;
440
+ /**
441
+ * Fidelity-aware recall: retrieve memories at the CHEAPEST fidelity
442
+ * level that satisfies the query, escalating L0→L1→L2→L3 within the
443
+ * same call when the result set looks insufficient.
444
+ *
445
+ * This is an ADDITIVE API — `search()` / `semanticSearch()` are
446
+ * untouched. Scoring is a local IDF-weighted term-coverage pass over
447
+ * the level corpus (zero LLM, zero embeddings); each result carries
448
+ * the full node plus the level it was served at.
449
+ *
450
+ * Default behavior is L3-equivalent (verbatim text, no routing, no
451
+ * escalation): existing consumers see the same output shape as a
452
+ * plain search. Pass `fidelity: "adaptive"` (or a fixed lower level)
453
+ * to opt into token savings.
454
+ *
455
+ * Two guards keep an adaptive call answerable:
456
+ * - the escalation FLOOR (default L1) — an L0 tag/entity listing
457
+ * is a retrieval affordance, not an answer, so it is never the
458
+ * terminal level unless the caller explicitly asked for L0;
459
+ * - the DECISIVE-TOKEN check — when the query carries a number,
460
+ * unit or negation that the served text lacks, escalation
461
+ * continues regardless of the IDF coverage score.
462
+ *
463
+ * @example
464
+ * ```ts
465
+ * // Adaptive never serves tag soup: a factoid starts at L1 and
466
+ * // escalates to L3 when the decisive token is still missing.
467
+ * const hits = await memos.recall("postgres pool size", {
468
+ * fidelity: "adaptive",
469
+ * maxFidelity: "L2",
470
+ * });
471
+ * for (const h of hits) console.log(h.level, h.text);
472
+ * ```
473
+ */
474
+ recall(query: string, opts?: FidelityRecallOptions): Promise<FidelityRecallResult[]>;
475
+ /**
476
+ * Scope the recall candidate set. Mirrors the `SearchFilter` fields
477
+ * that make sense for an in-memory scan (namespace / type / tags /
478
+ * pool); full-text and vector legs live in `search()`. The harness
479
+ * scope comes from the explicit argument first, then
480
+ * `filter.harness` — `"all"` (or unset) keeps every harness.
481
+ */
482
+ private applyRecallFilter;
483
+ /**
484
+ * Eager fidelity backfill: generate missing L1/L2 levels for stored
485
+ * memories and persist them into the reserved `metadata.fidelity`
486
+ * slot. Idempotent — memories that already carry cached levels are
487
+ * skipped. Reads (`recall`, `contextPack`) also generate lazily, so
488
+ * this command is an optimization, not a requirement.
489
+ *
490
+ * `memos compact --stats` reports per-level token averages without
491
+ * writing anything.
492
+ */
493
+ compact(opts?: CompactOptions): Promise<CompactResult>;
494
+ /**
495
+ * Search and return results in TOON (Token-Optimized Object Notation)
496
+ * format — a compact pipe-delimited string that cuts token count.
497
+ *
498
+ * Format:
499
+ * # memos.search.v1
500
+ * # toon:pipe-delimited
501
+ * # fields: id|score|trust|source|updatedAt|tags|content
502
+ * mem_abc|0.950|local|user_input|2026-06-18T12:00:00.000Z|preference;ui|User likes dark mode
503
+ *
504
+ * @param opts — If a string, searches with that query. If an object,
505
+ * accepts `{ query, format: "toon" | "toon-compact", ...searchOptions }`.
506
+ * Default format is "toon-compact" for maximum token savings.
507
+ * @returns A TOON-formatted string.
508
+ */
509
+ searchToon(opts: string | (SearchFilter & {
510
+ format?: "toon" | "toon-compact";
511
+ })): Promise<string>;
512
+ /**
513
+ * Compose a scope filter into storage-facing namespace constraints.
514
+ * Explicit `namespace` always wins over `scope`; scope matches
515
+ * hierarchically (prefix) unless `scopeMatch: "exact"`.
516
+ */
517
+ private resolveScopeFilter;
518
+ /**
519
+ * Apply post-query sorting that the storage layer can't do natively
520
+ * (e.g. trustScore when the query went through FTS which always
521
+ * sorts by rank).
522
+ *
523
+ * The ranking-modulation chain (triple boost, effective importance,
524
+ * Ebbinghaus reinforcement) is owned by `hybridSearch` these days —
525
+ * see {@link applyRankingModulators} — so this method only runs it for
526
+ * the keyword-only path, where `hybridSearch` is never reached. On the
527
+ * hybrid path the incoming array is already modulated and is passed
528
+ * through untouched (the `rankingModulated` WeakSet tells the two
529
+ * apart).
530
+ */
531
+ private postSortResults;
532
+ /**
533
+ * The shared ranking-modulation chain: everything that re-ranks a
534
+ * fused result set AFTER relevance. Runs inside `hybridSearch`'s
535
+ * post-fusion stages so every caller of the hybrid pipeline —
536
+ * `search()`, `contextPack()`, `multiStreamSearch()`, the store-time
537
+ * evidence pass — ranks identically instead of `search()` alone.
538
+ *
539
+ * Three independent, individually-gated stages:
540
+ *
541
+ * 1. TRIPLE BOOST (`experimental.tripleExtraction`) — ×1.15 when a
542
+ * memory's stored triples match the query's triples.
543
+ * 2. EFFECTIVE IMPORTANCE (`config.importance`, opt-in) — a bounded
544
+ * multiplicative factor from `computeEffectiveImportance()`, so
545
+ * recency decay and access reinforcement modulate ranking the same
546
+ * way they already modulate eviction and archival.
547
+ * 3. EBBINGHAUS REINFORCEMENT (`experimental.ebbinghaus`) — a small
548
+ * additive forgetting-curve term, plus the access-counter bump.
549
+ *
550
+ * Every stage is off unless its own config says otherwise, and stage 2
551
+ * additionally requires the `importance` block to be non-empty — so an
552
+ * unconfigured instance passes its exact input array through.
553
+ */
554
+ private applyRankingModulators;
555
+ /**
556
+ * Resolve the effective-importance config for ranking / eviction /
557
+ * archival, or `null` when the user left it unconfigured.
558
+ *
559
+ * The constructor normalises `config.importance` to `{}`, so "unset"
560
+ * and "explicitly empty" are indistinguishable there — and BOTH must
561
+ * keep the historical behaviour (raw `node.importance`) exactly, since
562
+ * this is a published SDK. An object carrying at least one defined
563
+ * field is therefore the opt-in signal.
564
+ *
565
+ * @returns The configured tuning block, or `null` for "use raw importance".
566
+ */
567
+ private resolveImportanceRanking;
568
+ /**
569
+ * Bump accessCount and lastAccessed for retrieved nodes (Ebbinghaus
570
+ * reinforcement). Fire-and-forget; failures are ignored.
571
+ *
572
+ * MUST go through the storage adapter's `recordAccess` — never
573
+ * `updateNode`. `updateNode` stamps `updatedAt = Date.now()` and
574
+ * ignores the `accessCount`/`lastAccessed` fields in its UPDATE
575
+ * statement, so routing read telemetry through it did both of these:
576
+ *
577
+ * 1. Every search rewrote `updated_at` on up to 10 nodes without a
578
+ * tamper-log entry, so `verifyTamperLog` reported the whole store
579
+ * as "edited without logging" (false tamper alarm on a plain read).
580
+ * It also made recalled memories look freshly written to recency
581
+ * ordering, sync last-write-wins, and the embedding-freshness
582
+ * check (`info.updatedAt >= node.updatedAt` → needless re-embed).
583
+ * 2. The `accessCount`/`lastAccessed` it passed were silently dropped —
584
+ * both fields are absent from `updateNode`'s UPDATE statement — so
585
+ * every search paid a full row rewrite (plus tag/entity index
586
+ * resync) to persist none of its own telemetry.
587
+ *
588
+ * Adapters without `recordAccess` skip reinforcement (fail-open).
589
+ */
590
+ private bumpAccessCounters;
591
+ /**
592
+ * Boost results whose stored triples match the query's triples.
593
+ * Matching triples get a 15% score boost.
594
+ */
595
+ private applyTripleBoost;
596
+ /**
597
+ * Permanently forget a memory and all its connected edges.
598
+ *
599
+ * @param id — Memory node ID to forget.
600
+ * @returns `true` if the memory existed and was deleted.
601
+ */
602
+ forget(id: string): Promise<boolean>;
603
+ /**
604
+ * Generate a summary of all stored memories.
605
+ *
606
+ * Concatenates all node summaries and produces an extractive
607
+ * summary of the combined text.
608
+ *
609
+ * @returns A summary string.
610
+ */
611
+ summarize(): Promise<string>;
612
+ /**
613
+ * Manually create a link (edge) between two memories.
614
+ *
615
+ * @param sourceId — Source node ID.
616
+ * @param targetId — Target node ID.
617
+ * @param relation — Semantic relation type.
618
+ * @param weight — Edge weight [0, 1].
619
+ * @returns The created edge.
620
+ */
621
+ link(sourceId: string, targetId: string, relation?: MemoryEdge["relation"], weight?: number): Promise<MemoryEdge>;
622
+ /**
623
+ * Set a time-to-live on a memory node.
624
+ *
625
+ * @param id — Memory node ID.
626
+ * @param seconds — TTL in seconds from now.
627
+ */
628
+ setTTL(id: string, seconds: number): Promise<void>;
629
+ /**
630
+ * Clear the TTL on a memory node (make it persist indefinitely).
631
+ *
632
+ * @param id — Memory node ID.
633
+ */
634
+ clearTTL(id: string): Promise<void>;
635
+ /**
636
+ * Add tags to a memory node.
637
+ *
638
+ * @param id — Memory node ID.
639
+ * @param tags — Tags to add.
640
+ */
641
+ tag(id: string, tags: string[]): Promise<void>;
642
+ /**
643
+ * Remove tags from a memory node.
644
+ *
645
+ * @param id — Memory node ID.
646
+ * @param tags — Tags to remove.
647
+ */
648
+ untag(id: string, tags: string[]): Promise<void>;
649
+ /**
650
+ * List all memories with a specific tag.
651
+ *
652
+ * @param tag — Tag to filter by.
653
+ * @returns Array of memory nodes with the tag.
654
+ */
655
+ listByTag(tag: string): Promise<MemoryNode[]>;
656
+ /**
657
+ * Export memories in the specified format.
658
+ */
659
+ export(opts?: ExportOptions): Promise<ExportResult>;
660
+ /**
661
+ * Import memories from a JSON file or directory of Markdown/Obsidian files.
662
+ *
663
+ * @param opts — Import options (source path, format).
664
+ * @returns Import result with counts.
665
+ */
666
+ /**
667
+ * Import memories from a third-party export: ChatGPT
668
+ * (`conversations.json`), Claude (export directory or file), or Slack
669
+ * (export directory). The parser sniffs each entry, maps messages to
670
+ * timestamped memories, dedupes by content hash, and every item is
671
+ * stored with `source: "external_data"` (lower trust by default) plus
672
+ * provenance tags (`imported`, `<source>`), so imported memories are
673
+ * rankable but never outrank first-party facts.
674
+ *
675
+ * With `synthesize: true`, a post-import pass extracts structured
676
+ * decisions / preferences / milestones from the imported text using
677
+ * rule-based heuristics (no LLM) and stores them as tagged memories.
678
+ */
679
+ importExternal(opts: {
680
+ /** Path to the export JSON file. Mutually exclusive with `data`. */
681
+ file?: string;
682
+ /** Pre-loaded export payload (JSON string or decoded value). */
683
+ data?: unknown;
684
+ /**
685
+ * Path to an export directory. Required for `slack`; for
686
+ * `chatgpt`/`claude` scans every `*.json`/`*.jsonl` file in the
687
+ * directory. Mutually exclusive with `file`/`data`.
688
+ */
689
+ dir?: string;
690
+ /** Parser hint. Default `"auto"` sniffs the shape. */
691
+ source?: ExternalImportSource;
692
+ namespace?: string;
693
+ dryRun?: boolean;
694
+ /** Safety cap on imported items. Default 500. */
695
+ maxItems?: number;
696
+ /** Extra tags applied to every imported memory. */
697
+ tags?: string[];
698
+ /**
699
+ * Post-import synthesis pass: rule-based extraction of decisions,
700
+ * preferences, and milestones into structured memories. Default
701
+ * false.
702
+ */
703
+ synthesize?: boolean;
704
+ }): Promise<{
705
+ detected: DetectedExportSource;
706
+ total: number;
707
+ imported: number;
708
+ skipped: number;
709
+ dryRun: boolean;
710
+ durationMs: number;
711
+ sampleIds: string[];
712
+ synthesized: {
713
+ decisions: number;
714
+ preferences: number;
715
+ milestones: number;
716
+ };
717
+ }>;
718
+ private lessonStorage;
719
+ /**
720
+ * Capture a procedural lesson — behavioral guidance for future tasks,
721
+ * not a fact. Starts at the neutral prior score (0.5); outcome
722
+ * feedback via `recordLessonOutcome` moves it from there.
723
+ */
724
+ memorizeProcedural(lesson: string, opts?: {
725
+ context?: string;
726
+ tags?: string[];
727
+ namespace?: string;
728
+ scope?: MemoryScope;
729
+ initialScore?: number;
730
+ }): Promise<ProceduralLesson>;
731
+ /**
732
+ * Record an outcome for a lesson. Successes reinforce
733
+ * (`score += (1 - score) * 0.2`), failures demote
734
+ * (`score -= score * 0.3`) — pure local math, fully transparent.
735
+ * Also bumps `useCount` and refreshes timestamps (which resets the
736
+ * read-time decay clock).
737
+ */
738
+ recordLessonOutcome(id: string, outcome: LessonOutcome): Promise<ProceduralLesson>;
739
+ /**
740
+ * Retrieve the top-k procedural lessons for a query, ranked by a
741
+ * blend of decayed score (60%) and query-token relevance (40%).
742
+ * Lessons with no query overlap are skipped unless the query is
743
+ * empty (empty query = top lessons by score).
744
+ */
745
+ recallProcedural(query: string, k?: number, opts?: {
746
+ namespace?: string;
747
+ scope?: MemoryScope;
748
+ }): Promise<ProceduralLesson[]>;
749
+ /** List procedural lessons, highest score first. */
750
+ listProceduralLessons(opts?: {
751
+ namespace?: string;
752
+ scope?: MemoryScope;
753
+ }): Promise<ProceduralLesson[]>;
754
+ /**
755
+ * Trace a citation token (e.g. `[mem:a3f9]`, `a3f9`, or a full id)
756
+ * back to its source memory. Returns the full memory on an exact
757
+ * prefix match, `not_found` when nothing matches, and `ambiguous`
758
+ * with the candidate list when the token matches several memories
759
+ * (use a longer token to disambiguate).
760
+ */
761
+ resolveCitation(token: string): Promise<CitationResolution>;
762
+ /**
763
+ * Render a citation resolution exactly as `memos cite` prints it.
764
+ * Shared with the CLI so tests assert on the real output bytes.
765
+ */
766
+ formatCitation(resolution: CitationResolution, asJson?: boolean): string;
767
+ /**
768
+ * Screen text with the write-gate classifier without storing
769
+ * anything. Useful for pre-flight checks and for testing the
770
+ * classifier's verdict on a given input. Tier 1 only (synchronous
771
+ * regex screen) — the async semantic tier needs an initialized
772
+ * instance and runs on the real write path.
773
+ */
774
+ screenContent(content: string): QuarantineVerdict;
775
+ /**
776
+ * Lazily-constructed tier-2 semantic screen, or null when embeddings
777
+ * are disabled (no provider to embed with).
778
+ */
779
+ private getSemanticScreen;
780
+ /**
781
+ * Tier-2 semantic screen for the provenance-trust write gate:
782
+ * intent-level detection via cosine similarity to canonical
783
+ * attack-shape anchors (fastembed, no LLM — see
784
+ * `src/semantic-screen.ts`).
785
+ *
786
+ * Runs only when tier 1 left the write in the gray zone (score in
787
+ * [1.0, 2.0)) or the write is low-trust tiered — ordinary writes
788
+ * never pay the embedding cost, and the zero-LLM cheap screen stays
789
+ * the first and only mandatory tier.
790
+ *
791
+ * Escalation rules (both quarantine, never hard-block — flagged
792
+ * content is still stored, just excluded from recall pending review):
793
+ * - gray-zone: tier-1 score + `semanticEscalationPoints(sim)` >= 2.0 —
794
+ * the semantic tier escalates partial regex evidence, it cannot
795
+ * quarantine a clean-looking write on its own;
796
+ * - low-trust tiers: sim >= SEMANTIC_HIGH_SIM_BAR quarantines on
797
+ * intent alone (tool output is data, never instructions).
798
+ *
799
+ * Returns null when the tier doesn't run (out of scope, embeddings
800
+ * off) or is unavailable (hash fallback, transient embed error) —
801
+ * callers keep the tier-1 verdict. Fail-open for writes: a semantic
802
+ * outage never blocks or silently quarantines a write.
803
+ */
804
+ private maybeSemanticScreen;
805
+ /**
806
+ * Retrieval-time anomaly scoring (topic-conditioned): memories whose
807
+ * write-time attack-shape similarity (`metadata.semanticAttackSim`,
808
+ * stamped by {@link maybeSemanticScreen}) is at/above
809
+ * RETRIEVAL_ANOMALY_THRESHOLD are down-weighted — unless the query
810
+ * itself is attack-shaped (similarity at/above QUERY_ATTACK_SIM_CAP),
811
+ * in which case attack-shaped memories are expected (security
812
+ * discussion, red-team review) and the flag stays off.
813
+ *
814
+ * Cheap by construction: the query is embedded only when at least one
815
+ * stamped suspect is in the result set; stored memories reuse their
816
+ * write-time measurement.
817
+ */
818
+ private applyRetrievalAnomalyScoring;
819
+ /**
820
+ * List quarantined memories — the review queue — most recently
821
+ * flagged first. Quarantined memories are excluded from recall by
822
+ * default; this is the explicit review path.
823
+ */
824
+ listQuarantined(opts?: {
825
+ limit?: number;
826
+ namespace?: string;
827
+ }): Promise<MemoryNode[]>;
828
+ /**
829
+ * Release a memory from quarantine: it becomes recallable again, but
830
+ * stays flagged as low-trust for agent consumption — the
831
+ * `releasedFromQuarantine` metadata marker keeps
832
+ * `untrusted_source: true` on in MCP responses so a host agent
833
+ * confirms it instead of silently injecting it as context.
834
+ * Idempotent: releasing a non-quarantined memory is a no-op.
835
+ * Add-only: `quarantinedAt`/`quarantineReason` are preserved as the
836
+ * audit trail of the original flagging.
837
+ */
838
+ releaseFromQuarantine(id: string): Promise<MemoryNode>;
839
+ /**
840
+ * Set a memory's provenance tier. This is the only path to the
841
+ * `user-verified` tier — explicit user confirmation, never assigned
842
+ * automatically at write time.
843
+ */
844
+ setProvenance(id: string, tier: ProvenanceTier): Promise<MemoryNode>;
845
+ /**
846
+ * Per-harness memory counts — "which harnesses wrote what lives in
847
+ * this database". Powers `memos harness list`. Memories written
848
+ * before harness attribution existed (or by clients that never set
849
+ * the tag) read as `"unknown"`.
850
+ */
851
+ getHarnessCounts(): Promise<HarnessCount[]>;
852
+ /**
853
+ * Merge another harness's database file into this one — "one memory,
854
+ * every harness."
855
+ *
856
+ * The merge is a faithful row-level import, not a re-write: every
857
+ * source node keeps its id (remapped only on collision), timestamps,
858
+ * bitemporal validity interval (`validFrom`/`validTo`), provenance
859
+ * tier, trust score, tags, quarantine state, and harness tag. Edges
860
+ * follow their endpoints through the id remap.
861
+ *
862
+ * Cross-harness duplicates and contradictions are NOT resolved here:
863
+ * imported nodes go through the exact same post-write path as
864
+ * `store()` — re-embedded by the local provider and registered for
865
+ * the existing write-time contradiction scan — so the established
866
+ * contradiction detection + read-time resolution machinery converges
867
+ * them exactly as if they had been written locally. No second
868
+ * resolution system exists, by design.
869
+ *
870
+ * Deterministic: nodes import in (createdAt, id) order; collisions
871
+ * get fresh UUIDs recorded in the result's id map.
872
+ *
873
+ * @param fromPath — Path to the source `.db` file.
874
+ * @param opts — `{ dryRun }` reports the plan without writing.
875
+ */
876
+ mergeHarnessDb(fromPath: string, opts?: HarnessMergeOptions): Promise<HarnessMergeResult>;
877
+ importMemories(opts: ImportOptions): Promise<ImportResult>;
878
+ private importJson;
879
+ private parseMarkdownFile;
880
+ /**
881
+ * Semantic-only retrieval. Uses persisted embedding vectors when configured,
882
+ * with the older graph text-similarity path kept as a no-provider fallback.
883
+ *
884
+ * Two call styles (both supported):
885
+ * semanticSearch(query, 10, 0.2, { namespace: "demo" }) // positional
886
+ * semanticSearch(query, { limit: 10, threshold: 0.2, namespace: "demo" })
887
+ *
888
+ * @param query — Text query to search for.
889
+ * @param limitOrOpts — Result limit, or an options object with `limit`,
890
+ * `threshold`, and any `SearchFilter` field.
891
+ * @param threshold — Minimum similarity score in [0, 1] (positional style).
892
+ * @param filter — Extra search filters (positional style).
893
+ * @param internal — Internal plumbing (not part of the public contract):
894
+ * `queryVector` skips the query embedding when the caller already
895
+ * computed it (see `multiStreamSearch` embed-once threading).
896
+ */
897
+ semanticSearch(query: string, limitOrOpts?: number | SemanticSearchOptions, threshold?: number, filter?: SearchFilter, internal?: {
898
+ queryVector?: number[];
899
+ }): Promise<ScoredMemory[]>;
900
+ /**
901
+ * Generate a DOT-format graph visualization string.
902
+ */
903
+ graphViz(): Promise<string>;
904
+ /**
905
+ * List all namespaces.
906
+ */
907
+ listNamespaces(): Promise<string[]>;
908
+ /**
909
+ * Get the count of memories in a namespace.
910
+ */
911
+ namespaceCount(ns: string): Promise<number>;
912
+ /**
913
+ * Scoped-space inventory: every namespace with its live memory count,
914
+ * ordered by count descending. Free-form scopes (convention:
915
+ * `project:<name>`, `harness:<name>`) appear here alongside typed
916
+ * `user:/agent:/run:` scopes and the shared `default` namespace.
917
+ * Powers `memos scope list`.
918
+ */
919
+ listNamespaceCounts(): Promise<Array<{
920
+ namespace: string;
921
+ count: number;
922
+ }>>;
923
+ /**
924
+ * Get context for a node by walking the graph to a given depth.
925
+ * Returns the node's content plus contents of neighbours up to `depth` hops.
926
+ */
927
+ injectContext(id: string, depth?: number, maxChars?: number): Promise<string>;
928
+ /**
929
+ * Update an existing memory node.
930
+ */
931
+ update(id: string, input: UpdateMemoryInput): Promise<MemoryNode | null>;
932
+ /**
933
+ * Get the full graph snapshot (nodes + edges).
934
+ */
935
+ getGraph(): Promise<GraphSnapshot>;
936
+ /**
937
+ * Get the graph as of a bitemporal timestamp (unix ms) — time-travel
938
+ * over add-only history. Backs the MCP Apps explorer's as-of scrubber.
939
+ * Reads from storage (the in-memory graph only reflects live state);
940
+ * adapters without `getGraphAtTime` fall back to filtering the live
941
+ * snapshot with the same as-of predicates.
942
+ */
943
+ getGraphAtTime(atTime: number): Promise<GraphSnapshot>;
944
+ /**
945
+ * Get direct neighbours of a memory node.
946
+ */
947
+ getNeighbours(nodeId: string): Promise<MemoryNode[]>;
948
+ /**
949
+ * Get all edges connected to a memory node.
950
+ */
951
+ getEdges(nodeId: string): Promise<MemoryEdge[]>;
952
+ /**
953
+ * Find clusters of related memories.
954
+ */
955
+ clusters(minSize?: number): Promise<string[][]>;
956
+ /**
957
+ * Return the total number of stored memories.
958
+ */
959
+ get count(): number;
960
+ /**
961
+ * Remove all memories and edges.
962
+ */
963
+ clear(): Promise<void>;
964
+ /**
965
+ * Shut down MemOS and release resources.
966
+ */
967
+ close(): Promise<void>;
968
+ /**
969
+ * Build an AI Trio v1 context pack for a query.
970
+ *
971
+ * This is the canonical read-only envelope consumed by LLM Guardian
972
+ * and any other AI Trio member. Items are sorted by descending
973
+ * relevance and trimmed to fit `tokenBudget`. The score is
974
+ * reproducible for the same query, provider, model, and database.
975
+ *
976
+ * @example
977
+ * ```ts
978
+ * const pack = await memos.contextPack({
979
+ * query: "release blockers",
980
+ * namespace: "default",
981
+ * tokenBudget: 1200,
982
+ * });
983
+ * ```
984
+ */
985
+ contextPack(opts: {
986
+ query: string;
987
+ namespace?: string;
988
+ /** Typed multi-scope filter (hierarchical by default). */
989
+ scope?: MemoryScope;
990
+ /** How `scope` matches stored namespaces. Default `"hierarchical"`. */
991
+ scopeMatch?: "exact" | "hierarchical";
992
+ tokenBudget: number;
993
+ limit?: number;
994
+ trust?: string;
995
+ source?: string;
996
+ includeSummary?: boolean;
997
+ /**
998
+ * Fidelity level for pack item contents: a fixed level or
999
+ * `"adaptive"` (the query-adaptive router picks the starting level
1000
+ * per query). Default `undefined` = L3 verbatim — current behavior
1001
+ * preserved exactly. Lower levels cut tokens per item; the chosen
1002
+ * level is recorded per item (`item.fidelity`) and in the pack
1003
+ * metadata (`pack.fidelity`).
1004
+ */
1005
+ fidelity?: FidelityLevel | "adaptive";
1006
+ /** Output format: "json" (default), "toon", or "toon-compact" */
1007
+ format?: "json" | "toon" | "toon-compact";
1008
+ /**
1009
+ * Opt-in semantic dedup: drop pack items whose stored embedding is a
1010
+ * near-duplicate (cosine >= 0.90) of an already-selected item's.
1011
+ * Catches paraphrased memories that lexical dedup misses. Costs one
1012
+ * pass over stored embedding rows (filtered to the candidate set);
1013
+ * off by default.
1014
+ */
1015
+ semanticDedup?: boolean;
1016
+ /**
1017
+ * Multi-stream retrieval (LongMemEval-V2 design lesson): run the query
1018
+ * separately against each granularity pool (`event`, `note`,
1019
+ * `procedure`) and fuse the ranked lists. Beats a single fused query
1020
+ * once distilled notes or procedures exist, and degrades to the
1021
+ * plain single-stream path when only `event` content is present.
1022
+ * Default true.
1023
+ */
1024
+ multiStream?: boolean;
1025
+ /**
1026
+ * Bounded graph expansion for this pack: 1-hop neighbours of the top
1027
+ * seeds join the candidate set (via `derived_from` / relation edges),
1028
+ * so consolidated summaries carry their sources. Default true;
1029
+ * `experimental.graphExpansion` settings take precedence when set.
1030
+ */
1031
+ graphExpansion?: boolean;
1032
+ /**
1033
+ * Memory-grounded citations (opt-in, default off): every pack item
1034
+ * carries a `[mem:xxxx]` token (unique within the pack) rendered
1035
+ * ahead of its content in TOON output, resolvable via
1036
+ * `resolveCitation` / `memos cite`.
1037
+ */
1038
+ citations?: boolean;
1039
+ /**
1040
+ * Inject top procedural lessons as an "operating instructions"
1041
+ * section (additive; default off). `true` injects the top 3 lessons
1042
+ * for the query; a number sets k. Uses the pack's namespace.
1043
+ */
1044
+ lessons?: boolean | number;
1045
+ /**
1046
+ * Wire format the pack will be serialized to. When set, the token
1047
+ * budget is enforced against what each item actually costs on the
1048
+ * wire for that format — TOON row scaffolding (`id|score|trust|src|
1049
+ * epochDelta|tags|content`, base64url ids, citations) or the JSON
1050
+ * serializer's field names and quotes — instead of a flat
1051
+ * `content + summary + 4` estimate. Without it the item selection is
1052
+ * exactly what it has always been.
1053
+ */
1054
+ wireFormat?: ContextPackWireFormat;
1055
+ /**
1056
+ * Hard ceiling on the token count of the SERIALIZED output. When the
1057
+ * rendered string exceeds it, the lowest-ranked rows are dropped
1058
+ * until it fits. Only meaningful together with a TOON `format`;
1059
+ * ignored for "json" (an object has no length to trim). Omitted by
1060
+ * default, which leaves the rendered bytes untouched.
1061
+ */
1062
+ maxWireTokens?: number;
1063
+ /**
1064
+ * Compression for items that don't fit the budget: an oversized item
1065
+ * is re-rendered through `compressText` and admitted if the
1066
+ * compressed form fits. Defaults to
1067
+ * `experimental.packCompression`; off unless that config enables it,
1068
+ * so default pack contents stay byte-identical.
1069
+ */
1070
+ compression?: ContextPackCompressionOptions;
1071
+ /**
1072
+ * Attach image references to pack items that are image memories.
1073
+ * Default `true` when any candidate item is an image memory (the
1074
+ * attachment is a reference, never bytes, so there is no token cost
1075
+ * to justify an off-by-default); pass `false` to omit them. Bytes
1076
+ * are resolved separately via `memos.imageMemory(ref)`.
1077
+ */
1078
+ images?: boolean;
1079
+ }): Promise<ContextPack | string>;
1080
+ /**
1081
+ * Fetch procedural lessons for pack injection. Returns `{}` when the
1082
+ * caller didn't ask for lessons or the storage adapter has no lesson
1083
+ * support — packs stay additive and never fail on lessons.
1084
+ */
1085
+ private packLessons;
1086
+ /**
1087
+ * Lifetime token-savings counters for context packs. `savedPct` is the
1088
+ * percentage of the naive baseline (raw node JSON for the same
1089
+ * candidates) that TOON packing + budget trimming eliminated.
1090
+ */
1091
+ usageStats(): {
1092
+ packsBuilt: number;
1093
+ packTokens: number;
1094
+ naiveBaselineTokens: number;
1095
+ savedTokens: number;
1096
+ savedPct: number;
1097
+ };
1098
+ private trackPackUsage;
1099
+ /**
1100
+ * Whether any memory exists in the given pools. Cheap probe used by
1101
+ * `contextPack` to skip multi-stream retrieval on event-only stores.
1102
+ */
1103
+ private hasPoolContent;
1104
+ /**
1105
+ * Multi-stream retrieval: run the query separately against each
1106
+ * granularity pool and fuse the ranked lists by keeping each node's best
1107
+ * stream score. Candidates found by multiple streams are the strongest
1108
+ * evidence — exactly the behavior that beats single-query retrieval on
1109
+ * the LongMemEval-V2 ablations.
1110
+ */
1111
+ private multiStreamSearch;
1112
+ /**
1113
+ * Merge near-duplicate memories. Two memories with cosine
1114
+ * similarity over their embeddings above `threshold` collapse into
1115
+ * one: the survivor is the one with the highest importance (ties
1116
+ * broken by recency), and the merged-in nodes have their access
1117
+ * count and tag union folded in.
1118
+ */
1119
+ dedupe(opts?: DedupeOptions): Promise<DedupeResult>;
1120
+ /**
1121
+ * Move stale + low-importance memories to the `archived` namespace.
1122
+ * They are NOT deleted — they remain in the DB and can be restored
1123
+ * with `memos.update(id, { namespace: "default" })`.
1124
+ */
1125
+ archive(opts?: ArchiveOptions): Promise<ArchiveResult>;
1126
+ /**
1127
+ * Cluster memories by embedding similarity and produce an extractive
1128
+ * summary for each cluster above `minClusterSize`. The summary is
1129
+ * stored as a regular node with `derived_from` edges to each
1130
+ * source.
1131
+ */
1132
+ summarizeCluster(opts?: SummarizeClusterOptions): Promise<SummarizeClusterResult>;
1133
+ /**
1134
+ * Verify the tamper-evident mutation log.
1135
+ *
1136
+ * Checks the hash chain (sequence continuity, prev-hash linkage,
1137
+ * entry-hash recomputation) and cross-checks every node's current
1138
+ * content against its latest log entry. Pass `expectTip` with a
1139
+ * checkpoint hash from `memos log checkpoint` to also prove the
1140
+ * chain wasn't rewritten since the checkpoint.
1141
+ *
1142
+ * See `src/tamper-log.ts` for the honest threat model: without an
1143
+ * externally anchored checkpoint, a full-chain rewrite by someone
1144
+ * with raw DB write access is undetectable.
1145
+ */
1146
+ verifyTamperLog(opts?: {
1147
+ expectTip?: string;
1148
+ }): Promise<TamperVerifyResult>;
1149
+ /**
1150
+ * Read the tamper-evident mutation log in sequence order (newest
1151
+ * last). Returns an empty array when the storage adapter doesn't
1152
+ * implement the log. Entries contain hashes only — no memory
1153
+ * content — so they are safe to display or export.
1154
+ */
1155
+ readTamperLog(): Promise<TamperLogEntry[]>;
1156
+ /**
1157
+ * Export a cryptographic deletion certificate for a forgotten memory.
1158
+ *
1159
+ * Finds the "forget" entry in the tamper log for the given node ID and
1160
+ * returns a verifiable certificate proving what was deleted and when.
1161
+ * Use `verifyDeletionCertificate()` to check it.
1162
+ *
1163
+ * @param nodeId - The deleted node's ID.
1164
+ * @returns The deletion certificate, or null if no forget entry exists.
1165
+ */
1166
+ proveForget(nodeId: string): Promise<DeletionCertificate | null>;
1167
+ /**
1168
+ * Reconstruct the memory state as it existed at a given timestamp.
1169
+ *
1170
+ * Time-travel: answers "what did the agent believe on date X?" by
1171
+ * replaying snapshots. Nodes deleted before the target are excluded.
1172
+ *
1173
+ * Note: snapshots are captured from now onward (in-memory). Historical
1174
+ * state before this feature was enabled is not available.
1175
+ *
1176
+ * @param targetTs - Unix ms timestamp to reconstruct.
1177
+ * @returns Nodes as they existed at targetTs.
1178
+ */
1179
+ replayAt(targetTs: number): Promise<MemoryNode[]>;
1180
+ /**
1181
+ * Version timeline for one memory — the audit view behind "auditable by
1182
+ * design". Returns the memory, the older versions it replaced
1183
+ * (`supersedes`), the newer versions that replaced it (`supersededBy`),
1184
+ * consolidated notes derived from it, and every graph edge touching it.
1185
+ *
1186
+ * Related versions are inferred by content similarity (cosine over
1187
+ * stored embeddings where available, text similarity otherwise) combined
1188
+ * with creation order — superseded versions are the historical ones
1189
+ * (`validTo` set), so nothing here is guesswork about intent.
1190
+ */
1191
+ history(id: string, opts?: HistoryOptions): Promise<MemoryHistory>;
1192
+ /**
1193
+ * Run dedupe + archive (+ optional cluster summary) in one pass.
1194
+ * This is the canonical entry point for "memory maintenance".
1195
+ */
1196
+ consolidate(opts?: ConsolidateOptions): Promise<ConsolidateResult>;
1197
+ /**
1198
+ * Decay-based algorithmic forgetting. Computes a retention score per
1199
+ * memory — importance, trust, access reinforcement, and an exponential
1200
+ * recency term — and SUPERSEDES memories whose score falls below
1201
+ * `minScore`: `validTo = now`, so they become historical and drop out
1202
+ * of default search while remaining queryable via `searchTemporal`.
1203
+ * Nothing is ever deleted.
1204
+ *
1205
+ * Weights: importance 0.35 · trust 0.15 · access 0.15 · recency 0.35.
1206
+ * With the default half-life (60 days) a month-old, never-accessed,
1207
+ * agent-inferred low-importance memory crosses the default threshold
1208
+ * (~0.2) at roughly four months of age; frequently-reinforced or
1209
+ * user-stated memories effectively never do.
1210
+ */
1211
+ forgetByDecay(opts?: DecayForgettingOptions): Promise<DecayForgetResult>;
1212
+ /**
1213
+ * Internal: fetch every node's embedding for the given namespace.
1214
+ * Returns an empty array if the storage adapter doesn't expose
1215
+ * `getAllEmbeddings` (e.g. an in-memory custom adapter).
1216
+ */
1217
+ private collectEmbeddings;
1218
+ /** Clear the search result cache. Called on any mutation. */
1219
+ private invalidateSearchCache;
1220
+ /**
1221
+ * Wait for all pending embedding jobs to complete.
1222
+ *
1223
+ * Resolves once both the queue and any in-flight persist callbacks have
1224
+ * drained. Safe to call when no embedding provider is configured — it
1225
+ * resolves immediately.
1226
+ */
1227
+ flushEmbeddings(): Promise<void>;
1228
+ /**
1229
+ * Embed a query through the configured provider with an in-process LRU
1230
+ * cache. Returns `null` when no embedding provider is configured.
1231
+ *
1232
+ * The cache is recall-neutral by construction: the provider's embed
1233
+ * is deterministic in (provider id, model, query), so a hit returns
1234
+ * the exact vector a fresh inference would produce. This collapses the
1235
+ * repeated identical inferences an agent loop performs — most
1236
+ * importantly the 3× fan-out inside `multiStreamSearch`, which now
1237
+ * embeds once and threads the vector through all three pool streams.
1238
+ */
1239
+ private embedQueryCached;
1240
+ /**
1241
+ * Re-embed every node with the CURRENTLY configured provider, replacing
1242
+ * stale vectors in place. Required after switching embedding models:
1243
+ * vectors from different models are not comparable (semantic search
1244
+ * filters by stored model), and the embedding table has no automatic
1245
+ * migration.
1246
+ *
1247
+ * @param opts.purgeStale — When true, first delete all embedding rows
1248
+ * stored by any OTHER model (frees space; without it, old-model rows
1249
+ * stay on disk until overwritten node-by-node).
1250
+ * @returns Summary counters. `failed` counts nodes whose embedding
1251
+ * could not be recomputed (their status is marked `failed`).
1252
+ */
1253
+ reindexEmbeddings(opts?: {
1254
+ purgeStale?: boolean;
1255
+ }): Promise<{
1256
+ reembedded: number;
1257
+ purged: number;
1258
+ failed: number;
1259
+ model: string;
1260
+ }>;
1261
+ /**
1262
+ * Per-node embedding status, plus the queue's running/pending counters.
1263
+ *
1264
+ * @param nodeId — Optional filter; when provided, returns only that node.
1265
+ * @returns Aggregate status when called with no args, or a single info
1266
+ * object for the requested node.
1267
+ */
1268
+ embeddingStatus(): EmbeddingQueueStatus;
1269
+ embeddingStatus(nodeId: string): EmbeddingNodeStatusInfo | null;
1270
+ /**
1271
+ * Set the temporal validity window on a memory node.
1272
+ *
1273
+ * When `validTo` is set to a past timestamp, the memory becomes
1274
+ * "historical" — it is excluded from default search results but
1275
+ * remains queryable via `searchTemporal(query, atTime)`.
1276
+ *
1277
+ * @param id — Memory node ID.
1278
+ * @param validFrom — Validity start (Unix ms), or null to clear.
1279
+ * @param validTo — Validity end (Unix ms), or null to clear.
1280
+ * @returns The updated node, or null if not found.
1281
+ */
1282
+ setValidity(id: string, validFrom: number | null, validTo: number | null,
1283
+ /** Optional confidence to stamp alongside the validity change. */
1284
+ confidence?: number,
1285
+ /** Optional evidence count to stamp alongside the validity change. */
1286
+ evidenceCount?: number): Promise<MemoryNode | null>;
1287
+ /**
1288
+ * Search for memories that were valid at a specific point in time.
1289
+ *
1290
+ * This is the temporal knowledge graph query: "What did we know
1291
+ * at time T?" A memory is included if:
1292
+ * (validFrom is null OR validFrom <= atTime) AND
1293
+ * (validTo is null OR validTo >= atTime)
1294
+ *
1295
+ * @param query — Text search query.
1296
+ * @param atTime — The point in time to query (Unix ms).
1297
+ * @param opts — Additional search filters (limit, tags, namespace, etc.).
1298
+ * @returns Scored memories valid at `atTime`.
1299
+ */
1300
+ searchTemporal(query: string, atTime: number, opts?: Omit<SearchFilter, "query" | "validAt">): Promise<ScoredMemory[]>;
1301
+ /**
1302
+ * Supersede a memory — mark it as historical by setting `validTo`
1303
+ * to now, and optionally link the replacement memory with a
1304
+ * `temporal_precedes` edge.
1305
+ *
1306
+ * @param id — The memory to supersede.
1307
+ * @param replacementId — Optional ID of the new memory that replaces it.
1308
+ * @param confidence — Optional confidence to stamp on the retracted
1309
+ * node. Supersession is a contradiction: when the evidence state
1310
+ * machine has already computed the crushed score, pass it here so
1311
+ * the old version stops outranking its replacement. Omit to leave
1312
+ * confidence untouched (pure temporal supersession).
1313
+ * @returns The superseded (updated) node, or null if not found.
1314
+ */
1315
+ supersede(id: string, replacementId?: string, confidence?: number, evidenceCount?: number): Promise<MemoryNode | null>;
1316
+ /**
1317
+ * Find the predecessor of a memory in its version ledger — the older
1318
+ * version the target superseded. Signals, in priority order:
1319
+ *
1320
+ * 1. `temporal_precedes` edges where the target is the newer endpoint
1321
+ * (written by supersede(id, replacementId));
1322
+ * 2. `resolved` contradiction pairs involving the target (written by the
1323
+ * evidence state machine's contradict path). Pair ids are stored in
1324
+ * canonical sorted order, so direction is recovered from validity —
1325
+ * the historical (validTo-stamped) endpoint is the predecessor.
1326
+ *
1327
+ * The most recently created candidate wins. Returns null when the
1328
+ * memory has no supersession history.
1329
+ */
1330
+ findRevertPredecessor(targetId: string): Promise<MemoryNode | null>;
1331
+ /**
1332
+ * The most recent currently-valid, non-audit memory in a namespace —
1333
+ * what "revert that" / "revert the last thing" resolves to. Revert
1334
+ * audit events are excluded so a revert can never target its own
1335
+ * record.
1336
+ */
1337
+ private mostRecentRevertible;
1338
+ /**
1339
+ * Resolve a named entity to the best-matching memory. Disambiguation
1340
+ * rule: when several valid memories match, the most recently created
1341
+ * one wins and the rest are returned as `alternatives` for confirmation
1342
+ * (CLI --dry-run, MCP dry_run) instead of being guessed over.
1343
+ */
1344
+ private resolveRevertEntity;
1345
+ /**
1346
+ * Resolve a revert scope to a concrete target memory.
1347
+ *
1348
+ * - `{ kind: "id" }` — the memory with that ID.
1349
+ * - `{ kind: "last" }` — the most recent currently-valid, non-audit
1350
+ * memory in the namespace (durable across processes).
1351
+ * - `{ kind: "entity" }` — named-entity search, most-recent-wins.
1352
+ * - `{ kind: "text" }` — natural language run through the local intent
1353
+ * detector (`detectRevertIntent`); "last"-scope phrasing resolves as
1354
+ * "last", named phrasing as an entity. Throws when no revert intent
1355
+ * is detected.
1356
+ */
1357
+ resolveRevertTarget(scope: RevertScopeInput | string, namespace?: string): Promise<RevertTargetResolution>;
1358
+ /**
1359
+ * Revert a memory to its previous version — the command-driven belief
1360
+ * revision primitive. The user/agent says "revert that" / "go back" /
1361
+ * "that last thing was wrong"; MemOS closes the target's validity
1362
+ * interval NOW and reactivates the predecessor, so the next recall
1363
+ * returns the pre-correction belief with full provenance.
1364
+ *
1365
+ * Add-only: the reverted-away version stays in history (validTo
1366
+ * stamped, the row is never deleted), and the revert itself is recorded
1367
+ * as an audit event node (tags `audit`/`revert`, metadata.audit =
1368
+ * "revert") capturing who/when/why. Edge validity intervals closed by
1369
+ * the earlier supersession are NOT reopened — edges stay add-only; the
1370
+ * node-level belief is what recall returns.
1371
+ *
1372
+ * @param scope — where the target comes from (or a raw string, run
1373
+ * through the intent detector).
1374
+ * @param opts — actor / reason (recorded on the audit event), dryRun,
1375
+ * namespace. Defaults are unchanged everywhere else.
1376
+ * @throws When no target resolves, the target is already historical, or
1377
+ * it has no supersession history to restore.
1378
+ */
1379
+ revert(scope: RevertScopeInput | string, opts?: RevertOptions): Promise<RevertResult>;
1380
+ /**
1381
+ * Get the trust score of a memory.
1382
+ *
1383
+ * @param id — Memory node ID.
1384
+ * @returns The trust score [0, 1], or null if the node doesn't exist.
1385
+ */
1386
+ trust(id: string): Promise<number | null>;
1387
+ /**
1388
+ * Set the trust score of a memory.
1389
+ *
1390
+ * Trust scores influence retrieval ranking: higher-trust memories
1391
+ * rank above lower-trust ones for the same relevance score.
1392
+ *
1393
+ * @param id — Memory node ID.
1394
+ * @param score — Trust score in [0, 1].
1395
+ * @returns The updated node, or null if not found.
1396
+ */
1397
+ setTrust(id: string, score: number): Promise<MemoryNode | null>;
1398
+ /**
1399
+ * Adjust the trust score of a memory by a delta, clamped to [0, 1].
1400
+ *
1401
+ * @param id — Memory node ID.
1402
+ * @param delta — Amount to add (positive) or subtract (negative).
1403
+ * @returns The updated node, or null if not found.
1404
+ */
1405
+ adjustTrust(id: string, delta: number): Promise<MemoryNode | null>;
1406
+ /**
1407
+ * Extract candidate facts from a conversation.
1408
+ *
1409
+ * Uses a rule-based extractor that identifies user statements,
1410
+ * preferences, and entities from conversation messages. Each
1411
+ * candidate fact is scored by confidence and optionally stored.
1412
+ *
1413
+ * This is a local-first alternative to LLM-based extraction — no
1414
+ * API calls required. For higher-quality extraction, callers can
1415
+ * use an LLM to pre-process the conversation and pass the results
1416
+ * as plain text messages.
1417
+ *
1418
+ * @param messages — The conversation to extract facts from.
1419
+ * @param opts — Extraction options.
1420
+ * @returns Extracted facts and (if autoStore) stored node IDs.
1421
+ */
1422
+ extractFacts(messages: ConversationMessage[], opts?: ExtractFactsOptions): Promise<ExtractFactsResult>;
1423
+ /**
1424
+ * Rule-based fact extractor. Scans conversation messages for
1425
+ * patterns that indicate facts, preferences, and entities.
1426
+ *
1427
+ * Heuristics:
1428
+ * - "I like/prefer/use/work at" → preference
1429
+ * - "My name is/I am/I'm" → entity (about the user)
1430
+ * - "I need/want/should" → context
1431
+ * - Declarative statements from the user → fact
1432
+ */
1433
+ private runFactExtractor;
1434
+ /**
1435
+ * Return a detailed diagnostics snapshot of the memory store.
1436
+ *
1437
+ * Includes counts by source, type, and namespace; temporal and
1438
+ * trust statistics; embedding coverage; storage capabilities; and
1439
+ * database file size.
1440
+ *
1441
+ * @returns A diagnostics report.
1442
+ */
1443
+ /**
1444
+ * Runtime metadata about the active embedding provider: what was
1445
+ * requested vs. what actually resolved, and whether the FastEmbed
1446
+ * local-hash fallback is active. Returns null when embeddings are
1447
+ * disabled (`embeddings: { enabled: false }`).
1448
+ */
1449
+ getEmbeddingRuntimeInfo(): EmbeddingRuntimeInfo | null;
1450
+ diagnostics(): Promise<DiagnosticsResult>;
1451
+ /**
1452
+ * Register an event listener.
1453
+ */
1454
+ on(event: MemOSEvent, listener: MemOSEventListener): void;
1455
+ /**
1456
+ * Remove an event listener.
1457
+ */
1458
+ off(event: MemOSEvent, listener: MemOSEventListener): void;
1459
+ private emit;
1460
+ private assertInit;
1461
+ /**
1462
+ * Evict the least-important memory when `maxMemories` is exceeded.
1463
+ *
1464
+ * The selection is a single SQL statement — we do not load the full
1465
+ * table into memory the way the old in-JS sort did. Falls back to
1466
+ * the in-memory path if the storage adapter doesn't implement
1467
+ * `evictLeastImportant`.
1468
+ */
1469
+ private evict;
1470
+ /**
1471
+ * Evidence state machine hook for `store()` (opt-in via
1472
+ * `{ evidenceLearning: true }`). Compares the incoming content against
1473
+ * the most similar existing memory in the namespace and applies the
1474
+ * outcome:
1475
+ *
1476
+ * - `{ action: "reinforced", node }`: an existing memory was
1477
+ * confirmed — its confidence and evidence count were bumped and
1478
+ * the caller should NOT write a duplicate node. The returned node
1479
+ * is the freshly-updated version.
1480
+ * - `{ action: "superseded" }`: an existing memory was contradicted —
1481
+ * it has been marked historical (validTo = now) and the caller
1482
+ * SHOULD write the new version.
1483
+ * - `{ action: "new" }`: no decisive match; the caller writes a
1484
+ * fresh node.
1485
+ *
1486
+ * Candidate selection reuses the search index (top-8 by hybrid rank);
1487
+ * classification uses content-to-content pair similarity via
1488
+ * {@link classifyEvidence}, with a shared-subject guard so a negation
1489
+ * can only supersede a memory that is actually about the same thing.
1490
+ */
1491
+ private applyStoreEvidence;
1492
+ /** Cosine similarity over embeddings with a bag-of-words fallback. */
1493
+ private pairSimilarity;
1494
+ /**
1495
+ * The text that gets embedded for a node. `embeddings.embedText`
1496
+ * selects raw content vs. the historical summary+content blend (the
1497
+ * extractive summary roughly doubles input tokens for short facts).
1498
+ * Write-time context (`metadata.context`, from `store({ context })` or
1499
+ * the `enrichMemory` hook) is prepended so the vector carries
1500
+ * situational meaning — contextual retrieval, without re-embedding
1501
+ * queries. Only nodes stored with a context line are affected; changing
1502
+ * enrichment later requires `reindexEmbeddings()`.
1503
+ */
1504
+ private embeddedTextFor;
1505
+ private persistEmbedding;
1506
+ /**
1507
+ * Runs after an embedding vector is persisted for a node — all three
1508
+ * persist paths (background queue, synchronous queue fallback, inline
1509
+ * fallback) funnel through here.
1510
+ *
1511
+ * Beyond persisting, this is the write-time contradiction-detection
1512
+ * hook: when the node was freshly written by `store()` (the pending
1513
+ * gate), a bounded semantic-neighbor scan flags contradiction
1514
+ * candidates and persists them. The scan runs in the embedding
1515
+ * background context — never on the synchronous `store()` path — so
1516
+ * write-path overhead stays bounded. Best-effort: detection failures
1517
+ * must never break embedding persistence.
1518
+ */
1519
+ private onEmbeddingPersisted;
1520
+ /**
1521
+ * Write-time contradiction candidate detection.
1522
+ *
1523
+ * Scans a BOUNDED set of semantic neighbors (top
1524
+ * {@link CONTRADICTION_SCAN_LIMIT} above
1525
+ * {@link CONTRADICTION_SIM_MIN}, same namespace, same embedding
1526
+ * model — one indexed query, never a full table scan), flags pairs
1527
+ * in the contradiction band via `findContradictionCandidates`, and
1528
+ * persists them as `unresolved` pairs. Confirmed `contradicted`
1529
+ * outcomes from the evidence state machine are recorded separately
1530
+ * by `store()` with status `resolved` (see the provenance block
1531
+ * there).
1532
+ */
1533
+ private detectContradictionsForNode;
1534
+ /**
1535
+ * Enqueue a node for background embedding. Returns immediately.
1536
+ *
1537
+ * If `embeddingQueue.synchronous` is true, falls back to awaiting the
1538
+ * inline persist call (useful for tests that need read-after-write
1539
+ * semantics).
1540
+ */
1541
+ private scheduleEmbedding;
1542
+ private recordEmbeddingStatus;
1543
+ private backfillEmbeddings;
1544
+ /**
1545
+ * Read-time contradiction resolution (accuracy item 4).
1546
+ *
1547
+ * Fetches recorded contradiction pairs for the ids in the fused
1548
+ * result set (one bounded IN-query) and applies
1549
+ * {@link resolveContradictionsAtRead}: when both members of a pair
1550
+ * are present, the older one is score-demoted so the newer version
1551
+ * wins. Best-effort and storage-agnostic — any failure or a storage
1552
+ * without the contradictions table returns the input untouched.
1553
+ */
1554
+ private applyContradictionResolution;
1555
+ /**
1556
+ * ── item2: entity leg ── candidate generation from the entity→memories
1557
+ * inverted index (`src/entity-index.ts`). Returns ranked `ScoredMemory`s
1558
+ * (score = number of matched query entities, rank = match count desc /
1559
+ * node id tiebreak) for fusion as the third RRF leg in `fuseResults`.
1560
+ *
1561
+ * Applies the same namespace / type / pool / tag / temporal scoping as
1562
+ * the keyword and semantic legs, so the entity leg can never leak
1563
+ * out-of-scope memories into results. Degrades to `[]` when the query
1564
+ * has no entities or the storage has no entity index (custom adapters).
1565
+ */
1566
+ private fetchEntityLeg;
1567
+ private hybridSearch;
1568
+ /**
1569
+ * Search and report the relevance gate's verdict alongside the results.
1570
+ *
1571
+ * Backward-compatible sibling of {@link search}: `search()` keeps
1572
+ * returning a bare `ScoredMemory[]`, and every result still carries the
1573
+ * same `relevanceGate` metadata this method exposes as a first-class
1574
+ * member. Unlike `search()` this method bypasses the result cache, so
1575
+ * the reported verdict always describes the call you just made.
1576
+ *
1577
+ * @example
1578
+ * ```ts
1579
+ * const memos = new MemOS({
1580
+ * retrieval: { relevanceGate: { enabled: true, abstainSimilarity: 0.3 } },
1581
+ * });
1582
+ * const { results, gate } = await memos.searchWithGate("anything");
1583
+ * if (gate.abstained) {
1584
+ * // The store has nothing about this query — answer from world
1585
+ * // knowledge instead of inventing a memory recall.
1586
+ * }
1587
+ * ```
1588
+ */
1589
+ searchWithGate(queryOrFilter: string | SearchFilter): Promise<{
1590
+ results: ScoredMemory[];
1591
+ gate: RelevanceGateResult;
1592
+ }>;
1593
+ /**
1594
+ * Resolve the relevance-gate config, or `null` when the gate is off.
1595
+ *
1596
+ * Off means byte-identical output: the stage is skipped entirely and
1597
+ * the pipeline returns exactly what fusion + expansion + rerank
1598
+ * produced. `enabled` defaults to false because this is a published
1599
+ * SDK and an enabled gate changes how many memories a search returns
1600
+ * for the same query. `MEMOS_RELEVANCE_GATE=1` turns it on without a
1601
+ * config change (experiments and A/B runs).
1602
+ */
1603
+ private resolveRelevanceGate;
1604
+ /**
1605
+ * Apply the relevance gate to a finished, best-first result list.
1606
+ *
1607
+ * `applyRelevanceGate` (src/retrieval.ts) is pure and already covers
1608
+ * abstention and tail trimming; this wrapper adds the two things the
1609
+ * SDK needs around it:
1610
+ *
1611
+ * - a `minResults` floor, so a caller that needs N items is never left
1612
+ * with fewer by an aggressive ratio;
1613
+ * - reporting — the outcome is written to `opts.gateOut` (when the
1614
+ * caller wants it, e.g. `searchWithGate`) and stamped onto every
1615
+ * surviving result as `ScoredMemory.relevanceGate`.
1616
+ *
1617
+ * Only runs when the gate is configured; otherwise the input array is
1618
+ * returned untouched.
1619
+ */
1620
+ private applyRelevanceGateStage;
1621
+ /**
1622
+ * Two-stage reranking (`experimental.rerank`, off by default). The first
1623
+ * stage — hybrid retrieval — exists to RECALL candidates; a cross-encoder
1624
+ * then re-scores the top `candidates` fused results against the query and
1625
+ * the final ranking is reordered by that score. Cross-encoders read query
1626
+ * and document together, so they are far more precise than bi-encoder
1627
+ * similarity — at O(candidates) cost per query, which is why it only
1628
+ * ever runs on the narrow head of the pool.
1629
+ *
1630
+ * Two providers: `"endpoint"` (an HTTP `/rerank` server, e.g.
1631
+ * bge-reranker-v2-m3 served by llama-server) and `"local"` (in-process
1632
+ * cross-encoder via `src/rerank-local.ts`, no server needed).
1633
+ *
1634
+ * On reranker failure the fused order is kept unchanged (graceful
1635
+ * degradation) — a downed reranker degrades quality, not availability.
1636
+ */
1637
+ private applyRerank;
1638
+ /**
1639
+ * Graph-expansion recall leg. Takes the top `maxSeeds` fused results,
1640
+ * follows their graph edges in both directions, and injects unseen
1641
+ * neighbours at `seedScore × scoreFactor` (halved for each subsequent
1642
+ * neighbour of the same seed). Lifts multi-evidence / multi-hop
1643
+ * queries where related facts are connected by edges but neither
1644
+ * retrieval leg surfaces them.
1645
+ *
1646
+ * An EXPLICIT `config` (passed by `contextPack`, which resolves its
1647
+ * own `graphExpansion` opt-out) is authoritative — it is used as-is.
1648
+ * When `config` is omitted the leg resolves its own default:
1649
+ * `experimental.graphExpansion` when present, otherwise ON, and OFF
1650
+ * when `fusion.graphExpansion` is configured (that flag is the
1651
+ * PPR-leg knob — see `applyPprGraphExpansion` — and when a caller
1652
+ * tunes it the PPR walk owns graph expansion, so this older
1653
+ * injection leg stays out of the way rather than double-injecting
1654
+ * nodes the PPR walk should be reporting on).
1655
+ *
1656
+ * `SearchFilter.graphExpansion` is an opt-OUT here (`false` suppresses
1657
+ * injection for one query, which is how the pack path turns expansion
1658
+ * off); `true` is the PPR leg's business and must not turn injection
1659
+ * on when it is otherwise off.
1660
+ */
1661
+ private applyGraphExpansion;
1662
+ /**
1663
+ * PPR-lite graph expansion (single-step, HippoRAG-style). Runs after
1664
+ * fusion (and after the experimental neighbour-injection leg): the top
1665
+ * `graphExpansionSeeds` fused results seed a personalized PageRank walk
1666
+ * (damping 0.5, `graphExpansionHops` hops) over graph edges plus
1667
+ * shared-entity links, and the final score blends as
1668
+ * `(1 - graphExpansionAlpha) * fused + graphExpansionAlpha * pprNorm`
1669
+ * with PPR scores normalized to [0,1]. Graph neighbours of seeds that
1670
+ * neither retrieval leg surfaced can enter the results this way.
1671
+ *
1672
+ * Controlled by `config.fusion`: `graphExpansion` (default true),
1673
+ * `graphExpansionAlpha` (default 0.2), `graphExpansionHops` (default 2),
1674
+ * `graphExpansionSeeds` (default 10). `filter.graphExpansion === false`
1675
+ * disables it for a single query. With expansion disabled (or
1676
+ * alpha <= 0) the fused list is returned untouched — byte-identical to
1677
+ * the pre-expansion pipeline.
1678
+ *
1679
+ * Cost: one bounded BFS + power iteration over the seed neighbourhood
1680
+ * (no LLM, no embedding calls) plus at most `DEFAULT_PPR_MAX_INJECTED`
1681
+ * `peekNode` reads for newly injected neighbours.
1682
+ */
1683
+ private applyPprGraphExpansion;
1684
+ /**
1685
+ * In-memory mirror of the SQLite `queryNodes` structured-filter
1686
+ * semantics, applied to PPR-injected neighbours. The SQL legs filter
1687
+ * type, pool, tags, metadata, importance/trust bounds, and temporal
1688
+ * windows at scan time; graph traversal bypasses all of that, so an
1689
+ * injected node must satisfy the same constraints or the expansion
1690
+ * would leak across filter boundaries (e.g. a procedure-only search
1691
+ * surfacing an event-pool neighbour).
1692
+ *
1693
+ * Namespace scoping is enforced separately by the seed-namespace
1694
+ * guard (stricter: the injected node must share a seed's namespace,
1695
+ * and seeds already passed the query's namespace/scope filter). The
1696
+ * query text itself is intentionally not re-applied — injected nodes
1697
+ * are recall wins the keyword/semantic legs missed.
1698
+ */
1699
+ private pprInjectedNodePassesFilter;
1700
+ /**
1701
+ * Neighbour lookup for the PPR walk, over two link types:
1702
+ *
1703
+ * (a) Graph edges — from the in-memory `GraphEngine` adjacency (both
1704
+ * endpoints, i.e. undirected traversal). Cheapest source: no
1705
+ * storage I/O, O(1) per node.
1706
+ * (b) Shared-entity links — nodes in the fused candidate set sharing a
1707
+ * tag or `metadata.entities` entry (exact match, lowercased),
1708
+ * built in-memory over the candidate set. There is no
1709
+ * entity→memories inverted table on this branch, so this is the
1710
+ * zero-I/O analogue; nodes discovered via edges at hop 2+ do not
1711
+ * contribute entity links (their tags are never fetched, keeping
1712
+ * the hot path bounded) but remain reachable/expandable via edges.
1713
+ *
1714
+ * The returned list is sorted for deterministic iteration; fan-out
1715
+ * capping happens inside {@link personalizedPageRank}.
1716
+ */
1717
+ private buildPprNeighbors;
1718
+ /**
1719
+ * Near-duplicate suppression (opt-in via `filter.semanticDedup`). A
1720
+ * result whose stored embedding is a near-verbatim duplicate (cosine >=
1721
+ * `experimental.searchDedupThreshold`, default 0.95) of an
1722
+ * already-selected result is dropped and the next candidate takes its
1723
+ * slot — multi-evidence questions otherwise lose top-K slots to
1724
+ * restated facts. Similarity comes from the storage vector cache, so
1725
+ * the pass costs a few dot products, not a new embedding call.
1726
+ */
1727
+ private applySearchDedup;
1728
+ /**
1729
+ * Session-sibling expansion. Memories recorded from the same
1730
+ * conversation (`metadata.sessionId` / `metadata.instanceId`) are
1731
+ * pulled in behind their seed at a decaying fraction of the seed
1732
+ * score. Multi-hop answers typically span many turns of one session,
1733
+ * so recovering the neighbours of a single relevant turn raises
1734
+ * all-evidence recall without another embedding pass.
1735
+ *
1736
+ * ON by default (`experimental.sessionExpansion`, default true):
1737
+ * injected siblings sit BEHIND every organically-retrieved result at a
1738
+ * decaying `seed.score * scoreFactor^k`, so the head ordering never
1739
+ * moves and only the empty tail is filled — the injected node is only
1740
+ * ever ranked above nodes that scored below it. An explicit
1741
+ * `filter.sessionExpansion === false` disables it per query.
1742
+ */
1743
+ private applySessionExpansion;
1744
+ /**
1745
+ * Start the background TTL sweep timer.
1746
+ */
1747
+ private startSweep;
1748
+ /**
1749
+ * Stop the background TTL sweep timer.
1750
+ */
1751
+ private stopSweep;
1752
+ /**
1753
+ * Convert a memory node to markdown with YAML frontmatter.
1754
+ */
1755
+ private nodeToMarkdown;
1756
+ private escapeDot;
1757
+ private escapeRegex;
1758
+ }
1759
+ export type { MemoryNode, MemoryEdge, SearchFilter, ScoredMemory, GraphSnapshot, MemOSConfig, ExportOptions, ExportResult, ExperimentalConfig, } from "./types.js";
1760
+ export { GraphEngine, textSimilarity } from "./graph.js";
1761
+ export { SQLiteStorage } from "./storage/sqlite.js";
1762
+ //# sourceMappingURL=memory.d.ts.map