@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
package/dist/storage.js CHANGED
@@ -1,10 +1,31 @@
1
1
  import assert from "assert";
2
- import { SCHEMA_NAME, TABLE_MIGRATIONS } from "@kernl-sdk/storage";
2
+ import { KERNL_SCHEMA_NAME, TABLE_MIGRATIONS } from "@kernl-sdk/storage";
3
3
  import { UnimplementedError } from "@kernl-sdk/shared/lib";
4
4
  /* pg */
5
5
  import { PGThreadStore } from "./thread/store.js";
6
- import { migrations } from "./migrations.js";
6
+ import { PGMemoryStore } from "./memory/store.js";
7
+ import { MIGRATIONS } from "./migrations.js";
7
8
  import { SQL_IDENTIFIER_REGEX } from "./sql.js";
9
+ /**
10
+ * Default vector configuration.
11
+ */
12
+ export const DEFAULT_VECTOR_CONFIG = {
13
+ dimensions: 1536,
14
+ similarity: "cosine",
15
+ };
16
+ /**
17
+ * Resolve vector config, applying defaults.
18
+ */
19
+ export function resolveVectorConfig(config) {
20
+ if (!config)
21
+ return undefined;
22
+ if (config === true)
23
+ return DEFAULT_VECTOR_CONFIG;
24
+ return {
25
+ dimensions: config.dimensions ?? DEFAULT_VECTOR_CONFIG.dimensions,
26
+ similarity: config.similarity ?? DEFAULT_VECTOR_CONFIG.similarity,
27
+ };
28
+ }
8
29
  /**
9
30
  * PostgreSQL storage adapter.
10
31
  *
@@ -19,9 +40,11 @@ export class PGStorage {
19
40
  pool;
20
41
  initPromise = null;
21
42
  threads;
43
+ memories;
22
44
  constructor(config) {
23
45
  this.pool = config.pool;
24
46
  this.threads = new PGThreadStore(this.pool, () => this.ensureInit());
47
+ this.memories = new PGMemoryStore(this.pool, () => this.ensureInit());
25
48
  }
26
49
  /**
27
50
  * Ensure storage is initialized before any operation.
@@ -53,7 +76,7 @@ export class PGStorage {
53
76
  * Initialize the storage backend.
54
77
  */
55
78
  async init() {
56
- await this.pool.query(`CREATE SCHEMA IF NOT EXISTS "${SCHEMA_NAME}"`);
79
+ await this.pool.query(`CREATE SCHEMA IF NOT EXISTS "${KERNL_SCHEMA_NAME}"`);
57
80
  await this.createTable(TABLE_MIGRATIONS);
58
81
  await this.migrate();
59
82
  }
@@ -71,10 +94,10 @@ export class PGStorage {
71
94
  try {
72
95
  await client.query("BEGIN");
73
96
  // read applied migration IDs
74
- const result = await client.query(`SELECT id FROM "${SCHEMA_NAME}".migrations ORDER BY applied_at ASC`);
97
+ const result = await client.query(`SELECT id FROM "${KERNL_SCHEMA_NAME}".migrations ORDER BY applied_at ASC`);
75
98
  const applied = new Set(result.rows.map((row) => row.id));
76
99
  // filter pending migrations
77
- const pending = migrations.filter((m) => !applied.has(m.id));
100
+ const pending = MIGRATIONS.filter((m) => !applied.has(m.id));
78
101
  if (pending.length === 0) {
79
102
  await client.query("COMMIT");
80
103
  return;
@@ -87,7 +110,7 @@ export class PGStorage {
87
110
  await this._createTable(client, table);
88
111
  },
89
112
  });
90
- await client.query(`INSERT INTO "${SCHEMA_NAME}".migrations (id, applied_at) VALUES ($1, $2)`, [migration.id, Date.now()]);
113
+ await client.query(`INSERT INTO "${KERNL_SCHEMA_NAME}".migrations (id, applied_at) VALUES ($1, $2)`, [migration.id, Date.now()]);
91
114
  }
92
115
  await client.query("COMMIT");
93
116
  }
@@ -127,7 +150,7 @@ export class PGStorage {
127
150
  }
128
151
  // foreign key reference
129
152
  if (col._fk) {
130
- let ref = `REFERENCES "${SCHEMA_NAME}"."${col._fk.table}" ("${col._fk.column}")`;
153
+ let ref = `REFERENCES "${KERNL_SCHEMA_NAME}"."${col._fk.table}" ("${col._fk.column}")`;
131
154
  if (col._onDelete) {
132
155
  ref += ` ON DELETE ${col._onDelete}`;
133
156
  }
@@ -171,7 +194,7 @@ export class PGStorage {
171
194
  }
172
195
  const constraints = [...columns, ...tableConstraints];
173
196
  const sql = `
174
- CREATE TABLE IF NOT EXISTS "${SCHEMA_NAME}"."${table.name}" (
197
+ CREATE TABLE IF NOT EXISTS "${KERNL_SCHEMA_NAME}"."${table.name}" (
175
198
  ${constraints.join(",\n ")}
176
199
  )
177
200
  `.trim();
@@ -209,7 +232,7 @@ export class PGStorage {
209
232
  const indexName = `idx_${tableName}_${index.columns.join("_")}`;
210
233
  const sql = `
211
234
  CREATE ${uniqueKeyword} INDEX IF NOT EXISTS "${indexName}"
212
- ON "${SCHEMA_NAME}"."${tableName}" (${columns})
235
+ ON "${KERNL_SCHEMA_NAME}"."${tableName}" (${columns})
213
236
  `.trim();
214
237
  await client.query(sql);
215
238
  }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Thread 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 { ThreadFilter, ThreadUpdate, SortOrder } from "kernl";
8
+ export interface SQLClause {
9
+ sql: string;
10
+ params: unknown[];
11
+ }
12
+ export interface WhereInput {
13
+ filter?: ThreadFilter;
14
+ startIdx: number;
15
+ }
16
+ /**
17
+ * Encode ThreadFilter to SQL WHERE clause.
18
+ */
19
+ export declare const SQL_WHERE: Codec<WhereInput, SQLClause>;
20
+ export interface OrderInput {
21
+ order?: {
22
+ createdAt?: SortOrder;
23
+ updatedAt?: SortOrder;
24
+ };
25
+ }
26
+ /**
27
+ * Encode order options to SQL ORDER BY clause.
28
+ */
29
+ export declare const SQL_ORDER: Codec<OrderInput, string>;
30
+ export interface UpdateInput {
31
+ patch: ThreadUpdate;
32
+ startIdx: number;
33
+ }
34
+ /**
35
+ * Encode ThreadUpdate to SQL SET clause.
36
+ */
37
+ export declare const SQL_UPDATE: Codec<UpdateInput, SQLClause>;
38
+ //# sourceMappingURL=sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/thread/sql.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEnE,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;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,UAAU,EAAE,SAAS,CAsDlD,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE;QACN,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,SAAS,CAAC,EAAE,SAAS,CAAC;KACvB,CAAC;CACH;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,UAAU,EAAE,MAAM,CAqB/C,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,WAAW,EAAE,SAAS,CAuCpD,CAAC"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Thread SQL conversion codecs.
3
+ *
4
+ * TODO: generalize object -> SQL conversion into a shared utility
5
+ */
6
+ /**
7
+ * Encode ThreadFilter to SQL WHERE clause.
8
+ */
9
+ export const SQL_WHERE = {
10
+ encode({ filter, startIdx }) {
11
+ if (!filter) {
12
+ return { sql: "", params: [] };
13
+ }
14
+ const conditions = [];
15
+ const params = [];
16
+ let idx = startIdx;
17
+ if (filter.namespace !== undefined) {
18
+ conditions.push(`namespace = $${idx++}`);
19
+ params.push(filter.namespace);
20
+ }
21
+ if (filter.state !== undefined) {
22
+ if (Array.isArray(filter.state)) {
23
+ conditions.push(`state = ANY($${idx++})`);
24
+ params.push(filter.state);
25
+ }
26
+ else {
27
+ conditions.push(`state = $${idx++}`);
28
+ params.push(filter.state);
29
+ }
30
+ }
31
+ if (filter.agentId !== undefined) {
32
+ conditions.push(`agent_id = $${idx++}`);
33
+ params.push(filter.agentId);
34
+ }
35
+ if (filter.parentTaskId !== undefined) {
36
+ conditions.push(`parent_task_id = $${idx++}`);
37
+ params.push(filter.parentTaskId);
38
+ }
39
+ if (filter.createdAfter !== undefined) {
40
+ conditions.push(`created_at > $${idx++}`);
41
+ params.push(filter.createdAfter.getTime());
42
+ }
43
+ if (filter.createdBefore !== undefined) {
44
+ conditions.push(`created_at < $${idx++}`);
45
+ params.push(filter.createdBefore.getTime());
46
+ }
47
+ return {
48
+ sql: conditions.length > 0 ? conditions.join(" AND ") : "",
49
+ params,
50
+ };
51
+ },
52
+ decode() {
53
+ throw new Error("SQL_WHERE.decode not implemented");
54
+ },
55
+ };
56
+ /**
57
+ * Encode order options to SQL ORDER BY clause.
58
+ */
59
+ export const SQL_ORDER = {
60
+ encode({ order }) {
61
+ const clauses = [];
62
+ if (order?.createdAt) {
63
+ clauses.push(`created_at ${order.createdAt.toUpperCase()}`);
64
+ }
65
+ if (order?.updatedAt) {
66
+ clauses.push(`updated_at ${order.updatedAt.toUpperCase()}`);
67
+ }
68
+ if (clauses.length === 0) {
69
+ return "created_at DESC";
70
+ }
71
+ return clauses.join(", ");
72
+ },
73
+ decode() {
74
+ throw new Error("SQL_ORDER.decode not implemented");
75
+ },
76
+ };
77
+ /**
78
+ * Encode ThreadUpdate to SQL SET clause.
79
+ */
80
+ export const SQL_UPDATE = {
81
+ encode({ patch, startIdx }) {
82
+ const sets = [];
83
+ const params = [];
84
+ let idx = startIdx;
85
+ if (patch.tick !== undefined) {
86
+ sets.push(`tick = $${idx++}`);
87
+ params.push(patch.tick);
88
+ }
89
+ if (patch.state !== undefined) {
90
+ sets.push(`state = $${idx++}`);
91
+ params.push(patch.state);
92
+ }
93
+ if (patch.context !== undefined) {
94
+ sets.push(`context = $${idx++}`);
95
+ params.push(JSON.stringify(patch.context.context));
96
+ }
97
+ if (patch.metadata !== undefined) {
98
+ sets.push(`metadata = $${idx++}`);
99
+ params.push(patch.metadata ? JSON.stringify(patch.metadata) : null);
100
+ }
101
+ // always update updated_at
102
+ sets.push(`updated_at = $${idx++}`);
103
+ params.push(Date.now());
104
+ return {
105
+ sql: sets.join(", "),
106
+ params,
107
+ };
108
+ },
109
+ decode() {
110
+ throw new Error("SQL_UPDATE.decode not implemented");
111
+ },
112
+ };
@@ -1,7 +1,7 @@
1
1
  import type { Pool, PoolClient } from "pg";
2
- import { type ThreadRecord } from "@kernl-sdk/storage";
3
- import { Thread, type ThreadEvent } from "kernl/internal";
4
2
  import { type AgentRegistry, type ModelRegistry, type ThreadStore, type NewThread, type ThreadUpdate, type ThreadInclude, type ThreadListOptions, type ThreadHistoryOptions } from "kernl";
3
+ import { Thread, type ThreadEvent } from "kernl/internal";
4
+ import { type ThreadRecord } from "@kernl-sdk/storage";
5
5
  /**
6
6
  * PostgreSQL Thread store implementation.
7
7
  *
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/thread/store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAE3C,OAAO,EAIL,KAAK,YAAY,EAElB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,OAAO,CAAC;AAEf;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,WAAW;IAC/C,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,UAAU,CAA0D;IAC5E,OAAO,CAAC,UAAU,CAAsB;gBAE5B,EAAE,EAAE,IAAI,GAAG,UAAU,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAMlE;;;;OAIG;IACH,IAAI,CAAC,UAAU,EAAE;QAAE,MAAM,EAAE,aAAa,CAAC;QAAC,MAAM,EAAE,aAAa,CAAA;KAAE,GAAG,IAAI;IAIxE;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAuGvE;;OAEG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAmG1D;;OAEG;IACG,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;IA4BhD;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IA+C/D;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxC;;OAEG;IACG,OAAO,CACX,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,oBAAoB,GAC1B,OAAO,CAAC,WAAW,EAAE,CAAC;IAuCzB;;;;;;;;;OASG;IACG,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAmClD;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,YAAY,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,EAAE,CAAA;KAAE,GAAG,MAAM;CAkC1E"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/thread/store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAE3C,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAIL,KAAK,YAAY,EAElB,MAAM,oBAAoB,CAAC;AAI5B;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,WAAW;IAC/C,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,UAAU,CAA0D;IAC5E,OAAO,CAAC,UAAU,CAAsB;gBAE5B,EAAE,EAAE,IAAI,GAAG,UAAU,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAMlE;;;;OAIG;IACH,IAAI,CAAC,UAAU,EAAE;QAAE,MAAM,EAAE,aAAa,CAAC;QAAC,MAAM,EAAE,aAAa,CAAA;KAAE,GAAG,IAAI;IAIxE;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAuGvE;;OAEG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAuC1D;;OAEG;IACG,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;IA4BhD;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAkB/D;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxC;;OAEG;IACG,OAAO,CACX,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,oBAAoB,GAC1B,OAAO,CAAC,WAAW,EAAE,CAAC;IAuCzB;;;;;;;;;OASG;IACG,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAmClD;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,YAAY,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,EAAE,CAAA;KAAE,GAAG,MAAM;CAmC1E"}
@@ -1,7 +1,8 @@
1
1
  import assert from "assert";
2
- import { SCHEMA_NAME, NewThreadCodec, ThreadEventRecordCodec, } from "@kernl-sdk/storage";
3
- import { Thread } from "kernl/internal";
4
2
  import { Context, } from "kernl";
3
+ import { Thread } from "kernl/internal";
4
+ import { KERNL_SCHEMA_NAME, NewThreadCodec, ThreadEventRecordCodec, } from "@kernl-sdk/storage";
5
+ import { SQL_WHERE, SQL_ORDER, SQL_UPDATE } from "./sql.js";
5
6
  /**
6
7
  * PostgreSQL Thread store implementation.
7
8
  *
@@ -56,8 +57,8 @@ export class PGThreadStore {
56
57
  e.timestamp,
57
58
  e.data,
58
59
  e.metadata as event_metadata
59
- FROM ${SCHEMA_NAME}.threads t
60
- LEFT JOIN ${SCHEMA_NAME}.thread_events e ON t.id = e.tid${eventFilter}
60
+ FROM ${KERNL_SCHEMA_NAME}.threads t
61
+ LEFT JOIN ${KERNL_SCHEMA_NAME}.thread_events e ON t.id = e.tid${eventFilter}
61
62
  WHERE t.id = $1
62
63
  ORDER BY e.seq ${order.toUpperCase()}
63
64
  ${limit}
@@ -101,7 +102,7 @@ export class PGThreadStore {
101
102
  }
102
103
  }
103
104
  // simple query without events
104
- const result = await this.db.query(`SELECT * FROM ${SCHEMA_NAME}.threads WHERE id = $1`, [tid]);
105
+ const result = await this.db.query(`SELECT * FROM ${KERNL_SCHEMA_NAME}.threads WHERE id = $1`, [tid]);
105
106
  if (result.rows.length === 0) {
106
107
  return null;
107
108
  }
@@ -117,71 +118,24 @@ export class PGThreadStore {
117
118
  */
118
119
  async list(options) {
119
120
  await this.ensureInit();
120
- let query = `SELECT * FROM ${SCHEMA_NAME}.threads`;
121
- const values = [];
122
- let paramIndex = 1;
123
- // build WHERE clause
124
- const conditions = [];
125
- if (options?.filter) {
126
- const { state, agentId, parentTaskId, createdAfter, createdBefore, namespace, } = options.filter;
127
- if (namespace) {
128
- conditions.push(`namespace = $${paramIndex++}`);
129
- values.push(namespace);
130
- }
131
- if (state) {
132
- if (Array.isArray(state)) {
133
- conditions.push(`state = ANY($${paramIndex++})`);
134
- values.push(state);
135
- }
136
- else {
137
- conditions.push(`state = $${paramIndex++}`);
138
- values.push(state);
139
- }
140
- }
141
- if (agentId) {
142
- conditions.push(`agent_id = $${paramIndex++}`);
143
- values.push(agentId);
144
- }
145
- if (parentTaskId) {
146
- conditions.push(`parent_task_id = $${paramIndex++}`);
147
- values.push(parentTaskId);
148
- }
149
- if (createdAfter) {
150
- conditions.push(`created_at > $${paramIndex++}`);
151
- values.push(createdAfter.getTime());
152
- }
153
- if (createdBefore) {
154
- conditions.push(`created_at < $${paramIndex++}`);
155
- values.push(createdBefore.getTime());
156
- }
157
- }
158
- if (conditions.length > 0) {
159
- query += ` WHERE ${conditions.join(" AND ")}`;
160
- }
161
- // build ORDER BY clause
162
- const orderClauses = [];
163
- if (options?.order?.createdAt) {
164
- orderClauses.push(`created_at ${options.order.createdAt.toUpperCase()}`);
165
- }
166
- if (options?.order?.updatedAt) {
167
- orderClauses.push(`updated_at ${options.order.updatedAt.toUpperCase()}`);
168
- }
169
- if (orderClauses.length > 0) {
170
- query += ` ORDER BY ${orderClauses.join(", ")}`;
171
- }
172
- else {
173
- // default: most recent first
174
- query += ` ORDER BY created_at DESC`;
175
- }
121
+ const { sql: where, params } = SQL_WHERE.encode({
122
+ filter: options?.filter,
123
+ startIdx: 1,
124
+ });
125
+ let idx = params.length + 1;
126
+ let query = `SELECT * FROM ${KERNL_SCHEMA_NAME}.threads`;
127
+ if (where)
128
+ query += ` WHERE ${where}`;
129
+ query += ` ORDER BY ${SQL_ORDER.encode({ order: options?.order })}`;
176
130
  if (options?.limit) {
177
- query += ` LIMIT $${paramIndex++}`;
178
- values.push(options.limit);
131
+ query += ` LIMIT $${idx++}`;
132
+ params.push(options.limit);
179
133
  }
180
134
  if (options?.offset) {
181
- query += ` OFFSET $${paramIndex++}`;
182
- values.push(options.offset);
135
+ query += ` OFFSET $${idx++}`;
136
+ params.push(options.offset);
183
137
  }
184
- const result = await this.db.query(query, values);
138
+ const result = await this.db.query(query, params);
185
139
  return result.rows
186
140
  .map((record) => {
187
141
  try {
@@ -202,7 +156,7 @@ export class PGThreadStore {
202
156
  async insert(thread) {
203
157
  await this.ensureInit();
204
158
  const record = NewThreadCodec.encode(thread);
205
- const result = await this.db.query(`INSERT INTO ${SCHEMA_NAME}.threads
159
+ const result = await this.db.query(`INSERT INTO ${KERNL_SCHEMA_NAME}.threads
206
160
  (id, namespace, agent_id, model, context, tick, state, parent_task_id, metadata, created_at, updated_at)
207
161
  VALUES ($1, $2, $3, $4, $5::jsonb, $6, $7, $8, $9::jsonb, $10, $11)
208
162
  RETURNING *`, [
@@ -225,36 +179,13 @@ export class PGThreadStore {
225
179
  */
226
180
  async update(tid, patch) {
227
181
  await this.ensureInit();
228
- const updates = [];
229
- const values = [];
230
- let paramIndex = 1;
231
- if (patch.tick !== undefined) {
232
- updates.push(`tick = $${paramIndex++}`);
233
- values.push(patch.tick);
234
- }
235
- if (patch.state !== undefined) {
236
- updates.push(`state = $${paramIndex++}`);
237
- values.push(patch.state);
238
- }
239
- if (patch.context !== undefined) {
240
- updates.push(`context = $${paramIndex++}`);
241
- // NOTE: Store the raw context value, not the Context wrapper.
242
- //
243
- // THis may change in the future depending on Context implementation.
244
- values.push(JSON.stringify(patch.context.context));
245
- }
246
- if (patch.metadata !== undefined) {
247
- updates.push(`metadata = $${paramIndex++}`);
248
- values.push(patch.metadata ? JSON.stringify(patch.metadata) : null);
249
- }
250
- // always update `updated_at`
251
- updates.push(`updated_at = $${paramIndex++}`);
252
- values.push(Date.now());
253
- values.push(tid); // WHERE id = $N
254
- const result = await this.db.query(`UPDATE ${SCHEMA_NAME}.threads
255
- SET ${updates.join(", ")}
256
- WHERE id = $${paramIndex}
257
- RETURNING *`, values);
182
+ const { sql: updates, params } = SQL_UPDATE.encode({ patch, startIdx: 1 });
183
+ const idx = params.length + 1;
184
+ params.push(tid);
185
+ const result = await this.db.query(`UPDATE ${KERNL_SCHEMA_NAME}.threads
186
+ SET ${updates}
187
+ WHERE id = $${idx}
188
+ RETURNING *`, params);
258
189
  return this.hydrate({ record: result.rows[0] });
259
190
  }
260
191
  /**
@@ -262,16 +193,14 @@ export class PGThreadStore {
262
193
  */
263
194
  async delete(tid) {
264
195
  await this.ensureInit();
265
- await this.db.query(`DELETE FROM ${SCHEMA_NAME}.threads WHERE id = $1`, [
266
- tid,
267
- ]);
196
+ await this.db.query(`DELETE FROM ${KERNL_SCHEMA_NAME}.threads WHERE id = $1`, [tid]);
268
197
  }
269
198
  /**
270
199
  * Get the event history for a thread.
271
200
  */
272
201
  async history(tid, opts) {
273
202
  await this.ensureInit();
274
- let query = `SELECT * FROM ${SCHEMA_NAME}.thread_events WHERE tid = $1`;
203
+ let query = `SELECT * FROM ${KERNL_SCHEMA_NAME}.thread_events WHERE tid = $1`;
275
204
  const values = [tid];
276
205
  let paramIndex = 2;
277
206
  // - filter:seq -
@@ -321,7 +250,7 @@ export class PGThreadStore {
321
250
  values.push(record.id, record.tid, record.seq, record.kind, record.timestamp, record.data, record.metadata);
322
251
  }
323
252
  // insert with ON CONFLICT DO NOTHING for idempotency
324
- await this.db.query(`INSERT INTO ${SCHEMA_NAME}.thread_events
253
+ await this.db.query(`INSERT INTO ${KERNL_SCHEMA_NAME}.thread_events
325
254
  (id, tid, seq, kind, timestamp, data, metadata)
326
255
  VALUES ${placeholders.join(", ")}
327
256
  ON CONFLICT (tid, id) DO NOTHING`, values);
@@ -334,6 +263,7 @@ export class PGThreadStore {
334
263
  const { record, events = [] } = thread;
335
264
  const agent = this.registries.agents.get(record.agent_id);
336
265
  const model = this.registries.models.get(record.model);
266
+ // (TODO): we might want to allow this in the future, unclear how it would look though..
337
267
  if (!agent || !model) {
338
268
  throw new Error(`Thread ${record.id} references non-existent agent/model (agent: ${record.agent_id}, model: ${record.model})`);
339
269
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kernl-sdk/pg",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "PostgreSQL storage adapter for kernl",
5
5
  "keywords": [
6
6
  "kernl",
@@ -35,15 +35,18 @@
35
35
  "tsc-alias": "^1.8.10",
36
36
  "typescript": "5.9.2",
37
37
  "vitest": "^4.0.8",
38
- "@kernl-sdk/protocol": "^0.2.5"
38
+ "@kernl-sdk/protocol": "^0.2.5",
39
+ "@kernl-sdk/ai": "^0.2.7"
39
40
  },
40
41
  "dependencies": {
41
42
  "pg": "^8.16.3",
42
- "kernl": "^0.6.2",
43
43
  "@kernl-sdk/shared": "^0.1.6",
44
- "@kernl-sdk/storage": "0.1.11"
44
+ "kernl": "^0.6.3",
45
+ "@kernl-sdk/retrieval": "^0.1.0",
46
+ "@kernl-sdk/storage": "0.1.12"
45
47
  },
46
48
  "scripts": {
49
+ "clean": "rm -rf dist",
47
50
  "build": "tsc && tsc-alias --resolve-full-paths",
48
51
  "dev": "tsc --watch",
49
52
  "check-types": "tsc --noEmit",
@@ -10,12 +10,10 @@ import {
10
10
  type ModelRegistry,
11
11
  } from "kernl";
12
12
  import { Thread } from "kernl/internal";
13
+ import type { LanguageModel } from "@kernl-sdk/protocol";
13
14
 
14
15
  const TEST_DB_URL = process.env.KERNL_PG_TEST_URL;
15
16
 
16
- type TestLanguageModel =
17
- ModelRegistry extends { get(key: string): infer T | undefined } ? T : never;
18
-
19
17
  describe.sequential("PGStorage auto-initialization", () => {
20
18
  if (!TEST_DB_URL) {
21
19
  it.skip("requires KERNL_PG_TEST_URL to be set", () => {});
@@ -42,7 +40,7 @@ describe.sequential("PGStorage auto-initialization", () => {
42
40
  spec: "1.0" as const,
43
41
  provider: "test",
44
42
  modelId: "auto-init-model",
45
- } as unknown as TestLanguageModel;
43
+ } as unknown as LanguageModel;
46
44
 
47
45
  const agent = new Agent({
48
46
  id: "auto-init-agent",
@@ -172,7 +170,7 @@ describe.sequential("PGStorage integration", () => {
172
170
  `SELECT id FROM "kernl".migrations ORDER BY applied_at ASC`,
173
171
  );
174
172
  const appliedMigrationIds = migrationsResult.rows.map((row) => row.id);
175
- expect(appliedMigrationIds).toEqual(["0001_initial"]);
173
+ expect(appliedMigrationIds).toEqual(["001_threads", "002_memories"]);
176
174
 
177
175
  // ---- verify indexes created by table definitions ----
178
176
  const indexesResult = await pool.query<{
@@ -307,7 +305,7 @@ describe.sequential("PGStorage integration", () => {
307
305
  spec: "1.0" as const,
308
306
  provider: "test",
309
307
  modelId: "test-model",
310
- } as unknown as TestLanguageModel;
308
+ } as unknown as LanguageModel;
311
309
 
312
310
  const agent = new Agent({
313
311
  id: "agent-1",
@@ -317,7 +315,7 @@ describe.sequential("PGStorage integration", () => {
317
315
  });
318
316
 
319
317
  const agents: AgentRegistry = new Map<string, Agent>([["agent-1", agent]]);
320
- const models: ModelRegistry = new Map<string, TestLanguageModel>([
318
+ const models: ModelRegistry = new Map<string, LanguageModel>([
321
319
  ["provider/model", model],
322
320
  ]) as unknown as ModelRegistry;
323
321
 
@@ -503,7 +501,7 @@ describe.sequential("PGStorage integration", () => {
503
501
  spec: "1.0" as const,
504
502
  provider: "test",
505
503
  modelId: "test-model",
506
- } as unknown as TestLanguageModel;
504
+ } as unknown as LanguageModel;
507
505
 
508
506
  const agent = new Agent({
509
507
  id: "agent-1",
@@ -513,7 +511,7 @@ describe.sequential("PGStorage integration", () => {
513
511
  });
514
512
 
515
513
  const agents: AgentRegistry = new Map<string, Agent>([["agent-1", agent]]);
516
- const models: ModelRegistry = new Map<string, TestLanguageModel>([
514
+ const models: ModelRegistry = new Map<string, LanguageModel>([
517
515
  ["provider/model", model],
518
516
  ]) as unknown as ModelRegistry;
519
517
 
@@ -576,7 +574,7 @@ describe.sequential("PGStorage integration", () => {
576
574
  modelId: "test-model",
577
575
  // generate/stream are not used in this test - we only advance stream
578
576
  // far enough to trigger the first checkpoint.
579
- } as unknown as TestLanguageModel;
577
+ } as unknown as LanguageModel;
580
578
 
581
579
  const agent = new Agent({
582
580
  id: "agent-1",
@@ -586,7 +584,7 @@ describe.sequential("PGStorage integration", () => {
586
584
  });
587
585
 
588
586
  const agents: AgentRegistry = new Map<string, Agent>([["agent-1", agent]]);
589
- const models: ModelRegistry = new Map<string, TestLanguageModel>([
587
+ const models: ModelRegistry = new Map<string, LanguageModel>([
590
588
  ["provider/model", model],
591
589
  ]) as unknown as ModelRegistry;
592
590
 
@@ -634,7 +632,7 @@ describe.sequential("PGStorage integration", () => {
634
632
  spec: "1.0" as const,
635
633
  provider: "test",
636
634
  modelId: "test-model",
637
- } as unknown as TestLanguageModel;
635
+ } as unknown as LanguageModel;
638
636
 
639
637
  const agent = new Agent({
640
638
  id: "agent-1",
@@ -644,7 +642,7 @@ describe.sequential("PGStorage integration", () => {
644
642
  });
645
643
 
646
644
  const agents: AgentRegistry = new Map<string, Agent>([["agent-1", agent]]);
647
- const models: ModelRegistry = new Map<string, TestLanguageModel>([
645
+ const models: ModelRegistry = new Map<string, LanguageModel>([
648
646
  ["provider/model", model],
649
647
  ]) as unknown as ModelRegistry;
650
648
 
@@ -696,7 +694,7 @@ describe.sequential("PGStorage integration", () => {
696
694
  spec: "1.0" as const,
697
695
  provider: "test",
698
696
  modelId: "test-model",
699
- } as unknown as TestLanguageModel;
697
+ } as unknown as LanguageModel;
700
698
 
701
699
  const agent1 = new Agent({
702
700
  id: "agent-1",
@@ -716,7 +714,7 @@ describe.sequential("PGStorage integration", () => {
716
714
  ["agent-1", agent1],
717
715
  ["agent-2", agent2],
718
716
  ]);
719
- const models: ModelRegistry = new Map<string, TestLanguageModel>([
717
+ const models: ModelRegistry = new Map<string, LanguageModel>([
720
718
  ["provider/model", model],
721
719
  ]) as unknown as ModelRegistry;
722
720
 
@@ -820,7 +818,7 @@ describe.sequential("PGStorage integration", () => {
820
818
  spec: "1.0" as const,
821
819
  provider: "test",
822
820
  modelId: "test-model",
823
- } as unknown as TestLanguageModel;
821
+ } as unknown as LanguageModel;
824
822
 
825
823
  const agent = new Agent({
826
824
  id: "agent-1",
@@ -830,7 +828,7 @@ describe.sequential("PGStorage integration", () => {
830
828
  });
831
829
 
832
830
  const agents: AgentRegistry = new Map<string, Agent>([["agent-1", agent]]);
833
- const models: ModelRegistry = new Map<string, TestLanguageModel>([
831
+ const models: ModelRegistry = new Map<string, LanguageModel>([
834
832
  ["provider/model", model],
835
833
  ]) as unknown as ModelRegistry;
836
834