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,839 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { createRequire } from 'module';
4
+ import { normalizeScope, scopeValues } from '../../contracts/identity.js';
5
+ import { estimateTokens } from '../../core/tokens.js';
6
+ import { emitMemoryEvent } from '../../core/telemetry.js';
7
+ import { matchesKnowledgeSearchOptions } from '../../core/retrieval.js';
8
+ import { assertArchiveInput, nowSeconds, validateContextMonitorUpsert, validateNewCompactionLog, validateNewKnowledgeCandidate, validateNewKnowledgeEvidence, validateNewKnowledgeMemoryAudit, validateNewKnowledgeMemory, validateNewWorkItem, validateTimeRange, validateNewTurn, validateNewWorkingMemory, } from '../../core/validation.js';
9
+ import { rowToCompactionLog, rowToContextMonitor, rowToKnowledgeCandidate, rowToKnowledgeEvidence, rowToKnowledgeMemory, rowToKnowledgeMemoryAudit, rowToTurn, rowToWorkItem, rowToWorkingMemory, serializeNumberArray, serializeStringArray, } from './mappers.js';
10
+ import { createSQLiteEmbeddingAdapter } from './embeddings.js';
11
+ import { createSQLiteSchema } from './schema.js';
12
+ const SCOPE_WHERE = 'tenant_id = ? AND system_id = ? AND workspace_id = ? AND collaboration_id = ? AND scope_id = ?';
13
+ const require = createRequire(import.meta.url);
14
+ function loadBetterSqlite3() {
15
+ try {
16
+ return require('better-sqlite3');
17
+ }
18
+ catch (error) {
19
+ throw new Error('memory-layer: SQLite support requires the optional "better-sqlite3" package. Install it with: npm install better-sqlite3', { cause: error });
20
+ }
21
+ }
22
+ function scopeWhereForLevel(scope, level) {
23
+ const normalized = normalizeScope(scope);
24
+ if (level === 'tenant')
25
+ return 'tenant_id = ?';
26
+ if (level === 'system')
27
+ return 'tenant_id = ? AND system_id = ?';
28
+ if (level === 'workspace') {
29
+ return normalized.collaboration_id.length > 0
30
+ ? 'tenant_id = ? AND collaboration_id = ?'
31
+ : 'tenant_id = ? AND system_id = ? AND workspace_id = ?';
32
+ }
33
+ return SCOPE_WHERE;
34
+ }
35
+ function scopeParamsForLevel(scope, level) {
36
+ const normalized = normalizeScope(scope);
37
+ if (level === 'tenant')
38
+ return [normalized.tenant_id];
39
+ if (level === 'system')
40
+ return [normalized.tenant_id, normalized.system_id];
41
+ if (level === 'workspace') {
42
+ return normalized.collaboration_id.length > 0
43
+ ? [normalized.tenant_id, normalized.collaboration_id]
44
+ : [normalized.tenant_id, normalized.system_id, normalized.workspace_id];
45
+ }
46
+ return [...scopeValues(normalized)];
47
+ }
48
+ function timeRangeWhere(range, column = 'created_at') {
49
+ validateTimeRange(range);
50
+ const clauses = [];
51
+ const params = [];
52
+ if (range.start_at !== undefined) {
53
+ clauses.push(`${column} >= ?`);
54
+ params.push(range.start_at);
55
+ }
56
+ if (range.end_at !== undefined) {
57
+ clauses.push(`${column} <= ?`);
58
+ params.push(range.end_at);
59
+ }
60
+ return {
61
+ clause: clauses.length > 0 ? ` AND ${clauses.join(' AND ')}` : '',
62
+ params,
63
+ };
64
+ }
65
+ function sessionWhere(sessionId, column = 'session_id') {
66
+ if (!sessionId) {
67
+ return { clause: '', params: [] };
68
+ }
69
+ return {
70
+ clause: ` AND ${column} = ?`,
71
+ params: [sessionId],
72
+ };
73
+ }
74
+ function normalizeRank(rawRank) {
75
+ const safe = Number.isFinite(rawRank) ? Math.max(0, Number(rawRank)) : 0;
76
+ return 1 / (1 + safe);
77
+ }
78
+ function tokenizeSearch(text) {
79
+ return text
80
+ .toLowerCase()
81
+ .split(/[^a-z0-9]+/g)
82
+ .filter((token) => token.length > 0);
83
+ }
84
+ function scoreSearchText(query, text) {
85
+ const queryTokens = new Set(tokenizeSearch(query));
86
+ const textTokens = new Set(tokenizeSearch(text));
87
+ if (queryTokens.size === 0 || textTokens.size === 0)
88
+ return 0;
89
+ let matches = 0;
90
+ for (const token of queryTokens) {
91
+ if (textTokens.has(token))
92
+ matches += 1;
93
+ }
94
+ if (matches === 0)
95
+ return 0;
96
+ return matches / queryTokens.size + (text.toLowerCase().includes(query.toLowerCase()) ? 0.25 : 0);
97
+ }
98
+ function toSafeFtsQuery(query) {
99
+ return query
100
+ .toLowerCase()
101
+ .split(/[^a-z0-9]+/g)
102
+ .filter((token) => token.length > 0)
103
+ .join(' ');
104
+ }
105
+ function resolveSearchOptions(options) {
106
+ return {
107
+ limit: options?.limit ?? 10,
108
+ activeOnly: options?.activeOnly ?? true,
109
+ includeProvisional: options?.includeProvisional ?? false,
110
+ includeDisputed: options?.includeDisputed ?? false,
111
+ minimumTrustScore: options?.minimumTrustScore ?? 0,
112
+ knowledgeStates: options?.knowledgeStates ?? [],
113
+ knowledgeClasses: options?.knowledgeClasses ?? [],
114
+ preferLocalTrusted: options?.preferLocalTrusted ?? false,
115
+ preferLineageMemory: options?.preferLineageMemory ?? false,
116
+ };
117
+ }
118
+ function resolvePaginationOptions(options) {
119
+ return {
120
+ limit: options?.limit ?? 25,
121
+ offset: options?.offset ?? 0,
122
+ cursor: options?.cursor ?? 0,
123
+ };
124
+ }
125
+ export function createSQLiteAdapter(dbPath, telemetry) {
126
+ const db = openSQLiteDatabase(dbPath);
127
+ return createAdapterFromDatabase(db, telemetry);
128
+ }
129
+ export function createSQLiteAdapterWithEmbeddings(dbPath, telemetry) {
130
+ const db = openSQLiteDatabase(dbPath);
131
+ const adapter = createAdapterFromDatabase(db, telemetry);
132
+ const embeddings = createSQLiteEmbeddingAdapter(db, telemetry?.logger);
133
+ return Object.assign(adapter, { embeddings });
134
+ }
135
+ function openSQLiteDatabase(dbPath) {
136
+ if (dbPath !== ':memory:') {
137
+ const dir = path.dirname(dbPath);
138
+ if (!fs.existsSync(dir)) {
139
+ fs.mkdirSync(dir, { recursive: true });
140
+ }
141
+ }
142
+ const BetterSqlite3 = loadBetterSqlite3();
143
+ const db = new BetterSqlite3(dbPath);
144
+ createSQLiteSchema(db);
145
+ return db;
146
+ }
147
+ function createAdapterFromDatabase(db, telemetry) {
148
+ function getTurnById(id) {
149
+ const row = db.prepare('SELECT * FROM turns WHERE id = ?').get(id);
150
+ return row ? rowToTurn(row) : null;
151
+ }
152
+ function getWorkingMemoryById(id) {
153
+ const row = db
154
+ .prepare('SELECT * FROM working_memory WHERE id = ?')
155
+ .get(id);
156
+ return row ? rowToWorkingMemory(row) : null;
157
+ }
158
+ function getKnowledgeMemoryById(id) {
159
+ const row = db
160
+ .prepare('SELECT * FROM knowledge_memory WHERE id = ?')
161
+ .get(id);
162
+ return row ? rowToKnowledgeMemory(row) : null;
163
+ }
164
+ function getKnowledgeCandidateById(id) {
165
+ const row = db
166
+ .prepare('SELECT * FROM knowledge_candidate WHERE id = ?')
167
+ .get(id);
168
+ return row ? rowToKnowledgeCandidate(row) : null;
169
+ }
170
+ function getContextMonitor(scope) {
171
+ const row = db
172
+ .prepare(`SELECT * FROM context_monitor WHERE ${SCOPE_WHERE}`)
173
+ .get(...scopeValues(scope));
174
+ return row ? rowToContextMonitor(row) : null;
175
+ }
176
+ function getCompactionLogById(id) {
177
+ const row = db
178
+ .prepare('SELECT * FROM compaction_log WHERE id = ?')
179
+ .get(id);
180
+ return row ? rowToCompactionLog(row) : null;
181
+ }
182
+ function getRecentKnowledgeMemoryAudits(scope, limit = 10) {
183
+ const rows = db
184
+ .prepare(`SELECT * FROM knowledge_memory_audit
185
+ WHERE ${SCOPE_WHERE}
186
+ ORDER BY id DESC
187
+ LIMIT ?`)
188
+ .all(...scopeValues(scope), limit);
189
+ return rows.map(rowToKnowledgeMemoryAudit);
190
+ }
191
+ function getKnowledgeMemoryAuditsForKnowledge(scope, knowledgeId, limit = 10) {
192
+ const rows = db
193
+ .prepare(`SELECT * FROM knowledge_memory_audit
194
+ WHERE ${SCOPE_WHERE}
195
+ AND (created_knowledge_id = ? OR related_knowledge_id = ?)
196
+ ORDER BY id DESC
197
+ LIMIT ?`)
198
+ .all(...scopeValues(scope), knowledgeId, knowledgeId, limit);
199
+ return rows.map(rowToKnowledgeMemoryAudit);
200
+ }
201
+ function insertValidatedTurn(input) {
202
+ const scope = validateNewTurn(input);
203
+ const tokenEstimate = input.token_estimate ?? estimateTokens(input.content);
204
+ const createdAt = input.created_at ?? nowSeconds();
205
+ const result = db
206
+ .prepare(`INSERT INTO turns
207
+ (session_id, tenant_id, system_id, workspace_id, collaboration_id, scope_id, actor, role, content, priority, token_estimate, created_at)
208
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
209
+ .run(input.session_id, scope.tenant_id, scope.system_id, scope.workspace_id, scope.collaboration_id, scope.scope_id, input.actor, input.role, input.content, input.priority ?? (input.role === 'system' ? 1.5 : 1), tokenEstimate, createdAt);
210
+ return getTurnById(Number(result.lastInsertRowid));
211
+ }
212
+ function insertValidatedKnowledgeMemory(input) {
213
+ const scope = validateNewKnowledgeMemory(input);
214
+ const createdAt = nowSeconds();
215
+ const result = db
216
+ .prepare(`INSERT INTO knowledge_memory
217
+ (tenant_id, system_id, workspace_id, collaboration_id, scope_id, fact, fact_type, knowledge_state,
218
+ knowledge_class, fact_subject, fact_attribute, fact_value, normalized_fact, slot_key,
219
+ is_negated, source, confidence, confidence_score, grounding_strength, evidence_count,
220
+ trust_score, verification_status, verification_notes, last_verified_at,
221
+ next_reverification_at, last_confirmed_at, confirmation_count,
222
+ source_system_id, source_scope_id, source_collaboration_id, source_working_memory_id,
223
+ source_turn_ids, successful_use_count, failed_use_count, disputed_at, dispute_reason,
224
+ contradiction_score, superseded_at, retired_at, created_at, last_accessed_at)
225
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
226
+ .run(scope.tenant_id, scope.system_id, scope.workspace_id, scope.collaboration_id, scope.scope_id, input.fact, input.fact_type, input.knowledge_state ?? 'trusted', input.knowledge_class ?? 'project_fact', input.fact_subject ?? null, input.fact_attribute ?? null, input.fact_value ?? null, input.normalized_fact ?? null, input.slot_key ?? null, input.is_negated ? 1 : 0, input.source, input.confidence, input.confidence_score ?? 0.5, input.grounding_strength ?? 'moderate', input.evidence_count ?? Math.max(1, (input.source_turn_ids ?? []).length), input.trust_score ?? (input.confidence_score ?? 0.5), input.verification_status ?? 'unverified', input.verification_notes ?? null, input.last_verified_at ?? null, input.next_reverification_at ?? null, input.last_confirmed_at ?? null, input.confirmation_count ?? 0, input.source_system_id ?? scope.system_id, input.source_scope_id ?? scope.scope_id, input.source_collaboration_id ?? scope.collaboration_id, input.source_working_memory_id ?? null, serializeNumberArray(input.source_turn_ids ?? []), input.successful_use_count ?? 0, input.failed_use_count ?? 0, input.disputed_at ?? null, input.dispute_reason ?? null, input.contradiction_score ?? 0, input.superseded_at ?? null, input.retired_at ?? null, createdAt, createdAt);
227
+ return getKnowledgeMemoryById(Number(result.lastInsertRowid));
228
+ }
229
+ return {
230
+ insertTurn(input) {
231
+ return insertValidatedTurn(input);
232
+ },
233
+ insertTurns(inputs) {
234
+ return db.transaction(() => inputs.map((input) => insertValidatedTurn(input)))();
235
+ },
236
+ getTurnById,
237
+ getActiveTurns(scope, sessionId) {
238
+ const session = sessionWhere(sessionId);
239
+ const rows = db
240
+ .prepare(`SELECT * FROM turns
241
+ WHERE ${SCOPE_WHERE} AND archived_at IS NULL${session.clause}
242
+ ORDER BY id ASC`)
243
+ .all(...scopeValues(scope), ...session.params);
244
+ return rows.map(rowToTurn);
245
+ },
246
+ getActiveTurnsPaginated(scope, options) {
247
+ const resolved = resolvePaginationOptions(options);
248
+ const cursorClause = resolved.cursor > 0 ? ' AND id > ?' : '';
249
+ const offsetClause = resolved.cursor > 0 ? '' : ' OFFSET ?';
250
+ const rows = db
251
+ .prepare(`SELECT * FROM turns
252
+ WHERE ${SCOPE_WHERE} AND archived_at IS NULL${cursorClause}
253
+ ORDER BY id ASC
254
+ LIMIT ?${offsetClause}`)
255
+ .all(...scopeValues(scope), ...(resolved.cursor > 0 ? [resolved.cursor] : []), resolved.limit + 1, ...(resolved.cursor > 0 ? [] : [resolved.offset]));
256
+ const pageRows = rows.slice(0, resolved.limit).map(rowToTurn);
257
+ return {
258
+ items: pageRows,
259
+ hasMore: rows.length > resolved.limit,
260
+ nextCursor: rows.length > resolved.limit ? pageRows[pageRows.length - 1]?.id ?? null : null,
261
+ };
262
+ },
263
+ getTurnsByTimeRange(scope, range) {
264
+ const time = timeRangeWhere(range, 'created_at');
265
+ const rows = db
266
+ .prepare(`SELECT * FROM turns
267
+ WHERE ${SCOPE_WHERE}${time.clause}
268
+ ORDER BY created_at ASC`)
269
+ .all(...scopeValues(scope), ...time.params);
270
+ return rows.map(rowToTurn);
271
+ },
272
+ searchTurns(scope, query, options) {
273
+ const startedAt = Date.now();
274
+ const resolved = resolveSearchOptions(options);
275
+ try {
276
+ const rows = db
277
+ .prepare(`SELECT turns.*, bm25(turns_fts) AS raw_rank
278
+ FROM turns_fts
279
+ JOIN turns ON turns_fts.rowid = turns.id
280
+ WHERE turns_fts MATCH ?
281
+ AND ${SCOPE_WHERE}
282
+ AND (? = 0 OR turns.archived_at IS NULL)
283
+ ORDER BY bm25(turns_fts)
284
+ LIMIT ?`)
285
+ .all(query, ...scopeValues(scope), resolved.activeOnly ? 1 : 0, resolved.limit);
286
+ const results = rows.map((row) => ({
287
+ item: rowToTurn(row),
288
+ rank: normalizeRank(row.raw_rank),
289
+ }));
290
+ emitMemoryEvent('search', scope, telemetry, Date.now() - startedAt, {
291
+ entity: 'turns',
292
+ query,
293
+ resultCount: results.length,
294
+ });
295
+ return results;
296
+ }
297
+ catch {
298
+ emitMemoryEvent('search', scope, telemetry, Date.now() - startedAt, {
299
+ entity: 'turns',
300
+ query,
301
+ resultCount: 0,
302
+ invalidQuery: true,
303
+ });
304
+ return [];
305
+ }
306
+ },
307
+ archiveTurn(id, archivedAt, compactionLogId) {
308
+ assertArchiveInput(id, archivedAt, compactionLogId);
309
+ db.prepare(`UPDATE turns
310
+ SET archived_at = ?, compaction_log_id = ?
311
+ WHERE id = ? AND archived_at IS NULL`).run(archivedAt, compactionLogId, id);
312
+ },
313
+ getArchivedTurnRange(sessionId, startId, endId, scope) {
314
+ const query = `SELECT * FROM turns
315
+ WHERE session_id = ? AND id >= ? AND id <= ? AND archived_at IS NOT NULL
316
+ AND ${SCOPE_WHERE}
317
+ ORDER BY id ASC`;
318
+ const rows = db
319
+ .prepare(query)
320
+ .all(sessionId, startId, endId, ...scopeValues(scope));
321
+ return rows.map(rowToTurn);
322
+ },
323
+ insertWorkingMemory(input) {
324
+ const scope = validateNewWorkingMemory(input);
325
+ const createdAt = nowSeconds();
326
+ const expiresAt = input.expires_at ?? createdAt + 86400;
327
+ const result = db
328
+ .prepare(`INSERT INTO working_memory
329
+ (session_id, tenant_id, system_id, workspace_id, collaboration_id, scope_id, summary, key_entities, topic_tags,
330
+ turn_id_start, turn_id_end, turn_count, compaction_trigger, created_at, expires_at)
331
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
332
+ .run(input.session_id, scope.tenant_id, scope.system_id, scope.workspace_id, scope.collaboration_id, scope.scope_id, input.summary, serializeStringArray(input.key_entities), serializeStringArray(input.topic_tags), input.turn_id_start, input.turn_id_end, input.turn_count, input.compaction_trigger, createdAt, expiresAt);
333
+ return getWorkingMemoryById(Number(result.lastInsertRowid));
334
+ },
335
+ getWorkingMemoryById,
336
+ getWorkingMemoryBySession(sessionId, scope) {
337
+ const query = `SELECT * FROM working_memory
338
+ WHERE session_id = ? AND ${SCOPE_WHERE}
339
+ ORDER BY id ASC`;
340
+ const rows = db
341
+ .prepare(query)
342
+ .all(sessionId, ...scopeValues(scope));
343
+ return rows.map(rowToWorkingMemory);
344
+ },
345
+ getActiveWorkingMemory(scope, sessionId) {
346
+ const now = nowSeconds();
347
+ const session = sessionWhere(sessionId);
348
+ const rows = db
349
+ .prepare(`SELECT * FROM working_memory
350
+ WHERE ${SCOPE_WHERE}
351
+ AND (expires_at IS NULL OR expires_at > ?)${session.clause}
352
+ ORDER BY id DESC`)
353
+ .all(...scopeValues(scope), now, ...session.params);
354
+ return rows.map(rowToWorkingMemory);
355
+ },
356
+ getLatestWorkingMemory(scope, sessionId) {
357
+ const now = nowSeconds();
358
+ const session = sessionWhere(sessionId);
359
+ const row = db
360
+ .prepare(`SELECT * FROM working_memory
361
+ WHERE ${SCOPE_WHERE}
362
+ AND (expires_at IS NULL OR expires_at > ?)${session.clause}
363
+ ORDER BY id DESC
364
+ LIMIT 1`)
365
+ .get(...scopeValues(scope), now, ...session.params);
366
+ return row ? rowToWorkingMemory(row) : null;
367
+ },
368
+ getWorkingMemoryByTimeRange(scope, range) {
369
+ const time = timeRangeWhere(range, 'created_at');
370
+ const rows = db
371
+ .prepare(`SELECT * FROM working_memory
372
+ WHERE ${SCOPE_WHERE}${time.clause}
373
+ ORDER BY created_at ASC`)
374
+ .all(...scopeValues(scope), ...time.params);
375
+ return rows.map(rowToWorkingMemory);
376
+ },
377
+ expireWorkingMemory(id) {
378
+ db.prepare('UPDATE working_memory SET expires_at = ? WHERE id = ?').run(nowSeconds(), id);
379
+ },
380
+ markWorkingMemoryPromoted(id, knowledgeMemoryId) {
381
+ db.prepare('UPDATE working_memory SET promoted_to_knowledge_id = ? WHERE id = ?').run(knowledgeMemoryId, id);
382
+ },
383
+ insertKnowledgeMemory(input) {
384
+ return insertValidatedKnowledgeMemory(input);
385
+ },
386
+ insertKnowledgeMemories(inputs) {
387
+ return db.transaction(() => inputs.map((input) => insertValidatedKnowledgeMemory(input)))();
388
+ },
389
+ insertKnowledgeCandidate(input) {
390
+ const scope = validateNewKnowledgeCandidate(input);
391
+ const createdAt = input.created_at ?? nowSeconds();
392
+ const result = db
393
+ .prepare(`INSERT INTO knowledge_candidate
394
+ (tenant_id, system_id, workspace_id, collaboration_id, scope_id, working_memory_id, fact, fact_type,
395
+ knowledge_class, normalized_fact, slot_key, confidence, source_summary, source_turns,
396
+ grounding_strength, evidence_count, trust_score, state, promoted_knowledge_id, created_at)
397
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
398
+ .run(scope.tenant_id, scope.system_id, scope.workspace_id, scope.collaboration_id, scope.scope_id, input.working_memory_id, input.fact, input.fact_type, input.knowledge_class, input.normalized_fact, input.slot_key ?? null, input.confidence, input.source_summary ? 1 : 0, input.source_turns === false ? 0 : 1, input.grounding_strength ?? 'weak', input.evidence_count ?? 0, input.trust_score ?? 0, input.state ?? 'candidate', input.promoted_knowledge_id ?? null, createdAt);
399
+ return getKnowledgeCandidateById(Number(result.lastInsertRowid));
400
+ },
401
+ insertKnowledgeCandidates(inputs) {
402
+ return db.transaction(() => inputs.map((input) => this.insertKnowledgeCandidate(input)))();
403
+ },
404
+ getKnowledgeCandidateById,
405
+ listKnowledgeCandidates(scope, options) {
406
+ const rows = db
407
+ .prepare(`SELECT * FROM knowledge_candidate
408
+ WHERE ${SCOPE_WHERE}
409
+ ORDER BY created_at DESC, id DESC`)
410
+ .all(...scopeValues(scope));
411
+ return rows
412
+ .map(rowToKnowledgeCandidate)
413
+ .filter((item) => !options?.state || options.state.includes(item.state));
414
+ },
415
+ insertKnowledgeEvidence(input) {
416
+ const scope = validateNewKnowledgeEvidence(input);
417
+ const createdAt = input.created_at ?? nowSeconds();
418
+ const result = db
419
+ .prepare(`INSERT INTO knowledge_evidence
420
+ (tenant_id, system_id, workspace_id, collaboration_id, scope_id, knowledge_memory_id, knowledge_candidate_id,
421
+ working_memory_id, turn_id, source_type, support_polarity, speaker_role, actor, excerpt,
422
+ start_offset, end_offset, is_explicit, explicitness_score, outcome, created_at)
423
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
424
+ .run(scope.tenant_id, scope.system_id, scope.workspace_id, scope.collaboration_id, scope.scope_id, input.knowledge_memory_id ?? null, input.knowledge_candidate_id ?? null, input.working_memory_id ?? null, input.turn_id ?? null, input.source_type, input.support_polarity, input.speaker_role ?? null, input.actor ?? null, input.excerpt, input.start_offset ?? null, input.end_offset ?? null, input.is_explicit ? 1 : 0, input.explicitness_score ?? 0, input.outcome ?? null, createdAt);
425
+ const row = db
426
+ .prepare('SELECT * FROM knowledge_evidence WHERE id = ?')
427
+ .get(Number(result.lastInsertRowid));
428
+ return rowToKnowledgeEvidence(row);
429
+ },
430
+ insertKnowledgeEvidenceBatch(inputs) {
431
+ return db.transaction(() => inputs.map((input) => this.insertKnowledgeEvidence(input)))();
432
+ },
433
+ listKnowledgeEvidenceForKnowledge(knowledgeId) {
434
+ const rows = db
435
+ .prepare('SELECT * FROM knowledge_evidence WHERE knowledge_memory_id = ? ORDER BY created_at DESC, id DESC')
436
+ .all(knowledgeId);
437
+ return rows.map(rowToKnowledgeEvidence);
438
+ },
439
+ listKnowledgeEvidenceForCandidate(candidateId) {
440
+ const rows = db
441
+ .prepare('SELECT * FROM knowledge_evidence WHERE knowledge_candidate_id = ? ORDER BY created_at DESC, id DESC')
442
+ .all(candidateId);
443
+ return rows.map(rowToKnowledgeEvidence);
444
+ },
445
+ promoteKnowledgeCandidate(candidateId, input) {
446
+ const knowledge = insertValidatedKnowledgeMemory(input);
447
+ db.prepare('UPDATE knowledge_candidate SET promoted_knowledge_id = ?, state = ? WHERE id = ?').run(knowledge.id, 'provisional', candidateId);
448
+ return knowledge;
449
+ },
450
+ getKnowledgeMemoryById,
451
+ getActiveKnowledgeMemory(scope) {
452
+ const rows = db
453
+ .prepare(`SELECT * FROM knowledge_memory
454
+ WHERE ${SCOPE_WHERE} AND superseded_by_id IS NULL AND retired_at IS NULL
455
+ ORDER BY last_accessed_at DESC`)
456
+ .all(...scopeValues(scope));
457
+ return rows.map(rowToKnowledgeMemory);
458
+ },
459
+ getActiveKnowledgeMemoryPaginated(scope, options) {
460
+ const resolved = resolvePaginationOptions(options);
461
+ const cursorClause = resolved.cursor > 0 ? ' AND id > ?' : '';
462
+ const offsetClause = resolved.cursor > 0 ? '' : ' OFFSET ?';
463
+ const rows = db
464
+ .prepare(`SELECT * FROM knowledge_memory
465
+ WHERE ${SCOPE_WHERE} AND superseded_by_id IS NULL AND retired_at IS NULL${cursorClause}
466
+ ORDER BY id ASC
467
+ LIMIT ?${offsetClause}`)
468
+ .all(...scopeValues(scope), ...(resolved.cursor > 0 ? [resolved.cursor] : []), resolved.limit + 1, ...(resolved.cursor > 0 ? [] : [resolved.offset]));
469
+ const pageRows = rows.slice(0, resolved.limit).map(rowToKnowledgeMemory);
470
+ return {
471
+ items: pageRows,
472
+ hasMore: rows.length > resolved.limit,
473
+ nextCursor: rows.length > resolved.limit ? pageRows[pageRows.length - 1]?.id ?? null : null,
474
+ };
475
+ },
476
+ getActiveKnowledgeCrossScope(scope, level) {
477
+ const rows = db
478
+ .prepare(`SELECT * FROM knowledge_memory
479
+ WHERE ${scopeWhereForLevel(scope, level)} AND superseded_by_id IS NULL AND retired_at IS NULL
480
+ ORDER BY last_accessed_at DESC`)
481
+ .all(...scopeParamsForLevel(scope, level));
482
+ return rows.map(rowToKnowledgeMemory);
483
+ },
484
+ getKnowledgeSince(scope, level, since) {
485
+ const rows = db
486
+ .prepare(`SELECT * FROM knowledge_memory
487
+ WHERE ${scopeWhereForLevel(scope, level)}
488
+ AND created_at >= ?
489
+ AND superseded_by_id IS NULL
490
+ AND retired_at IS NULL
491
+ ORDER BY created_at ASC, id ASC`)
492
+ .all(...scopeParamsForLevel(scope, level), since);
493
+ return rows.map(rowToKnowledgeMemory);
494
+ },
495
+ getKnowledgeByTimeRange(scope, range) {
496
+ const time = timeRangeWhere(range, 'created_at');
497
+ const rows = db
498
+ .prepare(`SELECT * FROM knowledge_memory
499
+ WHERE ${SCOPE_WHERE}${time.clause}
500
+ ORDER BY created_at ASC`)
501
+ .all(...scopeValues(scope), ...time.params);
502
+ return rows.map(rowToKnowledgeMemory);
503
+ },
504
+ searchKnowledge(scope, query, options) {
505
+ const startedAt = Date.now();
506
+ const resolved = resolveSearchOptions(options);
507
+ try {
508
+ const statement = db.prepare(`SELECT knowledge_memory.*, bm25(knowledge_memory_fts) AS raw_rank
509
+ FROM knowledge_memory_fts
510
+ JOIN knowledge_memory ON knowledge_memory_fts.rowid = knowledge_memory.id
511
+ WHERE knowledge_memory_fts MATCH ?
512
+ AND ${SCOPE_WHERE}
513
+ AND (? = 0 OR (knowledge_memory.superseded_by_id IS NULL AND knowledge_memory.retired_at IS NULL))
514
+ ORDER BY bm25(knowledge_memory_fts)
515
+ LIMIT ?`);
516
+ let rows = statement.all(query, ...scopeValues(scope), resolved.activeOnly ? 1 : 0, resolved.limit);
517
+ const safeQuery = toSafeFtsQuery(query);
518
+ if (rows.length === 0 && safeQuery.length > 0 && safeQuery !== query && !/["']/.test(query)) {
519
+ rows = statement.all(safeQuery, ...scopeValues(scope), resolved.activeOnly ? 1 : 0, resolved.limit);
520
+ }
521
+ let results = rows
522
+ .map((row) => ({
523
+ item: rowToKnowledgeMemory(row),
524
+ rank: normalizeRank(row.raw_rank),
525
+ }))
526
+ .filter((result) => matchesKnowledgeSearchOptions(result.item, resolved))
527
+ .slice(0, resolved.limit);
528
+ if (results.length === 0 && !/["']/.test(query)) {
529
+ const fallbackRows = db
530
+ .prepare(`SELECT knowledge_memory.*
531
+ FROM knowledge_memory
532
+ WHERE ${SCOPE_WHERE}
533
+ AND (? = 0 OR (knowledge_memory.superseded_by_id IS NULL AND knowledge_memory.retired_at IS NULL))`)
534
+ .all(...scopeValues(scope), resolved.activeOnly ? 1 : 0);
535
+ results = fallbackRows
536
+ .map((row) => {
537
+ const item = rowToKnowledgeMemory(row);
538
+ return {
539
+ item,
540
+ rank: scoreSearchText(query, item.fact),
541
+ };
542
+ })
543
+ .filter((result) => result.rank > 0 && matchesKnowledgeSearchOptions(result.item, resolved))
544
+ .sort((a, b) => b.rank - a.rank || b.item.last_accessed_at - a.item.last_accessed_at)
545
+ .slice(0, resolved.limit);
546
+ }
547
+ emitMemoryEvent('search', scope, telemetry, Date.now() - startedAt, {
548
+ entity: 'knowledge',
549
+ query,
550
+ resultCount: results.length,
551
+ });
552
+ return results;
553
+ }
554
+ catch {
555
+ if (!/["']/.test(query)) {
556
+ const fallbackRows = db
557
+ .prepare(`SELECT knowledge_memory.*
558
+ FROM knowledge_memory
559
+ WHERE ${SCOPE_WHERE}
560
+ AND (? = 0 OR (knowledge_memory.superseded_by_id IS NULL AND knowledge_memory.retired_at IS NULL))`)
561
+ .all(...scopeValues(scope), resolved.activeOnly ? 1 : 0);
562
+ const fallbackResults = fallbackRows
563
+ .map((row) => {
564
+ const item = rowToKnowledgeMemory(row);
565
+ return {
566
+ item,
567
+ rank: scoreSearchText(query, item.fact),
568
+ };
569
+ })
570
+ .filter((result) => result.rank > 0 && matchesKnowledgeSearchOptions(result.item, resolved))
571
+ .sort((a, b) => b.rank - a.rank || b.item.last_accessed_at - a.item.last_accessed_at)
572
+ .slice(0, resolved.limit);
573
+ emitMemoryEvent('search', scope, telemetry, Date.now() - startedAt, {
574
+ entity: 'knowledge',
575
+ query,
576
+ resultCount: fallbackResults.length,
577
+ fallbackQuery: true,
578
+ });
579
+ return fallbackResults;
580
+ }
581
+ emitMemoryEvent('search', scope, telemetry, Date.now() - startedAt, {
582
+ entity: 'knowledge',
583
+ query,
584
+ resultCount: 0,
585
+ invalidQuery: true,
586
+ });
587
+ return [];
588
+ }
589
+ },
590
+ searchKnowledgeCrossScope(scope, level, query, options) {
591
+ const startedAt = Date.now();
592
+ const resolved = resolveSearchOptions(options);
593
+ try {
594
+ const statement = db.prepare(`SELECT knowledge_memory.*, bm25(knowledge_memory_fts) AS raw_rank
595
+ FROM knowledge_memory_fts
596
+ JOIN knowledge_memory ON knowledge_memory_fts.rowid = knowledge_memory.id
597
+ WHERE knowledge_memory_fts MATCH ?
598
+ AND ${scopeWhereForLevel(scope, level)}
599
+ AND (? = 0 OR (knowledge_memory.superseded_by_id IS NULL AND knowledge_memory.retired_at IS NULL))
600
+ ORDER BY bm25(knowledge_memory_fts)
601
+ LIMIT ?`);
602
+ let rows = statement.all(query, ...scopeParamsForLevel(scope, level), resolved.activeOnly ? 1 : 0, resolved.limit);
603
+ const safeQuery = toSafeFtsQuery(query);
604
+ if (rows.length === 0 && safeQuery.length > 0 && safeQuery !== query && !/["']/.test(query)) {
605
+ rows = statement.all(safeQuery, ...scopeParamsForLevel(scope, level), resolved.activeOnly ? 1 : 0, resolved.limit);
606
+ }
607
+ let results = rows
608
+ .map((row) => ({
609
+ item: rowToKnowledgeMemory(row),
610
+ rank: normalizeRank(row.raw_rank),
611
+ }))
612
+ .filter((result) => matchesKnowledgeSearchOptions(result.item, resolved))
613
+ .slice(0, resolved.limit);
614
+ if (results.length === 0 && !/["']/.test(query)) {
615
+ const fallbackRows = db
616
+ .prepare(`SELECT knowledge_memory.*
617
+ FROM knowledge_memory
618
+ WHERE ${scopeWhereForLevel(scope, level)}
619
+ AND (? = 0 OR (knowledge_memory.superseded_by_id IS NULL AND knowledge_memory.retired_at IS NULL))`)
620
+ .all(...scopeParamsForLevel(scope, level), resolved.activeOnly ? 1 : 0);
621
+ results = fallbackRows
622
+ .map((row) => {
623
+ const item = rowToKnowledgeMemory(row);
624
+ return {
625
+ item,
626
+ rank: scoreSearchText(query, item.fact),
627
+ };
628
+ })
629
+ .filter((result) => result.rank > 0 && matchesKnowledgeSearchOptions(result.item, resolved))
630
+ .sort((a, b) => b.rank - a.rank || b.item.last_accessed_at - a.item.last_accessed_at)
631
+ .slice(0, resolved.limit);
632
+ }
633
+ emitMemoryEvent('search', scope, telemetry, Date.now() - startedAt, {
634
+ entity: 'knowledge',
635
+ query,
636
+ resultCount: results.length,
637
+ scopeLevel: level,
638
+ });
639
+ return results;
640
+ }
641
+ catch {
642
+ if (!/["']/.test(query)) {
643
+ const fallbackRows = db
644
+ .prepare(`SELECT knowledge_memory.*
645
+ FROM knowledge_memory
646
+ WHERE ${scopeWhereForLevel(scope, level)}
647
+ AND (? = 0 OR (knowledge_memory.superseded_by_id IS NULL AND knowledge_memory.retired_at IS NULL))`)
648
+ .all(...scopeParamsForLevel(scope, level), resolved.activeOnly ? 1 : 0);
649
+ const fallbackResults = fallbackRows
650
+ .map((row) => {
651
+ const item = rowToKnowledgeMemory(row);
652
+ return {
653
+ item,
654
+ rank: scoreSearchText(query, item.fact),
655
+ };
656
+ })
657
+ .filter((result) => result.rank > 0 && matchesKnowledgeSearchOptions(result.item, resolved))
658
+ .sort((a, b) => b.rank - a.rank || b.item.last_accessed_at - a.item.last_accessed_at)
659
+ .slice(0, resolved.limit);
660
+ emitMemoryEvent('search', scope, telemetry, Date.now() - startedAt, {
661
+ entity: 'knowledge',
662
+ query,
663
+ resultCount: fallbackResults.length,
664
+ scopeLevel: level,
665
+ fallbackQuery: true,
666
+ });
667
+ return fallbackResults;
668
+ }
669
+ emitMemoryEvent('search', scope, telemetry, Date.now() - startedAt, {
670
+ entity: 'knowledge',
671
+ query,
672
+ resultCount: 0,
673
+ invalidQuery: true,
674
+ scopeLevel: level,
675
+ });
676
+ return [];
677
+ }
678
+ },
679
+ insertKnowledgeMemoryAudit(input) {
680
+ const scope = validateNewKnowledgeMemoryAudit(input);
681
+ const createdAt = input.created_at ?? nowSeconds();
682
+ const result = db
683
+ .prepare(`INSERT INTO knowledge_memory_audit
684
+ (tenant_id, system_id, workspace_id, collaboration_id, scope_id, working_memory_id, fact, fact_type,
685
+ fact_subject, fact_attribute, fact_value, normalized_fact, slot_key, is_negated,
686
+ confidence, confidence_score, verification_status, source_text, decision,
687
+ created_knowledge_id, related_knowledge_id, detail, created_at)
688
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
689
+ .run(scope.tenant_id, scope.system_id, scope.workspace_id, scope.collaboration_id, scope.scope_id, input.working_memory_id ?? null, input.fact, input.fact_type, input.fact_subject ?? null, input.fact_attribute ?? null, input.fact_value ?? null, input.normalized_fact ?? null, input.slot_key ?? null, input.is_negated ? 1 : 0, input.confidence, input.confidence_score ?? 0.5, input.verification_status ?? 'unverified', input.source_text ?? null, input.decision, input.created_knowledge_id ?? null, input.related_knowledge_id ?? null, input.detail ?? null, createdAt);
690
+ const row = db
691
+ .prepare('SELECT * FROM knowledge_memory_audit WHERE id = ?')
692
+ .get(Number(result.lastInsertRowid));
693
+ return rowToKnowledgeMemoryAudit(row);
694
+ },
695
+ getRecentKnowledgeMemoryAudits,
696
+ getKnowledgeMemoryAuditsForKnowledge,
697
+ updateKnowledgeMemory(id, patch) {
698
+ const assignments = [];
699
+ const values = [];
700
+ const push = (column, value) => {
701
+ assignments.push(`${column} = ?`);
702
+ values.push(value);
703
+ };
704
+ if (patch.knowledge_state !== undefined)
705
+ push('knowledge_state', patch.knowledge_state);
706
+ if (patch.knowledge_class !== undefined)
707
+ push('knowledge_class', patch.knowledge_class);
708
+ if (patch.trust_score !== undefined)
709
+ push('trust_score', patch.trust_score);
710
+ if (patch.verification_status !== undefined)
711
+ push('verification_status', patch.verification_status);
712
+ if (patch.verification_notes !== undefined)
713
+ push('verification_notes', patch.verification_notes);
714
+ if (patch.last_verified_at !== undefined)
715
+ push('last_verified_at', patch.last_verified_at);
716
+ if (patch.next_reverification_at !== undefined) {
717
+ push('next_reverification_at', patch.next_reverification_at);
718
+ }
719
+ if (patch.last_confirmed_at !== undefined)
720
+ push('last_confirmed_at', patch.last_confirmed_at);
721
+ if (patch.confirmation_count !== undefined)
722
+ push('confirmation_count', patch.confirmation_count);
723
+ if (patch.disputed_at !== undefined)
724
+ push('disputed_at', patch.disputed_at);
725
+ if (patch.dispute_reason !== undefined)
726
+ push('dispute_reason', patch.dispute_reason);
727
+ if (patch.contradiction_score !== undefined)
728
+ push('contradiction_score', patch.contradiction_score);
729
+ if (patch.superseded_at !== undefined)
730
+ push('superseded_at', patch.superseded_at);
731
+ if (patch.successful_use_count !== undefined)
732
+ push('successful_use_count', patch.successful_use_count);
733
+ if (patch.failed_use_count !== undefined)
734
+ push('failed_use_count', patch.failed_use_count);
735
+ if (assignments.length === 0) {
736
+ return getKnowledgeMemoryById(id);
737
+ }
738
+ db.prepare(`UPDATE knowledge_memory SET ${assignments.join(', ')} WHERE id = ?`).run(...values, id);
739
+ return getKnowledgeMemoryById(id);
740
+ },
741
+ insertWorkItem(input) {
742
+ const scope = validateNewWorkItem(input);
743
+ const createdAt = input.created_at ?? nowSeconds();
744
+ const result = db
745
+ .prepare(`INSERT INTO work_items
746
+ (session_id, tenant_id, system_id, workspace_id, collaboration_id, scope_id, kind, title, detail, status,
747
+ source_working_memory_id, created_at, updated_at)
748
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
749
+ .run(input.session_id ?? null, scope.tenant_id, scope.system_id, scope.workspace_id, scope.collaboration_id, scope.scope_id, input.kind, input.title, input.detail ?? null, input.status ?? 'open', input.source_working_memory_id ?? null, createdAt, createdAt);
750
+ const row = db
751
+ .prepare('SELECT * FROM work_items WHERE id = ?')
752
+ .get(Number(result.lastInsertRowid));
753
+ return rowToWorkItem(row);
754
+ },
755
+ getActiveWorkItems(scope) {
756
+ const rows = db
757
+ .prepare(`SELECT * FROM work_items
758
+ WHERE ${SCOPE_WHERE} AND status != 'done'
759
+ ORDER BY updated_at DESC`)
760
+ .all(...scopeValues(scope));
761
+ return rows.map(rowToWorkItem);
762
+ },
763
+ getWorkItemsByTimeRange(scope, range) {
764
+ const time = timeRangeWhere(range, 'created_at');
765
+ const rows = db
766
+ .prepare(`SELECT * FROM work_items
767
+ WHERE ${SCOPE_WHERE}${time.clause}
768
+ ORDER BY created_at ASC`)
769
+ .all(...scopeValues(scope), ...time.params);
770
+ return rows.map(rowToWorkItem);
771
+ },
772
+ updateWorkItemStatus(id, status) {
773
+ db.prepare('UPDATE work_items SET status = ?, updated_at = ? WHERE id = ?').run(status, nowSeconds(), id);
774
+ },
775
+ deleteWorkItem(id) {
776
+ db.prepare('DELETE FROM work_items WHERE id = ?').run(id);
777
+ },
778
+ touchKnowledgeMemory(id) {
779
+ db.prepare(`UPDATE knowledge_memory
780
+ SET last_accessed_at = ?, access_count = access_count + 1
781
+ WHERE id = ?`).run(nowSeconds(), id);
782
+ },
783
+ retireKnowledgeMemory(id, retiredAt = nowSeconds()) {
784
+ db.prepare('UPDATE knowledge_memory SET retired_at = ? WHERE id = ?').run(retiredAt, id);
785
+ },
786
+ supersedeKnowledgeMemory(oldId, newId) {
787
+ db.prepare(`UPDATE knowledge_memory
788
+ SET superseded_by_id = ?, superseded_at = ?, knowledge_state = 'superseded'
789
+ WHERE id = ?`).run(newId, nowSeconds(), oldId);
790
+ },
791
+ upsertContextMonitor(input) {
792
+ const scope = validateContextMonitorUpsert(input);
793
+ const updatedAt = nowSeconds();
794
+ db.prepare(`INSERT INTO context_monitor
795
+ (tenant_id, system_id, workspace_id, collaboration_id, scope_id, compaction_state, last_compaction_at,
796
+ active_turn_count, active_token_estimate, compaction_score, updated_at)
797
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
798
+ ON CONFLICT(tenant_id, system_id, workspace_id, collaboration_id, scope_id) DO UPDATE SET
799
+ compaction_state = excluded.compaction_state,
800
+ last_compaction_at = excluded.last_compaction_at,
801
+ active_turn_count = excluded.active_turn_count,
802
+ active_token_estimate = excluded.active_token_estimate,
803
+ compaction_score = excluded.compaction_score,
804
+ updated_at = excluded.updated_at`).run(scope.tenant_id, scope.system_id, scope.workspace_id, scope.collaboration_id, scope.scope_id, input.compaction_state, input.last_compaction_at ?? null, input.active_turn_count, input.active_token_estimate, input.compaction_score, updatedAt);
805
+ return getContextMonitor(scope);
806
+ },
807
+ getContextMonitor,
808
+ insertCompactionLog(input) {
809
+ const scope = validateNewCompactionLog(input);
810
+ const createdAt = input.created_at ?? nowSeconds();
811
+ const result = db
812
+ .prepare(`INSERT INTO compaction_log
813
+ (session_id, tenant_id, system_id, workspace_id, collaboration_id, scope_id, trigger_type,
814
+ turn_id_start, turn_id_end, turns_compacted, tokens_compacted_estimate,
815
+ working_memory_id, active_turn_count_before, active_turn_count_after,
816
+ duration_ms, model_call_made, error, created_at)
817
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
818
+ .run(input.session_id, scope.tenant_id, scope.system_id, scope.workspace_id, scope.collaboration_id, scope.scope_id, input.trigger_type, input.turn_id_start, input.turn_id_end, input.turns_compacted, input.tokens_compacted_estimate, input.working_memory_id, input.active_turn_count_before, input.active_turn_count_after, input.duration_ms, input.model_call_made ? 1 : 0, input.error ?? null, createdAt);
819
+ return getCompactionLogById(Number(result.lastInsertRowid));
820
+ },
821
+ getCompactionLogById,
822
+ getRecentCompactionLogs(scope, limit = 10) {
823
+ const rows = db
824
+ .prepare(`SELECT * FROM compaction_log
825
+ WHERE ${SCOPE_WHERE}
826
+ ORDER BY id DESC
827
+ LIMIT ?`)
828
+ .all(...scopeValues(scope), limit);
829
+ return rows.map(rowToCompactionLog);
830
+ },
831
+ transaction(fn) {
832
+ return db.transaction(fn)();
833
+ },
834
+ close() {
835
+ db.close();
836
+ },
837
+ };
838
+ }
839
+ //# sourceMappingURL=index.js.map