@seanhogg/builderforce-memory 2026.7.12 → 2026.9.27

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 (223) hide show
  1. package/README.md +10 -0
  2. package/dist/bridges/AnthropicBridge.d.ts +9 -1
  3. package/dist/bridges/AnthropicBridge.d.ts.map +1 -1
  4. package/dist/bridges/AnthropicBridge.js +58 -2
  5. package/dist/bridges/AnthropicBridge.js.map +1 -1
  6. package/dist/bridges/CachingBridge.d.ts +9 -1
  7. package/dist/bridges/CachingBridge.d.ts.map +1 -1
  8. package/dist/bridges/CachingBridge.js +24 -1
  9. package/dist/bridges/CachingBridge.js.map +1 -1
  10. package/dist/bridges/OpenAIBridge.d.ts +4 -1
  11. package/dist/bridges/OpenAIBridge.d.ts.map +1 -1
  12. package/dist/bridges/OpenAIBridge.js +40 -2
  13. package/dist/bridges/OpenAIBridge.js.map +1 -1
  14. package/dist/bridges/SemanticCachingBridge.d.ts +8 -1
  15. package/dist/bridges/SemanticCachingBridge.d.ts.map +1 -1
  16. package/dist/bridges/SemanticCachingBridge.js +26 -1
  17. package/dist/bridges/SemanticCachingBridge.js.map +1 -1
  18. package/dist/bridges/TransformerBridge.d.ts +13 -0
  19. package/dist/bridges/TransformerBridge.d.ts.map +1 -1
  20. package/dist/bridges/VertexAIBridge.d.ts +102 -0
  21. package/dist/bridges/VertexAIBridge.d.ts.map +1 -0
  22. package/dist/bridges/VertexAIBridge.js +224 -0
  23. package/dist/bridges/VertexAIBridge.js.map +1 -0
  24. package/dist/bridges/index.d.ts +3 -1
  25. package/dist/bridges/index.d.ts.map +1 -1
  26. package/dist/bridges/index.js +1 -0
  27. package/dist/bridges/index.js.map +1 -1
  28. package/dist/eval/EvalHarness.d.ts +37 -0
  29. package/dist/eval/EvalHarness.d.ts.map +1 -0
  30. package/dist/eval/EvalHarness.js +178 -0
  31. package/dist/eval/EvalHarness.js.map +1 -0
  32. package/dist/eval/graders.d.ts +76 -0
  33. package/dist/eval/graders.d.ts.map +1 -0
  34. package/dist/eval/graders.js +168 -0
  35. package/dist/eval/graders.js.map +1 -0
  36. package/dist/eval/index.d.ts +26 -0
  37. package/dist/eval/index.d.ts.map +1 -0
  38. package/dist/eval/index.js +26 -0
  39. package/dist/eval/index.js.map +1 -0
  40. package/dist/eval/types.d.ts +97 -0
  41. package/dist/eval/types.d.ts.map +1 -0
  42. package/dist/eval/types.js +5 -0
  43. package/dist/eval/types.js.map +1 -0
  44. package/dist/evermind/contract.d.ts +40 -0
  45. package/dist/evermind/contract.d.ts.map +1 -0
  46. package/dist/evermind/contract.js +13 -0
  47. package/dist/evermind/contract.js.map +1 -0
  48. package/dist/evermind/index.d.ts +15 -0
  49. package/dist/evermind/index.d.ts.map +1 -0
  50. package/dist/evermind/index.js +4 -0
  51. package/dist/evermind/index.js.map +1 -0
  52. package/dist/evermind/memoryBlock.d.ts +7 -0
  53. package/dist/evermind/memoryBlock.d.ts.map +1 -0
  54. package/dist/evermind/memoryBlock.js +37 -0
  55. package/dist/evermind/memoryBlock.js.map +1 -0
  56. package/dist/evermind/recall.d.ts +55 -0
  57. package/dist/evermind/recall.d.ts.map +1 -0
  58. package/dist/evermind/recall.js +105 -0
  59. package/dist/evermind/recall.js.map +1 -0
  60. package/dist/index.d.ts +18 -3
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +14 -1
  63. package/dist/index.js.map +1 -1
  64. package/dist/ingest/IngestionPipeline.d.ts +95 -0
  65. package/dist/ingest/IngestionPipeline.d.ts.map +1 -0
  66. package/dist/ingest/IngestionPipeline.js +295 -0
  67. package/dist/ingest/IngestionPipeline.js.map +1 -0
  68. package/dist/ingest/index.d.ts +10 -0
  69. package/dist/ingest/index.d.ts.map +1 -0
  70. package/dist/ingest/index.js +8 -0
  71. package/dist/ingest/index.js.map +1 -0
  72. package/dist/ingest/parsers.d.ts +53 -0
  73. package/dist/ingest/parsers.d.ts.map +1 -0
  74. package/dist/ingest/parsers.js +306 -0
  75. package/dist/ingest/parsers.js.map +1 -0
  76. package/dist/ingest/types.d.ts +114 -0
  77. package/dist/ingest/types.d.ts.map +1 -0
  78. package/dist/ingest/types.js +19 -0
  79. package/dist/ingest/types.js.map +1 -0
  80. package/dist/memory/MemoryStore.d.ts +33 -0
  81. package/dist/memory/MemoryStore.d.ts.map +1 -1
  82. package/dist/memory/MemoryStore.js +29 -3
  83. package/dist/memory/MemoryStore.js.map +1 -1
  84. package/dist/memory/index.d.ts +1 -1
  85. package/dist/memory/index.d.ts.map +1 -1
  86. package/dist/orchestration/AgentGraph.d.ts +104 -0
  87. package/dist/orchestration/AgentGraph.d.ts.map +1 -0
  88. package/dist/orchestration/AgentGraph.js +364 -0
  89. package/dist/orchestration/AgentGraph.js.map +1 -0
  90. package/dist/orchestration/checkpoint.d.ts +58 -0
  91. package/dist/orchestration/checkpoint.d.ts.map +1 -0
  92. package/dist/orchestration/checkpoint.js +92 -0
  93. package/dist/orchestration/checkpoint.js.map +1 -0
  94. package/dist/orchestration/index.d.ts +14 -0
  95. package/dist/orchestration/index.d.ts.map +1 -0
  96. package/dist/orchestration/index.js +10 -0
  97. package/dist/orchestration/index.js.map +1 -0
  98. package/dist/orchestration/patterns.d.ts +159 -0
  99. package/dist/orchestration/patterns.d.ts.map +1 -0
  100. package/dist/orchestration/patterns.js +371 -0
  101. package/dist/orchestration/patterns.js.map +1 -0
  102. package/dist/orchestration/types.d.ts +146 -0
  103. package/dist/orchestration/types.d.ts.map +1 -0
  104. package/dist/orchestration/types.js +20 -0
  105. package/dist/orchestration/types.js.map +1 -0
  106. package/dist/rag/EnterpriseRetriever.d.ts +114 -0
  107. package/dist/rag/EnterpriseRetriever.d.ts.map +1 -0
  108. package/dist/rag/EnterpriseRetriever.js +240 -0
  109. package/dist/rag/EnterpriseRetriever.js.map +1 -0
  110. package/dist/rag/index.d.ts +6 -0
  111. package/dist/rag/index.d.ts.map +1 -0
  112. package/dist/rag/index.js +5 -0
  113. package/dist/rag/index.js.map +1 -0
  114. package/dist/retrieval/bm25.d.ts +48 -1
  115. package/dist/retrieval/bm25.d.ts.map +1 -1
  116. package/dist/retrieval/bm25.js +59 -13
  117. package/dist/retrieval/bm25.js.map +1 -1
  118. package/dist/retrieval/index.d.ts +2 -2
  119. package/dist/retrieval/index.d.ts.map +1 -1
  120. package/dist/retrieval/index.js +1 -1
  121. package/dist/retrieval/index.js.map +1 -1
  122. package/dist/telemetry/InstrumentedBridge.d.ts +62 -0
  123. package/dist/telemetry/InstrumentedBridge.d.ts.map +1 -0
  124. package/dist/telemetry/InstrumentedBridge.js +120 -0
  125. package/dist/telemetry/InstrumentedBridge.js.map +1 -0
  126. package/dist/telemetry/MetricsRegistry.d.ts +88 -0
  127. package/dist/telemetry/MetricsRegistry.d.ts.map +1 -0
  128. package/dist/telemetry/MetricsRegistry.js +167 -0
  129. package/dist/telemetry/MetricsRegistry.js.map +1 -0
  130. package/dist/telemetry/Tracer.d.ts +137 -0
  131. package/dist/telemetry/Tracer.d.ts.map +1 -0
  132. package/dist/telemetry/Tracer.js +244 -0
  133. package/dist/telemetry/Tracer.js.map +1 -0
  134. package/dist/telemetry/exporters.d.ts +68 -0
  135. package/dist/telemetry/exporters.d.ts.map +1 -0
  136. package/dist/telemetry/exporters.js +145 -0
  137. package/dist/telemetry/exporters.js.map +1 -0
  138. package/dist/telemetry/index.d.ts +19 -0
  139. package/dist/telemetry/index.d.ts.map +1 -0
  140. package/dist/telemetry/index.js +12 -0
  141. package/dist/telemetry/index.js.map +1 -0
  142. package/dist/telemetry/pricing.d.ts +74 -0
  143. package/dist/telemetry/pricing.d.ts.map +1 -0
  144. package/dist/telemetry/pricing.js +98 -0
  145. package/dist/telemetry/pricing.js.map +1 -0
  146. package/dist/telemetry/traceparent.d.ts +50 -0
  147. package/dist/telemetry/traceparent.d.ts.map +1 -0
  148. package/dist/telemetry/traceparent.js +77 -0
  149. package/dist/telemetry/traceparent.js.map +1 -0
  150. package/dist/telemetry/types.d.ts +90 -0
  151. package/dist/telemetry/types.d.ts.map +1 -0
  152. package/dist/telemetry/types.js +17 -0
  153. package/dist/telemetry/types.js.map +1 -0
  154. package/dist/vectorstore/MemoryVectorStore.d.ts +72 -0
  155. package/dist/vectorstore/MemoryVectorStore.d.ts.map +1 -0
  156. package/dist/vectorstore/MemoryVectorStore.js +205 -0
  157. package/dist/vectorstore/MemoryVectorStore.js.map +1 -0
  158. package/dist/vectorstore/RestVectorStore.d.ts +79 -0
  159. package/dist/vectorstore/RestVectorStore.d.ts.map +1 -0
  160. package/dist/vectorstore/RestVectorStore.js +170 -0
  161. package/dist/vectorstore/RestVectorStore.js.map +1 -0
  162. package/dist/vectorstore/dialects.d.ts +101 -0
  163. package/dist/vectorstore/dialects.d.ts.map +1 -0
  164. package/dist/vectorstore/dialects.js +618 -0
  165. package/dist/vectorstore/dialects.js.map +1 -0
  166. package/dist/vectorstore/filter.d.ts +42 -0
  167. package/dist/vectorstore/filter.d.ts.map +1 -0
  168. package/dist/vectorstore/filter.js +132 -0
  169. package/dist/vectorstore/filter.js.map +1 -0
  170. package/dist/vectorstore/index.d.ts +15 -0
  171. package/dist/vectorstore/index.d.ts.map +1 -0
  172. package/dist/vectorstore/index.js +11 -0
  173. package/dist/vectorstore/index.js.map +1 -0
  174. package/dist/vectorstore/types.d.ts +151 -0
  175. package/dist/vectorstore/types.d.ts.map +1 -0
  176. package/dist/vectorstore/types.js +30 -0
  177. package/dist/vectorstore/types.js.map +1 -0
  178. package/package.json +39 -5
  179. package/src/bridges/AnthropicBridge.ts +68 -3
  180. package/src/bridges/CachingBridge.ts +28 -2
  181. package/src/bridges/OpenAIBridge.ts +48 -3
  182. package/src/bridges/SemanticCachingBridge.ts +29 -2
  183. package/src/bridges/TransformerBridge.ts +16 -0
  184. package/src/bridges/VertexAIBridge.ts +297 -0
  185. package/src/bridges/index.ts +3 -1
  186. package/src/eval/EvalHarness.ts +214 -0
  187. package/src/eval/graders.ts +218 -0
  188. package/src/eval/index.ts +26 -0
  189. package/src/eval/types.ts +107 -0
  190. package/src/evermind/contract.ts +39 -0
  191. package/src/evermind/index.ts +21 -0
  192. package/src/evermind/memoryBlock.ts +38 -0
  193. package/src/evermind/recall.ts +122 -0
  194. package/src/index.ts +232 -0
  195. package/src/ingest/IngestionPipeline.ts +378 -0
  196. package/src/ingest/index.ts +46 -0
  197. package/src/ingest/parsers.ts +318 -0
  198. package/src/ingest/types.ts +116 -0
  199. package/src/memory/MemoryStore.ts +55 -3
  200. package/src/memory/index.ts +1 -1
  201. package/src/orchestration/AgentGraph.ts +462 -0
  202. package/src/orchestration/checkpoint.ts +129 -0
  203. package/src/orchestration/index.ts +63 -0
  204. package/src/orchestration/patterns.ts +532 -0
  205. package/src/orchestration/types.ts +128 -0
  206. package/src/rag/EnterpriseRetriever.ts +348 -0
  207. package/src/rag/index.ts +18 -0
  208. package/src/retrieval/bm25.ts +71 -13
  209. package/src/retrieval/index.ts +2 -2
  210. package/src/telemetry/InstrumentedBridge.ts +152 -0
  211. package/src/telemetry/MetricsRegistry.ts +263 -0
  212. package/src/telemetry/Tracer.ts +310 -0
  213. package/src/telemetry/exporters.ts +187 -0
  214. package/src/telemetry/index.ts +56 -0
  215. package/src/telemetry/pricing.ts +129 -0
  216. package/src/telemetry/traceparent.ts +89 -0
  217. package/src/telemetry/types.ts +105 -0
  218. package/src/vectorstore/MemoryVectorStore.ts +250 -0
  219. package/src/vectorstore/RestVectorStore.ts +236 -0
  220. package/src/vectorstore/dialects.ts +702 -0
  221. package/src/vectorstore/filter.ts +148 -0
  222. package/src/vectorstore/index.ts +53 -0
  223. package/src/vectorstore/types.ts +149 -0
@@ -0,0 +1,148 @@
1
+ /**
2
+ * vectorstore/filter.ts — the ONE evaluator and the ONE scope compiler.
3
+ *
4
+ * Two rules this file exists to enforce:
5
+ *
6
+ * • A filter means the same thing everywhere. The in-process store, the
7
+ * post-filter a remote adapter applies when its dialect cannot express a
8
+ * clause, the ingest de-duplicator and the eval harness all call
9
+ * {@link matchesFilter}. A second implementation would eventually disagree
10
+ * about `null` or about a missing field, and a filter that disagrees with
11
+ * itself across two code paths is a cross-tenant leak waiting for traffic.
12
+ *
13
+ * • Access scope is compiled, never hand-written. {@link accessFilter} is the
14
+ * single definition of "what may this caller see", so a new adapter or a new
15
+ * query path inherits the rule instead of re-deriving it.
16
+ */
17
+
18
+ import {
19
+ ACL_FIELD,
20
+ ACL_PUBLIC,
21
+ SENSITIVITY_FIELD,
22
+ TENANT_FIELD,
23
+ type AccessScope,
24
+ type MetadataFilter,
25
+ type MetadataRecord,
26
+ type MetadataValue,
27
+ } from './types.js';
28
+
29
+ /**
30
+ * Compiles an access scope into a filter.
31
+ *
32
+ * The ACL clause is `anyOf(acl, [...principals, '*'])`. Records are required to
33
+ * carry a non-empty `acl` (ingestion defaults it to `['*']`), which is what makes
34
+ * the rule fail CLOSED: a record written without an ACL matches nothing rather
35
+ * than matching everyone.
36
+ */
37
+ export function accessFilter(scope: AccessScope): MetadataFilter {
38
+ const clauses: MetadataFilter[] = [
39
+ { op: 'eq', field: TENANT_FIELD, value: scope.tenantId },
40
+ { op: 'anyOf', field: ACL_FIELD, values: [...(scope.principals ?? []), ACL_PUBLIC] },
41
+ ];
42
+
43
+ if (scope.maxSensitivity !== undefined) {
44
+ clauses.push({ op: 'lte', field: SENSITIVITY_FIELD, value: scope.maxSensitivity });
45
+ }
46
+
47
+ return { op: 'and', filters: clauses };
48
+ }
49
+
50
+ /**
51
+ * ANDs a caller filter with a scope, dropping either when absent.
52
+ * Returns `undefined` only when neither is present — an unscoped query, which is
53
+ * legitimate for single-tenant use but never happens by accident, because the
54
+ * caller had to omit the scope explicitly.
55
+ */
56
+ export function combineFilters(
57
+ filter?: MetadataFilter,
58
+ scope?: AccessScope,
59
+ ): MetadataFilter | undefined {
60
+ const scoped = scope ? accessFilter(scope) : undefined;
61
+ if (filter && scoped) return { op: 'and', filters: [scoped, filter] };
62
+ return filter ?? scoped;
63
+ }
64
+
65
+ /** Evaluates a filter against one record's metadata. */
66
+ export function matchesFilter(metadata: MetadataRecord, filter?: MetadataFilter): boolean {
67
+ if (!filter) return true;
68
+
69
+ switch (filter.op) {
70
+ case 'and':
71
+ return filter.filters.every((f) => matchesFilter(metadata, f));
72
+ case 'or':
73
+ return filter.filters.some((f) => matchesFilter(metadata, f));
74
+ case 'not':
75
+ return !matchesFilter(metadata, filter.filter);
76
+ case 'exists':
77
+ return metadata[filter.field] !== undefined && metadata[filter.field] !== null;
78
+ case 'in':
79
+ return filter.values.some((v) => scalarEquals(metadata[filter.field], v));
80
+ case 'nin':
81
+ return !filter.values.some((v) => scalarEquals(metadata[filter.field], v));
82
+ case 'anyOf': {
83
+ const actual = metadata[filter.field];
84
+ if (actual === undefined || actual === null) return false;
85
+ const held = Array.isArray(actual) ? actual : [actual];
86
+ return held.some((h) => filter.values.some((v) => scalarEquals(h, v)));
87
+ }
88
+ case 'contains': {
89
+ const actual = metadata[filter.field];
90
+ if (typeof actual !== 'string') return false;
91
+ return actual.toLowerCase().includes(filter.value.toLowerCase());
92
+ }
93
+ default:
94
+ return compare(metadata[filter.field], filter.op, filter.value);
95
+ }
96
+ }
97
+
98
+ function scalarEquals(a: MetadataValue | undefined, b: MetadataValue): boolean {
99
+ if (Array.isArray(a) || Array.isArray(b)) {
100
+ return Array.isArray(a) && Array.isArray(b)
101
+ && a.length === b.length
102
+ && a.every((v, i) => v === b[i]);
103
+ }
104
+ return a === b;
105
+ }
106
+
107
+ function compare(
108
+ actual: MetadataValue | undefined,
109
+ op: 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte',
110
+ expected: MetadataValue,
111
+ ): boolean {
112
+ if (op === 'eq') return scalarEquals(actual, expected);
113
+ if (op === 'ne') return !scalarEquals(actual, expected);
114
+
115
+ // Ordering comparisons on a missing or non-scalar field are false rather than
116
+ // throwing: a record without the field simply does not satisfy `sensitivity <= n`.
117
+ if (actual === undefined || actual === null) return false;
118
+ if (Array.isArray(actual) || Array.isArray(expected) || expected === null) return false;
119
+ if (typeof actual !== typeof expected) return false;
120
+ if (typeof actual === 'boolean' || typeof expected === 'boolean') return false;
121
+
122
+ switch (op) {
123
+ case 'gt': return actual > expected;
124
+ case 'gte': return actual >= expected;
125
+ case 'lt': return actual < expected;
126
+ default: return actual <= expected;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Walks a filter tree, reporting every field it touches. Adapters use this to
132
+ * decide up front whether a dialect can push the whole filter down, or whether a
133
+ * post-filter pass is needed.
134
+ */
135
+ export function filterFields(filter: MetadataFilter, out = new Set<string>()): Set<string> {
136
+ switch (filter.op) {
137
+ case 'and':
138
+ case 'or':
139
+ for (const f of filter.filters) filterFields(f, out);
140
+ break;
141
+ case 'not':
142
+ filterFields(filter.filter, out);
143
+ break;
144
+ default:
145
+ out.add(filter.field);
146
+ }
147
+ return out;
148
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * vectorstore — the VectorStore port, its declarative filter algebra, and the
3
+ * adapters. Tenant/ACL scope is compiled into every read, so multi-tenant RAG
4
+ * fails closed rather than depending on each caller remembering to scope.
5
+ */
6
+
7
+ export { MemoryVectorStore } from './MemoryVectorStore.js';
8
+ export type { MemoryVectorStoreOptions } from './MemoryVectorStore.js';
9
+
10
+ export { RestVectorStore } from './RestVectorStore.js';
11
+ export type { RestVectorStoreOptions } from './RestVectorStore.js';
12
+
13
+ export {
14
+ getDialect,
15
+ listDialects,
16
+ registerDialect,
17
+ evermindDialect,
18
+ qdrantDialect,
19
+ pineconeDialect,
20
+ vertexAiDialect,
21
+ } from './dialects.js';
22
+ export type {
23
+ VectorDialect,
24
+ DialectContext,
25
+ HttpRequest,
26
+ TranslatedFilter,
27
+ } from './dialects.js';
28
+
29
+ export {
30
+ accessFilter,
31
+ combineFilters,
32
+ matchesFilter,
33
+ filterFields,
34
+ } from './filter.js';
35
+
36
+ export {
37
+ TENANT_FIELD,
38
+ ACL_FIELD,
39
+ SENSITIVITY_FIELD,
40
+ ACL_PUBLIC,
41
+ } from './types.js';
42
+ export type {
43
+ AccessScope,
44
+ ComparisonOp,
45
+ MetadataFilter,
46
+ MetadataRecord,
47
+ MetadataValue,
48
+ UpsertResult,
49
+ VectorMatch,
50
+ VectorQuery,
51
+ VectorRecord,
52
+ VectorStore,
53
+ } from './types.js';
@@ -0,0 +1,149 @@
1
+ /**
2
+ * vectorstore/types.ts — the VectorStore port.
3
+ *
4
+ * Enterprise deployments do not get to choose the vector database; the customer
5
+ * already has one (pgvector in their Cloud SQL, Vertex AI Vector Search because
6
+ * they are a GCP shop, Qdrant because platform standardised on it). So the store
7
+ * is a PORT, and every adapter is infrastructure behind it. Retrieval, ingestion
8
+ * and evaluation depend on this interface and never on a vendor.
9
+ *
10
+ * Two things make it enterprise-grade rather than a toy vector index:
11
+ *
12
+ * 1. **Filters are DATA, not predicates.** A `(record) => boolean` cannot be
13
+ * pushed down to a remote database — it forces fetch-everything-then-filter,
14
+ * which is both slow and a data-leak: rows the caller may not see are read
15
+ * into the process before being dropped. {@link MetadataFilter} is a small
16
+ * declarative algebra each adapter translates into its own query language.
17
+ *
18
+ * 2. **Access scope is first-class.** Multi-tenant RAG fails closed only if the
19
+ * tenant predicate is part of the query the database runs. {@link AccessScope}
20
+ * compiles to a filter that is AND-ed into every read, so "forgot to scope the
21
+ * query" is not a reachable state.
22
+ */
23
+
24
+ export type MetadataValue = string | number | boolean | null | string[];
25
+
26
+ export type MetadataRecord = Record<string, MetadataValue>;
27
+
28
+ /** A stored unit of retrievable knowledge. */
29
+ export interface VectorRecord {
30
+ id: string;
31
+ /** The chunk text — returned with matches so a retriever can rerank on it. */
32
+ text: string;
33
+ /** Embedding. Optional so a store can embed server-side. */
34
+ vector?: Float32Array;
35
+ metadata: MetadataRecord;
36
+ }
37
+
38
+ export interface VectorMatch {
39
+ id: string;
40
+ text: string;
41
+ /** Similarity in [0,1] — cosine for every adapter here. */
42
+ score: number;
43
+ metadata: MetadataRecord;
44
+ vector?: Float32Array;
45
+ }
46
+
47
+ // ── Filter algebra ────────────────────────────────────────────────────────────
48
+
49
+ export type ComparisonOp = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte';
50
+
51
+ export type MetadataFilter =
52
+ | { op: ComparisonOp; field: string; value: MetadataValue }
53
+ /** Field equals any of `values` (scalar field). */
54
+ | { op: 'in' | 'nin'; field: string; values: MetadataValue[] }
55
+ /**
56
+ * Field (an array, or a scalar treated as a one-element array) intersects
57
+ * `values`. This is the ACL primitive — every mainstream vector database can
58
+ * express "array field matches any of", which is why ACLs are modelled as an
59
+ * array rather than as a bitmask or a nested object.
60
+ */
61
+ | { op: 'anyOf'; field: string; values: MetadataValue[] }
62
+ /** Case-insensitive substring match on a string field. */
63
+ | { op: 'contains'; field: string; value: string }
64
+ | { op: 'exists'; field: string }
65
+ // `and` and `or` are separate members rather than one `op: 'and' | 'or'`
66
+ // variant so that a discriminant check narrows them away cleanly; a union-typed
67
+ // discriminant is not removable by control flow, which forces casts on every
68
+ // dialect that walks the tree.
69
+ | { op: 'and'; filters: MetadataFilter[] }
70
+ | { op: 'or'; filters: MetadataFilter[] }
71
+ | { op: 'not'; filter: MetadataFilter };
72
+
73
+ /**
74
+ * The non-composite half of the algebra — every variant that names a `field`.
75
+ * Dialect translation works leaf-by-leaf, so having the type say so removes a
76
+ * cast from every adapter.
77
+ */
78
+ export type LeafFilter = Extract<MetadataFilter, { field: string }>;
79
+
80
+ // ── Access scope ──────────────────────────────────────────────────────────────
81
+
82
+ /** Metadata keys the access model reserves. Ingestion always populates them. */
83
+ export const TENANT_FIELD = 'tenantId';
84
+ export const ACL_FIELD = 'acl';
85
+ export const SENSITIVITY_FIELD = 'sensitivity';
86
+ /** ACL entry meaning "any principal within the tenant". */
87
+ export const ACL_PUBLIC = '*';
88
+
89
+ export interface AccessScope {
90
+ /** Hard tenant boundary. Every read is AND-ed with an equality on this. */
91
+ tenantId: string;
92
+ /**
93
+ * Group / role / user ids the caller holds. A record is visible when its `acl`
94
+ * array intersects these, or contains {@link ACL_PUBLIC}. Omit for
95
+ * tenant-wide access to public records only.
96
+ */
97
+ principals?: string[];
98
+ /**
99
+ * Highest classification level the caller may read (records carry a numeric
100
+ * `sensitivity`). Omit for no ceiling.
101
+ */
102
+ maxSensitivity?: number;
103
+ }
104
+
105
+ // ── The port ──────────────────────────────────────────────────────────────────
106
+
107
+ export interface VectorQuery {
108
+ /** Dense query embedding. Required unless the adapter embeds server-side. */
109
+ vector?: Float32Array;
110
+ /** Raw query text — used by `keywordSearch`, and by server-side embedders. */
111
+ text?: string;
112
+ /** Results to return. Default 10. */
113
+ topK?: number;
114
+ /** Declarative metadata predicate, pushed down to the store. */
115
+ filter?: MetadataFilter;
116
+ /** Tenant/ACL scope, AND-ed with `filter`. */
117
+ scope?: AccessScope;
118
+ /** Drop matches scoring below this. Default 0. */
119
+ minScore?: number;
120
+ /** Return stored vectors on matches (costs bandwidth). Default false. */
121
+ includeVectors?: boolean;
122
+ }
123
+
124
+ export interface UpsertResult {
125
+ upserted: number;
126
+ /** Ids the store considered unchanged and skipped, when it can tell. */
127
+ skipped: number;
128
+ }
129
+
130
+ /**
131
+ * The port. `keywordSearch` is optional because not every vector database has a
132
+ * lexical index; retrievers degrade to dense-only and SAY SO on the span rather
133
+ * than silently returning worse results.
134
+ */
135
+ export interface VectorStore {
136
+ /** Adapter name, stamped on telemetry spans. */
137
+ readonly name: string;
138
+
139
+ upsert(records: readonly VectorRecord[]): Promise<UpsertResult>;
140
+ query(query: VectorQuery): Promise<VectorMatch[]>;
141
+ delete(ids: readonly string[]): Promise<number>;
142
+ /** Bulk delete by predicate — how an incremental sync removes a stale source. */
143
+ deleteByFilter(filter: MetadataFilter, scope?: AccessScope): Promise<number>;
144
+ fetch(ids: readonly string[]): Promise<VectorRecord[]>;
145
+ count(filter?: MetadataFilter, scope?: AccessScope): Promise<number>;
146
+
147
+ /** Lexical/BM25 search, when the adapter supports one. */
148
+ keywordSearch?(query: VectorQuery): Promise<VectorMatch[]>;
149
+ }