@happyvertical/smrt-core 0.40.70 → 0.42.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 (193) hide show
  1. package/AGENTS.md +3 -3
  2. package/agents/change-feed.md +1 -1
  3. package/agents/schema-paths.md +561 -32
  4. package/dist/browser.js +5 -4
  5. package/dist/cascade.d.ts +120 -0
  6. package/dist/cascade.d.ts.map +1 -0
  7. package/dist/cascade.js +430 -0
  8. package/dist/cascade.js.map +1 -0
  9. package/dist/change-feed.d.ts +34 -2
  10. package/dist/change-feed.d.ts.map +1 -1
  11. package/dist/change-feed.js +54 -13
  12. package/dist/change-feed.js.map +1 -1
  13. package/dist/change-signals.d.ts.map +1 -1
  14. package/dist/change-signals.js +8 -1
  15. package/dist/change-signals.js.map +1 -1
  16. package/dist/class.d.ts +36 -3
  17. package/dist/class.d.ts.map +1 -1
  18. package/dist/class.js +69 -14
  19. package/dist/class.js.map +1 -1
  20. package/dist/collection-cache.js +0 -0
  21. package/dist/collection-cache.js.map +1 -1
  22. package/dist/collection.d.ts +29 -0
  23. package/dist/collection.d.ts.map +1 -1
  24. package/dist/collection.js +68 -16
  25. package/dist/collection.js.map +1 -1
  26. package/dist/config.d.ts +10 -0
  27. package/dist/config.d.ts.map +1 -1
  28. package/dist/config.js.map +1 -1
  29. package/dist/decorators/index.d.ts +63 -5
  30. package/dist/decorators/index.d.ts.map +1 -1
  31. package/dist/decorators/index.js +37 -5
  32. package/dist/decorators/index.js.map +1 -1
  33. package/dist/dispatch/collections/Dispatches.d.ts.map +1 -1
  34. package/dist/dispatch/collections/Dispatches.js +21 -6
  35. package/dist/dispatch/collections/Dispatches.js.map +1 -1
  36. package/dist/dispatch/models/Dispatch.d.ts +1 -1
  37. package/dist/dispatch/models/Dispatch.d.ts.map +1 -1
  38. package/dist/dispatch/models/Dispatch.js +3 -2
  39. package/dist/dispatch/models/Dispatch.js.map +1 -1
  40. package/dist/dispatch/models/DispatchSubscription.d.ts +1 -1
  41. package/dist/dispatch/models/DispatchSubscription.d.ts.map +1 -1
  42. package/dist/dispatch/models/DispatchSubscription.js +2 -1
  43. package/dist/dispatch/models/DispatchSubscription.js.map +1 -1
  44. package/dist/dispatch/types.d.ts +5 -0
  45. package/dist/dispatch/types.d.ts.map +1 -1
  46. package/dist/embedded-write-queue.d.ts +46 -0
  47. package/dist/embedded-write-queue.d.ts.map +1 -0
  48. package/dist/embedded-write-queue.js +66 -0
  49. package/dist/embedded-write-queue.js.map +1 -0
  50. package/dist/embeddings/storage.d.ts +7 -0
  51. package/dist/embeddings/storage.d.ts.map +1 -1
  52. package/dist/embeddings/storage.js +31 -13
  53. package/dist/embeddings/storage.js.map +1 -1
  54. package/dist/errors.d.ts +1 -1
  55. package/dist/hierarchical.js +1 -1
  56. package/dist/index.d.ts +2 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +8 -6
  59. package/dist/interceptors.d.ts +21 -0
  60. package/dist/interceptors.d.ts.map +1 -1
  61. package/dist/interceptors.js +27 -1
  62. package/dist/interceptors.js.map +1 -1
  63. package/dist/learning/memory.d.ts.map +1 -1
  64. package/dist/learning/memory.js +5 -4
  65. package/dist/learning/memory.js.map +1 -1
  66. package/dist/manifest/generator.d.ts.map +1 -1
  67. package/dist/manifest/generator.js +4 -7
  68. package/dist/manifest/generator.js.map +1 -1
  69. package/dist/manifest/static-manifest.js +10 -10
  70. package/dist/manifest/static-manifest.js.map +1 -1
  71. package/dist/manifest/store.js +1 -1
  72. package/dist/manifest/store.js.map +1 -1
  73. package/dist/manifest.json +19 -19
  74. package/dist/migrations/differ.d.ts +9 -1
  75. package/dist/migrations/differ.d.ts.map +1 -1
  76. package/dist/migrations/differ.js +12 -7
  77. package/dist/migrations/differ.js.map +1 -1
  78. package/dist/migrations/index.d.ts +2 -0
  79. package/dist/migrations/index.d.ts.map +1 -1
  80. package/dist/migrations/index.js +3 -1
  81. package/dist/migrations/integer-width.d.ts +99 -0
  82. package/dist/migrations/integer-width.d.ts.map +1 -0
  83. package/dist/migrations/integer-width.js +237 -0
  84. package/dist/migrations/integer-width.js.map +1 -0
  85. package/dist/migrations/minor-units.d.ts +162 -0
  86. package/dist/migrations/minor-units.d.ts.map +1 -0
  87. package/dist/migrations/minor-units.js +383 -0
  88. package/dist/migrations/minor-units.js.map +1 -0
  89. package/dist/migrations/tracker.d.ts.map +1 -1
  90. package/dist/migrations/tracker.js +28 -20
  91. package/dist/migrations/tracker.js.map +1 -1
  92. package/dist/migrations.js +3 -1
  93. package/dist/object.d.ts +98 -5
  94. package/dist/object.d.ts.map +1 -1
  95. package/dist/object.js +138 -30
  96. package/dist/object.js.map +1 -1
  97. package/dist/query-bounds.d.ts +5 -1
  98. package/dist/query-bounds.d.ts.map +1 -1
  99. package/dist/query-bounds.js +5 -1
  100. package/dist/query-bounds.js.map +1 -1
  101. package/dist/registry/class-registration.d.ts.map +1 -1
  102. package/dist/registry/class-registration.js +3 -1
  103. package/dist/registry/class-registration.js.map +1 -1
  104. package/dist/registry/manifest-field-merge.d.ts +12 -0
  105. package/dist/registry/manifest-field-merge.d.ts.map +1 -1
  106. package/dist/registry/manifest-field-merge.js +14 -2
  107. package/dist/registry/manifest-field-merge.js.map +1 -1
  108. package/dist/registry/schema-builder.d.ts +22 -1
  109. package/dist/registry/schema-builder.d.ts.map +1 -1
  110. package/dist/registry/schema-builder.js +205 -165
  111. package/dist/registry/schema-builder.js.map +1 -1
  112. package/dist/registry/types.d.ts +3 -2
  113. package/dist/registry/types.d.ts.map +1 -1
  114. package/dist/registry.d.ts +41 -46
  115. package/dist/registry.d.ts.map +1 -1
  116. package/dist/registry.js +61 -83
  117. package/dist/registry.js.map +1 -1
  118. package/dist/scanner/manifest-generator.d.ts +45 -0
  119. package/dist/scanner/manifest-generator.d.ts.map +1 -1
  120. package/dist/scanner/manifest-generator.js +92 -28
  121. package/dist/scanner/manifest-generator.js.map +1 -1
  122. package/dist/schema/conflict-target.d.ts +104 -0
  123. package/dist/schema/conflict-target.d.ts.map +1 -0
  124. package/dist/schema/conflict-target.js +129 -0
  125. package/dist/schema/conflict-target.js.map +1 -0
  126. package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -1
  127. package/dist/schema/ddl/duckdb-strategy.js +3 -1
  128. package/dist/schema/ddl/duckdb-strategy.js.map +1 -1
  129. package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -1
  130. package/dist/schema/ddl/postgres-strategy.js +14 -1
  131. package/dist/schema/ddl/postgres-strategy.js.map +1 -1
  132. package/dist/schema/generator.d.ts +245 -41
  133. package/dist/schema/generator.d.ts.map +1 -1
  134. package/dist/schema/generator.js +397 -241
  135. package/dist/schema/generator.js.map +1 -1
  136. package/dist/schema/index-utils.d.ts +120 -0
  137. package/dist/schema/index-utils.d.ts.map +1 -1
  138. package/dist/schema/index-utils.js +242 -1
  139. package/dist/schema/index-utils.js.map +1 -1
  140. package/dist/schema/index.d.ts +2 -3
  141. package/dist/schema/index.d.ts.map +1 -1
  142. package/dist/schema/index.js +2 -3
  143. package/dist/schema/live-parity.d.ts +3 -1
  144. package/dist/schema/live-parity.d.ts.map +1 -1
  145. package/dist/schema/live-parity.js +23 -2
  146. package/dist/schema/live-parity.js.map +1 -1
  147. package/dist/schema/system-table-shapes.d.ts +13 -0
  148. package/dist/schema/system-table-shapes.d.ts.map +1 -1
  149. package/dist/schema/system-table-shapes.js +14 -1
  150. package/dist/schema/system-table-shapes.js.map +1 -1
  151. package/dist/schema/types.d.ts +17 -10
  152. package/dist/schema/types.d.ts.map +1 -1
  153. package/dist/schema/utils.d.ts +1 -1
  154. package/dist/schema/utils.d.ts.map +1 -1
  155. package/dist/schema/utils.js +3 -3
  156. package/dist/schema/utils.js.map +1 -1
  157. package/dist/schema.js +2 -3
  158. package/dist/smrt-knowledge.json +9 -9
  159. package/dist/system/compatibility.d.ts +42 -0
  160. package/dist/system/compatibility.d.ts.map +1 -1
  161. package/dist/system/compatibility.js +182 -9
  162. package/dist/system/compatibility.js.map +1 -1
  163. package/dist/system/index.d.ts +1 -0
  164. package/dist/system/index.d.ts.map +1 -1
  165. package/dist/system/index.js +3 -2
  166. package/dist/system/retention.d.ts +237 -0
  167. package/dist/system/retention.d.ts.map +1 -0
  168. package/dist/system/retention.js +497 -0
  169. package/dist/system/retention.js.map +1 -0
  170. package/dist/system/schema.d.ts +102 -16
  171. package/dist/system/schema.d.ts.map +1 -1
  172. package/dist/system/schema.js +85 -47
  173. package/dist/system/schema.js.map +1 -1
  174. package/dist/system/types.d.ts +0 -2
  175. package/dist/system/types.d.ts.map +1 -1
  176. package/dist/testing/database.d.ts.map +1 -1
  177. package/dist/testing/database.js.map +1 -1
  178. package/dist/utils/safe-integer.d.ts +19 -0
  179. package/dist/utils/safe-integer.d.ts.map +1 -0
  180. package/dist/utils/safe-integer.js +31 -0
  181. package/dist/utils/safe-integer.js.map +1 -0
  182. package/dist/utils.d.ts +2 -1
  183. package/dist/utils.d.ts.map +1 -1
  184. package/dist/utils.js +6 -3
  185. package/dist/utils.js.map +1 -1
  186. package/dist/vite-plugin/index.d.ts.map +1 -1
  187. package/dist/vite-plugin/index.js +4 -62
  188. package/dist/vite-plugin/index.js.map +1 -1
  189. package/package.json +4 -4
  190. package/dist/schema/override-system.d.ts +0 -43
  191. package/dist/schema/override-system.d.ts.map +0 -1
  192. package/dist/schema/override-system.js +0 -205
  193. package/dist/schema/override-system.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Dispatch.d.ts","sourceRoot":"","sources":["../../../src/dispatch/models/Dispatch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAgCpE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,QAAQ;IACnB,gCAAgC;IAChC,MAAM,CAAC,QAAQ,CAAC,SAAS,oBAAoB;IAE7C,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IAEX,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IAEb,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC;IAEf,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IAEjB,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEjC,qBAAqB;IACrB,MAAM,EAAE,cAAc,CAAC;IAEvB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IAEjB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAElB,sCAAsC;IACtC,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IAEzB,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IAEpB,qEAAqE;IACrE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC,iEAAiE;IACjE,aAAa,EAAE,MAAM,CAAC;IAEtB;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAElC,yBAAyB;IACzB,SAAS,EAAE,IAAI,CAAC;IAEhB,4BAA4B;IAC5B,SAAS,EAAE,IAAI,CAAC;gBAEJ,IAAI,GAAE,OAAO,CAAC,YAAY,CAAM;IA0B5C;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,QAAQ;IAI3C;;OAEG;IACH,KAAK,IAAI,YAAY;IAqBrB;;OAEG;IACH,WAAW,IAAI,gBAAgB;IAa/B;;OAEG;IACH,cAAc,IAAI,IAAI;IAMtB;;OAEG;IACH,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAQxC;;OAEG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAM/B;;OAEG;IACH,aAAa,IAAI,IAAI;IAKrB;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;CAGvC"}
1
+ {"version":3,"file":"Dispatch.d.ts","sourceRoot":"","sources":["../../../src/dispatch/models/Dispatch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAgCpE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACnC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,QAAQ;IACnB,gCAAgC;IAChC,MAAM,CAAC,QAAQ,CAAC,SAAS,oBAAoB;IAE7C,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IAEX,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IAEb,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC;IAEf,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IAEjB,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEjC,qBAAqB;IACrB,MAAM,EAAE,cAAc,CAAC;IAEvB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IAEjB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAElB,sCAAsC;IACtC,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IAEzB,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IAEpB,qEAAqE;IACrE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC,iEAAiE;IACjE,aAAa,EAAE,MAAM,CAAC;IAEtB;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAElC,yBAAyB;IACzB,SAAS,EAAE,IAAI,CAAC;IAEhB,4BAA4B;IAC5B,SAAS,EAAE,IAAI,CAAC;gBAEJ,IAAI,GAAE,OAAO,CAAC,YAAY,CAAM;IA0B5C;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,QAAQ;IAI3C;;OAEG;IACH,KAAK,IAAI,YAAY;IAqBrB;;OAEG;IACH,WAAW,IAAI,gBAAgB;IAa/B;;OAEG;IACH,cAAc,IAAI,IAAI;IAMtB;;OAEG;IACH,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAQxC;;OAEG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAM/B;;OAEG;IACH,aAAa,IAAI,IAAI;IAKrB;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;CAGvC"}
@@ -1,3 +1,4 @@
1
+ import { toSafeInteger } from "../../utils/safe-integer.js";
1
2
  import { createLogger } from "@happyvertical/logger";
2
3
  import { makeId } from "@happyvertical/utils";
3
4
  //#region src/dispatch/models/Dispatch.ts
@@ -79,7 +80,7 @@ var Dispatch = class Dispatch {
79
80
  this.source = data.source || "";
80
81
  this.sourceId = data.source_id || "";
81
82
  this.status = data.status || "pending";
82
- this.attempts = data.attempts || 0;
83
+ this.attempts = toSafeInteger(data.attempts ?? 0, "Dispatch attempts");
83
84
  this.lastError = data.last_error || "";
84
85
  this.processedBy = data.processed_by || "";
85
86
  this.targetSubscriber = data.target_subscriber || null;
@@ -140,7 +141,7 @@ var Dispatch = class Dispatch {
140
141
  */
141
142
  markProcessing() {
142
143
  this.status = "processing";
143
- this.attempts += 1;
144
+ this.attempts = toSafeInteger(this.attempts + 1, "Dispatch attempts");
144
145
  this.updatedAt = /* @__PURE__ */ new Date();
145
146
  }
146
147
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Dispatch.js","names":[],"sources":["../../../src/dispatch/models/Dispatch.ts"],"sourcesContent":["/**\n * Dispatch Model - Persistent dispatch message for inter-agent communication\n *\n * Dispatches are stored in the _smrt_dispatch system table and represent\n * messages sent between agents. They support async processing with retry logic.\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport { makeId } from '@happyvertical/utils';\nimport type { DispatchMetadata, DispatchStatus } from '../types.js';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Safely parses a JSON column from a `_smrt_dispatch` row.\n *\n * A single corrupted `payload`/`metadata` value must not throw out of the\n * `Dispatch` constructor, since `process()`/`list()` map `fromRow` over a whole\n * batch — one poison row would otherwise stall every dispatch in the batch\n * (#1378). On a parse failure, logs the offending row id + field and falls back\n * to an empty object so the rest of the batch keeps flowing.\n */\nfunction parseJsonField(\n raw: string,\n field: 'payload' | 'metadata',\n rowId: string,\n): Record<string, unknown> {\n try {\n // Only malformed JSON is treated as a poison value; valid JSON parses as\n // before (preserving prior behavior for the well-formed path).\n return JSON.parse(raw) as Record<string, unknown>;\n } catch (error) {\n logger.warn('Skipping corrupted _smrt_dispatch JSON field', {\n dispatchId: rowId,\n field,\n error: error instanceof Error ? error.message : String(error),\n });\n return {};\n }\n}\n\n/**\n * Raw dispatch data as stored in the database\n */\nexport interface DispatchData {\n id: string;\n type: string;\n source: string;\n source_id: string | null;\n payload: string | null;\n status: DispatchStatus;\n attempts: number;\n last_error: string | null;\n processed_at: string | null;\n processed_by: string | null;\n target_subscriber: string | null;\n correlation_id: string | null;\n tenant_id: string | null;\n metadata: string | null;\n created_at: string;\n updated_at: string;\n}\n\n/**\n * Dispatch model for inter-agent communication\n */\nexport class Dispatch {\n /** Table name for this model */\n static readonly tableName = '_smrt_dispatch';\n\n /** Unique identifier */\n id: string;\n\n /** Signal type (e.g., 'campaign.completed') */\n type: string;\n\n /** Emitting agent name */\n source: string;\n\n /** Emitting agent instance ID */\n sourceId: string;\n\n /** Dispatch payload */\n payload: Record<string, unknown>;\n\n /** Current status */\n status: DispatchStatus;\n\n /** Number of processing attempts */\n attempts: number;\n\n /** Last error message if failed */\n lastError: string;\n\n /** When the dispatch was processed */\n processedAt: Date | null;\n\n /** Which subscriber processed it */\n processedBy: string;\n\n /** Target subscriber for fan-out delivery (null for compete mode) */\n targetSubscriber: string | null;\n\n /** Correlation ID for linking request/response dispatch pairs */\n correlationId: string;\n\n /**\n * Tenant the dispatch was emitted in (server-derived, untrusted-input-proof).\n *\n * Stamped from the active tenant context at emit time — never from caller\n * options — so it cannot be spoofed. `null` for global / non-tenant\n * dispatches. Used to isolate reads/claims across tenants (S5 #1398).\n */\n tenantId: string | null;\n\n /** Additional metadata */\n metadata: Record<string, unknown>;\n\n /** Creation timestamp */\n createdAt: Date;\n\n /** Last update timestamp */\n updatedAt: Date;\n\n constructor(data: Partial<DispatchData> = {}) {\n this.id = data.id || makeId();\n this.type = data.type || '';\n this.source = data.source || '';\n this.sourceId = data.source_id || '';\n this.status = (data.status as DispatchStatus) || 'pending';\n this.attempts = data.attempts || 0;\n this.lastError = data.last_error || '';\n this.processedBy = data.processed_by || '';\n this.targetSubscriber = data.target_subscriber || null;\n this.correlationId = data.correlation_id || '';\n this.tenantId = data.tenant_id ?? null;\n this.createdAt = data.created_at ? new Date(data.created_at) : new Date();\n this.updatedAt = data.updated_at ? new Date(data.updated_at) : new Date();\n this.processedAt = data.processed_at ? new Date(data.processed_at) : null;\n\n // Parse JSON fields. Guarded so a single corrupted row cannot stall an\n // entire process()/list() batch (#1378).\n this.payload = data.payload\n ? parseJsonField(data.payload, 'payload', this.id)\n : {};\n this.metadata = data.metadata\n ? parseJsonField(data.metadata, 'metadata', this.id)\n : {};\n }\n\n /**\n * Create a Dispatch from a database row\n */\n static fromRow(row: DispatchData): Dispatch {\n return new Dispatch(row);\n }\n\n /**\n * Convert to database row format\n */\n toRow(): DispatchData {\n return {\n id: this.id,\n type: this.type,\n source: this.source,\n source_id: this.sourceId || null,\n payload: JSON.stringify(this.payload),\n status: this.status,\n attempts: this.attempts,\n last_error: this.lastError || null,\n processed_at: this.processedAt?.toISOString() || null,\n processed_by: this.processedBy || null,\n target_subscriber: this.targetSubscriber || null,\n correlation_id: this.correlationId || null,\n tenant_id: this.tenantId ?? null,\n metadata: JSON.stringify(this.metadata),\n created_at: this.createdAt.toISOString(),\n updated_at: new Date().toISOString(),\n };\n }\n\n /**\n * Get dispatch metadata for handlers\n */\n getMetadata(): DispatchMetadata {\n return {\n id: this.id,\n type: this.type,\n source: this.source,\n sourceId: this.sourceId,\n correlationId: this.correlationId,\n createdAt: this.createdAt,\n attempts: this.attempts,\n metadata: this.metadata,\n };\n }\n\n /**\n * Mark as processing\n */\n markProcessing(): void {\n this.status = 'processing';\n this.attempts += 1;\n this.updatedAt = new Date();\n }\n\n /**\n * Mark as completed\n */\n markCompleted(processedBy: string): void {\n this.status = 'completed';\n this.processedAt = new Date();\n this.processedBy = processedBy;\n this.lastError = '';\n this.updatedAt = new Date();\n }\n\n /**\n * Mark as failed\n */\n markFailed(error: string): void {\n this.status = 'failed';\n this.lastError = error;\n this.updatedAt = new Date();\n }\n\n /**\n * Reset to pending for retry\n */\n resetForRetry(): void {\n this.status = 'pending';\n this.updatedAt = new Date();\n }\n\n /**\n * Check if this dispatch can be retried\n */\n canRetry(maxAttempts: number): boolean {\n return this.status === 'failed' && this.attempts < maxAttempts;\n }\n}\n"],"mappings":";;;;;;;;;AAWA,IAAM,SAAS,aAAa,EAAE,OAAO,OAAO,CAAC;;;;;;;;;;AAW7C,SAAS,eACP,KACA,OACA,OACyB;CACzB,IAAI;EAGF,OAAO,KAAK,MAAM,GAAG;CACvB,SAAS,OAAO;EACd,OAAO,KAAK,gDAAgD;GAC1D,YAAY;GACZ;GACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAC9D,CAAC;EACD,OAAO,CAAC;CACV;AACF;;;;AA2BA,IAAa,WAAb,MAAa,SAAS;;CAEpB,OAAgB,YAAY;;CAG5B;;CAGA;;CAGA;;CAGA;;CAGA;;CAGA;;CAGA;;CAGA;;CAGA;;CAGA;;CAGA;;CAGA;;;;;;;;CASA;;CAGA;;CAGA;;CAGA;CAEA,YAAY,OAA8B,CAAC,GAAG;EAC5C,KAAK,KAAK,KAAK,MAAM,OAAO;EAC5B,KAAK,OAAO,KAAK,QAAQ;EACzB,KAAK,SAAS,KAAK,UAAU;EAC7B,KAAK,WAAW,KAAK,aAAa;EAClC,KAAK,SAAU,KAAK,UAA6B;EACjD,KAAK,WAAW,KAAK,YAAY;EACjC,KAAK,YAAY,KAAK,cAAc;EACpC,KAAK,cAAc,KAAK,gBAAgB;EACxC,KAAK,mBAAmB,KAAK,qBAAqB;EAClD,KAAK,gBAAgB,KAAK,kBAAkB;EAC5C,KAAK,WAAW,KAAK,aAAa;EAClC,KAAK,YAAY,KAAK,aAAa,IAAI,KAAK,KAAK,UAAU,oBAAI,IAAI,KAAK;EACxE,KAAK,YAAY,KAAK,aAAa,IAAI,KAAK,KAAK,UAAU,oBAAI,IAAI,KAAK;EACxE,KAAK,cAAc,KAAK,eAAe,IAAI,KAAK,KAAK,YAAY,IAAI;EAIrE,KAAK,UAAU,KAAK,UAChB,eAAe,KAAK,SAAS,WAAW,KAAK,EAAE,IAC/C,CAAC;EACL,KAAK,WAAW,KAAK,WACjB,eAAe,KAAK,UAAU,YAAY,KAAK,EAAE,IACjD,CAAC;CACP;;;;CAKA,OAAO,QAAQ,KAA6B;EAC1C,OAAO,IAAI,SAAS,GAAG;CACzB;;;;CAKA,QAAsB;EACpB,OAAO;GACL,IAAI,KAAK;GACT,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,WAAW,KAAK,YAAY;GAC5B,SAAS,KAAK,UAAU,KAAK,OAAO;GACpC,QAAQ,KAAK;GACb,UAAU,KAAK;GACf,YAAY,KAAK,aAAa;GAC9B,cAAc,KAAK,aAAa,YAAY,KAAK;GACjD,cAAc,KAAK,eAAe;GAClC,mBAAmB,KAAK,oBAAoB;GAC5C,gBAAgB,KAAK,iBAAiB;GACtC,WAAW,KAAK,YAAY;GAC5B,UAAU,KAAK,UAAU,KAAK,QAAQ;GACtC,YAAY,KAAK,UAAU,YAAY;GACvC,6BAAY,IAAI,KAAK,EAAA,CAAE,YAAY;EACrC;CACF;;;;CAKA,cAAgC;EAC9B,OAAO;GACL,IAAI,KAAK;GACT,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,UAAU,KAAK;GACf,eAAe,KAAK;GACpB,WAAW,KAAK;GAChB,UAAU,KAAK;GACf,UAAU,KAAK;EACjB;CACF;;;;CAKA,iBAAuB;EACrB,KAAK,SAAS;EACd,KAAK,YAAY;EACjB,KAAK,4BAAY,IAAI,KAAK;CAC5B;;;;CAKA,cAAc,aAA2B;EACvC,KAAK,SAAS;EACd,KAAK,8BAAc,IAAI,KAAK;EAC5B,KAAK,cAAc;EACnB,KAAK,YAAY;EACjB,KAAK,4BAAY,IAAI,KAAK;CAC5B;;;;CAKA,WAAW,OAAqB;EAC9B,KAAK,SAAS;EACd,KAAK,YAAY;EACjB,KAAK,4BAAY,IAAI,KAAK;CAC5B;;;;CAKA,gBAAsB;EACpB,KAAK,SAAS;EACd,KAAK,4BAAY,IAAI,KAAK;CAC5B;;;;CAKA,SAAS,aAA8B;EACrC,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW;CACrD;AACF"}
1
+ {"version":3,"file":"Dispatch.js","names":[],"sources":["../../../src/dispatch/models/Dispatch.ts"],"sourcesContent":["/**\n * Dispatch Model - Persistent dispatch message for inter-agent communication\n *\n * Dispatches are stored in the _smrt_dispatch system table and represent\n * messages sent between agents. They support async processing with retry logic.\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport { makeId } from '@happyvertical/utils';\nimport { toSafeInteger } from '../../utils/safe-integer.js';\nimport type { DispatchMetadata, DispatchStatus } from '../types.js';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Safely parses a JSON column from a `_smrt_dispatch` row.\n *\n * A single corrupted `payload`/`metadata` value must not throw out of the\n * `Dispatch` constructor, since `process()`/`list()` map `fromRow` over a whole\n * batch — one poison row would otherwise stall every dispatch in the batch\n * (#1378). On a parse failure, logs the offending row id + field and falls back\n * to an empty object so the rest of the batch keeps flowing.\n */\nfunction parseJsonField(\n raw: string,\n field: 'payload' | 'metadata',\n rowId: string,\n): Record<string, unknown> {\n try {\n // Only malformed JSON is treated as a poison value; valid JSON parses as\n // before (preserving prior behavior for the well-formed path).\n return JSON.parse(raw) as Record<string, unknown>;\n } catch (error) {\n logger.warn('Skipping corrupted _smrt_dispatch JSON field', {\n dispatchId: rowId,\n field,\n error: error instanceof Error ? error.message : String(error),\n });\n return {};\n }\n}\n\n/**\n * Raw dispatch data as stored in the database\n */\nexport interface DispatchData {\n id: string;\n type: string;\n source: string;\n source_id: string | null;\n payload: string | null;\n status: DispatchStatus;\n attempts: number | string | bigint;\n last_error: string | null;\n processed_at: string | null;\n processed_by: string | null;\n target_subscriber: string | null;\n correlation_id: string | null;\n tenant_id: string | null;\n metadata: string | null;\n created_at: string;\n updated_at: string;\n}\n\n/**\n * Dispatch model for inter-agent communication\n */\nexport class Dispatch {\n /** Table name for this model */\n static readonly tableName = '_smrt_dispatch';\n\n /** Unique identifier */\n id: string;\n\n /** Signal type (e.g., 'campaign.completed') */\n type: string;\n\n /** Emitting agent name */\n source: string;\n\n /** Emitting agent instance ID */\n sourceId: string;\n\n /** Dispatch payload */\n payload: Record<string, unknown>;\n\n /** Current status */\n status: DispatchStatus;\n\n /** Number of processing attempts */\n attempts: number;\n\n /** Last error message if failed */\n lastError: string;\n\n /** When the dispatch was processed */\n processedAt: Date | null;\n\n /** Which subscriber processed it */\n processedBy: string;\n\n /** Target subscriber for fan-out delivery (null for compete mode) */\n targetSubscriber: string | null;\n\n /** Correlation ID for linking request/response dispatch pairs */\n correlationId: string;\n\n /**\n * Tenant the dispatch was emitted in (server-derived, untrusted-input-proof).\n *\n * Stamped from the active tenant context at emit time — never from caller\n * options — so it cannot be spoofed. `null` for global / non-tenant\n * dispatches. Used to isolate reads/claims across tenants (S5 #1398).\n */\n tenantId: string | null;\n\n /** Additional metadata */\n metadata: Record<string, unknown>;\n\n /** Creation timestamp */\n createdAt: Date;\n\n /** Last update timestamp */\n updatedAt: Date;\n\n constructor(data: Partial<DispatchData> = {}) {\n this.id = data.id || makeId();\n this.type = data.type || '';\n this.source = data.source || '';\n this.sourceId = data.source_id || '';\n this.status = (data.status as DispatchStatus) || 'pending';\n this.attempts = toSafeInteger(data.attempts ?? 0, 'Dispatch attempts');\n this.lastError = data.last_error || '';\n this.processedBy = data.processed_by || '';\n this.targetSubscriber = data.target_subscriber || null;\n this.correlationId = data.correlation_id || '';\n this.tenantId = data.tenant_id ?? null;\n this.createdAt = data.created_at ? new Date(data.created_at) : new Date();\n this.updatedAt = data.updated_at ? new Date(data.updated_at) : new Date();\n this.processedAt = data.processed_at ? new Date(data.processed_at) : null;\n\n // Parse JSON fields. Guarded so a single corrupted row cannot stall an\n // entire process()/list() batch (#1378).\n this.payload = data.payload\n ? parseJsonField(data.payload, 'payload', this.id)\n : {};\n this.metadata = data.metadata\n ? parseJsonField(data.metadata, 'metadata', this.id)\n : {};\n }\n\n /**\n * Create a Dispatch from a database row\n */\n static fromRow(row: DispatchData): Dispatch {\n return new Dispatch(row);\n }\n\n /**\n * Convert to database row format\n */\n toRow(): DispatchData {\n return {\n id: this.id,\n type: this.type,\n source: this.source,\n source_id: this.sourceId || null,\n payload: JSON.stringify(this.payload),\n status: this.status,\n attempts: this.attempts,\n last_error: this.lastError || null,\n processed_at: this.processedAt?.toISOString() || null,\n processed_by: this.processedBy || null,\n target_subscriber: this.targetSubscriber || null,\n correlation_id: this.correlationId || null,\n tenant_id: this.tenantId ?? null,\n metadata: JSON.stringify(this.metadata),\n created_at: this.createdAt.toISOString(),\n updated_at: new Date().toISOString(),\n };\n }\n\n /**\n * Get dispatch metadata for handlers\n */\n getMetadata(): DispatchMetadata {\n return {\n id: this.id,\n type: this.type,\n source: this.source,\n sourceId: this.sourceId,\n correlationId: this.correlationId,\n createdAt: this.createdAt,\n attempts: this.attempts,\n metadata: this.metadata,\n };\n }\n\n /**\n * Mark as processing\n */\n markProcessing(): void {\n this.status = 'processing';\n this.attempts = toSafeInteger(this.attempts + 1, 'Dispatch attempts');\n this.updatedAt = new Date();\n }\n\n /**\n * Mark as completed\n */\n markCompleted(processedBy: string): void {\n this.status = 'completed';\n this.processedAt = new Date();\n this.processedBy = processedBy;\n this.lastError = '';\n this.updatedAt = new Date();\n }\n\n /**\n * Mark as failed\n */\n markFailed(error: string): void {\n this.status = 'failed';\n this.lastError = error;\n this.updatedAt = new Date();\n }\n\n /**\n * Reset to pending for retry\n */\n resetForRetry(): void {\n this.status = 'pending';\n this.updatedAt = new Date();\n }\n\n /**\n * Check if this dispatch can be retried\n */\n canRetry(maxAttempts: number): boolean {\n return this.status === 'failed' && this.attempts < maxAttempts;\n }\n}\n"],"mappings":"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"}
@@ -13,7 +13,7 @@ export interface DispatchSubscriptionData {
13
13
  subscriber: string;
14
14
  handler: string;
15
15
  delivery: string;
16
- enabled: number;
16
+ enabled: number | string | bigint | null;
17
17
  tenant_id: string | null;
18
18
  created_at: string;
19
19
  updated_at: string;
@@ -1 +1 @@
1
- {"version":3,"file":"DispatchSubscription.d.ts","sourceRoot":"","sources":["../../../src/dispatch/models/DispatchSubscription.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,oBAAoB;IAC/B,gCAAgC;IAChC,MAAM,CAAC,QAAQ,CAAC,SAAS,kCAAkC;IAE3D,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IAEX,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IAEnB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IAEnB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAEhB,kFAAkF;IAClF,QAAQ,EAAE,SAAS,GAAG,QAAQ,CAAC;IAE/B,qCAAqC;IACrC,OAAO,EAAE,OAAO,CAAC;IAEjB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB,yBAAyB;IACzB,SAAS,EAAE,IAAI,CAAC;IAEhB,4BAA4B;IAC5B,SAAS,EAAE,IAAI,CAAC;gBAEJ,IAAI,GAAE,OAAO,CAAC,wBAAwB,CAAM;IAYxD;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,wBAAwB,GAAG,oBAAoB;IAInE;;OAEG;IACH,KAAK,IAAI,wBAAwB;IAcjC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAmCpC;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;OAEG;IACH,MAAM,IAAI,IAAI;IAKd;;OAEG;IACH,OAAO,IAAI,IAAI;CAIhB"}
1
+ {"version":3,"file":"DispatchSubscription.d.ts","sourceRoot":"","sources":["../../../src/dispatch/models/DispatchSubscription.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACzC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,oBAAoB;IAC/B,gCAAgC;IAChC,MAAM,CAAC,QAAQ,CAAC,SAAS,kCAAkC;IAE3D,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IAEX,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IAEnB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IAEnB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAEhB,kFAAkF;IAClF,QAAQ,EAAE,SAAS,GAAG,QAAQ,CAAC;IAE/B,qCAAqC;IACrC,OAAO,EAAE,OAAO,CAAC;IAEjB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB,yBAAyB;IACzB,SAAS,EAAE,IAAI,CAAC;IAEhB,4BAA4B;IAC5B,SAAS,EAAE,IAAI,CAAC;gBAEJ,IAAI,GAAE,OAAO,CAAC,wBAAwB,CAAM;IAkBxD;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,wBAAwB,GAAG,oBAAoB;IAInE;;OAEG;IACH,KAAK,IAAI,wBAAwB;IAcjC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAmCpC;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;OAEG;IACH,MAAM,IAAI,IAAI;IAKd;;OAEG;IACH,OAAO,IAAI,IAAI;CAIhB"}
@@ -1,3 +1,4 @@
1
+ import { toSafeBooleanInteger } from "../../utils/safe-integer.js";
1
2
  import { makeId } from "@happyvertical/utils";
2
3
  //#region src/dispatch/models/DispatchSubscription.ts
3
4
  /**
@@ -40,7 +41,7 @@ var DispatchSubscription = class DispatchSubscription {
40
41
  this.subscriber = data.subscriber || "";
41
42
  this.handler = data.handler || "handleDispatch";
42
43
  this.delivery = data.delivery || "compete";
43
- this.enabled = data.enabled !== void 0 ? Boolean(data.enabled) : true;
44
+ this.enabled = data.enabled != null ? toSafeBooleanInteger(data.enabled, "Dispatch subscription enabled flag") : true;
44
45
  this.tenantId = data.tenant_id ?? null;
45
46
  this.createdAt = data.created_at ? new Date(data.created_at) : /* @__PURE__ */ new Date();
46
47
  this.updatedAt = data.updated_at ? new Date(data.updated_at) : /* @__PURE__ */ new Date();
@@ -1 +1 @@
1
- {"version":3,"file":"DispatchSubscription.js","names":[],"sources":["../../../src/dispatch/models/DispatchSubscription.ts"],"sourcesContent":["/**\n * DispatchSubscription Model - Persistent subscription for dispatch processing\n *\n * Subscriptions are stored in _smrt_dispatch_subscriptions and define which\n * agents should receive which dispatch types. Supports wildcard patterns.\n */\n\nimport { makeId } from '@happyvertical/utils';\n\n/**\n * Raw subscription data as stored in the database\n */\nexport interface DispatchSubscriptionData {\n id: string;\n signal_type: string;\n subscriber: string;\n handler: string;\n delivery: string;\n enabled: number;\n tenant_id: string | null;\n created_at: string;\n updated_at: string;\n}\n\n/**\n * DispatchSubscription model for persistent dispatch routing\n */\nexport class DispatchSubscription {\n /** Table name for this model */\n static readonly tableName = '_smrt_dispatch_subscriptions';\n\n /** Unique identifier */\n id: string;\n\n /** Signal type pattern (supports wildcards like 'campaign.*') */\n signalType: string;\n\n /** Subscriber name (usually agent name) */\n subscriber: string;\n\n /** Handler method name */\n handler: string;\n\n /** Delivery mode: 'compete' (first-to-claim) or 'fanout' (per-subscriber copy) */\n delivery: 'compete' | 'fanout';\n\n /** Whether subscription is active */\n enabled: boolean;\n\n /**\n * Tenant the subscription was registered in (server-derived). `null` for\n * global / non-tenant subscriptions. Recorded for auditing and per-tenant\n * subscription separation (S5 #1398).\n */\n tenantId: string | null;\n\n /** Creation timestamp */\n createdAt: Date;\n\n /** Last update timestamp */\n updatedAt: Date;\n\n constructor(data: Partial<DispatchSubscriptionData> = {}) {\n this.id = data.id || makeId();\n this.signalType = data.signal_type || '';\n this.subscriber = data.subscriber || '';\n this.handler = data.handler || 'handleDispatch';\n this.delivery = (data.delivery as 'compete' | 'fanout') || 'compete';\n this.enabled = data.enabled !== undefined ? Boolean(data.enabled) : true;\n this.tenantId = data.tenant_id ?? null;\n this.createdAt = data.created_at ? new Date(data.created_at) : new Date();\n this.updatedAt = data.updated_at ? new Date(data.updated_at) : new Date();\n }\n\n /**\n * Create a DispatchSubscription from a database row\n */\n static fromRow(row: DispatchSubscriptionData): DispatchSubscription {\n return new DispatchSubscription(row);\n }\n\n /**\n * Convert to database row format\n */\n toRow(): DispatchSubscriptionData {\n return {\n id: this.id,\n signal_type: this.signalType,\n subscriber: this.subscriber,\n handler: this.handler,\n delivery: this.delivery,\n enabled: this.enabled ? 1 : 0,\n tenant_id: this.tenantId ?? null,\n created_at: this.createdAt.toISOString(),\n updated_at: new Date().toISOString(),\n };\n }\n\n /**\n * Check if this subscription matches a given signal type\n *\n * Supports wildcard patterns:\n * - 'campaign.*' matches 'campaign.started', 'campaign.completed'\n * - '*' matches everything\n * - 'campaign.*.completed' matches 'campaign.summer.completed'\n *\n * @param signalType - The signal type to match against\n * @returns True if this subscription matches the signal type\n */\n matches(signalType: string): boolean {\n if (!this.enabled) {\n return false;\n }\n\n // Exact match\n if (this.signalType === signalType) {\n return true;\n }\n\n // If no wildcards, no match\n if (!this.signalType.includes('*')) {\n return false;\n }\n\n // Convert wildcard pattern to regex\n // First escape special regex characters except * and .\n // Then convert . to \\. and * to [^.]+\n let pattern = this.signalType;\n\n // Escape regex special chars except * and .\n pattern = pattern.replace(/[+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n // Escape dots\n pattern = pattern.replace(/\\./g, '\\\\.');\n\n // Convert * to match one segment (anything except .)\n pattern = pattern.replace(/\\*/g, '[^.]+');\n\n // Create regex for full match\n const regex = new RegExp(`^${pattern}$`);\n\n return regex.test(signalType);\n }\n\n /**\n * Check if this is a wildcard subscription\n */\n isWildcard(): boolean {\n return this.signalType.includes('*');\n }\n\n /**\n * Enable this subscription\n */\n enable(): void {\n this.enabled = true;\n this.updatedAt = new Date();\n }\n\n /**\n * Disable this subscription\n */\n disable(): void {\n this.enabled = false;\n this.updatedAt = new Date();\n }\n}\n"],"mappings":";;;;;;;;;;;AA2BA,IAAa,uBAAb,MAAa,qBAAqB;;CAEhC,OAAgB,YAAY;;CAG5B;;CAGA;;CAGA;;CAGA;;CAGA;;CAGA;;;;;;CAOA;;CAGA;;CAGA;CAEA,YAAY,OAA0C,CAAC,GAAG;EACxD,KAAK,KAAK,KAAK,MAAM,OAAO;EAC5B,KAAK,aAAa,KAAK,eAAe;EACtC,KAAK,aAAa,KAAK,cAAc;EACrC,KAAK,UAAU,KAAK,WAAW;EAC/B,KAAK,WAAY,KAAK,YAAqC;EAC3D,KAAK,UAAU,KAAK,YAAY,KAAA,IAAY,QAAQ,KAAK,OAAO,IAAI;EACpE,KAAK,WAAW,KAAK,aAAa;EAClC,KAAK,YAAY,KAAK,aAAa,IAAI,KAAK,KAAK,UAAU,oBAAI,IAAI,KAAK;EACxE,KAAK,YAAY,KAAK,aAAa,IAAI,KAAK,KAAK,UAAU,oBAAI,IAAI,KAAK;CAC1E;;;;CAKA,OAAO,QAAQ,KAAqD;EAClE,OAAO,IAAI,qBAAqB,GAAG;CACrC;;;;CAKA,QAAkC;EAChC,OAAO;GACL,IAAI,KAAK;GACT,aAAa,KAAK;GAClB,YAAY,KAAK;GACjB,SAAS,KAAK;GACd,UAAU,KAAK;GACf,SAAS,KAAK,UAAU,IAAI;GAC5B,WAAW,KAAK,YAAY;GAC5B,YAAY,KAAK,UAAU,YAAY;GACvC,6BAAY,IAAI,KAAK,EAAA,CAAE,YAAY;EACrC;CACF;;;;;;;;;;;;CAaA,QAAQ,YAA6B;EACnC,IAAI,CAAC,KAAK,SACR,OAAO;EAIT,IAAI,KAAK,eAAe,YACtB,OAAO;EAIT,IAAI,CAAC,KAAK,WAAW,SAAS,GAAG,GAC/B,OAAO;EAMT,IAAI,UAAU,KAAK;EAGnB,UAAU,QAAQ,QAAQ,qBAAqB,MAAM;EAGrD,UAAU,QAAQ,QAAQ,OAAO,KAAK;EAGtC,UAAU,QAAQ,QAAQ,OAAO,OAAO;EAKxC,OAAO,IAFW,OAAO,IAAI,QAAQ,EAE9B,CAAA,CAAM,KAAK,UAAU;CAC9B;;;;CAKA,aAAsB;EACpB,OAAO,KAAK,WAAW,SAAS,GAAG;CACrC;;;;CAKA,SAAe;EACb,KAAK,UAAU;EACf,KAAK,4BAAY,IAAI,KAAK;CAC5B;;;;CAKA,UAAgB;EACd,KAAK,UAAU;EACf,KAAK,4BAAY,IAAI,KAAK;CAC5B;AACF"}
1
+ {"version":3,"file":"DispatchSubscription.js","names":[],"sources":["../../../src/dispatch/models/DispatchSubscription.ts"],"sourcesContent":["/**\n * DispatchSubscription Model - Persistent subscription for dispatch processing\n *\n * Subscriptions are stored in _smrt_dispatch_subscriptions and define which\n * agents should receive which dispatch types. Supports wildcard patterns.\n */\n\nimport { makeId } from '@happyvertical/utils';\nimport { toSafeBooleanInteger } from '../../utils/safe-integer.js';\n\n/**\n * Raw subscription data as stored in the database\n */\nexport interface DispatchSubscriptionData {\n id: string;\n signal_type: string;\n subscriber: string;\n handler: string;\n delivery: string;\n enabled: number | string | bigint | null;\n tenant_id: string | null;\n created_at: string;\n updated_at: string;\n}\n\n/**\n * DispatchSubscription model for persistent dispatch routing\n */\nexport class DispatchSubscription {\n /** Table name for this model */\n static readonly tableName = '_smrt_dispatch_subscriptions';\n\n /** Unique identifier */\n id: string;\n\n /** Signal type pattern (supports wildcards like 'campaign.*') */\n signalType: string;\n\n /** Subscriber name (usually agent name) */\n subscriber: string;\n\n /** Handler method name */\n handler: string;\n\n /** Delivery mode: 'compete' (first-to-claim) or 'fanout' (per-subscriber copy) */\n delivery: 'compete' | 'fanout';\n\n /** Whether subscription is active */\n enabled: boolean;\n\n /**\n * Tenant the subscription was registered in (server-derived). `null` for\n * global / non-tenant subscriptions. Recorded for auditing and per-tenant\n * subscription separation (S5 #1398).\n */\n tenantId: string | null;\n\n /** Creation timestamp */\n createdAt: Date;\n\n /** Last update timestamp */\n updatedAt: Date;\n\n constructor(data: Partial<DispatchSubscriptionData> = {}) {\n this.id = data.id || makeId();\n this.signalType = data.signal_type || '';\n this.subscriber = data.subscriber || '';\n this.handler = data.handler || 'handleDispatch';\n this.delivery = (data.delivery as 'compete' | 'fanout') || 'compete';\n this.enabled =\n data.enabled != null\n ? toSafeBooleanInteger(\n data.enabled,\n 'Dispatch subscription enabled flag',\n )\n : true;\n this.tenantId = data.tenant_id ?? null;\n this.createdAt = data.created_at ? new Date(data.created_at) : new Date();\n this.updatedAt = data.updated_at ? new Date(data.updated_at) : new Date();\n }\n\n /**\n * Create a DispatchSubscription from a database row\n */\n static fromRow(row: DispatchSubscriptionData): DispatchSubscription {\n return new DispatchSubscription(row);\n }\n\n /**\n * Convert to database row format\n */\n toRow(): DispatchSubscriptionData {\n return {\n id: this.id,\n signal_type: this.signalType,\n subscriber: this.subscriber,\n handler: this.handler,\n delivery: this.delivery,\n enabled: this.enabled ? 1 : 0,\n tenant_id: this.tenantId ?? null,\n created_at: this.createdAt.toISOString(),\n updated_at: new Date().toISOString(),\n };\n }\n\n /**\n * Check if this subscription matches a given signal type\n *\n * Supports wildcard patterns:\n * - 'campaign.*' matches 'campaign.started', 'campaign.completed'\n * - '*' matches everything\n * - 'campaign.*.completed' matches 'campaign.summer.completed'\n *\n * @param signalType - The signal type to match against\n * @returns True if this subscription matches the signal type\n */\n matches(signalType: string): boolean {\n if (!this.enabled) {\n return false;\n }\n\n // Exact match\n if (this.signalType === signalType) {\n return true;\n }\n\n // If no wildcards, no match\n if (!this.signalType.includes('*')) {\n return false;\n }\n\n // Convert wildcard pattern to regex\n // First escape special regex characters except * and .\n // Then convert . to \\. and * to [^.]+\n let pattern = this.signalType;\n\n // Escape regex special chars except * and .\n pattern = pattern.replace(/[+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n // Escape dots\n pattern = pattern.replace(/\\./g, '\\\\.');\n\n // Convert * to match one segment (anything except .)\n pattern = pattern.replace(/\\*/g, '[^.]+');\n\n // Create regex for full match\n const regex = new RegExp(`^${pattern}$`);\n\n return regex.test(signalType);\n }\n\n /**\n * Check if this is a wildcard subscription\n */\n isWildcard(): boolean {\n return this.signalType.includes('*');\n }\n\n /**\n * Enable this subscription\n */\n enable(): void {\n this.enabled = true;\n this.updatedAt = new Date();\n }\n\n /**\n * Disable this subscription\n */\n disable(): void {\n this.enabled = false;\n this.updatedAt = new Date();\n }\n}\n"],"mappings":";;;;;;;;;;;;AA4BA,IAAa,uBAAb,MAAa,qBAAqB;;CAEhC,OAAgB,YAAY;;CAG5B;;CAGA;;CAGA;;CAGA;;CAGA;;CAGA;;;;;;CAOA;;CAGA;;CAGA;CAEA,YAAY,OAA0C,CAAC,GAAG;EACxD,KAAK,KAAK,KAAK,MAAM,OAAO;EAC5B,KAAK,aAAa,KAAK,eAAe;EACtC,KAAK,aAAa,KAAK,cAAc;EACrC,KAAK,UAAU,KAAK,WAAW;EAC/B,KAAK,WAAY,KAAK,YAAqC;EAC3D,KAAK,UACH,KAAK,WAAW,OACZ,qBACE,KAAK,SACL,oCACF,IACA;EACN,KAAK,WAAW,KAAK,aAAa;EAClC,KAAK,YAAY,KAAK,aAAa,IAAI,KAAK,KAAK,UAAU,oBAAI,IAAI,KAAK;EACxE,KAAK,YAAY,KAAK,aAAa,IAAI,KAAK,KAAK,UAAU,oBAAI,IAAI,KAAK;CAC1E;;;;CAKA,OAAO,QAAQ,KAAqD;EAClE,OAAO,IAAI,qBAAqB,GAAG;CACrC;;;;CAKA,QAAkC;EAChC,OAAO;GACL,IAAI,KAAK;GACT,aAAa,KAAK;GAClB,YAAY,KAAK;GACjB,SAAS,KAAK;GACd,UAAU,KAAK;GACf,SAAS,KAAK,UAAU,IAAI;GAC5B,WAAW,KAAK,YAAY;GAC5B,YAAY,KAAK,UAAU,YAAY;GACvC,6BAAY,IAAI,KAAK,EAAA,CAAE,YAAY;EACrC;CACF;;;;;;;;;;;;CAaA,QAAQ,YAA6B;EACnC,IAAI,CAAC,KAAK,SACR,OAAO;EAIT,IAAI,KAAK,eAAe,YACtB,OAAO;EAIT,IAAI,CAAC,KAAK,WAAW,SAAS,GAAG,GAC/B,OAAO;EAMT,IAAI,UAAU,KAAK;EAGnB,UAAU,QAAQ,QAAQ,qBAAqB,MAAM;EAGrD,UAAU,QAAQ,QAAQ,OAAO,KAAK;EAGtC,UAAU,QAAQ,QAAQ,OAAO,OAAO;EAKxC,OAAO,IAFW,OAAO,IAAI,QAAQ,EAE9B,CAAA,CAAM,KAAK,UAAU;CAC9B;;;;CAKA,aAAsB;EACpB,OAAO,KAAK,WAAW,SAAS,GAAG;CACrC;;;;CAKA,SAAe;EACb,KAAK,UAAU;EACf,KAAK,4BAAY,IAAI,KAAK;CAC5B;;;;CAKA,UAAgB;EACd,KAAK,UAAU;EACf,KAAK,4BAAY,IAAI,KAAK;CAC5B;AACF"}
@@ -121,6 +121,11 @@ export interface DispatchCleanupOptions {
121
121
  completedOlderThanDays?: number;
122
122
  /** Delete failed dispatches older than this many days */
123
123
  failedOlderThanDays?: number;
124
+ /**
125
+ * Count the dispatches the cutoffs select without deleting them (#2375).
126
+ * The result reports what a real cleanup with the same options would remove.
127
+ */
128
+ dryRun?: boolean;
124
129
  }
125
130
  /**
126
131
  * Result of dispatch cleanup
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/dispatch/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/dispatch/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * In-process write serialization for embedded database engines (#2360).
3
+ *
4
+ * File-backed SQLite (libsql) allows one writer at a time and the SDK opens a
5
+ * NEW connection for every null-aware upsert attempt (`transaction("write")`),
6
+ * with no busy timeout on either connection. Any other write on the root
7
+ * connection while such a transaction is open — most commonly the change-feed
8
+ * append that follows every model save — collides as `SQLITE_BUSY`, and under
9
+ * sustained concurrency (a `Promise.all` of creates) the two sides livelock
10
+ * through every retry tier. Before the tenant-led conflict target (#2360) the
11
+ * null-aware path was rare (custom conflict keys with NULL values, #1246);
12
+ * with `tenant_id` leading every tenant-scoped default key, EVERY NULL-tenant
13
+ * (global) create takes it, so the collision became the steady state.
14
+ *
15
+ * The queue makes the process a single writer per database for exactly the
16
+ * two participants of that collision:
17
+ *
18
+ * - a `save()` whose upsert conflict values contain NULL (the only shape the
19
+ * SDK routes through a second-connection transaction), and
20
+ * - the change-feed append (a root-connection write that follows every save).
21
+ *
22
+ * Each hold is a LEAF database call — never an interceptor or hook — so the
23
+ * queue cannot deadlock on nested saves. PostgreSQL is never serialized here:
24
+ * its null-aware upsert already arbitrates through advisory locks
25
+ * server-side, and cross-process writers exist that an in-process queue could
26
+ * not order anyway (which is also why this queue is a mitigation for embedded
27
+ * engines' in-process writers, not a cross-process guarantee — embedded
28
+ * engines have no cross-process writers in supported deployments).
29
+ */
30
+ interface QueueableDatabase {
31
+ url?: string;
32
+ }
33
+ /**
34
+ * Whether `db` is an embedded engine (SQLite/DuckDB/JSON — anything that is
35
+ * not PostgreSQL). Handles without a URL are treated as embedded: serializing
36
+ * them is at worst a throughput cost, while not serializing an embedded
37
+ * handle reintroduces the livelock.
38
+ */
39
+ export declare function isEmbeddedDatabase(db: QueueableDatabase): boolean;
40
+ /**
41
+ * Run `operation` after every previously queued write on the same database
42
+ * has settled. When `serialize` is false the operation runs immediately.
43
+ */
44
+ export declare function withEmbeddedWriteQueue<T>(db: QueueableDatabase, serialize: boolean, operation: () => Promise<T>): Promise<T>;
45
+ export {};
46
+ //# sourceMappingURL=embedded-write-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedded-write-queue.d.ts","sourceRoot":"","sources":["../src/embedded-write-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAMH,UAAU,iBAAiB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAEjE;AAMD;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,CAAC,EAC5C,EAAE,EAAE,iBAAiB,EACrB,SAAS,EAAE,OAAO,EAClB,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC,CAoBZ"}
@@ -0,0 +1,66 @@
1
+ //#region src/embedded-write-queue.ts
2
+ /**
3
+ * In-process write serialization for embedded database engines (#2360).
4
+ *
5
+ * File-backed SQLite (libsql) allows one writer at a time and the SDK opens a
6
+ * NEW connection for every null-aware upsert attempt (`transaction("write")`),
7
+ * with no busy timeout on either connection. Any other write on the root
8
+ * connection while such a transaction is open — most commonly the change-feed
9
+ * append that follows every model save — collides as `SQLITE_BUSY`, and under
10
+ * sustained concurrency (a `Promise.all` of creates) the two sides livelock
11
+ * through every retry tier. Before the tenant-led conflict target (#2360) the
12
+ * null-aware path was rare (custom conflict keys with NULL values, #1246);
13
+ * with `tenant_id` leading every tenant-scoped default key, EVERY NULL-tenant
14
+ * (global) create takes it, so the collision became the steady state.
15
+ *
16
+ * The queue makes the process a single writer per database for exactly the
17
+ * two participants of that collision:
18
+ *
19
+ * - a `save()` whose upsert conflict values contain NULL (the only shape the
20
+ * SDK routes through a second-connection transaction), and
21
+ * - the change-feed append (a root-connection write that follows every save).
22
+ *
23
+ * Each hold is a LEAF database call — never an interceptor or hook — so the
24
+ * queue cannot deadlock on nested saves. PostgreSQL is never serialized here:
25
+ * its null-aware upsert already arbitrates through advisory locks
26
+ * server-side, and cross-process writers exist that an in-process queue could
27
+ * not order anyway (which is also why this queue is a mitigation for embedded
28
+ * engines' in-process writers, not a cross-process guarantee — embedded
29
+ * engines have no cross-process writers in supported deployments).
30
+ */
31
+ /** Queue tail per database identity (URL string, or the handle itself). */
32
+ var urlQueues = /* @__PURE__ */ new Map();
33
+ var handleQueues = /* @__PURE__ */ new WeakMap();
34
+ /**
35
+ * Whether `db` is an embedded engine (SQLite/DuckDB/JSON — anything that is
36
+ * not PostgreSQL). Handles without a URL are treated as embedded: serializing
37
+ * them is at worst a throughput cost, while not serializing an embedded
38
+ * handle reintroduces the livelock.
39
+ */
40
+ function isEmbeddedDatabase(db) {
41
+ return !/^postgres(?:ql)?:/iu.test(db?.url ?? "");
42
+ }
43
+ function queueKey(db) {
44
+ return typeof db?.url === "string" && db.url.length > 0 ? db.url : db;
45
+ }
46
+ /**
47
+ * Run `operation` after every previously queued write on the same database
48
+ * has settled. When `serialize` is false the operation runs immediately.
49
+ */
50
+ async function withEmbeddedWriteQueue(db, serialize, operation) {
51
+ if (!serialize) return operation();
52
+ const key = queueKey(db);
53
+ const next = ((typeof key === "string" ? urlQueues.get(key) : handleQueues.get(key)) ?? Promise.resolve()).then(operation, operation);
54
+ const tail = next.catch(() => void 0);
55
+ if (typeof key === "string") {
56
+ urlQueues.set(key, tail);
57
+ tail.finally(() => {
58
+ if (urlQueues.get(key) === tail) urlQueues.delete(key);
59
+ });
60
+ } else handleQueues.set(key, tail);
61
+ return next;
62
+ }
63
+ //#endregion
64
+ export { isEmbeddedDatabase, withEmbeddedWriteQueue };
65
+
66
+ //# sourceMappingURL=embedded-write-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedded-write-queue.js","names":[],"sources":["../src/embedded-write-queue.ts"],"sourcesContent":["/**\n * In-process write serialization for embedded database engines (#2360).\n *\n * File-backed SQLite (libsql) allows one writer at a time and the SDK opens a\n * NEW connection for every null-aware upsert attempt (`transaction(\"write\")`),\n * with no busy timeout on either connection. Any other write on the root\n * connection while such a transaction is open — most commonly the change-feed\n * append that follows every model save — collides as `SQLITE_BUSY`, and under\n * sustained concurrency (a `Promise.all` of creates) the two sides livelock\n * through every retry tier. Before the tenant-led conflict target (#2360) the\n * null-aware path was rare (custom conflict keys with NULL values, #1246);\n * with `tenant_id` leading every tenant-scoped default key, EVERY NULL-tenant\n * (global) create takes it, so the collision became the steady state.\n *\n * The queue makes the process a single writer per database for exactly the\n * two participants of that collision:\n *\n * - a `save()` whose upsert conflict values contain NULL (the only shape the\n * SDK routes through a second-connection transaction), and\n * - the change-feed append (a root-connection write that follows every save).\n *\n * Each hold is a LEAF database call — never an interceptor or hook — so the\n * queue cannot deadlock on nested saves. PostgreSQL is never serialized here:\n * its null-aware upsert already arbitrates through advisory locks\n * server-side, and cross-process writers exist that an in-process queue could\n * not order anyway (which is also why this queue is a mitigation for embedded\n * engines' in-process writers, not a cross-process guarantee — embedded\n * engines have no cross-process writers in supported deployments).\n */\n\n/** Queue tail per database identity (URL string, or the handle itself). */\nconst urlQueues = new Map<string, Promise<unknown>>();\nconst handleQueues = new WeakMap<object, Promise<unknown>>();\n\ninterface QueueableDatabase {\n url?: string;\n}\n\n/**\n * Whether `db` is an embedded engine (SQLite/DuckDB/JSON — anything that is\n * not PostgreSQL). Handles without a URL are treated as embedded: serializing\n * them is at worst a throughput cost, while not serializing an embedded\n * handle reintroduces the livelock.\n */\nexport function isEmbeddedDatabase(db: QueueableDatabase): boolean {\n return !/^postgres(?:ql)?:/iu.test(db?.url ?? '');\n}\n\nfunction queueKey(db: QueueableDatabase): string | object {\n return typeof db?.url === 'string' && db.url.length > 0 ? db.url : db;\n}\n\n/**\n * Run `operation` after every previously queued write on the same database\n * has settled. When `serialize` is false the operation runs immediately.\n */\nexport async function withEmbeddedWriteQueue<T>(\n db: QueueableDatabase,\n serialize: boolean,\n operation: () => Promise<T>,\n): Promise<T> {\n if (!serialize) return operation();\n\n const key = queueKey(db);\n const previous =\n (typeof key === 'string' ? urlQueues.get(key) : handleQueues.get(key)) ??\n Promise.resolve();\n const next = previous.then(operation, operation);\n // The stored tail must never reject, or one failed write would poison the\n // queue for every later writer.\n const tail = next.catch(() => undefined);\n if (typeof key === 'string') {\n urlQueues.set(key, tail);\n void tail.finally(() => {\n if (urlQueues.get(key) === tail) urlQueues.delete(key);\n });\n } else {\n handleQueues.set(key, tail);\n }\n return next;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAM,4BAAY,IAAI,IAA8B;AACpD,IAAM,+BAAe,IAAI,QAAkC;;;;;;;AAY3D,SAAgB,mBAAmB,IAAgC;CACjE,OAAO,CAAC,sBAAsB,KAAK,IAAI,OAAO,EAAE;AAClD;AAEA,SAAS,SAAS,IAAwC;CACxD,OAAO,OAAO,IAAI,QAAQ,YAAY,GAAG,IAAI,SAAS,IAAI,GAAG,MAAM;AACrE;;;;;AAMA,eAAsB,uBACpB,IACA,WACA,WACY;CACZ,IAAI,CAAC,WAAW,OAAO,UAAU;CAEjC,MAAM,MAAM,SAAS,EAAE;CAIvB,MAAM,SAFH,OAAO,QAAQ,WAAW,UAAU,IAAI,GAAG,IAAI,aAAa,IAAI,GAAG,MACpE,QAAQ,QAAQ,EAAA,CACI,KAAK,WAAW,SAAS;CAG/C,MAAM,OAAO,KAAK,YAAY,KAAA,CAAS;CACvC,IAAI,OAAO,QAAQ,UAAU;EAC3B,UAAU,IAAI,KAAK,IAAI;EACvB,KAAU,cAAc;GACtB,IAAI,UAAU,IAAI,GAAG,MAAM,MAAM,UAAU,OAAO,GAAG;EACvD,CAAC;CACH,OACE,aAAa,IAAI,KAAK,IAAI;CAE5B,OAAO;AACT"}
@@ -40,6 +40,13 @@ export declare class EmbeddingStorage {
40
40
  model?: string;
41
41
  limit?: number;
42
42
  minSimilarity?: number;
43
+ /**
44
+ * Restrict candidates to these object ids BEFORE ranking (#2365).
45
+ * `undefined` means no restriction; an empty array means no candidates
46
+ * (the search returns no results). Used by tenant-scoped collections so
47
+ * top-K never ranks rows the caller is not allowed to see.
48
+ */
49
+ objectIds?: string[];
43
50
  }, vector?: VectorCapabilities): Promise<Array<{
44
51
  objectId: string;
45
52
  similarity: number;
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/embeddings/storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAEhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAO/C;;GAEG;AACH,qBAAa,gBAAgB;IAC3B;;;;;;OAMG;WACU,MAAM,CACjB,EAAE,EAAE,iBAAiB,EACrB,IAAI,EAAE;QACJ,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,EACD,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC;IA6ChB;;;;;;;;;;;;;OAaG;WACU,aAAa,CACxB,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,aAAa,CAAC,EAAE,MAAM,CAAC;KACnB,EACN,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAuE3D;;;;;;;OAOG;WACU,mBAAmB,CAC9B,GAAG,EAAE,iBAAiB,EACtB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC;IAShB;;;;;;;;;OASG;WACU,GAAG,CACd,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAclC;;;;;;;OAOG;WACU,YAAY,CACvB,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,eAAe,EAAE,CAAC;IAY7B;;;;;;;;OAQG;WACU,YAAY,CACvB,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,eAAe,EAAE,CAAC;IAqB7B;;;;;;;OAOG;WACU,0BAA0B,CACrC,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC;IAWpB;;;;;;OAMG;WACU,eAAe,CAC1B,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAQhB;;;;;;;;OAQG;WACU,MAAM,CACjB,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC;IAWhB;;;;;OAKG;WACU,WAAW,CAAC,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IASjE;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ;CAexB"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/embeddings/storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAIhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAO/C;;GAEG;AACH,qBAAa,gBAAgB;IAC3B;;;;;;OAMG;WACU,MAAM,CACjB,EAAE,EAAE,iBAAiB,EACrB,IAAI,EAAE;QACJ,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,EACD,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC;IA6ChB;;;;;;;;;;;;;OAaG;WACU,aAAa,CACxB,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB;;;;;WAKG;QACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;KACjB,EACN,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IA+G3D;;;;;;;OAOG;WACU,mBAAmB,CAC9B,GAAG,EAAE,iBAAiB,EACtB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC;IAShB;;;;;;;;;OASG;WACU,GAAG,CACd,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAclC;;;;;;;OAOG;WACU,YAAY,CACvB,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,eAAe,EAAE,CAAC;IAY7B;;;;;;;;OAQG;WACU,YAAY,CACvB,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,eAAe,EAAE,CAAC;IAqB7B;;;;;;;OAOG;WACU,0BAA0B,CACrC,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC;IAWpB;;;;;;OAMG;WACU,eAAe,CAC1B,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAQhB;;;;;;;;OAQG;WACU,MAAM,CACjB,EAAE,EAAE,iBAAiB,EACrB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC;IAWhB;;;;;OAKG;WACU,WAAW,CAAC,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IASjE;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ;CAexB"}
@@ -1,3 +1,5 @@
1
+ import { chunkArray } from "../utils/chunk.js";
2
+ import { toSafeInteger } from "../utils/safe-integer.js";
1
3
  import { CosineSimilarity } from "./similarity.js";
2
4
  import { createLogger } from "@happyvertical/logger";
3
5
  import { randomUUID } from "node:crypto";
@@ -69,7 +71,8 @@ var EmbeddingStorage = class EmbeddingStorage {
69
71
  * @returns Array of { objectId, similarity } ranked by similarity (highest first)
70
72
  */
71
73
  static async searchSimilar(db, objectClass, embedding, options = {}, vector) {
72
- const { field, model, limit = 10, minSimilarity = 0 } = options;
74
+ const { field, model, limit = 10, minSimilarity = 0, objectIds } = options;
75
+ if (objectIds !== void 0 && objectIds.length === 0) return [];
73
76
  if (vector) try {
74
77
  const conditions = ["object_class = $2"];
75
78
  const params = [objectClass];
@@ -81,21 +84,36 @@ var EmbeddingStorage = class EmbeddingStorage {
81
84
  conditions.push(`model = $${params.length + 2}`);
82
85
  params.push(model);
83
86
  }
84
- return (await vector.search("_smrt_embeddings", VECTOR_COLUMN, embedding, {
85
- limit,
86
- metric: "cosine",
87
- where: conditions.join(" AND "),
88
- params
89
- })).map((r) => ({
90
- objectId: r.object_id,
91
- similarity: 1 - r.distance
92
- })).filter((r) => r.similarity >= minSimilarity);
87
+ const idChunks = objectIds ? chunkArray(objectIds, 900) : [void 0];
88
+ const merged = [];
89
+ for (const chunk of idChunks) {
90
+ const chunkConditions = [...conditions];
91
+ const chunkParams = [...params];
92
+ if (chunk) {
93
+ const placeholders = chunk.map((_, index) => `$${chunkParams.length + 2 + index}`);
94
+ chunkConditions.push(`object_id IN (${placeholders.join(", ")})`);
95
+ chunkParams.push(...chunk);
96
+ }
97
+ const results = await vector.search("_smrt_embeddings", VECTOR_COLUMN, embedding, {
98
+ limit,
99
+ metric: "cosine",
100
+ where: chunkConditions.join(" AND "),
101
+ params: chunkParams
102
+ });
103
+ merged.push(...results.map((r) => ({
104
+ objectId: r.object_id,
105
+ similarity: 1 - r.distance
106
+ })));
107
+ }
108
+ return merged.filter((r) => r.similarity >= minSimilarity).sort((a, b) => b.similarity - a.similarity).slice(0, limit);
93
109
  } catch (error) {
94
110
  logger.warn(`[embeddings] Native vector search failed, falling back to in-memory: ${error instanceof Error ? error.message : String(error)}`);
95
111
  }
96
112
  const storedEmbeddings = await EmbeddingStorage.listForClass(db, objectClass, field, model);
97
- if (storedEmbeddings.length === 0) return [];
98
- return storedEmbeddings.map((stored) => ({
113
+ const allowedIds = objectIds ? new Set(objectIds) : void 0;
114
+ const candidates = allowedIds ? storedEmbeddings.filter((stored) => allowedIds.has(stored.object_id)) : storedEmbeddings;
115
+ if (candidates.length === 0) return [];
116
+ return candidates.map((stored) => ({
99
117
  objectId: stored.object_id,
100
118
  similarity: CosineSimilarity.calculate(embedding, stored.embedding)
101
119
  })).filter((item) => item.similarity >= minSimilarity).sort((a, b) => b.similarity - a.similarity).slice(0, limit);
@@ -234,7 +252,7 @@ var EmbeddingStorage = class EmbeddingStorage {
234
252
  content_hash: row.content_hash,
235
253
  embedding: JSON.parse(row.embedding),
236
254
  model: row.model,
237
- dimensions: row.dimensions,
255
+ dimensions: toSafeInteger(row.dimensions, "Embedding dimensions"),
238
256
  provider: row.provider,
239
257
  created_at: new Date(row.created_at),
240
258
  updated_at: new Date(row.updated_at)
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","names":[],"sources":["../../src/embeddings/storage.ts"],"sourcesContent":["/**\n * Embedding Storage Operations\n *\n * CRUD operations for the _smrt_embeddings system table.\n * Supports both JSON-based and native vector storage strategies.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { createLogger } from '@happyvertical/logger';\nimport type { DatabaseInterface, VectorCapabilities } from '@happyvertical/sql';\nimport { CosineSimilarity } from './similarity';\nimport type { StoredEmbedding } from './types';\n\nconst logger = createLogger({ level: 'info' });\n\n/** Column name for native vector storage */\nconst VECTOR_COLUMN = 'embedding_vector';\n\n/**\n * Storage operations for embeddings in _smrt_embeddings table\n */\nexport class EmbeddingStorage {\n /**\n * Insert or update an embedding\n *\n * @param db - Database interface\n * @param data - Embedding data to store\n * @param vector - Optional vector capabilities for native storage\n */\n static async upsert(\n db: DatabaseInterface,\n data: {\n objectClass: string;\n objectId: string;\n fieldName: string;\n contentHash: string;\n embedding: number[];\n model: string;\n dimensions: number;\n provider?: string;\n },\n vector?: VectorCapabilities,\n ): Promise<void> {\n const now = new Date().toISOString();\n const id = randomUUID();\n\n await db.upsert(\n '_smrt_embeddings',\n ['object_class', 'object_id', 'field_name', 'model'],\n {\n id,\n object_class: data.objectClass,\n object_id: data.objectId,\n field_name: data.fieldName,\n content_hash: data.contentHash,\n embedding: JSON.stringify(data.embedding),\n model: data.model,\n dimensions: data.dimensions,\n provider: data.provider || null,\n created_at: now,\n updated_at: now,\n },\n );\n\n // Also store native vector if capability is available\n if (vector) {\n try {\n await vector.upsertVector(\n '_smrt_embeddings',\n {\n object_class: data.objectClass,\n object_id: data.objectId,\n field_name: data.fieldName,\n model: data.model,\n },\n VECTOR_COLUMN,\n data.embedding,\n );\n } catch (error) {\n // Log but don't fail — JSON fallback is always available\n logger.warn(\n `[embeddings] Failed to store native vector: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n }\n\n /**\n * Search for similar embeddings using the best available strategy.\n *\n * When vector capabilities are provided, delegates to database-level\n * vector search. Otherwise falls back to loading all embeddings and\n * computing cosine similarity in memory.\n *\n * @param db - Database interface\n * @param objectClass - Class name to search within\n * @param embedding - Query embedding vector\n * @param options - Search options\n * @param vector - Optional vector capabilities for native search\n * @returns Array of { objectId, similarity } ranked by similarity (highest first)\n */\n static async searchSimilar(\n db: DatabaseInterface,\n objectClass: string,\n embedding: number[],\n options: {\n field?: string;\n model?: string;\n limit?: number;\n minSimilarity?: number;\n } = {},\n vector?: VectorCapabilities,\n ): Promise<Array<{ objectId: string; similarity: number }>> {\n const { field, model, limit = 10, minSimilarity = 0 } = options;\n\n // Native vector search path\n if (vector) {\n try {\n // Build WHERE clause for class/field/model filtering.\n // Parameter numbering starts at $2 because vector.search() reserves\n // $1 for the query vector embedding in its internal SQL query.\n const conditions: string[] = ['object_class = $2'];\n const params: unknown[] = [objectClass];\n\n if (field) {\n conditions.push(`field_name = $${params.length + 2}`);\n params.push(field);\n }\n\n if (model) {\n conditions.push(`model = $${params.length + 2}`);\n params.push(model);\n }\n\n const results = await vector.search(\n '_smrt_embeddings',\n VECTOR_COLUMN,\n embedding,\n {\n limit,\n metric: 'cosine',\n where: conditions.join(' AND '),\n params,\n },\n );\n\n // Convert cosine distance (0=identical, 2=opposite) to similarity (1=identical, -1=opposite)\n return results\n .map((r) => ({\n objectId: r.object_id as string,\n similarity: 1 - r.distance,\n }))\n .filter((r) => r.similarity >= minSimilarity);\n } catch (error) {\n // Fall back to in-memory search on vector search failure\n logger.warn(\n `[embeddings] Native vector search failed, falling back to in-memory: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n // In-memory fallback: load all embeddings and compute cosine similarity\n const storedEmbeddings = await EmbeddingStorage.listForClass(\n db,\n objectClass,\n field,\n model,\n );\n\n if (storedEmbeddings.length === 0) {\n return [];\n }\n\n return storedEmbeddings\n .map((stored) => ({\n objectId: stored.object_id,\n similarity: CosineSimilarity.calculate(embedding, stored.embedding),\n }))\n .filter((item) => item.similarity >= minSimilarity)\n .sort((a, b) => b.similarity - a.similarity)\n .slice(0, limit);\n }\n\n /**\n * Ensure the native vector column and index exist on _smrt_embeddings.\n * Called once during initialization when storage: 'native' is configured.\n *\n * @param db - Database interface\n * @param dimensions - Vector dimensions\n * @param vector - Vector capabilities\n */\n static async ensureVectorStorage(\n _db: DatabaseInterface,\n dimensions: number,\n vector: VectorCapabilities,\n ): Promise<void> {\n await vector.ensureColumn('_smrt_embeddings', VECTOR_COLUMN, dimensions);\n await vector.ensureIndex('_smrt_embeddings', VECTOR_COLUMN, {\n dimensions,\n metric: 'cosine',\n type: 'hnsw',\n });\n }\n\n /**\n * Get a specific embedding\n *\n * @param db - Database interface\n * @param objectClass - Class name\n * @param objectId - Object ID\n * @param fieldName - Field name\n * @param model - Model used\n * @returns Stored embedding or null\n */\n static async get(\n db: DatabaseInterface,\n objectClass: string,\n objectId: string,\n fieldName: string,\n model: string,\n ): Promise<StoredEmbedding | null> {\n const result = await db.single`\n SELECT * FROM _smrt_embeddings\n WHERE object_class = ${objectClass}\n AND object_id = ${objectId}\n AND field_name = ${fieldName}\n AND model = ${model}\n `;\n\n if (!result) return null;\n\n return EmbeddingStorage.parseRow(result);\n }\n\n /**\n * Get all embeddings for an object\n *\n * @param db - Database interface\n * @param objectClass - Class name\n * @param objectId - Object ID\n * @returns Array of stored embeddings\n */\n static async getForObject(\n db: DatabaseInterface,\n objectClass: string,\n objectId: string,\n ): Promise<StoredEmbedding[]> {\n const { rows } = await db.query(\n `SELECT * FROM _smrt_embeddings WHERE object_class = ? AND object_id = ?`,\n objectClass,\n objectId,\n );\n\n return rows.map((row: Record<string, unknown>) =>\n EmbeddingStorage.parseRow(row),\n );\n }\n\n /**\n * List embeddings for a class (optionally filtered by field and model)\n *\n * @param db - Database interface\n * @param objectClass - Class name\n * @param fieldName - Optional field filter\n * @param model - Optional model filter\n * @returns Array of stored embeddings\n */\n static async listForClass(\n db: DatabaseInterface,\n objectClass: string,\n fieldName?: string,\n model?: string,\n ): Promise<StoredEmbedding[]> {\n let sql = `SELECT * FROM _smrt_embeddings WHERE object_class = ?`;\n const params: unknown[] = [objectClass];\n\n if (fieldName) {\n sql += ` AND field_name = ?`;\n params.push(fieldName);\n }\n\n if (model) {\n sql += ` AND model = ?`;\n params.push(model);\n }\n\n const { rows } = await db.query(sql, ...params);\n\n return rows.map((row: Record<string, unknown>) =>\n EmbeddingStorage.parseRow(row),\n );\n }\n\n /**\n * Get object IDs that have embeddings for a specific field\n *\n * @param db - Database interface\n * @param objectClass - Class name\n * @param fieldName - Field name\n * @returns Array of object IDs\n */\n static async getObjectIdsWithEmbeddings(\n db: DatabaseInterface,\n objectClass: string,\n fieldName: string,\n ): Promise<string[]> {\n const { rows } = await db.query(\n `SELECT DISTINCT object_id FROM _smrt_embeddings\n WHERE object_class = ? AND field_name = ?`,\n objectClass,\n fieldName,\n );\n\n return rows.map((row: Record<string, unknown>) => row.object_id as string);\n }\n\n /**\n * Delete all embeddings for an object\n *\n * @param db - Database interface\n * @param objectClass - Class name\n * @param objectId - Object ID\n */\n static async deleteForObject(\n db: DatabaseInterface,\n objectClass: string,\n objectId: string,\n ): Promise<void> {\n await db.query(\n `DELETE FROM _smrt_embeddings WHERE object_class = ? AND object_id = ?`,\n objectClass,\n objectId,\n );\n }\n\n /**\n * Delete a specific embedding\n *\n * @param db - Database interface\n * @param objectClass - Class name\n * @param objectId - Object ID\n * @param fieldName - Field name\n * @param model - Model used\n */\n static async delete(\n db: DatabaseInterface,\n objectClass: string,\n objectId: string,\n fieldName: string,\n model: string,\n ): Promise<void> {\n await db.query(\n `DELETE FROM _smrt_embeddings\n WHERE object_class = ? AND object_id = ? AND field_name = ? AND model = ?`,\n objectClass,\n objectId,\n fieldName,\n model,\n );\n }\n\n /**\n * Check if embedding table exists\n *\n * @param db - Database interface\n * @returns True if table exists\n */\n static async tableExists(db: DatabaseInterface): Promise<boolean> {\n try {\n await db.query(`SELECT 1 FROM _smrt_embeddings LIMIT 1`);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Parse a database row into StoredEmbedding\n */\n private static parseRow(row: Record<string, unknown>): StoredEmbedding {\n return {\n id: row.id as string,\n object_class: row.object_class as string,\n object_id: row.object_id as string,\n field_name: row.field_name as string,\n content_hash: row.content_hash as string,\n embedding: JSON.parse(row.embedding as string) as number[],\n model: row.model as string,\n dimensions: row.dimensions as number,\n provider: row.provider as string | undefined,\n created_at: new Date(row.created_at as string),\n updated_at: new Date(row.updated_at as string),\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"storage.js","names":[],"sources":["../../src/embeddings/storage.ts"],"sourcesContent":["/**\n * Embedding Storage Operations\n *\n * CRUD operations for the _smrt_embeddings system table.\n * Supports both JSON-based and native vector storage strategies.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { createLogger } from '@happyvertical/logger';\nimport type { DatabaseInterface, VectorCapabilities } from '@happyvertical/sql';\nimport { chunkArray, IN_LIST_CHUNK_SIZE } from '../utils/chunk';\nimport { toSafeInteger } from '../utils/safe-integer.js';\nimport { CosineSimilarity } from './similarity';\nimport type { StoredEmbedding } from './types';\n\nconst logger = createLogger({ level: 'info' });\n\n/** Column name for native vector storage */\nconst VECTOR_COLUMN = 'embedding_vector';\n\n/**\n * Storage operations for embeddings in _smrt_embeddings table\n */\nexport class EmbeddingStorage {\n /**\n * Insert or update an embedding\n *\n * @param db - Database interface\n * @param data - Embedding data to store\n * @param vector - Optional vector capabilities for native storage\n */\n static async upsert(\n db: DatabaseInterface,\n data: {\n objectClass: string;\n objectId: string;\n fieldName: string;\n contentHash: string;\n embedding: number[];\n model: string;\n dimensions: number;\n provider?: string;\n },\n vector?: VectorCapabilities,\n ): Promise<void> {\n const now = new Date().toISOString();\n const id = randomUUID();\n\n await db.upsert(\n '_smrt_embeddings',\n ['object_class', 'object_id', 'field_name', 'model'],\n {\n id,\n object_class: data.objectClass,\n object_id: data.objectId,\n field_name: data.fieldName,\n content_hash: data.contentHash,\n embedding: JSON.stringify(data.embedding),\n model: data.model,\n dimensions: data.dimensions,\n provider: data.provider || null,\n created_at: now,\n updated_at: now,\n },\n );\n\n // Also store native vector if capability is available\n if (vector) {\n try {\n await vector.upsertVector(\n '_smrt_embeddings',\n {\n object_class: data.objectClass,\n object_id: data.objectId,\n field_name: data.fieldName,\n model: data.model,\n },\n VECTOR_COLUMN,\n data.embedding,\n );\n } catch (error) {\n // Log but don't fail — JSON fallback is always available\n logger.warn(\n `[embeddings] Failed to store native vector: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n }\n\n /**\n * Search for similar embeddings using the best available strategy.\n *\n * When vector capabilities are provided, delegates to database-level\n * vector search. Otherwise falls back to loading all embeddings and\n * computing cosine similarity in memory.\n *\n * @param db - Database interface\n * @param objectClass - Class name to search within\n * @param embedding - Query embedding vector\n * @param options - Search options\n * @param vector - Optional vector capabilities for native search\n * @returns Array of { objectId, similarity } ranked by similarity (highest first)\n */\n static async searchSimilar(\n db: DatabaseInterface,\n objectClass: string,\n embedding: number[],\n options: {\n field?: string;\n model?: string;\n limit?: number;\n minSimilarity?: number;\n /**\n * Restrict candidates to these object ids BEFORE ranking (#2365).\n * `undefined` means no restriction; an empty array means no candidates\n * (the search returns no results). Used by tenant-scoped collections so\n * top-K never ranks rows the caller is not allowed to see.\n */\n objectIds?: string[];\n } = {},\n vector?: VectorCapabilities,\n ): Promise<Array<{ objectId: string; similarity: number }>> {\n const { field, model, limit = 10, minSimilarity = 0, objectIds } = options;\n\n // An explicit empty candidate set means \"nothing is visible\" — never fall\n // through to the unrestricted search.\n if (objectIds !== undefined && objectIds.length === 0) {\n return [];\n }\n\n // Native vector search path\n if (vector) {\n try {\n // Build WHERE clause for class/field/model filtering.\n // Parameter numbering starts at $2 because vector.search() reserves\n // $1 for the query vector embedding in its internal SQL query.\n const conditions: string[] = ['object_class = $2'];\n const params: unknown[] = [objectClass];\n\n if (field) {\n conditions.push(`field_name = $${params.length + 2}`);\n params.push(field);\n }\n\n if (model) {\n conditions.push(`model = $${params.length + 2}`);\n params.push(model);\n }\n\n // Candidate restriction (#2365). The id list is chunked so one query\n // never exceeds the backend's bind-variable limit; per-chunk top-K\n // results merge into an exact global top-K because every chunk\n // returns its own best `limit` candidates.\n const idChunks = objectIds\n ? chunkArray(objectIds, IN_LIST_CHUNK_SIZE)\n : [undefined];\n\n const merged: Array<{ objectId: string; similarity: number }> = [];\n for (const chunk of idChunks) {\n const chunkConditions = [...conditions];\n const chunkParams = [...params];\n if (chunk) {\n const placeholders = chunk.map(\n (_, index) => `$${chunkParams.length + 2 + index}`,\n );\n chunkConditions.push(`object_id IN (${placeholders.join(', ')})`);\n chunkParams.push(...chunk);\n }\n\n const results = await vector.search(\n '_smrt_embeddings',\n VECTOR_COLUMN,\n embedding,\n {\n limit,\n metric: 'cosine',\n where: chunkConditions.join(' AND '),\n params: chunkParams,\n },\n );\n\n // Convert cosine distance (0=identical, 2=opposite) to similarity\n // (1=identical, -1=opposite)\n merged.push(\n ...results.map((r) => ({\n objectId: r.object_id as string,\n similarity: 1 - r.distance,\n })),\n );\n }\n\n return merged\n .filter((r) => r.similarity >= minSimilarity)\n .sort((a, b) => b.similarity - a.similarity)\n .slice(0, limit);\n } catch (error) {\n // Fall back to in-memory search on vector search failure\n logger.warn(\n `[embeddings] Native vector search failed, falling back to in-memory: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n // In-memory fallback: load all embeddings and compute cosine similarity\n const storedEmbeddings = await EmbeddingStorage.listForClass(\n db,\n objectClass,\n field,\n model,\n );\n\n // Candidate restriction (#2365): drop invisible rows BEFORE ranking so\n // top-K is computed only over what the caller may see.\n const allowedIds = objectIds ? new Set(objectIds) : undefined;\n const candidates = allowedIds\n ? storedEmbeddings.filter((stored) => allowedIds.has(stored.object_id))\n : storedEmbeddings;\n\n if (candidates.length === 0) {\n return [];\n }\n\n return candidates\n .map((stored) => ({\n objectId: stored.object_id,\n similarity: CosineSimilarity.calculate(embedding, stored.embedding),\n }))\n .filter((item) => item.similarity >= minSimilarity)\n .sort((a, b) => b.similarity - a.similarity)\n .slice(0, limit);\n }\n\n /**\n * Ensure the native vector column and index exist on _smrt_embeddings.\n * Called once during initialization when storage: 'native' is configured.\n *\n * @param db - Database interface\n * @param dimensions - Vector dimensions\n * @param vector - Vector capabilities\n */\n static async ensureVectorStorage(\n _db: DatabaseInterface,\n dimensions: number,\n vector: VectorCapabilities,\n ): Promise<void> {\n await vector.ensureColumn('_smrt_embeddings', VECTOR_COLUMN, dimensions);\n await vector.ensureIndex('_smrt_embeddings', VECTOR_COLUMN, {\n dimensions,\n metric: 'cosine',\n type: 'hnsw',\n });\n }\n\n /**\n * Get a specific embedding\n *\n * @param db - Database interface\n * @param objectClass - Class name\n * @param objectId - Object ID\n * @param fieldName - Field name\n * @param model - Model used\n * @returns Stored embedding or null\n */\n static async get(\n db: DatabaseInterface,\n objectClass: string,\n objectId: string,\n fieldName: string,\n model: string,\n ): Promise<StoredEmbedding | null> {\n const result = await db.single`\n SELECT * FROM _smrt_embeddings\n WHERE object_class = ${objectClass}\n AND object_id = ${objectId}\n AND field_name = ${fieldName}\n AND model = ${model}\n `;\n\n if (!result) return null;\n\n return EmbeddingStorage.parseRow(result);\n }\n\n /**\n * Get all embeddings for an object\n *\n * @param db - Database interface\n * @param objectClass - Class name\n * @param objectId - Object ID\n * @returns Array of stored embeddings\n */\n static async getForObject(\n db: DatabaseInterface,\n objectClass: string,\n objectId: string,\n ): Promise<StoredEmbedding[]> {\n const { rows } = await db.query(\n `SELECT * FROM _smrt_embeddings WHERE object_class = ? AND object_id = ?`,\n objectClass,\n objectId,\n );\n\n return rows.map((row: Record<string, unknown>) =>\n EmbeddingStorage.parseRow(row),\n );\n }\n\n /**\n * List embeddings for a class (optionally filtered by field and model)\n *\n * @param db - Database interface\n * @param objectClass - Class name\n * @param fieldName - Optional field filter\n * @param model - Optional model filter\n * @returns Array of stored embeddings\n */\n static async listForClass(\n db: DatabaseInterface,\n objectClass: string,\n fieldName?: string,\n model?: string,\n ): Promise<StoredEmbedding[]> {\n let sql = `SELECT * FROM _smrt_embeddings WHERE object_class = ?`;\n const params: unknown[] = [objectClass];\n\n if (fieldName) {\n sql += ` AND field_name = ?`;\n params.push(fieldName);\n }\n\n if (model) {\n sql += ` AND model = ?`;\n params.push(model);\n }\n\n const { rows } = await db.query(sql, ...params);\n\n return rows.map((row: Record<string, unknown>) =>\n EmbeddingStorage.parseRow(row),\n );\n }\n\n /**\n * Get object IDs that have embeddings for a specific field\n *\n * @param db - Database interface\n * @param objectClass - Class name\n * @param fieldName - Field name\n * @returns Array of object IDs\n */\n static async getObjectIdsWithEmbeddings(\n db: DatabaseInterface,\n objectClass: string,\n fieldName: string,\n ): Promise<string[]> {\n const { rows } = await db.query(\n `SELECT DISTINCT object_id FROM _smrt_embeddings\n WHERE object_class = ? AND field_name = ?`,\n objectClass,\n fieldName,\n );\n\n return rows.map((row: Record<string, unknown>) => row.object_id as string);\n }\n\n /**\n * Delete all embeddings for an object\n *\n * @param db - Database interface\n * @param objectClass - Class name\n * @param objectId - Object ID\n */\n static async deleteForObject(\n db: DatabaseInterface,\n objectClass: string,\n objectId: string,\n ): Promise<void> {\n await db.query(\n `DELETE FROM _smrt_embeddings WHERE object_class = ? AND object_id = ?`,\n objectClass,\n objectId,\n );\n }\n\n /**\n * Delete a specific embedding\n *\n * @param db - Database interface\n * @param objectClass - Class name\n * @param objectId - Object ID\n * @param fieldName - Field name\n * @param model - Model used\n */\n static async delete(\n db: DatabaseInterface,\n objectClass: string,\n objectId: string,\n fieldName: string,\n model: string,\n ): Promise<void> {\n await db.query(\n `DELETE FROM _smrt_embeddings\n WHERE object_class = ? AND object_id = ? AND field_name = ? AND model = ?`,\n objectClass,\n objectId,\n fieldName,\n model,\n );\n }\n\n /**\n * Check if embedding table exists\n *\n * @param db - Database interface\n * @returns True if table exists\n */\n static async tableExists(db: DatabaseInterface): Promise<boolean> {\n try {\n await db.query(`SELECT 1 FROM _smrt_embeddings LIMIT 1`);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Parse a database row into StoredEmbedding\n */\n private static parseRow(row: Record<string, unknown>): StoredEmbedding {\n return {\n id: row.id as string,\n object_class: row.object_class as string,\n object_id: row.object_id as string,\n field_name: row.field_name as string,\n content_hash: row.content_hash as string,\n embedding: JSON.parse(row.embedding as string) as number[],\n model: row.model as string,\n dimensions: toSafeInteger(row.dimensions, 'Embedding dimensions'),\n provider: row.provider as string | undefined,\n created_at: new Date(row.created_at as string),\n updated_at: new Date(row.updated_at as string),\n };\n }\n}\n"],"mappings":";;;;;;;;;;;;AAeA,IAAM,SAAS,aAAa,EAAE,OAAO,OAAO,CAAC;;AAG7C,IAAM,gBAAgB;;;;AAKtB,IAAa,mBAAb,MAAa,iBAAiB;;;;;;;;CAQ5B,aAAa,OACX,IACA,MAUA,QACe;EACf,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;EACnC,MAAM,KAAK,WAAW;EAEtB,MAAM,GAAG,OACP,oBACA;GAAC;GAAgB;GAAa;GAAc;EAAO,GACnD;GACE;GACA,cAAc,KAAK;GACnB,WAAW,KAAK;GAChB,YAAY,KAAK;GACjB,cAAc,KAAK;GACnB,WAAW,KAAK,UAAU,KAAK,SAAS;GACxC,OAAO,KAAK;GACZ,YAAY,KAAK;GACjB,UAAU,KAAK,YAAY;GAC3B,YAAY;GACZ,YAAY;EACd,CACF;EAGA,IAAI,QACF,IAAI;GACF,MAAM,OAAO,aACX,oBACA;IACE,cAAc,KAAK;IACnB,WAAW,KAAK;IAChB,YAAY,KAAK;IACjB,OAAO,KAAK;GACd,GACA,eACA,KAAK,SACP;EACF,SAAS,OAAO;GAEd,OAAO,KACL,+CAA+C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACtG;EACF;CAEJ;;;;;;;;;;;;;;;CAgBA,aAAa,cACX,IACA,aACA,WACA,UAYI,CAAC,GACL,QAC0D;EAC1D,MAAM,EAAE,OAAO,OAAO,QAAQ,IAAI,gBAAgB,GAAG,cAAc;EAInE,IAAI,cAAc,KAAA,KAAa,UAAU,WAAW,GAClD,OAAO,CAAC;EAIV,IAAI,QACF,IAAI;GAIF,MAAM,aAAuB,CAAC,mBAAmB;GACjD,MAAM,SAAoB,CAAC,WAAW;GAEtC,IAAI,OAAO;IACT,WAAW,KAAK,iBAAiB,OAAO,SAAS,GAAG;IACpD,OAAO,KAAK,KAAK;GACnB;GAEA,IAAI,OAAO;IACT,WAAW,KAAK,YAAY,OAAO,SAAS,GAAG;IAC/C,OAAO,KAAK,KAAK;GACnB;GAMA,MAAM,WAAW,YACb,WAAW,WAAA,GAA6B,IACxC,CAAC,KAAA,CAAS;GAEd,MAAM,SAA0D,CAAC;GACjE,KAAK,MAAM,SAAS,UAAU;IAC5B,MAAM,kBAAkB,CAAC,GAAG,UAAU;IACtC,MAAM,cAAc,CAAC,GAAG,MAAM;IAC9B,IAAI,OAAO;KACT,MAAM,eAAe,MAAM,KACxB,GAAG,UAAU,IAAI,YAAY,SAAS,IAAI,OAC7C;KACA,gBAAgB,KAAK,iBAAiB,aAAa,KAAK,IAAI,EAAE,EAAE;KAChE,YAAY,KAAK,GAAG,KAAK;IAC3B;IAEA,MAAM,UAAU,MAAM,OAAO,OAC3B,oBACA,eACA,WACA;KACE;KACA,QAAQ;KACR,OAAO,gBAAgB,KAAK,OAAO;KACnC,QAAQ;IACV,CACF;IAIA,OAAO,KACL,GAAG,QAAQ,KAAK,OAAO;KACrB,UAAU,EAAE;KACZ,YAAY,IAAI,EAAE;IACpB,EAAE,CACJ;GACF;GAEA,OAAO,OACJ,QAAQ,MAAM,EAAE,cAAc,aAAa,CAAC,CAC5C,MAAM,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU,CAAC,CAC3C,MAAM,GAAG,KAAK;EACnB,SAAS,OAAO;GAEd,OAAO,KACL,wEAAwE,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC/H;EACF;EAIF,MAAM,mBAAmB,MAAM,iBAAiB,aAC9C,IACA,aACA,OACA,KACF;EAIA,MAAM,aAAa,YAAY,IAAI,IAAI,SAAS,IAAI,KAAA;EACpD,MAAM,aAAa,aACf,iBAAiB,QAAQ,WAAW,WAAW,IAAI,OAAO,SAAS,CAAC,IACpE;EAEJ,IAAI,WAAW,WAAW,GACxB,OAAO,CAAC;EAGV,OAAO,WACJ,KAAK,YAAY;GAChB,UAAU,OAAO;GACjB,YAAY,iBAAiB,UAAU,WAAW,OAAO,SAAS;EACpE,EAAE,CAAC,CACF,QAAQ,SAAS,KAAK,cAAc,aAAa,CAAC,CAClD,MAAM,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU,CAAC,CAC3C,MAAM,GAAG,KAAK;CACnB;;;;;;;;;CAUA,aAAa,oBACX,KACA,YACA,QACe;EACf,MAAM,OAAO,aAAa,oBAAoB,eAAe,UAAU;EACvE,MAAM,OAAO,YAAY,oBAAoB,eAAe;GAC1D;GACA,QAAQ;GACR,MAAM;EACR,CAAC;CACH;;;;;;;;;;;CAYA,aAAa,IACX,IACA,aACA,UACA,WACA,OACiC;EACjC,MAAM,SAAS,MAAM,GAAG,MAAM;;6BAEL,YAAY;0BACf,SAAS;2BACR,UAAU;sBACf,MAAM;;EAGxB,IAAI,CAAC,QAAQ,OAAO;EAEpB,OAAO,iBAAiB,SAAS,MAAM;CACzC;;;;;;;;;CAUA,aAAa,aACX,IACA,aACA,UAC4B;EAC5B,MAAM,EAAE,SAAS,MAAM,GAAG,MACxB,2EACA,aACA,QACF;EAEA,OAAO,KAAK,KAAK,QACf,iBAAiB,SAAS,GAAG,CAC/B;CACF;;;;;;;;;;CAWA,aAAa,aACX,IACA,aACA,WACA,OAC4B;EAC5B,IAAI,MAAM;EACV,MAAM,SAAoB,CAAC,WAAW;EAEtC,IAAI,WAAW;GACb,OAAO;GACP,OAAO,KAAK,SAAS;EACvB;EAEA,IAAI,OAAO;GACT,OAAO;GACP,OAAO,KAAK,KAAK;EACnB;EAEA,MAAM,EAAE,SAAS,MAAM,GAAG,MAAM,KAAK,GAAG,MAAM;EAE9C,OAAO,KAAK,KAAK,QACf,iBAAiB,SAAS,GAAG,CAC/B;CACF;;;;;;;;;CAUA,aAAa,2BACX,IACA,aACA,WACmB;EACnB,MAAM,EAAE,SAAS,MAAM,GAAG,MACxB;mDAEA,aACA,SACF;EAEA,OAAO,KAAK,KAAK,QAAiC,IAAI,SAAmB;CAC3E;;;;;;;;CASA,aAAa,gBACX,IACA,aACA,UACe;EACf,MAAM,GAAG,MACP,yEACA,aACA,QACF;CACF;;;;;;;;;;CAWA,aAAa,OACX,IACA,aACA,UACA,WACA,OACe;EACf,MAAM,GAAG,MACP;mFAEA,aACA,UACA,WACA,KACF;CACF;;;;;;;CAQA,aAAa,YAAY,IAAyC;EAChE,IAAI;GACF,MAAM,GAAG,MAAM,wCAAwC;GACvD,OAAO;EACT,QAAQ;GACN,OAAO;EACT;CACF;;;;CAKA,OAAe,SAAS,KAA+C;EACrE,OAAO;GACL,IAAI,IAAI;GACR,cAAc,IAAI;GAClB,WAAW,IAAI;GACf,YAAY,IAAI;GAChB,cAAc,IAAI;GAClB,WAAW,KAAK,MAAM,IAAI,SAAmB;GAC7C,OAAO,IAAI;GACX,YAAY,cAAc,IAAI,YAAY,sBAAsB;GAChE,UAAU,IAAI;GACd,YAAY,IAAI,KAAK,IAAI,UAAoB;GAC7C,YAAY,IAAI,KAAK,IAAI,UAAoB;EAC/C;CACF;AACF"}
package/dist/errors.d.ts CHANGED
@@ -17,7 +17,7 @@ export declare abstract class SmrtError extends Error {
17
17
  name: string;
18
18
  message: string;
19
19
  code: string;
20
- category: "ai" | "database" | "filesystem" | "validation" | "network" | "configuration" | "runtime";
20
+ category: "database" | "ai" | "filesystem" | "validation" | "network" | "configuration" | "runtime";
21
21
  details: Record<string, unknown> | undefined;
22
22
  stack: string | undefined;
23
23
  cause: {
@@ -1,6 +1,6 @@
1
+ import { chunkArray } from "./utils/chunk.js";
1
2
  import { SmrtObject } from "./object.js";
2
3
  import { ObjectRegistry } from "./registry.js";
3
- import { chunkArray } from "./utils/chunk.js";
4
4
  //#region src/hierarchical.ts
5
5
  var SmrtHierarchical = class SmrtHierarchical extends SmrtObject {
6
6
  /**
package/dist/index.d.ts CHANGED
@@ -16,6 +16,7 @@
16
16
  * - MCPGenerator: Create MCP servers for AI model integration
17
17
  */
18
18
  export * from './adapters/index';
19
+ export { buildCascadePlan, type CascadePlan, type CascadePolymorphicReference, type CascadeReference, type CascadeRegistryView, type CascadeResult, cascadeReferencesTo, normalizeOnDelete, type OnDeleteAction, runCascadeDelete, } from './cascade';
19
20
  export { type AppendChangeInput, appendChange, bumpChangeFeed, CHANGE_FEED_INTERCEPTOR_NAME, CHANGE_FEED_TABLE, type ChangeFeedEntry, type ChangeFeedPage, type ChangeFeedRetention, type ChangeOperation, DEFAULT_CHANGES_LIMIT, ensureChangeFeedTable, type GetChangesOptions, getChangesSince, getTableVersion, getTenantScopedChangesSince, MAX_CHANGES_LIMIT, pruneChangeFeed, registerChangeFeedWriter, resetChangeFeedWarnings, unregisterChangeFeedWriter, } from './change-feed';
20
21
  export { CHANGE_SIGNAL_CHANNEL, type ChangeSignal, type ChangeSignalListener, resetChangeSignals, stopChangeSignalListeners, subscribeToChangeSignals, } from './change-signals';
21
22
  export { applyOneToManyChildAccessors, childAccessorName, } from './child-accessors';
@@ -54,7 +55,7 @@ export { smrt as smrtRegistry } from './registry';
54
55
  export * from './runtime/index';
55
56
  export { detectEngine, generateDDLForEngine } from './schema/ddl';
56
57
  export { type ConflictTargetInput, checkLiveSchemaParity, type ExpectedTableOrigin, type LiveParityFinding, type LiveParityFindingKind, type LiveParitySeverity, LiveSchemaParityError, type LiveSchemaParityOptions, type LiveSchemaParityReport, } from './schema/live-parity';
57
- export { getSystemTableShapes, type SystemTableShape, } from './schema/system-table-shapes';
58
+ export { getSystemTableShapes, SYSTEM_TABLE_NAMES, type SystemTableShape, } from './schema/system-table-shapes';
58
59
  export type { SchemaDefinition } from './schema/types';
59
60
  export * from './signals/index';
60
61
  export * from './sync/apply';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}