@kernl-sdk/pg 0.1.11 → 0.1.12

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 (153) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-check-types.log +36 -0
  3. package/CHANGELOG.md +32 -0
  4. package/README.md +124 -0
  5. package/dist/__tests__/integration.test.js +2 -2
  6. package/dist/__tests__/memory-integration.test.d.ts +2 -0
  7. package/dist/__tests__/memory-integration.test.d.ts.map +1 -0
  8. package/dist/__tests__/memory-integration.test.js +287 -0
  9. package/dist/__tests__/memory.test.d.ts +2 -0
  10. package/dist/__tests__/memory.test.d.ts.map +1 -0
  11. package/dist/__tests__/memory.test.js +357 -0
  12. package/dist/index.d.ts +5 -3
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +5 -3
  15. package/dist/memory/sql.d.ts +30 -0
  16. package/dist/memory/sql.d.ts.map +1 -0
  17. package/dist/memory/sql.js +100 -0
  18. package/dist/memory/store.d.ts +41 -0
  19. package/dist/memory/store.d.ts.map +1 -0
  20. package/dist/memory/store.js +114 -0
  21. package/dist/migrations.d.ts +1 -1
  22. package/dist/migrations.d.ts.map +1 -1
  23. package/dist/migrations.js +9 -3
  24. package/dist/pgvector/__tests__/handle.test.d.ts +2 -0
  25. package/dist/pgvector/__tests__/handle.test.d.ts.map +1 -0
  26. package/dist/pgvector/__tests__/handle.test.js +277 -0
  27. package/dist/pgvector/__tests__/hit.test.d.ts +2 -0
  28. package/dist/pgvector/__tests__/hit.test.d.ts.map +1 -0
  29. package/dist/pgvector/__tests__/hit.test.js +134 -0
  30. package/dist/pgvector/__tests__/integration/document.integration.test.d.ts +7 -0
  31. package/dist/pgvector/__tests__/integration/document.integration.test.d.ts.map +1 -0
  32. package/dist/pgvector/__tests__/integration/document.integration.test.js +587 -0
  33. package/dist/pgvector/__tests__/integration/edge.integration.test.d.ts +8 -0
  34. package/dist/pgvector/__tests__/integration/edge.integration.test.d.ts.map +1 -0
  35. package/dist/pgvector/__tests__/integration/edge.integration.test.js +663 -0
  36. package/dist/pgvector/__tests__/integration/filters.integration.test.d.ts +8 -0
  37. package/dist/pgvector/__tests__/integration/filters.integration.test.d.ts.map +1 -0
  38. package/dist/pgvector/__tests__/integration/filters.integration.test.js +609 -0
  39. package/dist/pgvector/__tests__/integration/lifecycle.integration.test.d.ts +8 -0
  40. package/dist/pgvector/__tests__/integration/lifecycle.integration.test.d.ts.map +1 -0
  41. package/dist/pgvector/__tests__/integration/lifecycle.integration.test.js +449 -0
  42. package/dist/pgvector/__tests__/integration/query.integration.test.d.ts +8 -0
  43. package/dist/pgvector/__tests__/integration/query.integration.test.d.ts.map +1 -0
  44. package/dist/pgvector/__tests__/integration/query.integration.test.js +544 -0
  45. package/dist/pgvector/__tests__/search.test.d.ts +2 -0
  46. package/dist/pgvector/__tests__/search.test.d.ts.map +1 -0
  47. package/dist/pgvector/__tests__/search.test.js +279 -0
  48. package/dist/pgvector/handle.d.ts +60 -0
  49. package/dist/pgvector/handle.d.ts.map +1 -0
  50. package/dist/pgvector/handle.js +213 -0
  51. package/dist/pgvector/hit.d.ts +10 -0
  52. package/dist/pgvector/hit.d.ts.map +1 -0
  53. package/dist/pgvector/hit.js +44 -0
  54. package/dist/pgvector/index.d.ts +7 -0
  55. package/dist/pgvector/index.d.ts.map +1 -0
  56. package/dist/pgvector/index.js +5 -0
  57. package/dist/pgvector/search.d.ts +60 -0
  58. package/dist/pgvector/search.d.ts.map +1 -0
  59. package/dist/pgvector/search.js +227 -0
  60. package/dist/pgvector/sql/__tests__/limit.test.d.ts +2 -0
  61. package/dist/pgvector/sql/__tests__/limit.test.d.ts.map +1 -0
  62. package/dist/pgvector/sql/__tests__/limit.test.js +161 -0
  63. package/dist/pgvector/sql/__tests__/order.test.d.ts +2 -0
  64. package/dist/pgvector/sql/__tests__/order.test.d.ts.map +1 -0
  65. package/dist/pgvector/sql/__tests__/order.test.js +218 -0
  66. package/dist/pgvector/sql/__tests__/query.test.d.ts +2 -0
  67. package/dist/pgvector/sql/__tests__/query.test.d.ts.map +1 -0
  68. package/dist/pgvector/sql/__tests__/query.test.js +392 -0
  69. package/dist/pgvector/sql/__tests__/select.test.d.ts +2 -0
  70. package/dist/pgvector/sql/__tests__/select.test.d.ts.map +1 -0
  71. package/dist/pgvector/sql/__tests__/select.test.js +293 -0
  72. package/dist/pgvector/sql/__tests__/where.test.d.ts +2 -0
  73. package/dist/pgvector/sql/__tests__/where.test.d.ts.map +1 -0
  74. package/dist/pgvector/sql/__tests__/where.test.js +488 -0
  75. package/dist/pgvector/sql/index.d.ts +7 -0
  76. package/dist/pgvector/sql/index.d.ts.map +1 -0
  77. package/dist/pgvector/sql/index.js +6 -0
  78. package/dist/pgvector/sql/limit.d.ts +8 -0
  79. package/dist/pgvector/sql/limit.d.ts.map +1 -0
  80. package/dist/pgvector/sql/limit.js +20 -0
  81. package/dist/pgvector/sql/order.d.ts +9 -0
  82. package/dist/pgvector/sql/order.d.ts.map +1 -0
  83. package/dist/pgvector/sql/order.js +47 -0
  84. package/dist/pgvector/sql/query.d.ts +46 -0
  85. package/dist/pgvector/sql/query.d.ts.map +1 -0
  86. package/dist/pgvector/sql/query.js +54 -0
  87. package/dist/pgvector/sql/schema.d.ts +16 -0
  88. package/dist/pgvector/sql/schema.d.ts.map +1 -0
  89. package/dist/pgvector/sql/schema.js +47 -0
  90. package/dist/pgvector/sql/select.d.ts +11 -0
  91. package/dist/pgvector/sql/select.d.ts.map +1 -0
  92. package/dist/pgvector/sql/select.js +87 -0
  93. package/dist/pgvector/sql/where.d.ts +8 -0
  94. package/dist/pgvector/sql/where.d.ts.map +1 -0
  95. package/dist/pgvector/sql/where.js +137 -0
  96. package/dist/pgvector/types.d.ts +20 -0
  97. package/dist/pgvector/types.d.ts.map +1 -0
  98. package/dist/pgvector/types.js +1 -0
  99. package/dist/pgvector/utils.d.ts +18 -0
  100. package/dist/pgvector/utils.d.ts.map +1 -0
  101. package/dist/pgvector/utils.js +22 -0
  102. package/dist/postgres.d.ts +19 -26
  103. package/dist/postgres.d.ts.map +1 -1
  104. package/dist/postgres.js +15 -27
  105. package/dist/storage.d.ts +48 -0
  106. package/dist/storage.d.ts.map +1 -1
  107. package/dist/storage.js +32 -9
  108. package/dist/thread/sql.d.ts +38 -0
  109. package/dist/thread/sql.d.ts.map +1 -0
  110. package/dist/thread/sql.js +112 -0
  111. package/dist/thread/store.d.ts +2 -2
  112. package/dist/thread/store.d.ts.map +1 -1
  113. package/dist/thread/store.js +32 -102
  114. package/package.json +7 -4
  115. package/src/__tests__/integration.test.ts +15 -17
  116. package/src/__tests__/memory-integration.test.ts +355 -0
  117. package/src/__tests__/memory.test.ts +428 -0
  118. package/src/index.ts +19 -3
  119. package/src/memory/sql.ts +141 -0
  120. package/src/memory/store.ts +166 -0
  121. package/src/migrations.ts +13 -3
  122. package/src/pgvector/README.md +50 -0
  123. package/src/pgvector/__tests__/handle.test.ts +335 -0
  124. package/src/pgvector/__tests__/hit.test.ts +165 -0
  125. package/src/pgvector/__tests__/integration/document.integration.test.ts +717 -0
  126. package/src/pgvector/__tests__/integration/edge.integration.test.ts +835 -0
  127. package/src/pgvector/__tests__/integration/filters.integration.test.ts +721 -0
  128. package/src/pgvector/__tests__/integration/lifecycle.integration.test.ts +570 -0
  129. package/src/pgvector/__tests__/integration/query.integration.test.ts +667 -0
  130. package/src/pgvector/__tests__/search.test.ts +366 -0
  131. package/src/pgvector/handle.ts +285 -0
  132. package/src/pgvector/hit.ts +56 -0
  133. package/src/pgvector/index.ts +7 -0
  134. package/src/pgvector/search.ts +330 -0
  135. package/src/pgvector/sql/__tests__/limit.test.ts +180 -0
  136. package/src/pgvector/sql/__tests__/order.test.ts +248 -0
  137. package/src/pgvector/sql/__tests__/query.test.ts +548 -0
  138. package/src/pgvector/sql/__tests__/select.test.ts +367 -0
  139. package/src/pgvector/sql/__tests__/where.test.ts +554 -0
  140. package/src/pgvector/sql/index.ts +14 -0
  141. package/src/pgvector/sql/limit.ts +29 -0
  142. package/src/pgvector/sql/order.ts +55 -0
  143. package/src/pgvector/sql/query.ts +112 -0
  144. package/src/pgvector/sql/schema.ts +61 -0
  145. package/src/pgvector/sql/select.ts +100 -0
  146. package/src/pgvector/sql/where.ts +152 -0
  147. package/src/pgvector/types.ts +21 -0
  148. package/src/pgvector/utils.ts +24 -0
  149. package/src/postgres.ts +31 -33
  150. package/src/storage.ts +77 -9
  151. package/src/thread/sql.ts +159 -0
  152. package/src/thread/store.ts +40 -127
  153. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,357 @@
1
+ import { describe, it, expect, beforeAll, afterAll, beforeEach } from "vitest";
2
+ import { Pool } from "pg";
3
+ import { PGStorage } from "../storage.js";
4
+ const TEST_DB_URL = process.env.KERNL_PG_TEST_URL;
5
+ describe.sequential("PGMemoryStore", () => {
6
+ if (!TEST_DB_URL) {
7
+ it.skip("requires KERNL_PG_TEST_URL environment variable", () => { });
8
+ return;
9
+ }
10
+ let pool;
11
+ let storage;
12
+ beforeAll(async () => {
13
+ pool = new Pool({ connectionString: TEST_DB_URL });
14
+ storage = new PGStorage({ pool });
15
+ await pool.query('DROP SCHEMA IF EXISTS "kernl" CASCADE');
16
+ await storage.init();
17
+ });
18
+ afterAll(async () => {
19
+ await storage.close();
20
+ });
21
+ beforeEach(async () => {
22
+ await pool.query('TRUNCATE "kernl"."memories" CASCADE');
23
+ });
24
+ describe("create", () => {
25
+ it("should create a memory with text content", async () => {
26
+ const memory = await storage.memories.create({
27
+ id: "mem-1",
28
+ scope: { namespace: "test", entityId: "user-1" },
29
+ kind: "semantic",
30
+ collection: "facts",
31
+ content: { text: "The user likes coffee" },
32
+ });
33
+ expect(memory.id).toBe("mem-1");
34
+ expect(memory.scope.namespace).toBe("test");
35
+ expect(memory.scope.entityId).toBe("user-1");
36
+ expect(memory.collection).toBe("facts");
37
+ expect(memory.content).toEqual({ text: "The user likes coffee" });
38
+ expect(memory.wmem).toBe(false);
39
+ expect(memory.smem.expiresAt).toBeNull();
40
+ expect(memory.createdAt).toBeTypeOf("number");
41
+ expect(memory.updatedAt).toBeTypeOf("number");
42
+ });
43
+ it("should create a memory with object content", async () => {
44
+ const memory = await storage.memories.create({
45
+ id: "mem-2",
46
+ scope: { namespace: "test" },
47
+ kind: "semantic",
48
+ collection: "preferences",
49
+ content: {
50
+ object: { theme: "dark", language: "en" },
51
+ },
52
+ metadata: { source: "settings" },
53
+ });
54
+ expect(memory.content).toEqual({
55
+ object: { theme: "dark", language: "en" },
56
+ });
57
+ expect(memory.metadata).toEqual({ source: "settings" });
58
+ });
59
+ it("should create a working memory (wmem=true)", async () => {
60
+ const memory = await storage.memories.create({
61
+ id: "mem-3",
62
+ scope: { namespace: "test" },
63
+ kind: "episodic",
64
+ collection: "working",
65
+ content: { text: "Current task context" },
66
+ wmem: true,
67
+ });
68
+ expect(memory.wmem).toBe(true);
69
+ });
70
+ it("should create a short-term memory with expiration", async () => {
71
+ const expiresAt = Date.now() + 3600000; // 1 hour from now
72
+ const memory = await storage.memories.create({
73
+ id: "mem-4",
74
+ scope: { namespace: "test" },
75
+ kind: "episodic",
76
+ collection: "short-term",
77
+ content: { text: "Temporary information" },
78
+ smem: { expiresAt },
79
+ });
80
+ expect(memory.smem.expiresAt).toBe(expiresAt);
81
+ });
82
+ });
83
+ describe("get", () => {
84
+ it("should get a memory by id", async () => {
85
+ await storage.memories.create({
86
+ id: "mem-get-1",
87
+ scope: { namespace: "test" },
88
+ kind: "semantic",
89
+ collection: "facts",
90
+ content: { text: "Test memory" },
91
+ });
92
+ const memory = await storage.memories.get("mem-get-1");
93
+ expect(memory).not.toBeNull();
94
+ expect(memory.id).toBe("mem-get-1");
95
+ expect(memory.content).toEqual({ text: "Test memory" });
96
+ });
97
+ it("should return null for non-existent memory", async () => {
98
+ const memory = await storage.memories.get("non-existent");
99
+ expect(memory).toBeNull();
100
+ });
101
+ });
102
+ describe("list", () => {
103
+ beforeEach(async () => {
104
+ // seed test data
105
+ await storage.memories.create({
106
+ id: "list-1",
107
+ scope: { namespace: "ns1", entityId: "user-1" },
108
+ kind: "semantic",
109
+ collection: "facts",
110
+ content: { text: "Fact 1" },
111
+ wmem: true,
112
+ timestamp: 1000,
113
+ });
114
+ await storage.memories.create({
115
+ id: "list-2",
116
+ scope: { namespace: "ns1", entityId: "user-1" },
117
+ kind: "semantic",
118
+ collection: "preferences",
119
+ content: { text: "Pref 1" },
120
+ wmem: false,
121
+ timestamp: 2000,
122
+ });
123
+ await storage.memories.create({
124
+ id: "list-3",
125
+ scope: { namespace: "ns2", entityId: "user-2" },
126
+ kind: "semantic",
127
+ collection: "facts",
128
+ content: { text: "Fact 2" },
129
+ wmem: false,
130
+ timestamp: 3000,
131
+ });
132
+ });
133
+ it("should list all memories", async () => {
134
+ const memories = await storage.memories.list();
135
+ expect(memories).toHaveLength(3);
136
+ });
137
+ it("should filter by namespace", async () => {
138
+ const memories = await storage.memories.list({
139
+ filter: { scope: { namespace: "ns1" } },
140
+ });
141
+ expect(memories).toHaveLength(2);
142
+ expect(memories.every((m) => m.scope.namespace === "ns1")).toBe(true);
143
+ });
144
+ it("should filter by entityId", async () => {
145
+ const memories = await storage.memories.list({
146
+ filter: { scope: { entityId: "user-1" } },
147
+ });
148
+ expect(memories).toHaveLength(2);
149
+ expect(memories.every((m) => m.scope.entityId === "user-1")).toBe(true);
150
+ });
151
+ it("should filter by collection", async () => {
152
+ const memories = await storage.memories.list({
153
+ filter: { collections: ["facts"] },
154
+ });
155
+ expect(memories).toHaveLength(2);
156
+ expect(memories.every((m) => m.collection === "facts")).toBe(true);
157
+ });
158
+ it("should filter by wmem", async () => {
159
+ const memories = await storage.memories.list({
160
+ filter: { wmem: true },
161
+ });
162
+ expect(memories).toHaveLength(1);
163
+ expect(memories[0].id).toBe("list-1");
164
+ });
165
+ it("should order by timestamp desc (default)", async () => {
166
+ const memories = await storage.memories.list();
167
+ expect(memories[0].id).toBe("list-3");
168
+ expect(memories[1].id).toBe("list-2");
169
+ expect(memories[2].id).toBe("list-1");
170
+ });
171
+ it("should order by timestamp asc", async () => {
172
+ const memories = await storage.memories.list({ order: "asc" });
173
+ expect(memories[0].id).toBe("list-1");
174
+ expect(memories[1].id).toBe("list-2");
175
+ expect(memories[2].id).toBe("list-3");
176
+ });
177
+ it("should apply limit", async () => {
178
+ const memories = await storage.memories.list({ limit: 2 });
179
+ expect(memories).toHaveLength(2);
180
+ });
181
+ it("should apply offset", async () => {
182
+ const memories = await storage.memories.list({ limit: 2, offset: 1 });
183
+ expect(memories).toHaveLength(2);
184
+ expect(memories[0].id).toBe("list-2");
185
+ });
186
+ it("should filter by timestamp range", async () => {
187
+ const memories = await storage.memories.list({
188
+ filter: { after: 1500, before: 2500 },
189
+ });
190
+ expect(memories).toHaveLength(1);
191
+ expect(memories[0].id).toBe("list-2");
192
+ });
193
+ });
194
+ describe("update", () => {
195
+ it("should update content", async () => {
196
+ await storage.memories.create({
197
+ id: "update-1",
198
+ scope: { namespace: "test" },
199
+ kind: "semantic",
200
+ collection: "facts",
201
+ content: { text: "Original" },
202
+ });
203
+ // Small delay to ensure timestamp changes
204
+ await new Promise((resolve) => setTimeout(resolve, 10));
205
+ const updated = await storage.memories.update("update-1", {
206
+ id: "update-1",
207
+ content: { text: "Updated" },
208
+ });
209
+ expect(updated.content).toEqual({ text: "Updated" });
210
+ expect(updated.updatedAt).toBeGreaterThan(updated.createdAt);
211
+ });
212
+ it("should update wmem flag", async () => {
213
+ await storage.memories.create({
214
+ id: "update-2",
215
+ scope: { namespace: "test" },
216
+ kind: "semantic",
217
+ collection: "facts",
218
+ content: { text: "Test" },
219
+ wmem: false,
220
+ });
221
+ const updated = await storage.memories.update("update-2", {
222
+ id: "update-2",
223
+ wmem: true,
224
+ });
225
+ expect(updated.wmem).toBe(true);
226
+ });
227
+ it("should update smem expiration", async () => {
228
+ await storage.memories.create({
229
+ id: "update-3",
230
+ scope: { namespace: "test" },
231
+ kind: "episodic",
232
+ collection: "facts",
233
+ content: { text: "Test" },
234
+ });
235
+ const expiresAt = Date.now() + 7200000;
236
+ const updated = await storage.memories.update("update-3", {
237
+ id: "update-3",
238
+ smem: { expiresAt },
239
+ });
240
+ expect(updated.smem.expiresAt).toBe(expiresAt);
241
+ });
242
+ it("should update metadata", async () => {
243
+ await storage.memories.create({
244
+ id: "update-4",
245
+ scope: { namespace: "test" },
246
+ kind: "semantic",
247
+ collection: "facts",
248
+ content: { text: "Test" },
249
+ });
250
+ const updated = await storage.memories.update("update-4", {
251
+ id: "update-4",
252
+ metadata: { updated: true, count: 1 },
253
+ });
254
+ expect(updated.metadata).toEqual({ updated: true, count: 1 });
255
+ });
256
+ it("should throw for non-existent memory", async () => {
257
+ await expect(storage.memories.update("non-existent", { id: "non-existent", wmem: true })).rejects.toThrow("memory not found");
258
+ });
259
+ });
260
+ describe("delete", () => {
261
+ it("should delete a memory", async () => {
262
+ await storage.memories.create({
263
+ id: "delete-1",
264
+ scope: { namespace: "test" },
265
+ kind: "semantic",
266
+ collection: "facts",
267
+ content: { text: "To be deleted" },
268
+ });
269
+ await storage.memories.delete("delete-1");
270
+ const memory = await storage.memories.get("delete-1");
271
+ expect(memory).toBeNull();
272
+ });
273
+ it("should not throw for non-existent memory", async () => {
274
+ await expect(storage.memories.delete("non-existent")).resolves.not.toThrow();
275
+ });
276
+ });
277
+ describe("mdelete", () => {
278
+ it("should delete multiple memories", async () => {
279
+ await storage.memories.create({
280
+ id: "mdelete-1",
281
+ scope: { namespace: "test" },
282
+ kind: "semantic",
283
+ collection: "facts",
284
+ content: { text: "Memory 1" },
285
+ });
286
+ await storage.memories.create({
287
+ id: "mdelete-2",
288
+ scope: { namespace: "test" },
289
+ kind: "semantic",
290
+ collection: "facts",
291
+ content: { text: "Memory 2" },
292
+ });
293
+ await storage.memories.create({
294
+ id: "mdelete-3",
295
+ scope: { namespace: "test" },
296
+ kind: "semantic",
297
+ collection: "facts",
298
+ content: { text: "Memory 3" },
299
+ });
300
+ await storage.memories.mdelete(["mdelete-1", "mdelete-2"]);
301
+ const remaining = await storage.memories.list();
302
+ expect(remaining).toHaveLength(1);
303
+ expect(remaining[0].id).toBe("mdelete-3");
304
+ });
305
+ it("should handle empty array", async () => {
306
+ await expect(storage.memories.mdelete([])).resolves.not.toThrow();
307
+ });
308
+ });
309
+ describe("smem filter", () => {
310
+ beforeEach(async () => {
311
+ const now = Date.now();
312
+ // active short-term memory
313
+ await storage.memories.create({
314
+ id: "smem-active",
315
+ scope: { namespace: "test" },
316
+ kind: "episodic",
317
+ collection: "short-term",
318
+ content: { text: "Active" },
319
+ smem: { expiresAt: now + 3600000 }, // expires in 1 hour
320
+ });
321
+ // expired short-term memory
322
+ await storage.memories.create({
323
+ id: "smem-expired",
324
+ scope: { namespace: "test" },
325
+ kind: "episodic",
326
+ collection: "short-term",
327
+ content: { text: "Expired" },
328
+ smem: { expiresAt: now - 3600000 }, // expired 1 hour ago
329
+ });
330
+ // long-term memory (no expiration)
331
+ await storage.memories.create({
332
+ id: "lmem",
333
+ scope: { namespace: "test" },
334
+ kind: "semantic",
335
+ collection: "long-term",
336
+ content: { text: "Long term" },
337
+ smem: { expiresAt: null },
338
+ });
339
+ });
340
+ it("should filter for active short-term memories (smem=true)", async () => {
341
+ const memories = await storage.memories.list({
342
+ filter: { smem: true },
343
+ });
344
+ expect(memories).toHaveLength(1);
345
+ expect(memories[0].id).toBe("smem-active");
346
+ });
347
+ it("should filter for non-short-term memories (smem=false)", async () => {
348
+ const memories = await storage.memories.list({
349
+ filter: { smem: false },
350
+ });
351
+ expect(memories).toHaveLength(2);
352
+ const ids = memories.map((m) => m.id);
353
+ expect(ids).toContain("smem-expired");
354
+ expect(ids).toContain("lmem");
355
+ });
356
+ });
357
+ });
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  /**
2
2
  * @kernl/pg - PostgreSQL storage adapter for Kernl
3
3
  */
4
- export { PGStorage, type PGStorageConfig } from "./storage.js";
5
- export { postgres, type PostgresConfig } from "./postgres.js";
6
- export { migrations, REQUIRED_SCHEMA_VERSION } from "./migrations.js";
4
+ export { PGStorage, type PGStorageConfig, type PGVectorConfig, type VectorSimilarity, type ResolvedVectorConfig, DEFAULT_VECTOR_CONFIG, resolveVectorConfig, } from "./storage.js";
5
+ export { PGMemoryStore } from "./memory/store.js";
6
+ export { postgres, pgvector, type PostgresConfig } from "./postgres.js";
7
+ export { MIGRATIONS, REQUIRED_SCHEMA_VERSION } from "./migrations.js";
8
+ export { PGSearchIndex, PGIndexHandle, type PGSearchIndexConfig, type PGIndexConfig, type PGFieldBinding, } from "./pgvector/index.js";
7
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,SAAS,EACT,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACnE,OAAO,EACL,aAAa,EACb,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * @kernl/pg - PostgreSQL storage adapter for Kernl
3
3
  */
4
- export { PGStorage } from "./storage.js";
5
- export { postgres } from "./postgres.js";
6
- export { migrations, REQUIRED_SCHEMA_VERSION } from "./migrations.js";
4
+ export { PGStorage, DEFAULT_VECTOR_CONFIG, resolveVectorConfig, } from "./storage.js";
5
+ export { PGMemoryStore } from "./memory/store.js";
6
+ export { postgres, pgvector } from "./postgres.js";
7
+ export { MIGRATIONS, REQUIRED_SCHEMA_VERSION } from "./migrations.js";
8
+ export { PGSearchIndex, PGIndexHandle, } from "./pgvector/index.js";
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Memory SQL conversion codecs.
3
+ *
4
+ * TODO: generalize object -> SQL conversion into a shared utility
5
+ */
6
+ import type { Codec } from "@kernl-sdk/shared/lib";
7
+ import type { MemoryFilter, MemoryRecordUpdate } from "kernl";
8
+ export interface SQLClause {
9
+ sql: string;
10
+ params: unknown[];
11
+ }
12
+ export interface WhereInput {
13
+ filter?: MemoryFilter;
14
+ startIdx: number;
15
+ }
16
+ export declare const SQL_WHERE: Codec<WhereInput, SQLClause>;
17
+ type OrderDirection = "asc" | "desc";
18
+ export interface OrderInput {
19
+ order?: OrderDirection;
20
+ defaultColumn?: string;
21
+ defaultDirection?: OrderDirection;
22
+ }
23
+ export declare const ORDER: Codec<OrderInput, string>;
24
+ export interface PatchInput {
25
+ patch: MemoryRecordUpdate;
26
+ startIdx: number;
27
+ }
28
+ export declare const SQL_UPDATE: Codec<PatchInput, SQLClause>;
29
+ export {};
30
+ //# sourceMappingURL=sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/memory/sql.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,UAAU,EAAE,SAAS,CA2DlD,CAAC;AAEF,KAAK,cAAc,GAAG,KAAK,GAAG,MAAM,CAAC;AAErC,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,cAAc,CAAC;CACnC;AAED,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,MAAM,CAS3C,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,SAAS,CAoCnD,CAAC"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Memory SQL conversion codecs.
3
+ *
4
+ * TODO: generalize object -> SQL conversion into a shared utility
5
+ */
6
+ export const SQL_WHERE = {
7
+ encode({ filter, startIdx }) {
8
+ if (!filter) {
9
+ return { sql: "", params: [] };
10
+ }
11
+ const conditions = [];
12
+ const params = [];
13
+ let idx = startIdx;
14
+ if (filter.scope?.namespace !== undefined) {
15
+ conditions.push(`namespace = $${idx++}`);
16
+ params.push(filter.scope.namespace);
17
+ }
18
+ if (filter.scope?.entityId !== undefined) {
19
+ conditions.push(`entity_id = $${idx++}`);
20
+ params.push(filter.scope.entityId);
21
+ }
22
+ if (filter.scope?.agentId !== undefined) {
23
+ conditions.push(`agent_id = $${idx++}`);
24
+ params.push(filter.scope.agentId);
25
+ }
26
+ if (filter.collections && filter.collections.length > 0) {
27
+ conditions.push(`collection = ANY($${idx++})`);
28
+ params.push(filter.collections);
29
+ }
30
+ if (filter.wmem !== undefined) {
31
+ conditions.push(`wmem = $${idx++}`);
32
+ params.push(filter.wmem);
33
+ }
34
+ if (filter.smem === true) {
35
+ conditions.push(`(smem_expires_at IS NOT NULL AND smem_expires_at > $${idx++})`);
36
+ params.push(Date.now());
37
+ }
38
+ else if (filter.smem === false) {
39
+ conditions.push(`(smem_expires_at IS NULL OR smem_expires_at <= $${idx++})`);
40
+ params.push(Date.now());
41
+ }
42
+ if (filter.after !== undefined) {
43
+ conditions.push(`timestamp > $${idx++}`);
44
+ params.push(filter.after);
45
+ }
46
+ if (filter.before !== undefined) {
47
+ conditions.push(`timestamp < $${idx++}`);
48
+ params.push(filter.before);
49
+ }
50
+ return {
51
+ sql: conditions.length > 0 ? conditions.join(" AND ") : "",
52
+ params,
53
+ };
54
+ },
55
+ decode() {
56
+ throw new Error("WHERE.decode not implemented");
57
+ },
58
+ };
59
+ export const ORDER = {
60
+ encode({ order, defaultColumn = "timestamp", defaultDirection = "desc" }) {
61
+ const dir = (order ?? defaultDirection).toUpperCase();
62
+ return `${defaultColumn} ${dir}`;
63
+ },
64
+ decode() {
65
+ throw new Error("ORDER.decode not implemented");
66
+ },
67
+ };
68
+ export const SQL_UPDATE = {
69
+ encode({ patch, startIdx }) {
70
+ const sets = [];
71
+ const params = [];
72
+ let idx = startIdx;
73
+ if (patch.content !== undefined) {
74
+ sets.push(`content = $${idx++}::jsonb`);
75
+ params.push(JSON.stringify(patch.content));
76
+ }
77
+ if (patch.wmem !== undefined) {
78
+ sets.push(`wmem = $${idx++}`);
79
+ params.push(patch.wmem);
80
+ }
81
+ if (patch.smem !== undefined) {
82
+ sets.push(`smem_expires_at = $${idx++}`);
83
+ params.push(patch.smem.expiresAt);
84
+ }
85
+ if (patch.metadata !== undefined) {
86
+ sets.push(`metadata = $${idx++}::jsonb`);
87
+ params.push(patch.metadata ? JSON.stringify(patch.metadata) : null);
88
+ }
89
+ // always update updated_at
90
+ sets.push(`updated_at = $${idx++}`);
91
+ params.push(Date.now());
92
+ return {
93
+ sql: sets.join(", "),
94
+ params,
95
+ };
96
+ },
97
+ decode() {
98
+ throw new Error("PATCH.decode not implemented");
99
+ },
100
+ };
@@ -0,0 +1,41 @@
1
+ /**
2
+ * PostgreSQL Memory store implementation.
3
+ */
4
+ import type { Pool, PoolClient } from "pg";
5
+ import type { MemoryStore, MemoryRecord, NewMemory, MemoryRecordUpdate, MemoryListOptions } from "kernl";
6
+ /**
7
+ * PostgreSQL memory store implementation.
8
+ *
9
+ * All async methods call `ensureInit()` before database operations
10
+ * to ensure schema/tables exist.
11
+ */
12
+ export declare class PGMemoryStore implements MemoryStore {
13
+ private db;
14
+ private ensureInit;
15
+ constructor(db: Pool | PoolClient, ensureInit: () => Promise<void>);
16
+ /**
17
+ * Get a memory by ID.
18
+ */
19
+ get(id: string): Promise<MemoryRecord | null>;
20
+ /**
21
+ * List memories matching optional filter criteria.
22
+ */
23
+ list(options?: MemoryListOptions): Promise<MemoryRecord[]>;
24
+ /**
25
+ * Create a new memory record.
26
+ */
27
+ create(memory: NewMemory): Promise<MemoryRecord>;
28
+ /**
29
+ * Update a memory record.
30
+ */
31
+ update(id: string, patch: MemoryRecordUpdate): Promise<MemoryRecord>;
32
+ /**
33
+ * Delete a memory by ID.
34
+ */
35
+ delete(id: string): Promise<void>;
36
+ /**
37
+ * Delete multiple memories by ID.
38
+ */
39
+ mdelete(ids: string[]): Promise<void>;
40
+ }
41
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/memory/store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAE3C,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,OAAO,CAAC;AAUf;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,WAAW;IAC/C,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,UAAU,CAAsB;gBAE5B,EAAE,EAAE,IAAI,GAAG,UAAU,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAKlE;;OAEG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAenD;;OAEG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IA6BhE;;OAEG;IACG,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC;IA8BtD;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC;IAoB1E;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQvC;;OAEG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAQ5C"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * PostgreSQL Memory store implementation.
3
+ */
4
+ import { KERNL_SCHEMA_NAME, MemoryRecordCodec, NewMemoryCodec, } from "@kernl-sdk/storage";
5
+ import { SQL_WHERE, ORDER, SQL_UPDATE } from "./sql.js";
6
+ /**
7
+ * PostgreSQL memory store implementation.
8
+ *
9
+ * All async methods call `ensureInit()` before database operations
10
+ * to ensure schema/tables exist.
11
+ */
12
+ export class PGMemoryStore {
13
+ db;
14
+ ensureInit;
15
+ constructor(db, ensureInit) {
16
+ this.db = db;
17
+ this.ensureInit = ensureInit;
18
+ }
19
+ /**
20
+ * Get a memory by ID.
21
+ */
22
+ async get(id) {
23
+ await this.ensureInit();
24
+ const result = await this.db.query(`SELECT * FROM ${KERNL_SCHEMA_NAME}.memories WHERE id = $1`, [id]);
25
+ if (result.rows.length === 0) {
26
+ return null;
27
+ }
28
+ return MemoryRecordCodec.decode(result.rows[0]);
29
+ }
30
+ /**
31
+ * List memories matching optional filter criteria.
32
+ */
33
+ async list(options) {
34
+ await this.ensureInit();
35
+ const { sql: where, params } = SQL_WHERE.encode({
36
+ filter: options?.filter,
37
+ startIdx: 1,
38
+ });
39
+ let idx = params.length + 1;
40
+ let query = `SELECT * FROM ${KERNL_SCHEMA_NAME}.memories`;
41
+ // build where + order by
42
+ if (where)
43
+ query += ` WHERE ${where}`;
44
+ query += ` ORDER BY ${ORDER.encode({ order: options?.order })}`;
45
+ // add limit + offset
46
+ if (options?.limit) {
47
+ query += ` LIMIT $${idx++}`;
48
+ params.push(options.limit);
49
+ }
50
+ if (options?.offset) {
51
+ query += ` OFFSET $${idx++}`;
52
+ params.push(options.offset);
53
+ }
54
+ const result = await this.db.query(query, params);
55
+ return result.rows.map((row) => MemoryRecordCodec.decode(row));
56
+ }
57
+ /**
58
+ * Create a new memory record.
59
+ */
60
+ async create(memory) {
61
+ await this.ensureInit();
62
+ const row = NewMemoryCodec.encode(memory);
63
+ const result = await this.db.query(`INSERT INTO ${KERNL_SCHEMA_NAME}.memories
64
+ (id, namespace, entity_id, agent_id, kind, collection, content, wmem, smem_expires_at, timestamp, created_at, updated_at, metadata)
65
+ VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8, $9, $10, $11, $12, $13::jsonb)
66
+ RETURNING *`, [
67
+ row.id,
68
+ row.namespace,
69
+ row.entity_id,
70
+ row.agent_id,
71
+ row.kind,
72
+ row.collection,
73
+ JSON.stringify(row.content), // ??
74
+ row.wmem,
75
+ row.smem_expires_at,
76
+ row.timestamp,
77
+ row.created_at,
78
+ row.updated_at,
79
+ row.metadata ? JSON.stringify(row.metadata) : null,
80
+ ]);
81
+ return MemoryRecordCodec.decode(result.rows[0]);
82
+ }
83
+ /**
84
+ * Update a memory record.
85
+ */
86
+ async update(id, patch) {
87
+ await this.ensureInit();
88
+ const { sql: updates, params } = SQL_UPDATE.encode({ patch, startIdx: 1 });
89
+ const idx = params.length + 1;
90
+ params.push(id);
91
+ // (TODO): might we not want to return the whole record sometimes?
92
+ const result = await this.db.query(`UPDATE ${KERNL_SCHEMA_NAME}.memories SET ${updates} WHERE id = $${idx} RETURNING *`, params);
93
+ if (result.rows.length === 0) {
94
+ throw new Error(`memory not found: ${id}`);
95
+ }
96
+ return MemoryRecordCodec.decode(result.rows[0]);
97
+ }
98
+ /**
99
+ * Delete a memory by ID.
100
+ */
101
+ async delete(id) {
102
+ await this.ensureInit();
103
+ await this.db.query(`DELETE FROM ${KERNL_SCHEMA_NAME}.memories WHERE id = $1`, [id]);
104
+ }
105
+ /**
106
+ * Delete multiple memories by ID.
107
+ */
108
+ async mdelete(ids) {
109
+ if (ids.length === 0)
110
+ return;
111
+ await this.ensureInit();
112
+ await this.db.query(`DELETE FROM ${KERNL_SCHEMA_NAME}.memories WHERE id = ANY($1)`, [ids]);
113
+ }
114
+ }