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,982 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { normalizeScope } from '../../contracts/identity.js';
3
+ import { matchesKnowledgeSearchOptions } from '../../core/retrieval.js';
4
+ import { estimateTokens } from '../../core/tokens.js';
5
+ function scopeParams(scope) {
6
+ const n = normalizeScope(scope);
7
+ return [n.tenant_id, n.system_id, n.workspace_id, n.collaboration_id, n.scope_id];
8
+ }
9
+ function scopeWhere(prefix = '') {
10
+ const p = prefix ? `${prefix}.` : '';
11
+ return `${p}tenant_id = $1 AND ${p}system_id = $2 AND ${p}workspace_id = $3 AND ${p}collaboration_id = $4 AND ${p}scope_id = $5`;
12
+ }
13
+ function wideScopeWhere(scope, level, prefix = '') {
14
+ const p = prefix ? `${prefix}.` : '';
15
+ const normalized = normalizeScope(scope);
16
+ switch (level) {
17
+ case 'tenant':
18
+ return `${p}tenant_id = $1`;
19
+ case 'system':
20
+ return `${p}tenant_id = $1 AND ${p}system_id = $2`;
21
+ case 'workspace':
22
+ return normalized.collaboration_id.length > 0
23
+ ? `${p}tenant_id = $1 AND ${p}collaboration_id = $2`
24
+ : `${p}tenant_id = $1 AND ${p}system_id = $2 AND ${p}workspace_id = $3`;
25
+ default:
26
+ return scopeWhere(prefix);
27
+ }
28
+ }
29
+ function wideScopeParams(scope, level) {
30
+ const n = normalizeScope(scope);
31
+ switch (level) {
32
+ case 'tenant':
33
+ return [n.tenant_id];
34
+ case 'system':
35
+ return [n.tenant_id, n.system_id];
36
+ case 'workspace':
37
+ return n.collaboration_id.length > 0
38
+ ? [n.tenant_id, n.collaboration_id]
39
+ : [n.tenant_id, n.system_id, n.workspace_id];
40
+ default:
41
+ return scopeParams(scope);
42
+ }
43
+ }
44
+ function nowSeconds() {
45
+ return Math.floor(Date.now() / 1000);
46
+ }
47
+ function vectorToLiteral(vector) {
48
+ return `[${Array.from(vector, (value) => (Number.isFinite(value) ? value : 0)).join(',')}]`;
49
+ }
50
+ function parseVectorValue(value) {
51
+ if (value == null)
52
+ return null;
53
+ if (value instanceof Float32Array)
54
+ return value;
55
+ if (Array.isArray(value)) {
56
+ return new Float32Array(value.map((entry) => Number(entry)));
57
+ }
58
+ if (typeof value === 'string') {
59
+ const trimmed = value.trim();
60
+ const normalized = trimmed
61
+ .replace(/^\[/, '')
62
+ .replace(/\]$/, '')
63
+ .replace(/^\{/, '')
64
+ .replace(/\}$/, '');
65
+ if (normalized.length === 0) {
66
+ return new Float32Array();
67
+ }
68
+ return new Float32Array(normalized
69
+ .split(',')
70
+ .map((entry) => entry.trim())
71
+ .filter((entry) => entry.length > 0)
72
+ .map((entry) => Number(entry)));
73
+ }
74
+ return null;
75
+ }
76
+ function resolvePaginationOptions(options) {
77
+ return {
78
+ limit: options?.limit ?? 25,
79
+ offset: options?.offset ?? 0,
80
+ cursor: options?.cursor ?? 0,
81
+ };
82
+ }
83
+ function mapTurn(row) {
84
+ return {
85
+ id: Number(row.id),
86
+ tenant_id: String(row.tenant_id),
87
+ system_id: String(row.system_id),
88
+ workspace_id: String(row.workspace_id ?? ''),
89
+ collaboration_id: String(row.collaboration_id ?? ''),
90
+ scope_id: String(row.scope_id),
91
+ session_id: String(row.session_id),
92
+ actor: String(row.actor),
93
+ role: row.role,
94
+ content: String(row.content),
95
+ priority: Number(row.priority ?? 1),
96
+ token_estimate: Number(row.token_estimate),
97
+ archived_at: row.archived_at != null ? Number(row.archived_at) : null,
98
+ compaction_log_id: row.compaction_log_id != null ? Number(row.compaction_log_id) : null,
99
+ created_at: Number(row.created_at),
100
+ schema_version: Number(row.schema_version ?? 1),
101
+ };
102
+ }
103
+ function mapWorkingMemory(row) {
104
+ return {
105
+ id: Number(row.id),
106
+ tenant_id: String(row.tenant_id),
107
+ system_id: String(row.system_id),
108
+ workspace_id: String(row.workspace_id ?? ''),
109
+ collaboration_id: String(row.collaboration_id ?? ''),
110
+ scope_id: String(row.scope_id),
111
+ session_id: String(row.session_id),
112
+ summary: String(row.summary),
113
+ key_entities: Array.isArray(row.key_entities) ? row.key_entities : JSON.parse(String(row.key_entities ?? '[]')),
114
+ topic_tags: Array.isArray(row.topic_tags) ? row.topic_tags : JSON.parse(String(row.topic_tags ?? '[]')),
115
+ turn_id_start: Number(row.turn_id_start),
116
+ turn_id_end: Number(row.turn_id_end),
117
+ turn_count: Number(row.turn_count),
118
+ compaction_trigger: row.compaction_trigger,
119
+ expires_at: row.expires_at != null ? Number(row.expires_at) : null,
120
+ promoted_to_knowledge_id: row.promoted_to_knowledge_id != null ? Number(row.promoted_to_knowledge_id) : null,
121
+ created_at: Number(row.created_at),
122
+ schema_version: Number(row.schema_version ?? 1),
123
+ };
124
+ }
125
+ function mapKnowledgeMemory(row) {
126
+ return {
127
+ id: Number(row.id),
128
+ tenant_id: String(row.tenant_id),
129
+ system_id: String(row.system_id),
130
+ workspace_id: String(row.workspace_id ?? ''),
131
+ collaboration_id: String(row.collaboration_id ?? ''),
132
+ scope_id: String(row.scope_id),
133
+ fact: String(row.fact),
134
+ fact_type: row.fact_type,
135
+ knowledge_state: row.knowledge_state ?? 'trusted',
136
+ knowledge_class: row.knowledge_class ?? 'project_fact',
137
+ fact_subject: row.fact_subject != null ? String(row.fact_subject) : null,
138
+ fact_attribute: row.fact_attribute != null ? String(row.fact_attribute) : null,
139
+ fact_value: row.fact_value != null ? String(row.fact_value) : null,
140
+ normalized_fact: row.normalized_fact != null ? String(row.normalized_fact) : null,
141
+ slot_key: row.slot_key != null ? String(row.slot_key) : null,
142
+ is_negated: Boolean(row.is_negated),
143
+ source: row.source,
144
+ confidence: row.confidence,
145
+ confidence_score: Number(row.confidence_score ?? 0.5),
146
+ grounding_strength: row.grounding_strength ?? 'moderate',
147
+ evidence_count: Number(row.evidence_count ?? 0),
148
+ trust_score: Number(row.trust_score ?? 0.5),
149
+ verification_status: row.verification_status ?? 'unverified',
150
+ verification_notes: row.verification_notes != null ? String(row.verification_notes) : null,
151
+ last_verified_at: row.last_verified_at != null ? Number(row.last_verified_at) : null,
152
+ next_reverification_at: row.next_reverification_at != null ? Number(row.next_reverification_at) : null,
153
+ last_confirmed_at: row.last_confirmed_at != null ? Number(row.last_confirmed_at) : null,
154
+ confirmation_count: Number(row.confirmation_count ?? 0),
155
+ source_system_id: row.source_system_id != null ? String(row.source_system_id) : null,
156
+ source_scope_id: row.source_scope_id != null ? String(row.source_scope_id) : null,
157
+ source_collaboration_id: row.source_collaboration_id != null ? String(row.source_collaboration_id) : null,
158
+ source_working_memory_id: row.source_working_memory_id != null ? Number(row.source_working_memory_id) : null,
159
+ source_turn_ids: Array.isArray(row.source_turn_ids)
160
+ ? row.source_turn_ids.map((value) => Number(value))
161
+ : [],
162
+ successful_use_count: Number(row.successful_use_count ?? 0),
163
+ failed_use_count: Number(row.failed_use_count ?? 0),
164
+ disputed_at: row.disputed_at != null ? Number(row.disputed_at) : null,
165
+ dispute_reason: row.dispute_reason != null ? String(row.dispute_reason) : null,
166
+ contradiction_score: Number(row.contradiction_score ?? 0),
167
+ superseded_at: row.superseded_at != null ? Number(row.superseded_at) : null,
168
+ superseded_by_id: row.superseded_by_id != null ? Number(row.superseded_by_id) : null,
169
+ retired_at: row.retired_at != null ? Number(row.retired_at) : null,
170
+ access_count: Number(row.access_count ?? 0),
171
+ last_accessed_at: Number(row.last_accessed_at ?? 0),
172
+ created_at: Number(row.created_at),
173
+ schema_version: Number(row.schema_version ?? 1),
174
+ };
175
+ }
176
+ function mapWorkItem(row) {
177
+ return {
178
+ id: Number(row.id),
179
+ tenant_id: String(row.tenant_id),
180
+ system_id: String(row.system_id),
181
+ workspace_id: String(row.workspace_id ?? ''),
182
+ collaboration_id: String(row.collaboration_id ?? ''),
183
+ scope_id: String(row.scope_id),
184
+ session_id: row.session_id != null ? String(row.session_id) : null,
185
+ title: String(row.title),
186
+ kind: row.kind,
187
+ status: row.status,
188
+ detail: row.detail != null ? String(row.detail) : null,
189
+ source_working_memory_id: row.source_working_memory_id != null ? Number(row.source_working_memory_id) : null,
190
+ created_at: Number(row.created_at),
191
+ updated_at: Number(row.updated_at),
192
+ };
193
+ }
194
+ function mapContextMonitor(row) {
195
+ return {
196
+ id: Number(row.id),
197
+ tenant_id: String(row.tenant_id),
198
+ system_id: String(row.system_id),
199
+ workspace_id: String(row.workspace_id ?? ''),
200
+ collaboration_id: String(row.collaboration_id ?? ''),
201
+ scope_id: String(row.scope_id),
202
+ compaction_state: row.compaction_state,
203
+ active_turn_count: Number(row.active_turn_count),
204
+ active_token_estimate: Number(row.active_token_estimate),
205
+ compaction_score: Number(row.compaction_score),
206
+ last_compaction_at: row.last_compaction_at != null ? Number(row.last_compaction_at) : null,
207
+ updated_at: Number(row.updated_at),
208
+ };
209
+ }
210
+ function mapCompactionLog(row) {
211
+ return {
212
+ id: Number(row.id),
213
+ tenant_id: String(row.tenant_id),
214
+ system_id: String(row.system_id),
215
+ workspace_id: String(row.workspace_id ?? ''),
216
+ collaboration_id: String(row.collaboration_id ?? ''),
217
+ scope_id: String(row.scope_id),
218
+ session_id: String(row.session_id),
219
+ trigger_type: row.trigger_type,
220
+ turn_id_start: Number(row.turn_id_start),
221
+ turn_id_end: Number(row.turn_id_end),
222
+ turns_compacted: Number(row.turns_compacted),
223
+ tokens_compacted_estimate: Number(row.tokens_compacted_estimate),
224
+ working_memory_id: Number(row.working_memory_id),
225
+ active_turn_count_before: Number(row.active_turn_count_before),
226
+ active_turn_count_after: Number(row.active_turn_count_after),
227
+ duration_ms: Number(row.duration_ms),
228
+ model_call_made: Boolean(row.model_call_made),
229
+ created_at: Number(row.created_at),
230
+ };
231
+ }
232
+ function mapKnowledgeMemoryAudit(row) {
233
+ return {
234
+ id: Number(row.id),
235
+ tenant_id: String(row.tenant_id),
236
+ system_id: String(row.system_id),
237
+ workspace_id: String(row.workspace_id ?? ''),
238
+ collaboration_id: String(row.collaboration_id ?? ''),
239
+ scope_id: String(row.scope_id),
240
+ working_memory_id: Number(row.working_memory_id),
241
+ fact: String(row.fact),
242
+ fact_type: row.fact_type,
243
+ fact_subject: row.fact_subject != null ? String(row.fact_subject) : null,
244
+ fact_attribute: row.fact_attribute != null ? String(row.fact_attribute) : null,
245
+ fact_value: row.fact_value != null ? String(row.fact_value) : null,
246
+ normalized_fact: row.normalized_fact != null ? String(row.normalized_fact) : null,
247
+ slot_key: row.slot_key != null ? String(row.slot_key) : null,
248
+ is_negated: Boolean(row.is_negated),
249
+ confidence: row.confidence,
250
+ confidence_score: Number(row.confidence_score ?? 0.5),
251
+ verification_status: row.verification_status ?? 'unverified',
252
+ source_text: String(row.source_text),
253
+ decision: row.decision,
254
+ detail: row.detail != null ? String(row.detail) : null,
255
+ related_knowledge_id: row.related_knowledge_id != null ? Number(row.related_knowledge_id) : null,
256
+ created_knowledge_id: row.created_knowledge_id != null ? Number(row.created_knowledge_id) : null,
257
+ created_at: Number(row.created_at),
258
+ };
259
+ }
260
+ function mapKnowledgeCandidate(row) {
261
+ return {
262
+ id: Number(row.id),
263
+ tenant_id: String(row.tenant_id),
264
+ system_id: String(row.system_id),
265
+ workspace_id: String(row.workspace_id ?? ''),
266
+ collaboration_id: String(row.collaboration_id ?? ''),
267
+ scope_id: String(row.scope_id),
268
+ working_memory_id: Number(row.working_memory_id),
269
+ fact: String(row.fact),
270
+ fact_type: row.fact_type,
271
+ knowledge_class: row.knowledge_class,
272
+ normalized_fact: String(row.normalized_fact),
273
+ slot_key: row.slot_key != null ? String(row.slot_key) : null,
274
+ confidence: row.confidence,
275
+ source_summary: Boolean(row.source_summary),
276
+ source_turns: Boolean(row.source_turns),
277
+ grounding_strength: row.grounding_strength,
278
+ evidence_count: Number(row.evidence_count ?? 0),
279
+ trust_score: Number(row.trust_score ?? 0),
280
+ state: row.state,
281
+ created_at: Number(row.created_at),
282
+ promoted_knowledge_id: row.promoted_knowledge_id != null ? Number(row.promoted_knowledge_id) : null,
283
+ };
284
+ }
285
+ function mapKnowledgeEvidenceRow(row) {
286
+ return {
287
+ id: Number(row.id),
288
+ tenant_id: String(row.tenant_id),
289
+ system_id: String(row.system_id),
290
+ workspace_id: String(row.workspace_id ?? ''),
291
+ collaboration_id: String(row.collaboration_id ?? ''),
292
+ scope_id: String(row.scope_id),
293
+ knowledge_memory_id: row.knowledge_memory_id != null ? Number(row.knowledge_memory_id) : null,
294
+ knowledge_candidate_id: row.knowledge_candidate_id != null ? Number(row.knowledge_candidate_id) : null,
295
+ working_memory_id: row.working_memory_id != null ? Number(row.working_memory_id) : null,
296
+ turn_id: row.turn_id != null ? Number(row.turn_id) : null,
297
+ source_type: row.source_type,
298
+ support_polarity: row.support_polarity,
299
+ speaker_role: row.speaker_role != null ? row.speaker_role : null,
300
+ actor: row.actor != null ? String(row.actor) : null,
301
+ excerpt: String(row.excerpt),
302
+ start_offset: row.start_offset != null ? Number(row.start_offset) : null,
303
+ end_offset: row.end_offset != null ? Number(row.end_offset) : null,
304
+ is_explicit: Boolean(row.is_explicit),
305
+ explicitness_score: Number(row.explicitness_score ?? 0),
306
+ outcome: row.outcome != null ? row.outcome : null,
307
+ created_at: Number(row.created_at),
308
+ };
309
+ }
310
+ /**
311
+ * Creates a PostgreSQL-backed AsyncStorageAdapter.
312
+ *
313
+ * Requires the `pg` package as an optional peer dependency.
314
+ *
315
+ * ```typescript
316
+ * import { createPostgresAdapter } from 'memory-layer/adapters/postgres';
317
+ * import pg from 'pg';
318
+ *
319
+ * const pool = new pg.Pool({ connectionString: 'postgresql://...' });
320
+ * const adapter = createPostgresAdapter(pool);
321
+ * ```
322
+ */
323
+ export function createPostgresAdapter(pool, options) {
324
+ const now = nowSeconds;
325
+ const txStorage = new AsyncLocalStorage();
326
+ const rootQuery = pool.query.bind(pool);
327
+ pool.query = ((text, values) => {
328
+ const context = txStorage.getStore();
329
+ return context ? context.client.query(text, values) : rootQuery(text, values);
330
+ });
331
+ return {
332
+ async insertTurn(input) {
333
+ const n = normalizeScope(input);
334
+ const tokenEst = input.token_estimate ?? estimateTokens(input.content);
335
+ const { rows } = await pool.query(`INSERT INTO turns (tenant_id, system_id, workspace_id, collaboration_id, scope_id, session_id, actor, role, content, priority, token_estimate, created_at)
336
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
337
+ RETURNING *`, [n.tenant_id, n.system_id, n.workspace_id, n.collaboration_id, n.scope_id, input.session_id, input.actor, input.role, input.content, input.priority ?? (input.role === 'system' ? 1.5 : 1), tokenEst, now()]);
338
+ return mapTurn(rows[0]);
339
+ },
340
+ async insertTurns(inputs) {
341
+ return this.transaction(async () => {
342
+ const inserted = [];
343
+ for (const input of inputs) {
344
+ inserted.push(await this.insertTurn(input));
345
+ }
346
+ return inserted;
347
+ });
348
+ },
349
+ async getTurnById(id) {
350
+ const { rows } = await pool.query('SELECT * FROM turns WHERE id = $1', [id]);
351
+ return rows[0] ? mapTurn(rows[0]) : null;
352
+ },
353
+ async getActiveTurns(scope, sessionId) {
354
+ const params = sessionId ? [...scopeParams(scope), sessionId] : scopeParams(scope);
355
+ const { rows } = await pool.query(`SELECT * FROM turns WHERE ${scopeWhere()} AND status = 'active'${sessionId ? ' AND session_id = $6' : ''} ORDER BY id ASC`, params);
356
+ return rows.map(mapTurn);
357
+ },
358
+ async getActiveTurnsPaginated(scope, options) {
359
+ const resolved = resolvePaginationOptions(options);
360
+ const params = [...scopeParams(scope)];
361
+ let query = `SELECT * FROM turns WHERE ${scopeWhere()} AND status = 'active'`;
362
+ if (resolved.cursor > 0) {
363
+ params.push(resolved.cursor);
364
+ query += ` AND id > $${params.length}`;
365
+ }
366
+ query += ' ORDER BY id ASC';
367
+ params.push(resolved.limit + 1);
368
+ query += ` LIMIT $${params.length}`;
369
+ if (resolved.cursor === 0) {
370
+ params.push(resolved.offset);
371
+ query += ` OFFSET $${params.length}`;
372
+ }
373
+ const { rows } = await pool.query(query, params);
374
+ const items = rows.slice(0, resolved.limit).map(mapTurn);
375
+ return {
376
+ items,
377
+ hasMore: rows.length > resolved.limit,
378
+ nextCursor: rows.length > resolved.limit ? items[items.length - 1]?.id ?? null : null,
379
+ };
380
+ },
381
+ async getTurnsByTimeRange(scope, range) {
382
+ const params = scopeParams(scope);
383
+ let query = `SELECT * FROM turns WHERE ${scopeWhere()}`;
384
+ if (range.start_at != null) {
385
+ params.push(range.start_at);
386
+ query += ` AND created_at >= $${params.length}`;
387
+ }
388
+ if (range.end_at != null) {
389
+ params.push(range.end_at);
390
+ query += ` AND created_at <= $${params.length}`;
391
+ }
392
+ query += ' ORDER BY id ASC';
393
+ const { rows } = await pool.query(query, params);
394
+ return rows.map(mapTurn);
395
+ },
396
+ async searchTurns(scope, queryText, searchOptions) {
397
+ const params = scopeParams(scope);
398
+ const limit = searchOptions?.limit ?? 10;
399
+ params.push(queryText, limit);
400
+ const activeClause = searchOptions?.activeOnly ? ` AND status = 'active'` : '';
401
+ const { rows } = await pool.query(`SELECT *, ts_rank(search_vector, plainto_tsquery('english', $5)) AS rank
402
+ FROM turns
403
+ WHERE ${scopeWhere()} ${activeClause}
404
+ AND search_vector @@ plainto_tsquery('english', $5)
405
+ ORDER BY rank DESC
406
+ LIMIT $6`, params);
407
+ return rows.map((row) => ({
408
+ item: mapTurn(row),
409
+ rank: Number(row.rank),
410
+ }));
411
+ },
412
+ async archiveTurn(id, archivedAt, compactionLogId) {
413
+ await pool.query(`UPDATE turns SET status = 'archived', archived_at = $2, compaction_log_id = $3 WHERE id = $1`, [id, archivedAt, compactionLogId]);
414
+ },
415
+ async getArchivedTurnRange(sessionId, startId, endId, scope) {
416
+ const n = normalizeScope(scope);
417
+ const params = [
418
+ sessionId,
419
+ startId,
420
+ endId,
421
+ n.tenant_id,
422
+ n.system_id,
423
+ n.workspace_id,
424
+ n.collaboration_id,
425
+ n.scope_id,
426
+ ];
427
+ let query = `SELECT * FROM turns WHERE session_id = $1 AND id >= $2 AND id <= $3 AND status = 'archived'` +
428
+ ` AND tenant_id = $4 AND system_id = $5 AND workspace_id = $6 AND collaboration_id = $7 AND scope_id = $8`;
429
+ query += ' ORDER BY id ASC';
430
+ const { rows } = await pool.query(query, params);
431
+ return rows.map(mapTurn);
432
+ },
433
+ async insertWorkingMemory(input) {
434
+ const n = normalizeScope(input);
435
+ const { rows } = await pool.query(`INSERT INTO working_memory (tenant_id, system_id, workspace_id, collaboration_id, scope_id, session_id, summary, key_entities, topic_tags, turn_id_start, turn_id_end, turn_count, compaction_trigger, created_at)
436
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
437
+ RETURNING *`, [n.tenant_id, n.system_id, n.workspace_id, n.collaboration_id, n.scope_id, input.session_id, input.summary,
438
+ JSON.stringify(input.key_entities), JSON.stringify(input.topic_tags),
439
+ input.turn_id_start, input.turn_id_end, input.turn_count, input.compaction_trigger, now()]);
440
+ return mapWorkingMemory(rows[0]);
441
+ },
442
+ async getWorkingMemoryById(id) {
443
+ const { rows } = await pool.query('SELECT * FROM working_memory WHERE id = $1', [id]);
444
+ return rows[0] ? mapWorkingMemory(rows[0]) : null;
445
+ },
446
+ async getWorkingMemoryBySession(sessionId, scope) {
447
+ const params = [sessionId, ...scopeParams(scope)];
448
+ const { rows } = await pool.query(`SELECT * FROM working_memory WHERE session_id = $1 AND tenant_id = $2 AND system_id = $3 AND workspace_id = $4 AND collaboration_id = $5 AND scope_id = $6 ORDER BY id DESC`, params);
449
+ return rows.map(mapWorkingMemory);
450
+ },
451
+ async getActiveWorkingMemory(scope, sessionId) {
452
+ const params = sessionId ? [...scopeParams(scope), sessionId] : scopeParams(scope);
453
+ const { rows } = await pool.query(`SELECT * FROM working_memory WHERE ${scopeWhere()} AND status = 'active'${sessionId ? ' AND session_id = $6' : ''} ORDER BY id DESC`, params);
454
+ return rows.map(mapWorkingMemory);
455
+ },
456
+ async getLatestWorkingMemory(scope, sessionId) {
457
+ const params = sessionId ? [...scopeParams(scope), sessionId] : scopeParams(scope);
458
+ const { rows } = await pool.query(`SELECT * FROM working_memory WHERE ${scopeWhere()} AND status = 'active'${sessionId ? ' AND session_id = $6' : ''} ORDER BY id DESC LIMIT 1`, params);
459
+ return rows[0] ? mapWorkingMemory(rows[0]) : null;
460
+ },
461
+ async getWorkingMemoryByTimeRange(scope, range) {
462
+ const params = scopeParams(scope);
463
+ let query = `SELECT * FROM working_memory WHERE ${scopeWhere()}`;
464
+ if (range.start_at != null) {
465
+ params.push(range.start_at);
466
+ query += ` AND created_at >= $${params.length}`;
467
+ }
468
+ if (range.end_at != null) {
469
+ params.push(range.end_at);
470
+ query += ` AND created_at <= $${params.length}`;
471
+ }
472
+ query += ' ORDER BY id DESC';
473
+ const { rows } = await pool.query(query, params);
474
+ return rows.map(mapWorkingMemory);
475
+ },
476
+ async expireWorkingMemory(id) {
477
+ await pool.query(`UPDATE working_memory SET status = 'expired', expires_at = $2 WHERE id = $1`, [id, now()]);
478
+ },
479
+ async markWorkingMemoryPromoted(id, knowledgeMemoryId) {
480
+ await pool.query(`UPDATE working_memory SET promoted_to_knowledge_id = $2 WHERE id = $1`, [id, knowledgeMemoryId]);
481
+ },
482
+ async insertKnowledgeMemory(input) {
483
+ const n = normalizeScope(input);
484
+ const { rows } = await pool.query(`INSERT INTO knowledge_memory (tenant_id, system_id, workspace_id, collaboration_id, scope_id, fact, fact_type, knowledge_state, knowledge_class, fact_subject, fact_attribute, fact_value, normalized_fact, slot_key, is_negated, source, confidence, confidence_score, grounding_strength, evidence_count, trust_score, verification_status, verification_notes, last_verified_at, next_reverification_at, last_confirmed_at, confirmation_count, source_working_memory_id, source_turn_ids, successful_use_count, failed_use_count, disputed_at, dispute_reason, contradiction_score, superseded_at, created_at, last_accessed_at)
485
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37, $37)
486
+ RETURNING *`, [n.tenant_id, n.system_id, n.workspace_id, n.collaboration_id, n.scope_id, input.fact, input.fact_type,
487
+ input.knowledge_state ?? 'trusted', input.knowledge_class ?? 'project_fact',
488
+ input.fact_subject ?? null, input.fact_attribute ?? null, input.fact_value ?? null,
489
+ input.normalized_fact ?? null, input.slot_key ?? null, input.is_negated ?? false,
490
+ input.source, input.confidence ?? 'medium', input.confidence_score ?? 0.5,
491
+ input.grounding_strength ?? 'moderate',
492
+ input.evidence_count ?? Math.max(1, (input.source_turn_ids ?? []).length),
493
+ input.trust_score ?? (input.confidence_score ?? 0.5),
494
+ input.verification_status ?? 'unverified', input.verification_notes ?? null,
495
+ input.last_verified_at ?? null, input.next_reverification_at ?? null,
496
+ input.last_confirmed_at ?? null, input.confirmation_count ?? 0,
497
+ input.source_working_memory_id ?? null, input.source_turn_ids ?? [],
498
+ input.successful_use_count ?? 0, input.failed_use_count ?? 0,
499
+ input.disputed_at ?? null, input.dispute_reason ?? null, input.contradiction_score ?? 0,
500
+ input.superseded_at ?? null, now()]);
501
+ return mapKnowledgeMemory(rows[0]);
502
+ },
503
+ async insertKnowledgeMemories(inputs) {
504
+ return this.transaction(async () => {
505
+ const inserted = [];
506
+ for (const input of inputs) {
507
+ inserted.push(await this.insertKnowledgeMemory(input));
508
+ }
509
+ return inserted;
510
+ });
511
+ },
512
+ async insertKnowledgeCandidate(input) {
513
+ const n = normalizeScope(input);
514
+ const { rows } = await pool.query(`INSERT INTO knowledge_candidate
515
+ (tenant_id, system_id, workspace_id, collaboration_id, scope_id, working_memory_id, fact, fact_type,
516
+ knowledge_class, normalized_fact, slot_key, confidence, source_summary, source_turns,
517
+ grounding_strength, evidence_count, trust_score, state, promoted_knowledge_id, created_at)
518
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
519
+ RETURNING *`, [
520
+ n.tenant_id,
521
+ n.system_id,
522
+ n.workspace_id,
523
+ n.collaboration_id,
524
+ n.scope_id,
525
+ input.working_memory_id,
526
+ input.fact,
527
+ input.fact_type,
528
+ input.knowledge_class,
529
+ input.normalized_fact,
530
+ input.slot_key ?? null,
531
+ input.confidence,
532
+ input.source_summary ?? false,
533
+ input.source_turns ?? true,
534
+ input.grounding_strength ?? 'weak',
535
+ input.evidence_count ?? 0,
536
+ input.trust_score ?? 0,
537
+ input.state ?? 'candidate',
538
+ input.promoted_knowledge_id ?? null,
539
+ input.created_at ?? now(),
540
+ ]);
541
+ return mapKnowledgeCandidate(rows[0]);
542
+ },
543
+ async insertKnowledgeCandidates(inputs) {
544
+ return this.transaction(async () => {
545
+ const inserted = [];
546
+ for (const input of inputs) {
547
+ inserted.push(await this.insertKnowledgeCandidate(input));
548
+ }
549
+ return inserted;
550
+ });
551
+ },
552
+ async getKnowledgeCandidateById(id) {
553
+ const { rows } = await pool.query('SELECT * FROM knowledge_candidate WHERE id = $1', [id]);
554
+ if (!rows[0])
555
+ return null;
556
+ return mapKnowledgeCandidate(rows[0]);
557
+ },
558
+ async listKnowledgeCandidates(scope, options) {
559
+ const { rows } = await pool.query(`SELECT * FROM knowledge_candidate WHERE ${scopeWhere()} ORDER BY created_at DESC, id DESC`, scopeParams(scope));
560
+ return rows
561
+ .map(mapKnowledgeCandidate)
562
+ .filter((item) => !options?.state || options.state.includes(item.state));
563
+ },
564
+ async insertKnowledgeEvidence(input) {
565
+ const n = normalizeScope(input);
566
+ const { rows } = await pool.query(`INSERT INTO knowledge_evidence
567
+ (tenant_id, system_id, workspace_id, collaboration_id, scope_id, knowledge_memory_id, knowledge_candidate_id,
568
+ working_memory_id, turn_id, source_type, support_polarity, speaker_role, actor, excerpt,
569
+ start_offset, end_offset, is_explicit, explicitness_score, outcome, created_at)
570
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
571
+ RETURNING *`, [
572
+ n.tenant_id, n.system_id, n.workspace_id, n.collaboration_id, n.scope_id,
573
+ input.knowledge_memory_id ?? null, input.knowledge_candidate_id ?? null,
574
+ input.working_memory_id ?? null, input.turn_id ?? null, input.source_type, input.support_polarity,
575
+ input.speaker_role ?? null, input.actor ?? null, input.excerpt, input.start_offset ?? null,
576
+ input.end_offset ?? null, input.is_explicit ?? false, input.explicitness_score ?? 0,
577
+ input.outcome ?? null, input.created_at ?? now(),
578
+ ]);
579
+ return mapKnowledgeEvidenceRow(rows[0]);
580
+ },
581
+ async insertKnowledgeEvidenceBatch(inputs) {
582
+ return this.transaction(async () => {
583
+ const inserted = [];
584
+ for (const input of inputs) {
585
+ inserted.push(await this.insertKnowledgeEvidence(input));
586
+ }
587
+ return inserted;
588
+ });
589
+ },
590
+ async listKnowledgeEvidenceForKnowledge(knowledgeId) {
591
+ const { rows } = await pool.query('SELECT * FROM knowledge_evidence WHERE knowledge_memory_id = $1 ORDER BY created_at DESC, id DESC', [knowledgeId]);
592
+ return rows.map(mapKnowledgeEvidenceRow);
593
+ },
594
+ async listKnowledgeEvidenceForCandidate(candidateId) {
595
+ const { rows } = await pool.query('SELECT * FROM knowledge_evidence WHERE knowledge_candidate_id = $1 ORDER BY created_at DESC, id DESC', [candidateId]);
596
+ return rows.map(mapKnowledgeEvidenceRow);
597
+ },
598
+ async promoteKnowledgeCandidate(candidateId, input) {
599
+ const knowledge = await this.insertKnowledgeMemory(input);
600
+ await pool.query('UPDATE knowledge_candidate SET promoted_knowledge_id = $1, state = $2 WHERE id = $3', [knowledge.id, 'provisional', candidateId]);
601
+ return knowledge;
602
+ },
603
+ async getKnowledgeMemoryById(id) {
604
+ const { rows } = await pool.query('SELECT * FROM knowledge_memory WHERE id = $1', [id]);
605
+ return rows[0] ? mapKnowledgeMemory(rows[0]) : null;
606
+ },
607
+ async getActiveKnowledgeMemory(scope) {
608
+ const { rows } = await pool.query(`SELECT * FROM knowledge_memory WHERE ${scopeWhere()} AND superseded_by_id IS NULL AND retired_at IS NULL ORDER BY last_accessed_at DESC`, scopeParams(scope));
609
+ return rows.map(mapKnowledgeMemory);
610
+ },
611
+ async getActiveKnowledgeMemoryPaginated(scope, options) {
612
+ const resolved = resolvePaginationOptions(options);
613
+ const params = [...scopeParams(scope)];
614
+ let query = `SELECT * FROM knowledge_memory WHERE ${scopeWhere()} AND superseded_by_id IS NULL AND retired_at IS NULL`;
615
+ if (resolved.cursor > 0) {
616
+ params.push(resolved.cursor);
617
+ query += ` AND id > $${params.length}`;
618
+ }
619
+ query += ' ORDER BY id ASC';
620
+ params.push(resolved.limit + 1);
621
+ query += ` LIMIT $${params.length}`;
622
+ if (resolved.cursor === 0) {
623
+ params.push(resolved.offset);
624
+ query += ` OFFSET $${params.length}`;
625
+ }
626
+ const { rows } = await pool.query(query, params);
627
+ const items = rows.slice(0, resolved.limit).map(mapKnowledgeMemory);
628
+ return {
629
+ items,
630
+ hasMore: rows.length > resolved.limit,
631
+ nextCursor: rows.length > resolved.limit ? items[items.length - 1]?.id ?? null : null,
632
+ };
633
+ },
634
+ async getActiveKnowledgeCrossScope(scope, level) {
635
+ const params = wideScopeParams(scope, level);
636
+ const { rows } = await pool.query(`SELECT * FROM knowledge_memory WHERE ${wideScopeWhere(scope, level)} AND superseded_by_id IS NULL AND retired_at IS NULL ORDER BY last_accessed_at DESC`, params);
637
+ return rows.map(mapKnowledgeMemory);
638
+ },
639
+ async getKnowledgeSince(scope, level, since) {
640
+ const params = [...wideScopeParams(scope, level), since];
641
+ const { rows } = await pool.query(`SELECT * FROM knowledge_memory
642
+ WHERE ${wideScopeWhere(scope, level)}
643
+ AND created_at >= $${params.length}
644
+ AND superseded_by_id IS NULL
645
+ AND retired_at IS NULL
646
+ ORDER BY created_at ASC, id ASC`, params);
647
+ return rows.map(mapKnowledgeMemory);
648
+ },
649
+ async getKnowledgeByTimeRange(scope, range) {
650
+ const params = scopeParams(scope);
651
+ let query = `SELECT * FROM knowledge_memory WHERE ${scopeWhere()}`;
652
+ if (range.start_at != null) {
653
+ params.push(range.start_at);
654
+ query += ` AND created_at >= $${params.length}`;
655
+ }
656
+ if (range.end_at != null) {
657
+ params.push(range.end_at);
658
+ query += ` AND created_at <= $${params.length}`;
659
+ }
660
+ query += ' ORDER BY id DESC';
661
+ const { rows } = await pool.query(query, params);
662
+ return rows.map(mapKnowledgeMemory);
663
+ },
664
+ async searchKnowledge(scope, queryText, searchOptions) {
665
+ const params = scopeParams(scope);
666
+ const limit = searchOptions?.limit ?? 10;
667
+ const activeClause = searchOptions?.activeOnly ? ' AND superseded_by_id IS NULL AND retired_at IS NULL' : '';
668
+ params.push(queryText, limit);
669
+ const { rows } = await pool.query(`SELECT *, ts_rank(search_vector, plainto_tsquery('english', $5)) AS rank
670
+ FROM knowledge_memory
671
+ WHERE ${scopeWhere()} ${activeClause}
672
+ AND search_vector @@ plainto_tsquery('english', $5)
673
+ ORDER BY rank DESC
674
+ LIMIT $6`, params);
675
+ return rows
676
+ .map((row) => ({
677
+ item: mapKnowledgeMemory(row),
678
+ rank: Number(row.rank),
679
+ }))
680
+ .filter((result) => matchesKnowledgeSearchOptions(result.item, searchOptions))
681
+ .slice(0, limit);
682
+ },
683
+ async searchKnowledgeCrossScope(scope, level, queryText, searchOptions) {
684
+ const params = wideScopeParams(scope, level);
685
+ const limit = searchOptions?.limit ?? 10;
686
+ const activeClause = searchOptions?.activeOnly ? ' AND superseded_by_id IS NULL AND retired_at IS NULL' : '';
687
+ const paramOffset = params.length;
688
+ params.push(queryText, limit);
689
+ const { rows } = await pool.query(`SELECT *, ts_rank(search_vector, plainto_tsquery('english', $${paramOffset + 1})) AS rank
690
+ FROM knowledge_memory
691
+ WHERE ${wideScopeWhere(scope, level)} ${activeClause}
692
+ AND search_vector @@ plainto_tsquery('english', $${paramOffset + 1})
693
+ ORDER BY rank DESC
694
+ LIMIT $${paramOffset + 2}`, params);
695
+ return rows
696
+ .map((row) => ({
697
+ item: mapKnowledgeMemory(row),
698
+ rank: Number(row.rank),
699
+ }))
700
+ .filter((result) => matchesKnowledgeSearchOptions(result.item, searchOptions))
701
+ .slice(0, limit);
702
+ },
703
+ async insertKnowledgeMemoryAudit(input) {
704
+ const n = normalizeScope(input);
705
+ const { rows } = await pool.query(`INSERT INTO knowledge_memory_audit (tenant_id, system_id, workspace_id, collaboration_id, scope_id, working_memory_id, fact, fact_type, fact_subject, fact_attribute, fact_value, normalized_fact, slot_key, is_negated, confidence, confidence_score, verification_status, source_text, decision, detail, related_knowledge_id, created_knowledge_id, created_at)
706
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)
707
+ RETURNING *`, [n.tenant_id, n.system_id, n.workspace_id, n.collaboration_id, n.scope_id, input.working_memory_id,
708
+ input.fact, input.fact_type, input.fact_subject ?? null, input.fact_attribute ?? null,
709
+ input.fact_value ?? null, input.normalized_fact ?? null, input.slot_key ?? null,
710
+ input.is_negated ?? false, input.confidence, input.confidence_score ?? 0.5,
711
+ input.verification_status ?? 'unverified', input.source_text, input.decision,
712
+ input.detail ?? null, input.related_knowledge_id ?? null, input.created_knowledge_id ?? null, now()]);
713
+ return mapKnowledgeMemoryAudit(rows[0]);
714
+ },
715
+ async getRecentKnowledgeMemoryAudits(scope, limit = 20) {
716
+ const params = [...scopeParams(scope), limit];
717
+ const { rows } = await pool.query(`SELECT * FROM knowledge_memory_audit WHERE ${scopeWhere()} ORDER BY id DESC LIMIT $6`, params);
718
+ return rows.map(mapKnowledgeMemoryAudit);
719
+ },
720
+ async getKnowledgeMemoryAuditsForKnowledge(scope, knowledgeId, limit = 20) {
721
+ const params = [...scopeParams(scope), knowledgeId, knowledgeId, limit];
722
+ const { rows } = await pool.query(`SELECT * FROM knowledge_memory_audit
723
+ WHERE ${scopeWhere()}
724
+ AND (created_knowledge_id = $6 OR related_knowledge_id = $7)
725
+ ORDER BY id DESC
726
+ LIMIT $8`, params);
727
+ return rows.map(mapKnowledgeMemoryAudit);
728
+ },
729
+ async updateKnowledgeMemory(id, patch) {
730
+ const assignments = [];
731
+ const values = [];
732
+ const push = (column, value) => {
733
+ values.push(value);
734
+ assignments.push(`${column} = $${values.length}`);
735
+ };
736
+ if (patch.knowledge_state !== undefined)
737
+ push('knowledge_state', patch.knowledge_state);
738
+ if (patch.knowledge_class !== undefined)
739
+ push('knowledge_class', patch.knowledge_class);
740
+ if (patch.trust_score !== undefined)
741
+ push('trust_score', patch.trust_score);
742
+ if (patch.verification_status !== undefined)
743
+ push('verification_status', patch.verification_status);
744
+ if (patch.verification_notes !== undefined)
745
+ push('verification_notes', patch.verification_notes);
746
+ if (patch.last_verified_at !== undefined)
747
+ push('last_verified_at', patch.last_verified_at);
748
+ if (patch.next_reverification_at !== undefined) {
749
+ push('next_reverification_at', patch.next_reverification_at);
750
+ }
751
+ if (patch.last_confirmed_at !== undefined)
752
+ push('last_confirmed_at', patch.last_confirmed_at);
753
+ if (patch.confirmation_count !== undefined)
754
+ push('confirmation_count', patch.confirmation_count);
755
+ if (patch.disputed_at !== undefined)
756
+ push('disputed_at', patch.disputed_at);
757
+ if (patch.dispute_reason !== undefined)
758
+ push('dispute_reason', patch.dispute_reason);
759
+ if (patch.contradiction_score !== undefined)
760
+ push('contradiction_score', patch.contradiction_score);
761
+ if (patch.superseded_at !== undefined)
762
+ push('superseded_at', patch.superseded_at);
763
+ if (patch.successful_use_count !== undefined)
764
+ push('successful_use_count', patch.successful_use_count);
765
+ if (patch.failed_use_count !== undefined)
766
+ push('failed_use_count', patch.failed_use_count);
767
+ if (assignments.length === 0) {
768
+ return this.getKnowledgeMemoryById(id);
769
+ }
770
+ values.push(id);
771
+ const { rows } = await pool.query(`UPDATE knowledge_memory SET ${assignments.join(', ')} WHERE id = $${values.length} RETURNING *`, values);
772
+ return rows[0] ? mapKnowledgeMemory(rows[0]) : null;
773
+ },
774
+ async touchKnowledgeMemory(id) {
775
+ await pool.query(`UPDATE knowledge_memory SET access_count = access_count + 1, last_accessed_at = $2 WHERE id = $1`, [id, now()]);
776
+ },
777
+ async retireKnowledgeMemory(id, retiredAt) {
778
+ await pool.query(`UPDATE knowledge_memory SET retired_at = $2 WHERE id = $1`, [id, retiredAt ?? now()]);
779
+ },
780
+ async supersedeKnowledgeMemory(oldId, newId) {
781
+ await pool.query(`UPDATE knowledge_memory
782
+ SET superseded_by_id = $2, superseded_at = $3, knowledge_state = 'superseded', retired_at = $3
783
+ WHERE id = $1`, [oldId, newId, now()]);
784
+ },
785
+ async insertWorkItem(input) {
786
+ const n = normalizeScope(input);
787
+ const { rows } = await pool.query(`INSERT INTO work_items (tenant_id, system_id, workspace_id, collaboration_id, scope_id, session_id, title, kind, status, detail, created_at, updated_at)
788
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $11)
789
+ RETURNING *`, [n.tenant_id, n.system_id, n.workspace_id, n.collaboration_id, n.scope_id, input.session_id,
790
+ input.title, input.kind ?? 'objective', input.status ?? 'open', input.detail ?? null, now()]);
791
+ return mapWorkItem(rows[0]);
792
+ },
793
+ async getActiveWorkItems(scope) {
794
+ const { rows } = await pool.query(`SELECT * FROM work_items WHERE ${scopeWhere()} AND status != 'done' ORDER BY id DESC`, scopeParams(scope));
795
+ return rows.map(mapWorkItem);
796
+ },
797
+ async getWorkItemsByTimeRange(scope, range) {
798
+ const params = scopeParams(scope);
799
+ let query = `SELECT * FROM work_items WHERE ${scopeWhere()}`;
800
+ if (range.start_at != null) {
801
+ params.push(range.start_at);
802
+ query += ` AND created_at >= $${params.length}`;
803
+ }
804
+ if (range.end_at != null) {
805
+ params.push(range.end_at);
806
+ query += ` AND created_at <= $${params.length}`;
807
+ }
808
+ query += ' ORDER BY id DESC';
809
+ const { rows } = await pool.query(query, params);
810
+ return rows.map(mapWorkItem);
811
+ },
812
+ async updateWorkItemStatus(id, status) {
813
+ await pool.query(`UPDATE work_items SET status = $2, updated_at = $3 WHERE id = $1`, [id, status, now()]);
814
+ },
815
+ async deleteWorkItem(id) {
816
+ await pool.query('DELETE FROM work_items WHERE id = $1', [id]);
817
+ },
818
+ async upsertContextMonitor(input) {
819
+ const n = normalizeScope(input);
820
+ const { rows } = await pool.query(`INSERT INTO context_monitor (tenant_id, system_id, workspace_id, collaboration_id, scope_id, compaction_state, active_turn_count, active_token_estimate, compaction_score, last_compaction_at, updated_at)
821
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
822
+ ON CONFLICT (tenant_id, system_id, workspace_id, collaboration_id, scope_id)
823
+ DO UPDATE SET compaction_state = $6, active_turn_count = $7, active_token_estimate = $8, compaction_score = $9, last_compaction_at = COALESCE($10, context_monitor.last_compaction_at), updated_at = $11
824
+ RETURNING *`, [n.tenant_id, n.system_id, n.workspace_id, n.collaboration_id, n.scope_id,
825
+ input.compaction_state, input.active_turn_count, input.active_token_estimate,
826
+ input.compaction_score, input.last_compaction_at ?? null, now()]);
827
+ return mapContextMonitor(rows[0]);
828
+ },
829
+ async getContextMonitor(scope) {
830
+ const { rows } = await pool.query(`SELECT * FROM context_monitor WHERE ${scopeWhere()}`, scopeParams(scope));
831
+ return rows[0] ? mapContextMonitor(rows[0]) : null;
832
+ },
833
+ async insertCompactionLog(input) {
834
+ const n = normalizeScope(input);
835
+ const { rows } = await pool.query(`INSERT INTO compaction_log (tenant_id, system_id, workspace_id, collaboration_id, scope_id, session_id, trigger_type, turn_id_start, turn_id_end, turns_compacted, tokens_compacted_estimate, working_memory_id, active_turn_count_before, active_turn_count_after, duration_ms, model_call_made, created_at)
836
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
837
+ RETURNING *`, [n.tenant_id, n.system_id, n.workspace_id, n.collaboration_id, n.scope_id, input.session_id,
838
+ input.trigger_type, input.turn_id_start, input.turn_id_end,
839
+ input.turns_compacted, input.tokens_compacted_estimate, input.working_memory_id,
840
+ input.active_turn_count_before, input.active_turn_count_after,
841
+ input.duration_ms, input.model_call_made, now()]);
842
+ return mapCompactionLog(rows[0]);
843
+ },
844
+ async getCompactionLogById(id) {
845
+ const { rows } = await pool.query('SELECT * FROM compaction_log WHERE id = $1', [id]);
846
+ return rows[0] ? mapCompactionLog(rows[0]) : null;
847
+ },
848
+ async getRecentCompactionLogs(scope, limit = 10) {
849
+ const params = [...scopeParams(scope), limit];
850
+ const { rows } = await pool.query(`SELECT * FROM compaction_log WHERE ${scopeWhere()} ORDER BY id DESC LIMIT $6`, params);
851
+ return rows.map(mapCompactionLog);
852
+ },
853
+ async transaction(fn) {
854
+ const existing = txStorage.getStore();
855
+ if (existing) {
856
+ const savepoint = `memory_layer_sp_${existing.savepointCounter++}`;
857
+ await existing.client.query(`SAVEPOINT ${savepoint}`);
858
+ try {
859
+ const result = await fn();
860
+ await existing.client.query(`RELEASE SAVEPOINT ${savepoint}`);
861
+ return result;
862
+ }
863
+ catch (error) {
864
+ await existing.client.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);
865
+ await existing.client.query(`RELEASE SAVEPOINT ${savepoint}`);
866
+ throw error;
867
+ }
868
+ }
869
+ const client = await pool.connect();
870
+ const context = { client, savepointCounter: 0 };
871
+ await client.query('BEGIN');
872
+ try {
873
+ const result = await txStorage.run(context, fn);
874
+ await client.query('COMMIT');
875
+ return result;
876
+ }
877
+ catch (error) {
878
+ await client.query('ROLLBACK');
879
+ throw error;
880
+ }
881
+ finally {
882
+ client.release();
883
+ }
884
+ },
885
+ async close() {
886
+ await pool.end();
887
+ },
888
+ };
889
+ }
890
+ export function createPostgresEmbeddingAdapter(pool, options) {
891
+ return {
892
+ async storeEmbedding(knowledgeMemoryId, vector) {
893
+ const { rows } = await pool.query(`INSERT INTO knowledge_embeddings (
894
+ knowledge_memory_id,
895
+ tenant_id,
896
+ system_id,
897
+ workspace_id,
898
+ collaboration_id,
899
+ scope_id,
900
+ embedding,
901
+ created_at
902
+ )
903
+ SELECT
904
+ km.id,
905
+ km.tenant_id,
906
+ km.system_id,
907
+ km.workspace_id,
908
+ km.collaboration_id,
909
+ km.scope_id,
910
+ $2::vector,
911
+ $3
912
+ FROM knowledge_memory km
913
+ WHERE km.id = $1
914
+ ON CONFLICT (knowledge_memory_id) DO UPDATE SET
915
+ tenant_id = EXCLUDED.tenant_id,
916
+ system_id = EXCLUDED.system_id,
917
+ workspace_id = EXCLUDED.workspace_id,
918
+ collaboration_id = EXCLUDED.collaboration_id,
919
+ scope_id = EXCLUDED.scope_id,
920
+ embedding = EXCLUDED.embedding,
921
+ created_at = EXCLUDED.created_at
922
+ RETURNING knowledge_memory_id`, [knowledgeMemoryId, vectorToLiteral(vector), nowSeconds()]);
923
+ if (!rows[0]) {
924
+ throw new Error(`memory-layer: cannot store embedding for missing knowledge ${knowledgeMemoryId}`);
925
+ }
926
+ },
927
+ async getEmbedding(knowledgeMemoryId) {
928
+ const { rows } = await pool.query('SELECT embedding FROM knowledge_embeddings WHERE knowledge_memory_id = $1', [knowledgeMemoryId]);
929
+ return rows[0] ? parseVectorValue(rows[0].embedding) : null;
930
+ },
931
+ async findSimilar(scope, queryVector, options) {
932
+ const params = [...scopeParams(scope), vectorToLiteral(queryVector)];
933
+ const minSimilarity = options?.minSimilarity ?? 0;
934
+ const limit = options?.limit ?? 10;
935
+ params.push(minSimilarity, limit);
936
+ const vectorParam = params.length - 2;
937
+ const minSimilarityParam = params.length - 1;
938
+ const limitParam = params.length;
939
+ const { rows } = await pool.query(`SELECT ke.knowledge_memory_id, 1 - (ke.embedding <=> $${vectorParam}::vector) AS similarity
940
+ FROM knowledge_embeddings ke
941
+ JOIN knowledge_memory km ON km.id = ke.knowledge_memory_id
942
+ WHERE ${scopeWhere('ke')}
943
+ AND km.superseded_by_id IS NULL
944
+ AND km.retired_at IS NULL
945
+ AND 1 - (ke.embedding <=> $${vectorParam}::vector) >= $${minSimilarityParam}
946
+ ORDER BY ke.embedding <=> $${vectorParam}::vector ASC
947
+ LIMIT $${limitParam}`, params);
948
+ return rows.map((row) => ({
949
+ knowledgeMemoryId: Number(row.knowledge_memory_id),
950
+ similarity: Number(row.similarity),
951
+ }));
952
+ },
953
+ async findSimilarCrossScope(scope, level, queryVector, options) {
954
+ const params = [...wideScopeParams(scope, level), vectorToLiteral(queryVector)];
955
+ const minSimilarity = options?.minSimilarity ?? 0;
956
+ const limit = options?.limit ?? 10;
957
+ params.push(minSimilarity, limit);
958
+ const vectorParam = params.length - 2;
959
+ const minSimilarityParam = params.length - 1;
960
+ const limitParam = params.length;
961
+ const { rows } = await pool.query(`SELECT ke.knowledge_memory_id, 1 - (ke.embedding <=> $${vectorParam}::vector) AS similarity
962
+ FROM knowledge_embeddings ke
963
+ JOIN knowledge_memory km ON km.id = ke.knowledge_memory_id
964
+ WHERE ${wideScopeWhere(scope, level, 'ke')}
965
+ AND km.superseded_by_id IS NULL
966
+ AND km.retired_at IS NULL
967
+ AND 1 - (ke.embedding <=> $${vectorParam}::vector) >= $${minSimilarityParam}
968
+ ORDER BY ke.embedding <=> $${vectorParam}::vector ASC
969
+ LIMIT $${limitParam}`, params);
970
+ return rows.map((row) => ({
971
+ knowledgeMemoryId: Number(row.knowledge_memory_id),
972
+ similarity: Number(row.similarity),
973
+ }));
974
+ },
975
+ async deleteEmbedding(knowledgeMemoryId) {
976
+ await pool.query('DELETE FROM knowledge_embeddings WHERE knowledge_memory_id = $1', [
977
+ knowledgeMemoryId,
978
+ ]);
979
+ },
980
+ };
981
+ }
982
+ //# sourceMappingURL=index.js.map