ai-memory-layer 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/LICENSE +21 -0
  3. package/README.md +765 -0
  4. package/bin/memory-server.mjs +157 -0
  5. package/dist/adapters/memory/embeddings.d.ts +4 -0
  6. package/dist/adapters/memory/embeddings.d.ts.map +1 -0
  7. package/dist/adapters/memory/embeddings.js +53 -0
  8. package/dist/adapters/memory/embeddings.js.map +1 -0
  9. package/dist/adapters/memory/index.d.ts +7 -0
  10. package/dist/adapters/memory/index.d.ts.map +1 -0
  11. package/dist/adapters/memory/index.js +650 -0
  12. package/dist/adapters/memory/index.js.map +1 -0
  13. package/dist/adapters/postgres/index.d.ts +38 -0
  14. package/dist/adapters/postgres/index.d.ts.map +1 -0
  15. package/dist/adapters/postgres/index.js +982 -0
  16. package/dist/adapters/postgres/index.js.map +1 -0
  17. package/dist/adapters/sqlite/embeddings.d.ts +5 -0
  18. package/dist/adapters/sqlite/embeddings.d.ts.map +1 -0
  19. package/dist/adapters/sqlite/embeddings.js +122 -0
  20. package/dist/adapters/sqlite/embeddings.js.map +1 -0
  21. package/dist/adapters/sqlite/index.d.ts +8 -0
  22. package/dist/adapters/sqlite/index.d.ts.map +1 -0
  23. package/dist/adapters/sqlite/index.js +839 -0
  24. package/dist/adapters/sqlite/index.js.map +1 -0
  25. package/dist/adapters/sqlite/mappers.d.ts +40 -0
  26. package/dist/adapters/sqlite/mappers.d.ts.map +1 -0
  27. package/dist/adapters/sqlite/mappers.js +95 -0
  28. package/dist/adapters/sqlite/mappers.js.map +1 -0
  29. package/dist/adapters/sqlite/schema.d.ts +4 -0
  30. package/dist/adapters/sqlite/schema.d.ts.map +1 -0
  31. package/dist/adapters/sqlite/schema.js +394 -0
  32. package/dist/adapters/sqlite/schema.js.map +1 -0
  33. package/dist/adapters/sync-to-async.d.ts +15 -0
  34. package/dist/adapters/sync-to-async.d.ts.map +1 -0
  35. package/dist/adapters/sync-to-async.js +95 -0
  36. package/dist/adapters/sync-to-async.js.map +1 -0
  37. package/dist/cli/inspect.d.ts +34 -0
  38. package/dist/cli/inspect.d.ts.map +1 -0
  39. package/dist/cli/inspect.js +190 -0
  40. package/dist/cli/inspect.js.map +1 -0
  41. package/dist/contracts/async-storage.d.ts +86 -0
  42. package/dist/contracts/async-storage.d.ts.map +1 -0
  43. package/dist/contracts/async-storage.js +2 -0
  44. package/dist/contracts/async-storage.js.map +1 -0
  45. package/dist/contracts/embedding.d.ts +22 -0
  46. package/dist/contracts/embedding.d.ts.map +1 -0
  47. package/dist/contracts/embedding.js +2 -0
  48. package/dist/contracts/embedding.js.map +1 -0
  49. package/dist/contracts/identity.d.ts +29 -0
  50. package/dist/contracts/identity.d.ts.map +1 -0
  51. package/dist/contracts/identity.js +34 -0
  52. package/dist/contracts/identity.js.map +1 -0
  53. package/dist/contracts/observability.d.ts +18 -0
  54. package/dist/contracts/observability.d.ts.map +1 -0
  55. package/dist/contracts/observability.js +7 -0
  56. package/dist/contracts/observability.js.map +1 -0
  57. package/dist/contracts/policy.d.ts +108 -0
  58. package/dist/contracts/policy.d.ts.map +1 -0
  59. package/dist/contracts/policy.js +107 -0
  60. package/dist/contracts/policy.js.map +1 -0
  61. package/dist/contracts/storage.d.ts +78 -0
  62. package/dist/contracts/storage.d.ts.map +1 -0
  63. package/dist/contracts/storage.js +2 -0
  64. package/dist/contracts/storage.js.map +1 -0
  65. package/dist/contracts/types.d.ts +381 -0
  66. package/dist/contracts/types.d.ts.map +1 -0
  67. package/dist/contracts/types.js +94 -0
  68. package/dist/contracts/types.js.map +1 -0
  69. package/dist/core/circuit-breaker.d.ts +11 -0
  70. package/dist/core/circuit-breaker.d.ts.map +1 -0
  71. package/dist/core/circuit-breaker.js +38 -0
  72. package/dist/core/circuit-breaker.js.map +1 -0
  73. package/dist/core/context.d.ts +56 -0
  74. package/dist/core/context.d.ts.map +1 -0
  75. package/dist/core/context.js +345 -0
  76. package/dist/core/context.js.map +1 -0
  77. package/dist/core/events.d.ts +8 -0
  78. package/dist/core/events.d.ts.map +1 -0
  79. package/dist/core/events.js +25 -0
  80. package/dist/core/events.js.map +1 -0
  81. package/dist/core/extractor.d.ts +37 -0
  82. package/dist/core/extractor.d.ts.map +1 -0
  83. package/dist/core/extractor.js +448 -0
  84. package/dist/core/extractor.js.map +1 -0
  85. package/dist/core/formatter.d.ts +25 -0
  86. package/dist/core/formatter.d.ts.map +1 -0
  87. package/dist/core/formatter.js +97 -0
  88. package/dist/core/formatter.js.map +1 -0
  89. package/dist/core/knowledge-lifecycle.d.ts +15 -0
  90. package/dist/core/knowledge-lifecycle.d.ts.map +1 -0
  91. package/dist/core/knowledge-lifecycle.js +103 -0
  92. package/dist/core/knowledge-lifecycle.js.map +1 -0
  93. package/dist/core/maintenance.d.ts +13 -0
  94. package/dist/core/maintenance.d.ts.map +1 -0
  95. package/dist/core/maintenance.js +102 -0
  96. package/dist/core/maintenance.js.map +1 -0
  97. package/dist/core/manager.d.ts +110 -0
  98. package/dist/core/manager.d.ts.map +1 -0
  99. package/dist/core/manager.js +640 -0
  100. package/dist/core/manager.js.map +1 -0
  101. package/dist/core/monitor.d.ts +73 -0
  102. package/dist/core/monitor.d.ts.map +1 -0
  103. package/dist/core/monitor.js +395 -0
  104. package/dist/core/monitor.js.map +1 -0
  105. package/dist/core/orchestrator.d.ts +64 -0
  106. package/dist/core/orchestrator.d.ts.map +1 -0
  107. package/dist/core/orchestrator.js +916 -0
  108. package/dist/core/orchestrator.js.map +1 -0
  109. package/dist/core/presets.d.ts +15 -0
  110. package/dist/core/presets.d.ts.map +1 -0
  111. package/dist/core/presets.js +99 -0
  112. package/dist/core/presets.js.map +1 -0
  113. package/dist/core/provider-managers.d.ts +47 -0
  114. package/dist/core/provider-managers.d.ts.map +1 -0
  115. package/dist/core/provider-managers.js +112 -0
  116. package/dist/core/provider-managers.js.map +1 -0
  117. package/dist/core/quick.d.ts +62 -0
  118. package/dist/core/quick.d.ts.map +1 -0
  119. package/dist/core/quick.js +300 -0
  120. package/dist/core/quick.js.map +1 -0
  121. package/dist/core/retrieval.d.ts +29 -0
  122. package/dist/core/retrieval.d.ts.map +1 -0
  123. package/dist/core/retrieval.js +150 -0
  124. package/dist/core/retrieval.js.map +1 -0
  125. package/dist/core/runtime.d.ts +67 -0
  126. package/dist/core/runtime.d.ts.map +1 -0
  127. package/dist/core/runtime.js +84 -0
  128. package/dist/core/runtime.js.map +1 -0
  129. package/dist/core/streaming.d.ts +37 -0
  130. package/dist/core/streaming.d.ts.map +1 -0
  131. package/dist/core/streaming.js +51 -0
  132. package/dist/core/streaming.js.map +1 -0
  133. package/dist/core/sync.d.ts +13 -0
  134. package/dist/core/sync.d.ts.map +1 -0
  135. package/dist/core/sync.js +46 -0
  136. package/dist/core/sync.js.map +1 -0
  137. package/dist/core/telemetry.d.ts +8 -0
  138. package/dist/core/telemetry.d.ts.map +1 -0
  139. package/dist/core/telemetry.js +14 -0
  140. package/dist/core/telemetry.js.map +1 -0
  141. package/dist/core/tokens.d.ts +8 -0
  142. package/dist/core/tokens.d.ts.map +1 -0
  143. package/dist/core/tokens.js +59 -0
  144. package/dist/core/tokens.js.map +1 -0
  145. package/dist/core/trust.d.ts +23 -0
  146. package/dist/core/trust.d.ts.map +1 -0
  147. package/dist/core/trust.js +164 -0
  148. package/dist/core/trust.js.map +1 -0
  149. package/dist/core/validation.d.ts +36 -0
  150. package/dist/core/validation.d.ts.map +1 -0
  151. package/dist/core/validation.js +185 -0
  152. package/dist/core/validation.js.map +1 -0
  153. package/dist/embeddings/local.d.ts +5 -0
  154. package/dist/embeddings/local.d.ts.map +1 -0
  155. package/dist/embeddings/local.js +128 -0
  156. package/dist/embeddings/local.js.map +1 -0
  157. package/dist/embeddings/openai.d.ts +26 -0
  158. package/dist/embeddings/openai.d.ts.map +1 -0
  159. package/dist/embeddings/openai.js +48 -0
  160. package/dist/embeddings/openai.js.map +1 -0
  161. package/dist/embeddings/resilience.d.ts +5 -0
  162. package/dist/embeddings/resilience.d.ts.map +1 -0
  163. package/dist/embeddings/resilience.js +53 -0
  164. package/dist/embeddings/resilience.js.map +1 -0
  165. package/dist/embeddings/voyage.d.ts +30 -0
  166. package/dist/embeddings/voyage.d.ts.map +1 -0
  167. package/dist/embeddings/voyage.js +53 -0
  168. package/dist/embeddings/voyage.js.map +1 -0
  169. package/dist/index.d.ts +72 -0
  170. package/dist/index.d.ts.map +1 -0
  171. package/dist/index.js +40 -0
  172. package/dist/index.js.map +1 -0
  173. package/dist/integrations/claude-agent.d.ts +21 -0
  174. package/dist/integrations/claude-agent.d.ts.map +1 -0
  175. package/dist/integrations/claude-agent.js +44 -0
  176. package/dist/integrations/claude-agent.js.map +1 -0
  177. package/dist/integrations/claude-tools.d.ts +18 -0
  178. package/dist/integrations/claude-tools.d.ts.map +1 -0
  179. package/dist/integrations/claude-tools.js +60 -0
  180. package/dist/integrations/claude-tools.js.map +1 -0
  181. package/dist/integrations/langchain.d.ts +24 -0
  182. package/dist/integrations/langchain.d.ts.map +1 -0
  183. package/dist/integrations/langchain.js +48 -0
  184. package/dist/integrations/langchain.js.map +1 -0
  185. package/dist/integrations/mcp.d.ts +23 -0
  186. package/dist/integrations/mcp.d.ts.map +1 -0
  187. package/dist/integrations/mcp.js +60 -0
  188. package/dist/integrations/mcp.js.map +1 -0
  189. package/dist/integrations/middleware.d.ts +15 -0
  190. package/dist/integrations/middleware.d.ts.map +1 -0
  191. package/dist/integrations/middleware.js +27 -0
  192. package/dist/integrations/middleware.js.map +1 -0
  193. package/dist/integrations/openai-tools.d.ts +21 -0
  194. package/dist/integrations/openai-tools.d.ts.map +1 -0
  195. package/dist/integrations/openai-tools.js +69 -0
  196. package/dist/integrations/openai-tools.js.map +1 -0
  197. package/dist/integrations/vercel-ai.d.ts +19 -0
  198. package/dist/integrations/vercel-ai.d.ts.map +1 -0
  199. package/dist/integrations/vercel-ai.js +41 -0
  200. package/dist/integrations/vercel-ai.js.map +1 -0
  201. package/dist/server/http-server.d.ts +61 -0
  202. package/dist/server/http-server.d.ts.map +1 -0
  203. package/dist/server/http-server.js +684 -0
  204. package/dist/server/http-server.js.map +1 -0
  205. package/dist/server/index.d.ts +5 -0
  206. package/dist/server/index.d.ts.map +1 -0
  207. package/dist/server/index.js +3 -0
  208. package/dist/server/index.js.map +1 -0
  209. package/dist/server/mcp-server.d.ts +61 -0
  210. package/dist/server/mcp-server.d.ts.map +1 -0
  211. package/dist/server/mcp-server.js +465 -0
  212. package/dist/server/mcp-server.js.map +1 -0
  213. package/dist/summarizers/claude.d.ts +11 -0
  214. package/dist/summarizers/claude.d.ts.map +1 -0
  215. package/dist/summarizers/claude.js +39 -0
  216. package/dist/summarizers/claude.js.map +1 -0
  217. package/dist/summarizers/client.d.ts +23 -0
  218. package/dist/summarizers/client.d.ts.map +1 -0
  219. package/dist/summarizers/client.js +24 -0
  220. package/dist/summarizers/client.js.map +1 -0
  221. package/dist/summarizers/extractive.d.ts +6 -0
  222. package/dist/summarizers/extractive.d.ts.map +1 -0
  223. package/dist/summarizers/extractive.js +204 -0
  224. package/dist/summarizers/extractive.js.map +1 -0
  225. package/dist/summarizers/extractor.d.ts +12 -0
  226. package/dist/summarizers/extractor.d.ts.map +1 -0
  227. package/dist/summarizers/extractor.js +75 -0
  228. package/dist/summarizers/extractor.js.map +1 -0
  229. package/dist/summarizers/openai.d.ts +11 -0
  230. package/dist/summarizers/openai.d.ts.map +1 -0
  231. package/dist/summarizers/openai.js +41 -0
  232. package/dist/summarizers/openai.js.map +1 -0
  233. package/dist/summarizers/prompts.d.ts +11 -0
  234. package/dist/summarizers/prompts.d.ts.map +1 -0
  235. package/dist/summarizers/prompts.js +104 -0
  236. package/dist/summarizers/prompts.js.map +1 -0
  237. package/docs/DEPLOYMENT.md +84 -0
  238. package/docs/INTEGRATIONS.md +64 -0
  239. package/docs/MEMORY_QUALITY_BASELINE.md +55 -0
  240. package/docs/MEMORY_QUALITY_RELEASE_GATE.md +63 -0
  241. package/docs/MEMORY_QUALITY_RUBRIC.md +249 -0
  242. package/docs/OPERATIONS.md +49 -0
  243. package/docs/SECURITY.md +25 -0
  244. package/openapi.yaml +843 -0
  245. package/package.json +157 -0
@@ -0,0 +1,684 @@
1
+ import { createServer } from 'http';
2
+ import { createMemoryWithAsyncAdapter, } from '../core/quick.js';
3
+ import { normalizeScope } from '../contracts/identity.js';
4
+ import { createSQLiteAdapterWithEmbeddings } from '../adapters/sqlite/index.js';
5
+ import { wrapSyncAdapter } from '../adapters/sync-to-async.js';
6
+ class HttpRequestError extends Error {
7
+ status;
8
+ constructor(status, message) {
9
+ super(message);
10
+ this.status = status;
11
+ }
12
+ }
13
+ function writeJson(res, status, data) {
14
+ const body = JSON.stringify(data);
15
+ res.writeHead(status, {
16
+ 'Content-Type': 'application/json',
17
+ 'Content-Length': Buffer.byteLength(body),
18
+ });
19
+ res.end(body);
20
+ }
21
+ function writeError(res, status, message) {
22
+ writeJson(res, status, { error: message });
23
+ }
24
+ async function readBody(req, limitBytes = 1_048_576) {
25
+ return new Promise((resolve, reject) => {
26
+ const chunks = [];
27
+ let totalBytes = 0;
28
+ let tooLarge = false;
29
+ req.on('data', (chunk) => {
30
+ if (!tooLarge) {
31
+ chunks.push(chunk);
32
+ }
33
+ totalBytes += chunk.length;
34
+ if (totalBytes > limitBytes) {
35
+ tooLarge = true;
36
+ }
37
+ });
38
+ req.on('end', () => {
39
+ try {
40
+ if (tooLarge) {
41
+ reject(new HttpRequestError(413, 'Request body too large'));
42
+ return;
43
+ }
44
+ const text = Buffer.concat(chunks).toString('utf-8');
45
+ const parsed = text ? JSON.parse(text) : {};
46
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
47
+ reject(new HttpRequestError(400, 'JSON body must be an object'));
48
+ return;
49
+ }
50
+ resolve(parsed);
51
+ }
52
+ catch {
53
+ reject(new HttpRequestError(400, 'Invalid JSON body'));
54
+ }
55
+ });
56
+ req.on('error', reject);
57
+ });
58
+ }
59
+ function parseQuery(url) {
60
+ const idx = url.indexOf('?');
61
+ if (idx === -1)
62
+ return {};
63
+ const params = {};
64
+ const search = url.slice(idx + 1);
65
+ for (const pair of search.split('&')) {
66
+ try {
67
+ const [key, value] = pair.split('=');
68
+ if (key)
69
+ params[decodeURIComponent(key)] = decodeURIComponent(value ?? '');
70
+ }
71
+ catch {
72
+ throw new HttpRequestError(400, 'Invalid query string');
73
+ }
74
+ }
75
+ return params;
76
+ }
77
+ function parseOptionalInteger(value) {
78
+ if (value == null || value === '')
79
+ return undefined;
80
+ const parsed = Number(value);
81
+ return Number.isInteger(parsed) ? parsed : undefined;
82
+ }
83
+ function normalizePath(path) {
84
+ if (path.length > 1 && path.endsWith('/')) {
85
+ return path.slice(0, -1);
86
+ }
87
+ return path;
88
+ }
89
+ function isRecord(value) {
90
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
91
+ }
92
+ function requireString(value, name) {
93
+ if (typeof value !== 'string' || value.trim().length === 0) {
94
+ throw new HttpRequestError(400, `Missing or invalid field: ${name}`);
95
+ }
96
+ return value;
97
+ }
98
+ function optionalString(value, name) {
99
+ if (value == null)
100
+ return undefined;
101
+ if (typeof value !== 'string' || value.trim().length === 0) {
102
+ throw new HttpRequestError(400, `Invalid field: ${name}`);
103
+ }
104
+ return value;
105
+ }
106
+ function requireEnum(value, allowed, name) {
107
+ if (typeof value !== 'string' || !allowed.includes(value)) {
108
+ throw new HttpRequestError(400, `Invalid field: ${name}`);
109
+ }
110
+ return value;
111
+ }
112
+ function parseLimit(value) {
113
+ const parsed = parseOptionalInteger(value);
114
+ if (value != null && parsed == null) {
115
+ throw new HttpRequestError(400, 'Invalid limit parameter');
116
+ }
117
+ return parsed;
118
+ }
119
+ function parseScopeLevel(value, name, allowed = ['scope', 'workspace', 'system', 'tenant']) {
120
+ if (value == null || value === '')
121
+ return undefined;
122
+ return requireEnum(value, allowed, name);
123
+ }
124
+ function resolvePartialScope(source, labels) {
125
+ const requiredValues = [source.tenant_id, source.system_id, source.scope_id];
126
+ const provided = requiredValues.filter((value) => value != null && value !== '').length;
127
+ if (provided === 0) {
128
+ return undefined;
129
+ }
130
+ if (provided !== 3) {
131
+ throw new HttpRequestError(400, `Incomplete scope override: ${labels.join(', ')}`);
132
+ }
133
+ return {
134
+ tenant_id: requireString(source.tenant_id, labels[0]),
135
+ system_id: requireString(source.system_id, labels[1]),
136
+ workspace_id: optionalString(source.workspace_id, 'workspace_id'),
137
+ collaboration_id: optionalString(source.collaboration_id, 'collaboration_id'),
138
+ scope_id: requireString(source.scope_id, labels[2]),
139
+ };
140
+ }
141
+ function parseEventTypes(value) {
142
+ if (!value)
143
+ return undefined;
144
+ const allowed = [
145
+ 'manager',
146
+ 'search',
147
+ 'compaction',
148
+ 'extraction',
149
+ 'promotion',
150
+ 'knowledge_change',
151
+ 'context_assembly',
152
+ 'semantic_search',
153
+ ];
154
+ return new Set(value
155
+ .split(',')
156
+ .map((entry) => entry.trim())
157
+ .filter(Boolean)
158
+ .map((entry) => requireEnum(entry, allowed, 'event_types')));
159
+ }
160
+ function resolveRequestScope(fallbackScope, req, query, body) {
161
+ const bodyScope = body?.scope;
162
+ if (bodyScope) {
163
+ if (!isRecord(bodyScope)) {
164
+ throw new HttpRequestError(400, 'Invalid scope override');
165
+ }
166
+ normalizeScope(bodyScope);
167
+ return bodyScope;
168
+ }
169
+ const headerScope = resolvePartialScope({
170
+ tenant_id: req.headers['x-memory-tenant'],
171
+ system_id: req.headers['x-memory-system'],
172
+ workspace_id: req.headers['x-memory-workspace'],
173
+ collaboration_id: req.headers['x-memory-collaboration'],
174
+ scope_id: req.headers['x-memory-scope'],
175
+ }, ['x-memory-tenant', 'x-memory-system', 'x-memory-scope']);
176
+ if (headerScope) {
177
+ normalizeScope(headerScope);
178
+ return headerScope;
179
+ }
180
+ const queryScope = resolvePartialScope({
181
+ tenant_id: query.tenant_id,
182
+ system_id: query.system_id,
183
+ workspace_id: query.workspace_id,
184
+ collaboration_id: query.collaboration_id,
185
+ scope_id: query.scope_id,
186
+ }, ['tenant_id', 'system_id', 'scope_id']);
187
+ if (queryScope) {
188
+ normalizeScope(queryScope);
189
+ return queryScope;
190
+ }
191
+ return fallbackScope ?? 'default';
192
+ }
193
+ function matchesEventScope(event, scope, level) {
194
+ const left = normalizeScope(event.scope);
195
+ const right = normalizeScope(scope);
196
+ if (left.tenant_id !== right.tenant_id)
197
+ return false;
198
+ if (level === 'tenant')
199
+ return true;
200
+ if (level === 'workspace' && left.collaboration_id && right.collaboration_id) {
201
+ return left.collaboration_id === right.collaboration_id;
202
+ }
203
+ if (left.system_id !== right.system_id)
204
+ return false;
205
+ if (level === 'system')
206
+ return true;
207
+ if (level === 'workspace') {
208
+ return left.workspace_id === right.workspace_id;
209
+ }
210
+ return left.workspace_id === right.workspace_id && left.scope_id === right.scope_id;
211
+ }
212
+ /**
213
+ * Creates and starts an HTTP server exposing memory operations as a REST API.
214
+ *
215
+ * Endpoints:
216
+ * - POST /v1/turns - Store a turn
217
+ * - POST /v1/exchanges - Store a user+assistant exchange
218
+ * - GET /v1/context - Get assembled context
219
+ * - GET /v1/search - Search turns and knowledge
220
+ * - POST /v1/facts - Learn a fact
221
+ * - POST /v1/work - Track a work item
222
+ * - POST /v1/compact - Force compaction
223
+ * - GET /v1/health - Get health report
224
+ * - POST /v1/maintenance - Run maintenance
225
+ * - GET /v1/inspect/* - Inspect knowledge, audits, monitor, and compactions
226
+ * - POST /v1/reverification - Run reverification workflows
227
+ * - GET /v1/events - SSE stream of memory events
228
+ */
229
+ export async function startHttpServer(config = {}) {
230
+ const port = config.port ?? 3100;
231
+ const host = config.host ?? '127.0.0.1';
232
+ const apiKey = config.apiKey ?? process.env.MEMORY_API_KEY;
233
+ const adminApiKey = config.adminApiKey ?? process.env.MEMORY_ADMIN_API_KEY;
234
+ const enableCors = config.cors ?? true;
235
+ const bodyLimitBytes = config.bodyLimitBytes ?? 1_048_576;
236
+ const managers = new Map();
237
+ const databaseUrl = config.databaseUrl ?? process.env.MEMORY_DATABASE_URL;
238
+ const adapterResources = databaseUrl
239
+ ? await (async () => {
240
+ const moduleName = 'pg';
241
+ const pgModule = await import(moduleName).catch(() => {
242
+ throw new Error('memory-layer: hosted Postgres mode requires the "pg" package. Install it with: npm install pg');
243
+ });
244
+ const { createPostgresAdapter, createPostgresEmbeddingAdapter } = await import('../adapters/postgres/index.js');
245
+ const Pool = pgModule.Pool ?? pgModule.default?.Pool;
246
+ const pool = new Pool({ connectionString: databaseUrl });
247
+ const asyncAdapter = createPostgresAdapter(pool);
248
+ return {
249
+ asyncAdapter,
250
+ embeddingAdapter: createPostgresEmbeddingAdapter(pool),
251
+ close: async () => {
252
+ await pool.end();
253
+ },
254
+ };
255
+ })()
256
+ : (() => {
257
+ const sqlite = createSQLiteAdapterWithEmbeddings(config.dbPath ?? ':memory:');
258
+ return {
259
+ asyncAdapter: wrapSyncAdapter(sqlite),
260
+ embeddingAdapter: sqlite.embeddings,
261
+ close: async () => {
262
+ sqlite.close();
263
+ },
264
+ };
265
+ })();
266
+ const sseClients = new Set();
267
+ function createHostedManager(scopeInput) {
268
+ const baseOptions = {
269
+ adapter: 'sqlite',
270
+ path: config.dbPath ?? ':memory:',
271
+ scope: scopeInput,
272
+ summarizer: config.summarizer ?? 'extractive',
273
+ extractor: config.extractor ?? 'regex',
274
+ preset: config.preset,
275
+ redactText: config.redactText,
276
+ qualityMode: config.qualityMode,
277
+ qualityTier: config.qualityTier,
278
+ crossScopeLevel: config.crossScopeLevel,
279
+ onEvent: (event) => {
280
+ if (sseClients.size === 0)
281
+ return;
282
+ const data = `data: ${JSON.stringify(event)}\n\n`;
283
+ for (const client of sseClients) {
284
+ if (client.eventTypes && !client.eventTypes.has(event.type))
285
+ continue;
286
+ if (client.scope && client.scopeLevel) {
287
+ if (!matchesEventScope(event, client.scope, client.scopeLevel))
288
+ continue;
289
+ }
290
+ client.response.write(data);
291
+ }
292
+ },
293
+ };
294
+ return createMemoryWithAsyncAdapter({
295
+ ...baseOptions,
296
+ asyncAdapter: adapterResources.asyncAdapter,
297
+ embeddingAdapter: adapterResources.embeddingAdapter,
298
+ });
299
+ }
300
+ function getManager(scopeInput) {
301
+ const key = typeof scopeInput === 'string'
302
+ ? `scope:${scopeInput}`
303
+ : JSON.stringify(normalizeScope(scopeInput));
304
+ const existing = managers.get(key);
305
+ if (existing) {
306
+ return existing;
307
+ }
308
+ const manager = createHostedManager(scopeInput);
309
+ managers.set(key, manager);
310
+ return manager;
311
+ }
312
+ const manager = getManager(config.scope ?? 'default');
313
+ const server = createServer(async (req, res) => {
314
+ // CORS
315
+ if (enableCors) {
316
+ res.setHeader('Access-Control-Allow-Origin', '*');
317
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
318
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, x-admin-key, x-memory-tenant, x-memory-system, x-memory-workspace, x-memory-collaboration, x-memory-scope, Last-Event-ID');
319
+ }
320
+ if (req.method === 'OPTIONS') {
321
+ res.writeHead(204);
322
+ res.end();
323
+ return;
324
+ }
325
+ // Auth
326
+ if (apiKey) {
327
+ const auth = req.headers.authorization;
328
+ if (!auth || auth !== `Bearer ${apiKey}`) {
329
+ writeError(res, 401, 'Unauthorized');
330
+ return;
331
+ }
332
+ }
333
+ try {
334
+ const url = req.url ?? '/';
335
+ const path = normalizePath(url.split('?')[0]);
336
+ const query = parseQuery(url);
337
+ // POST /v1/turns
338
+ if (path === '/v1/turns' && req.method === 'POST') {
339
+ const body = await readBody(req, bodyLimitBytes);
340
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query, body));
341
+ const turn = await requestManager.processTurn(requireEnum(body.role, ['user', 'assistant', 'system'], 'role'), requireString(body.content, 'content'), optionalString(body.actor, 'actor'));
342
+ writeJson(res, 201, { turnId: turn.id, role: turn.role });
343
+ return;
344
+ }
345
+ // POST /v1/exchanges
346
+ if (path === '/v1/exchanges' && req.method === 'POST') {
347
+ const body = await readBody(req, bodyLimitBytes);
348
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query, body));
349
+ const exchange = await requestManager.processExchange(requireString(body.userContent, 'userContent'), requireString(body.assistantContent, 'assistantContent'));
350
+ writeJson(res, 201, {
351
+ userTurnId: exchange.userTurn.id,
352
+ assistantTurnId: exchange.assistantTurn.id,
353
+ compacted: exchange.compactionResult !== null,
354
+ });
355
+ return;
356
+ }
357
+ // GET /v1/context
358
+ if (path === '/v1/context' && req.method === 'GET') {
359
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query));
360
+ const context = await requestManager.getContext(query.query || undefined);
361
+ writeJson(res, 200, {
362
+ currentObjective: context.currentObjective,
363
+ activeTurnCount: context.activeTurns.length,
364
+ workingMemory: context.workingMemory
365
+ ? {
366
+ summary: context.workingMemory.summary,
367
+ key_entities: context.workingMemory.key_entities,
368
+ topic_tags: context.workingMemory.topic_tags,
369
+ }
370
+ : null,
371
+ relevantKnowledge: context.relevantKnowledge.map((k) => ({
372
+ id: k.id,
373
+ fact: k.fact,
374
+ fact_type: k.fact_type,
375
+ confidence: k.confidence,
376
+ })),
377
+ activeObjectives: context.activeObjectives.map((o) => ({
378
+ title: o.title,
379
+ status: o.status,
380
+ })),
381
+ unresolvedWork: context.unresolvedWork,
382
+ tokenEstimate: context.tokenEstimate,
383
+ });
384
+ return;
385
+ }
386
+ // GET /v1/search
387
+ if (path === '/v1/search' && req.method === 'GET') {
388
+ if (!query.q) {
389
+ writeError(res, 400, 'Missing required query parameter: q');
390
+ return;
391
+ }
392
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query));
393
+ const results = await requestManager.search(query.q, query.limit ? { limit: parseLimit(query.limit) } : undefined);
394
+ writeJson(res, 200, {
395
+ turns: results.turns.map((r) => ({
396
+ id: r.item.id,
397
+ role: r.item.role,
398
+ content: r.item.content,
399
+ rank: r.rank,
400
+ })),
401
+ knowledge: results.knowledge.map((r) => ({
402
+ id: r.item.id,
403
+ fact: r.item.fact,
404
+ fact_type: r.item.fact_type,
405
+ rank: r.rank,
406
+ })),
407
+ });
408
+ return;
409
+ }
410
+ // GET /v1/search/cross-scope
411
+ if (path === '/v1/search/cross-scope' && req.method === 'GET') {
412
+ if (!query.q) {
413
+ writeError(res, 400, 'Missing required query parameter: q');
414
+ return;
415
+ }
416
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query));
417
+ const scopeLevel = parseScopeLevel(query.scope_level, 'scope_level', [
418
+ 'workspace',
419
+ 'system',
420
+ 'tenant',
421
+ ]) ?? 'workspace';
422
+ const results = await requestManager.searchCrossScope(query.q, scopeLevel, query.limit ? { limit: parseLimit(query.limit) } : undefined);
423
+ writeJson(res, 200, {
424
+ knowledge: results.knowledge.map((r) => ({
425
+ id: r.item.id,
426
+ fact: r.item.fact,
427
+ fact_type: r.item.fact_type,
428
+ scope_id: r.item.scope_id,
429
+ collaboration_id: r.item.collaboration_id,
430
+ rank: r.rank,
431
+ })),
432
+ });
433
+ return;
434
+ }
435
+ // GET /v1/inspect/knowledge
436
+ if (path === '/v1/inspect/knowledge' && req.method === 'GET') {
437
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query));
438
+ const limit = parseOptionalInteger(query.limit);
439
+ const cursor = parseOptionalInteger(query.cursor);
440
+ if ((query.limit && limit == null) || (query.cursor && cursor == null)) {
441
+ writeError(res, 400, 'Invalid pagination parameters');
442
+ return;
443
+ }
444
+ const knowledge = await requestManager.listKnowledge({
445
+ limit,
446
+ cursor,
447
+ });
448
+ writeJson(res, 200, knowledge);
449
+ return;
450
+ }
451
+ const knowledgeInspectMatch = path.match(/^\/v1\/inspect\/knowledge\/(\d+)$/);
452
+ if (knowledgeInspectMatch && req.method === 'GET') {
453
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query));
454
+ const detail = await requestManager.inspectKnowledge(Number(knowledgeInspectMatch[1]));
455
+ if (!detail.knowledge) {
456
+ writeError(res, 404, 'Knowledge not found');
457
+ return;
458
+ }
459
+ writeJson(res, 200, detail);
460
+ return;
461
+ }
462
+ // GET /v1/inspect/audits
463
+ if (path === '/v1/inspect/audits' && req.method === 'GET') {
464
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query));
465
+ const knowledgeId = parseOptionalInteger(query.knowledge_id);
466
+ const limit = parseOptionalInteger(query.limit);
467
+ if ((query.knowledge_id && knowledgeId == null) || (query.limit && limit == null)) {
468
+ writeError(res, 400, 'Invalid audit inspection parameters');
469
+ return;
470
+ }
471
+ const audits = await requestManager.getKnowledgeAudits({
472
+ knowledgeId,
473
+ limit,
474
+ });
475
+ writeJson(res, 200, { audits });
476
+ return;
477
+ }
478
+ // GET /v1/inspect/monitor
479
+ if (path === '/v1/inspect/monitor' && req.method === 'GET') {
480
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query));
481
+ const monitor = await requestManager.getContextMonitor();
482
+ writeJson(res, 200, { monitor });
483
+ return;
484
+ }
485
+ // GET /v1/inspect/compactions
486
+ if (path === '/v1/inspect/compactions' && req.method === 'GET') {
487
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query));
488
+ const limit = parseOptionalInteger(query.limit);
489
+ if (query.limit && limit == null) {
490
+ writeError(res, 400, 'Invalid compaction inspection parameters');
491
+ return;
492
+ }
493
+ const logs = await requestManager.getRecentCompactionLogs(limit);
494
+ writeJson(res, 200, { logs });
495
+ return;
496
+ }
497
+ // GET /v1/inspect/reverification
498
+ if (path === '/v1/inspect/reverification' && req.method === 'GET') {
499
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query));
500
+ const limit = parseOptionalInteger(query.limit);
501
+ if (query.limit && limit == null) {
502
+ writeError(res, 400, 'Invalid reverification inspection parameters');
503
+ return;
504
+ }
505
+ const due = await requestManager.getDueReverification({ limit });
506
+ writeJson(res, 200, { due });
507
+ return;
508
+ }
509
+ // POST /v1/facts
510
+ if (path === '/v1/facts' && req.method === 'POST') {
511
+ const body = await readBody(req, bodyLimitBytes);
512
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query, body));
513
+ const fact = await requestManager.learnFact(requireString(body.fact, 'fact'), requireEnum(body.factType, ['preference', 'entity', 'decision', 'constraint', 'reference'], 'factType'), (body.confidence == null
514
+ ? 'high'
515
+ : requireEnum(body.confidence, ['high', 'medium', 'low'], 'confidence')));
516
+ writeJson(res, 201, { knowledgeId: fact.id });
517
+ return;
518
+ }
519
+ // POST /v1/work
520
+ if (path === '/v1/work' && req.method === 'POST') {
521
+ const body = await readBody(req, bodyLimitBytes);
522
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query, body));
523
+ const item = await requestManager.trackWorkItem(requireString(body.title, 'title'), requireEnum(body.kind ?? 'objective', ['objective', 'unresolved_work', 'constraint'], 'kind'), requireEnum(body.status ?? 'open', ['open', 'in_progress', 'blocked', 'done'], 'status'), optionalString(body.detail, 'detail'));
524
+ writeJson(res, 201, { workItemId: item.id });
525
+ return;
526
+ }
527
+ // POST /v1/compact
528
+ if (path === '/v1/compact' && req.method === 'POST') {
529
+ if (adminApiKey && req.headers['x-admin-key'] !== adminApiKey) {
530
+ writeError(res, 403, 'Admin key required');
531
+ return;
532
+ }
533
+ const body = await readBody(req, bodyLimitBytes);
534
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query, body));
535
+ const result = await requestManager.forceCompact();
536
+ writeJson(res, 200, {
537
+ compacted: result !== null,
538
+ archivedTurnCount: result?.archivedTurnIds.length ?? 0,
539
+ });
540
+ return;
541
+ }
542
+ // GET /v1/health
543
+ if (path === '/v1/health' && req.method === 'GET') {
544
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query));
545
+ const context = await requestManager.getContext();
546
+ writeJson(res, 200, {
547
+ activeTurnCount: context.activeTurns.length,
548
+ tokenEstimate: context.tokenEstimate,
549
+ knowledgeCount: context.relevantKnowledge.length,
550
+ objectiveCount: context.activeObjectives.length,
551
+ unresolvedWorkCount: context.unresolvedWork.length,
552
+ });
553
+ return;
554
+ }
555
+ if ((path === '/healthz' || path === '/readyz') && req.method === 'GET') {
556
+ writeJson(res, 200, { ok: true, scopes: managers.size });
557
+ return;
558
+ }
559
+ // POST /v1/maintenance
560
+ if (path === '/v1/maintenance' && req.method === 'POST') {
561
+ if (adminApiKey && req.headers['x-admin-key'] !== adminApiKey) {
562
+ writeError(res, 403, 'Admin key required');
563
+ return;
564
+ }
565
+ const body = await readBody(req, bodyLimitBytes);
566
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query, body));
567
+ const report = await requestManager.runMaintenance();
568
+ writeJson(res, 200, {
569
+ expiredWorkingMemory: report.expiredWorkingMemoryIds.length,
570
+ retiredKnowledge: report.retiredKnowledgeIds.length,
571
+ deletedWorkItems: report.deletedWorkItemIds.length,
572
+ });
573
+ return;
574
+ }
575
+ const reverificationMatch = path.match(/^\/v1\/reverification\/(\d+)$/);
576
+ if (reverificationMatch && req.method === 'POST') {
577
+ if (adminApiKey && req.headers['x-admin-key'] !== adminApiKey) {
578
+ writeError(res, 403, 'Admin key required');
579
+ return;
580
+ }
581
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query));
582
+ const result = await requestManager.reverifyKnowledge(Number(reverificationMatch[1]));
583
+ writeJson(res, 200, result);
584
+ return;
585
+ }
586
+ // POST /v1/reverification/run
587
+ if (path === '/v1/reverification/run' && req.method === 'POST') {
588
+ if (adminApiKey && req.headers['x-admin-key'] !== adminApiKey) {
589
+ writeError(res, 403, 'Admin key required');
590
+ return;
591
+ }
592
+ const body = await readBody(req, bodyLimitBytes);
593
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query, body));
594
+ let limit;
595
+ if (body.limit != null) {
596
+ if (typeof body.limit !== 'number' || !Number.isInteger(body.limit)) {
597
+ throw new HttpRequestError(400, 'Invalid field: limit');
598
+ }
599
+ limit = body.limit;
600
+ }
601
+ const report = await requestManager.runReverification({ limit });
602
+ writeJson(res, 200, report);
603
+ return;
604
+ }
605
+ // GET /v1/changes
606
+ if (path === '/v1/changes' && req.method === 'GET') {
607
+ const requestManager = getManager(resolveRequestScope(config.scope, req, query));
608
+ const sinceValue = query.since ? new Date(query.since) : new Date(0);
609
+ if (Number.isNaN(sinceValue.valueOf())) {
610
+ writeError(res, 400, 'Invalid since parameter');
611
+ return;
612
+ }
613
+ const changes = await requestManager.pollForChanges(sinceValue, {
614
+ scopeLevel: parseScopeLevel(query.scope_level, 'scope_level') ?? 'scope',
615
+ });
616
+ writeJson(res, 200, {
617
+ changes: changes.map((knowledge) => ({
618
+ id: knowledge.id,
619
+ fact: knowledge.fact,
620
+ fact_type: knowledge.fact_type,
621
+ knowledge_state: knowledge.knowledge_state,
622
+ scope_id: knowledge.scope_id,
623
+ collaboration_id: knowledge.collaboration_id,
624
+ created_at: knowledge.created_at,
625
+ })),
626
+ });
627
+ return;
628
+ }
629
+ // GET /v1/events (SSE)
630
+ if (path === '/v1/events' && req.method === 'GET') {
631
+ res.writeHead(200, {
632
+ 'Content-Type': 'text/event-stream',
633
+ 'Cache-Control': 'no-cache',
634
+ Connection: 'keep-alive',
635
+ });
636
+ res.write('data: {"type":"connected"}\n\n');
637
+ const scope = resolveRequestScope(config.scope, req, query);
638
+ sseClients.add({
639
+ response: res,
640
+ scope: typeof scope === 'string' ? undefined : scope,
641
+ scopeLevel: parseScopeLevel(query.scope_level, 'scope_level') ?? 'scope',
642
+ eventTypes: parseEventTypes(query.event_types),
643
+ });
644
+ req.on('close', () => {
645
+ for (const client of sseClients) {
646
+ if (client.response === res) {
647
+ sseClients.delete(client);
648
+ }
649
+ }
650
+ });
651
+ return;
652
+ }
653
+ writeError(res, 404, `Not found: ${req.method} ${path}`);
654
+ }
655
+ catch (error) {
656
+ if (error instanceof HttpRequestError) {
657
+ writeError(res, error.status, error.message);
658
+ return;
659
+ }
660
+ writeError(res, 500, error instanceof Error ? error.message : String(error));
661
+ }
662
+ });
663
+ return new Promise((resolve) => {
664
+ server.listen(port, host, () => {
665
+ resolve({
666
+ server,
667
+ manager,
668
+ async close() {
669
+ for (const client of sseClients) {
670
+ client.response.end();
671
+ }
672
+ sseClients.clear();
673
+ server.close();
674
+ for (const cachedManager of managers.values()) {
675
+ await cachedManager.close();
676
+ }
677
+ managers.clear();
678
+ await adapterResources.close();
679
+ },
680
+ });
681
+ });
682
+ });
683
+ }
684
+ //# sourceMappingURL=http-server.js.map