nexus-ai-pro 2.3.0 → 2.5.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 (248) hide show
  1. package/API_STABILITY.md +271 -0
  2. package/CHANGELOG.md +330 -1
  3. package/README.md +18 -4
  4. package/SECURITY.md +209 -0
  5. package/dist/adapter-kit/contracts.d.ts +87 -0
  6. package/dist/adapter-kit/contracts.js +237 -0
  7. package/dist/adapter-kit/errors.d.ts +39 -0
  8. package/dist/adapter-kit/errors.js +52 -0
  9. package/dist/adapter-kit/index.d.ts +144 -0
  10. package/dist/adapter-kit/index.js +178 -0
  11. package/dist/adapter-kit/semver.d.ts +12 -0
  12. package/dist/adapter-kit/semver.js +55 -0
  13. package/dist/agent/middleware/pii.js +2 -33
  14. package/dist/context-hub/hub.d.ts +53 -0
  15. package/dist/context-hub/hub.js +108 -3
  16. package/dist/context-hub/index.d.ts +2 -1
  17. package/dist/context-hub/index.js +2 -1
  18. package/dist/context-hub/signing.d.ts +95 -0
  19. package/dist/context-hub/signing.js +88 -0
  20. package/dist/core/secure-stream.js +4 -3
  21. package/dist/doctor/index.d.ts +4 -2
  22. package/dist/doctor/index.js +29 -0
  23. package/dist/embeddings/adapters.js +2 -1
  24. package/dist/evals/judge.js +2 -5
  25. package/dist/evaluate/cache.d.ts +0 -14
  26. package/dist/evaluate/cache.js +0 -30
  27. package/dist/evaluate/file-cache.d.ts +15 -0
  28. package/dist/evaluate/file-cache.js +30 -0
  29. package/dist/evaluate/index.d.ts +2 -1
  30. package/dist/evaluate/index.js +2 -1
  31. package/dist/evaluate/review.js +2 -2
  32. package/dist/evaluate/run.js +2 -2
  33. package/dist/evaluate/threads.d.ts +187 -0
  34. package/dist/evaluate/threads.js +187 -0
  35. package/dist/evaluate/version.js +3 -3
  36. package/dist/graph/graph.js +2 -2
  37. package/dist/graph/node-cache.js +3 -3
  38. package/dist/hallucination/rag.d.ts +4 -1
  39. package/dist/hallucination/rag.js +6 -1
  40. package/dist/images/asset-stores.js +2 -3
  41. package/dist/images/asset-support.js +2 -2
  42. package/dist/images/assets.js +1 -2
  43. package/dist/images/evals.js +4 -4
  44. package/dist/insights/proposals.js +1 -2
  45. package/dist/insights/regressions.d.ts +26 -2
  46. package/dist/insights/regressions.js +129 -3
  47. package/dist/loaders/git.js +8 -1
  48. package/dist/loaders/web.js +7 -1
  49. package/dist/mcp/client.d.ts +14 -0
  50. package/dist/mcp/client.js +59 -1
  51. package/dist/mcp/registry.d.ts +6 -0
  52. package/dist/mcp/registry.js +7 -0
  53. package/dist/operations/adapters.d.ts +2 -1
  54. package/dist/operations/runner.js +2 -0
  55. package/dist/operations/stats.js +11 -4
  56. package/dist/postgres/index.d.ts +2 -1
  57. package/dist/postgres/index.js +4 -0
  58. package/dist/postgres/operations.js +11 -4
  59. package/dist/postgres/store.d.ts +7 -0
  60. package/dist/postgres/store.js +21 -0
  61. package/dist/postgres/tenancy.d.ts +43 -0
  62. package/dist/postgres/tenancy.js +71 -0
  63. package/dist/prompts/file.js +28 -11
  64. package/dist/protocols/a2a.d.ts +235 -0
  65. package/dist/protocols/a2a.js +386 -0
  66. package/dist/protocols/acp.d.ts +87 -0
  67. package/dist/protocols/acp.js +322 -0
  68. package/dist/protocols/ag-ui.d.ts +120 -0
  69. package/dist/protocols/ag-ui.js +180 -0
  70. package/dist/protocols/shared.d.ts +80 -0
  71. package/dist/protocols/shared.js +49 -0
  72. package/dist/providers/anthropic.js +1 -0
  73. package/dist/providers/cohere.js +2 -2
  74. package/dist/providers/google.js +2 -11
  75. package/dist/providers/openai.js +1 -0
  76. package/dist/rag/file-ingestion.js +15 -9
  77. package/dist/runtime/detect.d.ts +17 -0
  78. package/dist/runtime/detect.js +16 -0
  79. package/dist/runtime/index.d.ts +19 -0
  80. package/dist/runtime/index.js +7 -0
  81. package/dist/security/injection-detector.d.ts +2 -0
  82. package/dist/security/injection-detector.js +7 -1
  83. package/dist/security/input-guard.js +5 -2
  84. package/dist/security/output-guard.js +8 -4
  85. package/dist/security/pii-detector.js +22 -6
  86. package/dist/security/prompt-hardening.js +6 -2
  87. package/dist/security/semantic-injection-classifier.d.ts +2 -1
  88. package/dist/security/semantic-injection-classifier.js +31 -35
  89. package/dist/security/upload-scanner.d.ts +8 -0
  90. package/dist/security/upload-scanner.js +51 -9
  91. package/dist/server/deployments.d.ts +188 -11
  92. package/dist/server/deployments.js +474 -36
  93. package/dist/server/metrics.d.ts +2 -0
  94. package/dist/server/metrics.js +7 -0
  95. package/dist/server/runs.d.ts +18 -1
  96. package/dist/server/runs.js +66 -7
  97. package/dist/server/server.js +9 -1
  98. package/dist/server/state.d.ts +12 -2
  99. package/dist/server/state.js +15 -0
  100. package/dist/sqlite/operations.js +15 -4
  101. package/dist/sqlite/store.d.ts +7 -0
  102. package/dist/sqlite/store.js +28 -0
  103. package/dist/store/memory.d.ts +8 -0
  104. package/dist/store/memory.js +12 -1
  105. package/dist/store/redis.d.ts +13 -0
  106. package/dist/store/redis.js +38 -0
  107. package/dist/testing/record.js +2 -2
  108. package/dist/tracing/instrument.js +1 -0
  109. package/dist/tracing/stores.js +2 -1
  110. package/dist/tracing/tracer.d.ts +4 -0
  111. package/dist/tracing/tracer.js +8 -2
  112. package/dist/types/config.d.ts +20 -0
  113. package/dist/types/operations.d.ts +7 -0
  114. package/dist/types/server.d.ts +67 -2
  115. package/dist/types/store.d.ts +9 -0
  116. package/dist/utils/base64.d.ts +4 -0
  117. package/dist/utils/base64.js +21 -0
  118. package/dist/utils/checksums.d.ts +22 -0
  119. package/dist/utils/checksums.js +39 -0
  120. package/dist/utils/ids.d.ts +8 -0
  121. package/dist/utils/ids.js +26 -2
  122. package/dist/utils/sha256.d.ts +22 -0
  123. package/dist/utils/sha256.js +120 -0
  124. package/dist/version.d.ts +1 -1
  125. package/dist/version.js +1 -1
  126. package/dist-cjs/adapter-kit/contracts.d.ts +87 -0
  127. package/dist-cjs/adapter-kit/contracts.js +243 -0
  128. package/dist-cjs/adapter-kit/errors.d.ts +39 -0
  129. package/dist-cjs/adapter-kit/errors.js +57 -0
  130. package/dist-cjs/adapter-kit/index.d.ts +144 -0
  131. package/dist-cjs/adapter-kit/index.js +194 -0
  132. package/dist-cjs/adapter-kit/semver.d.ts +12 -0
  133. package/dist-cjs/adapter-kit/semver.js +59 -0
  134. package/dist-cjs/agent/middleware/pii.js +4 -35
  135. package/dist-cjs/context-hub/hub.d.ts +53 -0
  136. package/dist-cjs/context-hub/hub.js +110 -4
  137. package/dist-cjs/context-hub/index.d.ts +2 -1
  138. package/dist-cjs/context-hub/index.js +7 -1
  139. package/dist-cjs/context-hub/signing.d.ts +95 -0
  140. package/dist-cjs/context-hub/signing.js +96 -0
  141. package/dist-cjs/core/secure-stream.js +4 -3
  142. package/dist-cjs/doctor/index.d.ts +4 -2
  143. package/dist-cjs/doctor/index.js +29 -0
  144. package/dist-cjs/embeddings/adapters.js +2 -1
  145. package/dist-cjs/evals/judge.js +2 -5
  146. package/dist-cjs/evaluate/cache.d.ts +0 -14
  147. package/dist-cjs/evaluate/cache.js +1 -65
  148. package/dist-cjs/evaluate/file-cache.d.ts +15 -0
  149. package/dist-cjs/evaluate/file-cache.js +67 -0
  150. package/dist-cjs/evaluate/index.d.ts +2 -1
  151. package/dist-cjs/evaluate/index.js +3 -2
  152. package/dist-cjs/evaluate/review.js +2 -2
  153. package/dist-cjs/evaluate/run.js +2 -2
  154. package/dist-cjs/evaluate/threads.d.ts +187 -0
  155. package/dist-cjs/evaluate/threads.js +198 -0
  156. package/dist-cjs/evaluate/version.js +3 -3
  157. package/dist-cjs/graph/graph.js +2 -2
  158. package/dist-cjs/graph/node-cache.js +3 -3
  159. package/dist-cjs/hallucination/rag.d.ts +4 -1
  160. package/dist-cjs/hallucination/rag.js +6 -1
  161. package/dist-cjs/images/asset-stores.js +2 -3
  162. package/dist-cjs/images/asset-support.js +2 -2
  163. package/dist-cjs/images/assets.js +1 -2
  164. package/dist-cjs/images/evals.js +4 -4
  165. package/dist-cjs/insights/proposals.js +1 -2
  166. package/dist-cjs/insights/regressions.d.ts +26 -2
  167. package/dist-cjs/insights/regressions.js +129 -3
  168. package/dist-cjs/loaders/git.js +8 -1
  169. package/dist-cjs/loaders/web.js +7 -1
  170. package/dist-cjs/mcp/client.d.ts +14 -0
  171. package/dist-cjs/mcp/client.js +60 -1
  172. package/dist-cjs/mcp/registry.d.ts +6 -0
  173. package/dist-cjs/mcp/registry.js +7 -0
  174. package/dist-cjs/operations/adapters.d.ts +2 -1
  175. package/dist-cjs/operations/runner.js +2 -0
  176. package/dist-cjs/operations/stats.js +11 -4
  177. package/dist-cjs/postgres/index.d.ts +2 -1
  178. package/dist-cjs/postgres/index.js +8 -1
  179. package/dist-cjs/postgres/operations.js +11 -4
  180. package/dist-cjs/postgres/store.d.ts +7 -0
  181. package/dist-cjs/postgres/store.js +21 -0
  182. package/dist-cjs/postgres/tenancy.d.ts +43 -0
  183. package/dist-cjs/postgres/tenancy.js +110 -0
  184. package/dist-cjs/prompts/file.js +28 -11
  185. package/dist-cjs/protocols/a2a.d.ts +235 -0
  186. package/dist-cjs/protocols/a2a.js +393 -0
  187. package/dist-cjs/protocols/acp.d.ts +87 -0
  188. package/dist-cjs/protocols/acp.js +326 -0
  189. package/dist-cjs/protocols/ag-ui.d.ts +120 -0
  190. package/dist-cjs/protocols/ag-ui.js +186 -0
  191. package/dist-cjs/protocols/shared.d.ts +80 -0
  192. package/dist-cjs/protocols/shared.js +59 -0
  193. package/dist-cjs/providers/anthropic.js +1 -0
  194. package/dist-cjs/providers/cohere.js +2 -2
  195. package/dist-cjs/providers/google.js +3 -12
  196. package/dist-cjs/providers/openai.js +1 -0
  197. package/dist-cjs/rag/file-ingestion.js +14 -8
  198. package/dist-cjs/runtime/detect.d.ts +17 -0
  199. package/dist-cjs/runtime/detect.js +19 -0
  200. package/dist-cjs/runtime/index.d.ts +19 -0
  201. package/dist-cjs/runtime/index.js +31 -0
  202. package/dist-cjs/security/injection-detector.d.ts +2 -0
  203. package/dist-cjs/security/injection-detector.js +7 -1
  204. package/dist-cjs/security/input-guard.js +5 -2
  205. package/dist-cjs/security/output-guard.js +8 -4
  206. package/dist-cjs/security/pii-detector.js +22 -6
  207. package/dist-cjs/security/prompt-hardening.js +6 -2
  208. package/dist-cjs/security/semantic-injection-classifier.d.ts +2 -1
  209. package/dist-cjs/security/semantic-injection-classifier.js +31 -35
  210. package/dist-cjs/security/upload-scanner.d.ts +8 -0
  211. package/dist-cjs/security/upload-scanner.js +53 -9
  212. package/dist-cjs/server/deployments.d.ts +188 -11
  213. package/dist-cjs/server/deployments.js +474 -36
  214. package/dist-cjs/server/metrics.d.ts +2 -0
  215. package/dist-cjs/server/metrics.js +8 -0
  216. package/dist-cjs/server/runs.d.ts +18 -1
  217. package/dist-cjs/server/runs.js +65 -6
  218. package/dist-cjs/server/server.js +9 -1
  219. package/dist-cjs/server/state.d.ts +12 -2
  220. package/dist-cjs/server/state.js +15 -0
  221. package/dist-cjs/sqlite/operations.js +15 -4
  222. package/dist-cjs/sqlite/store.d.ts +7 -0
  223. package/dist-cjs/sqlite/store.js +28 -0
  224. package/dist-cjs/store/memory.d.ts +8 -0
  225. package/dist-cjs/store/memory.js +12 -1
  226. package/dist-cjs/store/redis.d.ts +13 -0
  227. package/dist-cjs/store/redis.js +38 -0
  228. package/dist-cjs/testing/record.js +2 -2
  229. package/dist-cjs/tracing/instrument.js +1 -0
  230. package/dist-cjs/tracing/stores.js +2 -1
  231. package/dist-cjs/tracing/tracer.d.ts +4 -0
  232. package/dist-cjs/tracing/tracer.js +8 -2
  233. package/dist-cjs/types/config.d.ts +20 -0
  234. package/dist-cjs/types/operations.d.ts +7 -0
  235. package/dist-cjs/types/server.d.ts +67 -2
  236. package/dist-cjs/types/store.d.ts +9 -0
  237. package/dist-cjs/utils/base64.d.ts +4 -0
  238. package/dist-cjs/utils/base64.js +25 -0
  239. package/dist-cjs/utils/checksums.d.ts +22 -0
  240. package/dist-cjs/utils/checksums.js +46 -0
  241. package/dist-cjs/utils/ids.d.ts +8 -0
  242. package/dist-cjs/utils/ids.js +28 -2
  243. package/dist-cjs/utils/sha256.d.ts +22 -0
  244. package/dist-cjs/utils/sha256.js +125 -0
  245. package/dist-cjs/version.d.ts +1 -1
  246. package/dist-cjs/version.js +1 -1
  247. package/llms.txt +5 -2
  248. package/package.json +102 -6
@@ -0,0 +1,87 @@
1
+ /**
2
+ * The contract suites the adapter kit ships: what every vector store, retriever, and long-term store
3
+ * must do, as checks that need no test framework. Each check is named for the behavior it proves,
4
+ * and a capability the adapter does not declare is reported as skipped, never as a failure.
5
+ */
6
+ import type { RagChunk } from '../hallucination/rag.js';
7
+ import type { EmbeddingProvider, VectorStore } from '../hallucination/retrieval.js';
8
+ import type { Retriever } from '../rag/retrievers.js';
9
+ import type { Store } from '../types/store.js';
10
+ /** One check of a contract suite. */
11
+ export interface AdapterCheck {
12
+ /** What was checked, as a sentence. */
13
+ name: string;
14
+ /** Whether it held. */
15
+ ok: boolean;
16
+ /** Why it did not hold, or why it was skipped. */
17
+ detail?: string;
18
+ /** True when the adapter does not declare the capability the check needs. */
19
+ skipped?: boolean;
20
+ }
21
+ /** The passages every retrieval contract runs on, and that benchmarks reuse. */
22
+ export declare const ADAPTER_FIXTURE_PASSAGES: readonly RagChunk[];
23
+ /** What a vector store declares it supports, for its contract. */
24
+ export interface VectorStoreCapabilities {
25
+ /** `searchVector()` with a vector computed elsewhere. Defaults to true. */
26
+ searchVector?: boolean;
27
+ /** `minScore` drops weak matches. Defaults to true. */
28
+ minScore?: boolean;
29
+ /** Metadata filters: `exact` matches values, `typed` also tells `3` from `'3'`, `none` has no filters. Defaults to `exact`. */
30
+ filters?: 'none' | 'exact' | 'typed';
31
+ }
32
+ /** Options for `runVectorStoreContract()`. */
33
+ export interface VectorStoreContractOptions {
34
+ /** The embedding function the store was given, to compute a query vector for `searchVector()`. */
35
+ embed: EmbeddingProvider;
36
+ /** What the store supports. */
37
+ capabilities?: VectorStoreCapabilities;
38
+ /** Waits for writes to become searchable, for a store whose index is eventually consistent. */
39
+ settle?: () => Promise<void>;
40
+ }
41
+ /**
42
+ * The `VectorStore` contract: ranking, `topK`, sources and metadata round-tripped, filters,
43
+ * `minScore`, vector search, replacement by id, deletes, and empty writes. The store should be empty
44
+ * when it starts.
45
+ */
46
+ export declare function runVectorStoreContract(store: VectorStore, options: VectorStoreContractOptions): Promise<AdapterCheck[]>;
47
+ /** What a retriever declares it supports, for its contract. */
48
+ export interface RetrieverCapabilities {
49
+ /** It holds its own passages, with `add()` and `delete()`: a `SparseRetriever`. Defaults to whether it has them. */
50
+ writes?: boolean;
51
+ /** Metadata filters. Defaults to true. */
52
+ filters?: boolean;
53
+ }
54
+ /** Options for `runRetrieverContract()`. */
55
+ export interface RetrieverContractOptions {
56
+ /** What the retriever supports. */
57
+ capabilities?: RetrieverCapabilities;
58
+ /**
59
+ * Loads the fixture passages, for a retriever that does not hold its own: into the vector store
60
+ * behind it, say. A retriever with `add()` is loaded through that.
61
+ */
62
+ load?: (passages: RagChunk[]) => Promise<void>;
63
+ /** Waits for writes to become searchable. */
64
+ settle?: () => Promise<void>;
65
+ }
66
+ /**
67
+ * The `Retriever` contract, and the `SparseRetriever` contract when the retriever takes writes:
68
+ * an exact term found, ranking, `topK`, filters, replacement, deletes, and an empty query.
69
+ */
70
+ export declare function runRetrieverContract(retriever: Retriever, options?: RetrieverContractOptions): Promise<AdapterCheck[]>;
71
+ /** What a long-term store declares it supports, for its contract. */
72
+ export interface StoreCapabilities {
73
+ /** `search()` lists a namespace prefix, newest first. Defaults to true. */
74
+ search?: boolean;
75
+ /** `search()` filters on value fields. Defaults to true. */
76
+ filters?: boolean;
77
+ /** `listNamespaces()`. Defaults to true. */
78
+ namespaces?: boolean;
79
+ }
80
+ /**
81
+ * The long-term `Store` contract: values round-trip, a replaced item keeps its creation time, a
82
+ * missing key reads as nothing, deletes, prefix search with filters and paging, and namespace
83
+ * listing. The store should be empty when it starts.
84
+ */
85
+ export declare function runStoreContract(store: Store, options?: {
86
+ capabilities?: StoreCapabilities;
87
+ }): Promise<AdapterCheck[]>;
@@ -0,0 +1,237 @@
1
+ export const ADAPTER_FIXTURE_PASSAGES = [
2
+ {
3
+ id: 'refunds#0',
4
+ content: 'Refunds are issued within fourteen days of a return',
5
+ source: 'policy.md',
6
+ metadata: { tenant: 'acme', page: 3 },
7
+ },
8
+ {
9
+ id: 'shipping#0',
10
+ content: 'Orders ship from the warehouse within two business days',
11
+ source: 'policy.md',
12
+ metadata: { tenant: 'acme' },
13
+ },
14
+ { id: 'refunds#1', content: 'A refund goes back to the original payment method', metadata: { tenant: 'globex' } },
15
+ { id: 'careers#0', content: 'We are hiring engineers in three offices' },
16
+ ];
17
+ function fixturePassages() {
18
+ return ADAPTER_FIXTURE_PASSAGES.map((passage) => ({
19
+ ...passage,
20
+ ...(passage.metadata ? { metadata: { ...passage.metadata } } : {}),
21
+ }));
22
+ }
23
+ class Suite {
24
+ checks = [];
25
+ async check(name, run) {
26
+ try {
27
+ const result = await run();
28
+ this.checks.push(result === true ? { name, ok: true } : { name, ok: false, detail: result === false ? 'did not hold' : result });
29
+ }
30
+ catch (error) {
31
+ this.checks.push({ name, ok: false, detail: error instanceof Error ? error.message : String(error) });
32
+ }
33
+ }
34
+ skip(name, reason) {
35
+ this.checks.push({ name, ok: true, skipped: true, detail: reason });
36
+ }
37
+ }
38
+ export async function runVectorStoreContract(store, options) {
39
+ const suite = new Suite();
40
+ const settle = options.settle ?? (async () => undefined);
41
+ const capabilities = options.capabilities ?? {};
42
+ const filters = capabilities.filters ?? 'exact';
43
+ await suite.check('adds passages', async () => {
44
+ await store.add(fixturePassages());
45
+ await settle();
46
+ return true;
47
+ });
48
+ await suite.check('ranks the closest passage first, best first, within topK', async () => {
49
+ const results = await store.search('how long do refunds take', { topK: 2 });
50
+ if (results.length !== 2)
51
+ return `expected 2 results for topK 2, got ${results.length}`;
52
+ if (results[0]?.id !== 'refunds#0')
53
+ return `expected refunds#0 first, got ${results[0]?.id}`;
54
+ return (results[0]?.score ?? 0) > (results[1]?.score ?? 0) || 'results are not ordered by score';
55
+ });
56
+ await suite.check('returns a passage source and its metadata whole', async () => {
57
+ const [first] = await store.search('how long do refunds take', { topK: 1 });
58
+ if (first?.source !== 'policy.md')
59
+ return `expected source policy.md, got ${first?.source}`;
60
+ return (JSON.stringify(first?.metadata) === JSON.stringify({ tenant: 'acme', page: 3 }) ||
61
+ `metadata came back as ${JSON.stringify(first?.metadata)}`);
62
+ });
63
+ if (filters === 'none')
64
+ suite.skip('narrows a search by metadata', 'the store declares no filters');
65
+ else {
66
+ await suite.check('narrows a search by metadata', async () => {
67
+ const results = await store.search('refund', { filter: { tenant: 'globex' } });
68
+ return ((results.length === 1 && results[0]?.id === 'refunds#1') ||
69
+ `got ${results.map((result) => result.id).join(', ')}`);
70
+ });
71
+ }
72
+ if (filters === 'typed') {
73
+ await suite.check('tells a number from the same digits as text in a filter', async () => {
74
+ const numeric = await store.search('refunds', { filter: { page: 3 }, topK: 10 });
75
+ const text = await store.search('refunds', { filter: { page: '3' }, topK: 10 });
76
+ return ((numeric.length === 1 && text.length === 0) || `number matched ${numeric.length}, text matched ${text.length}`);
77
+ });
78
+ }
79
+ else
80
+ suite.skip('tells a number from the same digits as text in a filter', 'the store declares untyped filters');
81
+ if (capabilities.minScore === false)
82
+ suite.skip('drops matches under minScore', 'the store declares no minScore');
83
+ else {
84
+ await suite.check('drops matches under minScore', async () => {
85
+ const results = await store.search('how long do refunds take', { minScore: 0.99 });
86
+ return results.length === 0 || `${results.length} results above 0.99`;
87
+ });
88
+ }
89
+ if (capabilities.searchVector === false)
90
+ suite.skip('searches by a precomputed vector', 'the store declares no searchVector');
91
+ else {
92
+ await suite.check('searches by a precomputed vector', async () => {
93
+ const [vector] = await options.embed(['orders ship from the warehouse']);
94
+ const [first] = await store.searchVector(vector, { topK: 1 });
95
+ return first?.id === 'shipping#0' || `expected shipping#0, got ${first?.id}`;
96
+ });
97
+ }
98
+ await suite.check('replaces a passage added again under its id', async () => {
99
+ await store.add([
100
+ { id: 'careers#0', content: 'Refunds for event tickets are not available', metadata: { tenant: 'acme' } },
101
+ ]);
102
+ await settle();
103
+ const results = await store.search('event tickets refunds', { topK: 10 });
104
+ const copies = results.filter((result) => result.id === 'careers#0');
105
+ if (copies.length !== 1)
106
+ return `found ${copies.length} copies`;
107
+ return /event tickets/.test(copies[0]?.content ?? '') || 'the old content is still there';
108
+ });
109
+ await suite.check('deletes by id, ignoring ids it does not hold', async () => {
110
+ await store.delete(['refunds#0', 'not-stored']);
111
+ await settle();
112
+ const results = await store.search('how long do refunds take', { topK: 10 });
113
+ return !results.some((result) => result.id === 'refunds#0') || 'the deleted passage is still found';
114
+ });
115
+ await suite.check('accepts empty writes', async () => {
116
+ await store.add([]);
117
+ await store.delete([]);
118
+ return true;
119
+ });
120
+ return suite.checks;
121
+ }
122
+ export async function runRetrieverContract(retriever, options = {}) {
123
+ const suite = new Suite();
124
+ const settle = options.settle ?? (async () => undefined);
125
+ const sparse = retriever;
126
+ const writes = options.capabilities?.writes ?? (typeof sparse.add === 'function' && typeof sparse.delete === 'function');
127
+ const passages = fixturePassages();
128
+ await suite.check('loads the fixture passages', async () => {
129
+ if (writes)
130
+ await sparse.add?.(passages);
131
+ else if (options.load)
132
+ await options.load(passages);
133
+ else
134
+ return 'a retriever without add() needs a load() to fill what it reads from';
135
+ await settle();
136
+ return true;
137
+ });
138
+ await suite.check('finds the passage holding the query terms first', async () => {
139
+ const [first] = await retriever.retrieve('warehouse business days', { topK: 3 });
140
+ return first?.id === 'shipping#0' || `expected shipping#0, got ${first?.id}`;
141
+ });
142
+ await suite.check('returns at most topK results, best first', async () => {
143
+ const results = await retriever.retrieve('refund refunds return', { topK: 1 });
144
+ return results.length <= 1 || `${results.length} results for topK 1`;
145
+ });
146
+ if (options.capabilities?.filters === false)
147
+ suite.skip('narrows by metadata', 'the retriever declares no filters');
148
+ else {
149
+ await suite.check('narrows by metadata', async () => {
150
+ const results = await retriever.retrieve('refund', { filter: { tenant: 'globex' }, topK: 10 });
151
+ return ((results.every((result) => result.metadata?.tenant === 'globex') && results.length > 0) ||
152
+ `got ${results.map((result) => result.id).join(', ')}`);
153
+ });
154
+ }
155
+ if (!writes) {
156
+ suite.skip('replaces a passage added again under its id', 'the retriever does not take writes');
157
+ suite.skip('deletes by id, ignoring ids it does not hold', 'the retriever does not take writes');
158
+ }
159
+ else {
160
+ await suite.check('replaces a passage added again under its id', async () => {
161
+ await sparse.add?.([{ id: 'careers#0', content: 'Engineers relocate with a stipend' }]);
162
+ await settle();
163
+ const results = await retriever.retrieve('stipend relocate', { topK: 10 });
164
+ return (results.filter((result) => result.id === 'careers#0').length === 1 || 'the passage is missing or duplicated');
165
+ });
166
+ await suite.check('deletes by id, ignoring ids it does not hold', async () => {
167
+ await sparse.delete?.(['shipping#0', 'not-stored']);
168
+ await settle();
169
+ const results = await retriever.retrieve('warehouse business days', { topK: 10 });
170
+ return !results.some((result) => result.id === 'shipping#0') || 'the deleted passage is still found';
171
+ });
172
+ }
173
+ await suite.check('answers an empty query without failing', async () => {
174
+ await retriever.retrieve('', { topK: 3 });
175
+ return true;
176
+ });
177
+ return suite.checks;
178
+ }
179
+ export async function runStoreContract(store, options = {}) {
180
+ const suite = new Suite();
181
+ const capabilities = options.capabilities ?? {};
182
+ const namespace = ['contract', 'users'];
183
+ await suite.check('round-trips a value', async () => {
184
+ await store.put(namespace, 'ann', { name: 'Ann', plan: 'pro' });
185
+ const item = await store.get(namespace, 'ann');
186
+ return (item?.value.name === 'Ann' && item.key === 'ann') || `read ${JSON.stringify(item)}`;
187
+ });
188
+ await suite.check('keeps the creation time when an item is replaced', async () => {
189
+ const before = await store.get(namespace, 'ann');
190
+ await new Promise((resolve) => setTimeout(resolve, 5));
191
+ await store.put(namespace, 'ann', { name: 'Ann', plan: 'team' });
192
+ const after = await store.get(namespace, 'ann');
193
+ if (after?.value.plan !== 'team')
194
+ return 'the replacement was not stored';
195
+ return after.createdAt === before?.createdAt || `createdAt moved from ${before?.createdAt} to ${after.createdAt}`;
196
+ });
197
+ await suite.check('reads a missing key as nothing', async () => (await store.get(namespace, 'nobody')) === undefined);
198
+ await suite.check('deletes an item', async () => {
199
+ await store.put(namespace, 'temp', { name: 'Temp' });
200
+ await store.delete(namespace, 'temp');
201
+ return (await store.get(namespace, 'temp')) === undefined;
202
+ });
203
+ if (capabilities.search === false)
204
+ suite.skip('lists a namespace prefix, with paging', 'the store declares no search');
205
+ else {
206
+ await suite.check('lists a namespace prefix, with paging', async () => {
207
+ await store.put([...namespace, 'archived'], 'bob', { name: 'Bob', plan: 'free' });
208
+ await store.put(namespace, 'cy', { name: 'Cy', plan: 'free' });
209
+ const all = await store.search(['contract'], { limit: 10 });
210
+ if (all.length !== 3)
211
+ return `expected 3 items under the prefix, got ${all.length}`;
212
+ const page = await store.search(['contract'], { limit: 2, offset: 2 });
213
+ return page.length === 1 || `expected 1 item on the second page, got ${page.length}`;
214
+ });
215
+ }
216
+ if (capabilities.search === false || capabilities.filters === false)
217
+ suite.skip('filters by a value field', 'the store declares no filters');
218
+ else {
219
+ await suite.check('filters by a value field', async () => {
220
+ const free = await store.search(['contract'], { filter: { plan: 'free' }, limit: 10 });
221
+ const names = free
222
+ .map((item) => item.value.name)
223
+ .sort()
224
+ .join(',');
225
+ return names === 'Bob,Cy' || `got ${names}`;
226
+ });
227
+ }
228
+ if (capabilities.namespaces === false)
229
+ suite.skip('lists namespaces under a prefix', 'the store declares no namespace listing');
230
+ else {
231
+ await suite.check('lists namespaces under a prefix', async () => {
232
+ const listed = (await store.listNamespaces({ prefix: ['contract'] })).map((parts) => parts.join('/')).sort();
233
+ return listed.join(',') === 'contract/users,contract/users/archived' || `got ${listed.join(', ')}`;
234
+ });
235
+ }
236
+ return suite.checks;
237
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * One vocabulary for adapter failures, so code above an adapter retries, alerts, and reports the same
3
+ * way whichever database, provider, or service the adapter talks to.
4
+ */
5
+ /** What kind of failure an adapter call met. */
6
+ export type AdapterErrorCode = 'AUTH' | 'RATE_LIMIT' | 'NOT_FOUND' | 'INVALID' | 'CONFLICT' | 'TIMEOUT' | 'UNAVAILABLE' | 'CANCELLED' | 'UNKNOWN';
7
+ /** A failure's code, and whether another attempt could succeed. */
8
+ export interface AdapterErrorClass {
9
+ /** The kind of failure. */
10
+ code: AdapterErrorCode;
11
+ /** True for a failure a retry could get past: a rate limit, a timeout, an unavailable service. */
12
+ retryable: boolean;
13
+ /** The HTTP status, when the failure carried one. */
14
+ status?: number;
15
+ }
16
+ /** A failed adapter call, normalized. The original error is its `cause`. */
17
+ export declare class AdapterError extends Error {
18
+ /** The adapter that failed. */
19
+ readonly adapter: string;
20
+ /** The method that failed, such as `search`. */
21
+ readonly operation: string;
22
+ /** The kind of failure. */
23
+ readonly code: AdapterErrorCode;
24
+ /** Whether another attempt could succeed. */
25
+ readonly retryable: boolean;
26
+ /** The HTTP status, when the failure carried one. */
27
+ readonly status?: number;
28
+ constructor(
29
+ /** The adapter that failed. */
30
+ adapter: string,
31
+ /** The method that failed, such as `search`. */
32
+ operation: string, failure: AdapterErrorClass, cause: unknown);
33
+ }
34
+ /**
35
+ * Classifies any error by what it carries: an HTTP status (`status`, `statusCode`, or
36
+ * `response.status`), a Node network code such as `ECONNREFUSED`, or an `AbortError` or
37
+ * `TimeoutError` name. An adapter's own `normalizeError` runs first and may override it.
38
+ */
39
+ export declare function classifyAdapterError(error: unknown): AdapterErrorClass;
@@ -0,0 +1,52 @@
1
+ export class AdapterError extends Error {
2
+ adapter;
3
+ operation;
4
+ code;
5
+ retryable;
6
+ status;
7
+ constructor(adapter, operation, failure, cause) {
8
+ super(`${adapter} ${operation} failed (${failure.code}): ${cause instanceof Error ? cause.message : String(cause)}`, {
9
+ cause,
10
+ });
11
+ this.adapter = adapter;
12
+ this.operation = operation;
13
+ this.name = 'AdapterError';
14
+ this.code = failure.code;
15
+ this.retryable = failure.retryable;
16
+ if (failure.status !== undefined)
17
+ this.status = failure.status;
18
+ }
19
+ }
20
+ const RETRYABLE = new Set(['RATE_LIMIT', 'TIMEOUT', 'UNAVAILABLE']);
21
+ export function classifyAdapterError(error) {
22
+ const value = error;
23
+ const raw = value?.status ?? value?.statusCode ?? value?.response?.status;
24
+ const status = typeof raw === 'number' ? raw : undefined;
25
+ const code = (() => {
26
+ if (value?.name === 'AbortError')
27
+ return 'CANCELLED';
28
+ if (value?.name === 'TimeoutError' || value?.code === 'ETIMEDOUT')
29
+ return 'TIMEOUT';
30
+ if (status !== undefined) {
31
+ if (status === 401 || status === 403)
32
+ return 'AUTH';
33
+ if (status === 404)
34
+ return 'NOT_FOUND';
35
+ if (status === 408)
36
+ return 'TIMEOUT';
37
+ if (status === 409 || status === 412)
38
+ return 'CONFLICT';
39
+ if (status === 429)
40
+ return 'RATE_LIMIT';
41
+ if (status >= 500)
42
+ return 'UNAVAILABLE';
43
+ if (status >= 400)
44
+ return 'INVALID';
45
+ }
46
+ if (typeof value?.code === 'string' && /^(ECONNREFUSED|ECONNRESET|ENOTFOUND|EAI_AGAIN|EPIPE)$/.test(value.code)) {
47
+ return 'UNAVAILABLE';
48
+ }
49
+ return 'UNKNOWN';
50
+ })();
51
+ return { code, retryable: RETRYABLE.has(code), ...(status === undefined ? {} : { status }) };
52
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * The adapter kit: how a package outside this repository builds an adapter — a provider, an
3
+ * embeddings backend, a vector store, a retriever, or a long-term store — and proves it works.
4
+ *
5
+ * Each `define*()` takes what the adapter is, the releases of `nexus-ai-pro` it supports, what it
6
+ * can do, and how to create it, and returns an adapter whose instances normalize every failure into
7
+ * an `AdapterError` and report every call for telemetry. Its `verify()` runs the same contract suite
8
+ * this package runs on its own adapters, skipping only what the adapter declares it cannot do, with a
9
+ * small benchmark held to the budgets it declares.
10
+ */
11
+ import type { EmbeddingProvider, VectorStore } from '../hallucination/retrieval.js';
12
+ import type { BaseProvider } from '../providers/base.js';
13
+ import type { Retriever } from '../rag/retrievers.js';
14
+ import { type EmbeddingProviderConformanceOptions } from '../testing/embedding-provider-conformance.js';
15
+ import { type ProviderConformanceOptions } from '../testing/provider-conformance.js';
16
+ import type { EmbeddingsProvider } from '../types/embeddings.js';
17
+ import type { Store } from '../types/store.js';
18
+ import { type AdapterCheck, type RetrieverCapabilities, type RetrieverContractOptions, type StoreCapabilities, type VectorStoreCapabilities } from './contracts.js';
19
+ import { type AdapterErrorClass } from './errors.js';
20
+ export { ADAPTER_FIXTURE_PASSAGES, type AdapterCheck, type RetrieverCapabilities, type RetrieverContractOptions, runRetrieverContract, runStoreContract, runVectorStoreContract, type StoreCapabilities, type VectorStoreCapabilities, type VectorStoreContractOptions, } from './contracts.js';
21
+ export { AdapterError, type AdapterErrorClass, type AdapterErrorCode, classifyAdapterError } from './errors.js';
22
+ export { parseVersion, satisfiesRange } from './semver.js';
23
+ /** The kinds of adapter the kit builds. */
24
+ export type AdapterKind = 'provider' | 'embeddings' | 'vector-store' | 'retriever' | 'store';
25
+ /** What every adapter declares about itself. */
26
+ export interface AdapterDefinition<Config, Instance, Capabilities> {
27
+ /** Its name, usually its package name, such as `@acme/nexus-milvus`. */
28
+ name: string;
29
+ /** Its own version. */
30
+ version: string;
31
+ /** The releases of `nexus-ai-pro` it works with, as a range: `>=2.4.0 <3`. */
32
+ nexus: string;
33
+ /** What it can do. The contract skips what it declares it cannot, and fails what it claims and cannot. */
34
+ capabilities: Capabilities;
35
+ /** Creates an instance from its configuration. */
36
+ create(config: Config): Instance | Promise<Instance>;
37
+ /**
38
+ * Classifies a failure from the service behind the adapter, when the kit's own reading of statuses
39
+ * and network codes is not enough. Returning nothing falls back to that reading.
40
+ */
41
+ normalizeError?(error: unknown, operation: string): AdapterErrorClass | undefined;
42
+ /** The p95 latency each operation must stay within in `verify()`'s benchmark, in milliseconds. */
43
+ budgets?: Readonly<Record<string, number>>;
44
+ }
45
+ /** One call through an adapter, for telemetry. */
46
+ export interface AdapterCallEvent {
47
+ /** The adapter. */
48
+ adapter: string;
49
+ /** Its kind. */
50
+ kind: AdapterKind;
51
+ /** The method called, such as `search`. */
52
+ operation: string;
53
+ /** How long the call took, in milliseconds. */
54
+ durationMs: number;
55
+ /** Whether it succeeded. */
56
+ ok: boolean;
57
+ /** How it failed, when it did. */
58
+ error?: AdapterErrorClass;
59
+ }
60
+ /** Options for an adapter's `create()`. */
61
+ export interface AdapterCreateOptions {
62
+ /** Receives every call: the hook a tracer, a metrics exporter, or a log subscribes with. */
63
+ onCall?: (event: AdapterCallEvent) => void;
64
+ }
65
+ /** What `verify()` measured for one operation over the contract's calls. */
66
+ export interface AdapterBenchmarkEntry {
67
+ /** Successful calls measured. */
68
+ calls: number;
69
+ /** Median latency, in milliseconds. */
70
+ p50Ms: number;
71
+ /** 95th-percentile latency, in milliseconds. */
72
+ p95Ms: number;
73
+ }
74
+ /** What `verify()` found. */
75
+ export interface AdapterReport {
76
+ /** The adapter. */
77
+ adapter: string;
78
+ /** Its version. */
79
+ version: string;
80
+ /** Its kind. */
81
+ kind: AdapterKind;
82
+ /** The `nexus-ai-pro` release it was verified against. */
83
+ nexusVersion: string;
84
+ /** Whether that release is inside the adapter's declared range. */
85
+ compatible: boolean;
86
+ /** True when it is compatible and every check that ran held. */
87
+ passed: boolean;
88
+ /** Every check, in order, with skipped ones marked. */
89
+ checks: AdapterCheck[];
90
+ /** Latency per operation over the contract's calls. */
91
+ benchmark: Record<string, AdapterBenchmarkEntry>;
92
+ }
93
+ /** An adapter built with the kit. */
94
+ export interface Adapter<Config, Instance, Capabilities, VerifyOptions> extends Readonly<Omit<AdapterDefinition<Config, Instance, Capabilities>, 'create'>> {
95
+ /** Its kind. */
96
+ readonly kind: AdapterKind;
97
+ /** Creates an instance whose every call normalizes its failures and reports itself. */
98
+ create(config: Config, options?: AdapterCreateOptions): Promise<Instance>;
99
+ /** Whether a release of `nexus-ai-pro` is inside the declared range. Defaults to the installed one. */
100
+ compatible(nexusVersion?: string): boolean;
101
+ /** Runs the contract suite for its kind against a fresh instance, with a benchmark. */
102
+ verify(config: Config, options: VerifyOptions): Promise<AdapterReport>;
103
+ }
104
+ /** What a chat provider supports, which decides the conformance cases it runs. */
105
+ export interface ProviderAdapterCapabilities {
106
+ /** `stream()`. */
107
+ streaming: boolean;
108
+ /** JSON output through `responseFormat`. */
109
+ json: boolean;
110
+ /** Tool calls. */
111
+ tools: boolean;
112
+ /** `healthCheck()`. */
113
+ health: boolean;
114
+ }
115
+ /** Options for a provider adapter's `verify()`: the model and, if you like, cases of your own. */
116
+ export type ProviderVerifyOptions = Pick<ProviderConformanceOptions, 'model' | 'fixtures'>;
117
+ /** Builds a chat-provider adapter, verified by the provider conformance suite. */
118
+ export declare function defineProviderAdapter<Config, Instance extends BaseProvider>(definition: AdapterDefinition<Config, Instance, ProviderAdapterCapabilities>): Adapter<Config, Instance, ProviderAdapterCapabilities, ProviderVerifyOptions>;
119
+ /** What an embeddings backend supports. */
120
+ export interface EmbeddingsAdapterCapabilities {
121
+ /** Stops an embedding call when its signal aborts. */
122
+ abort: boolean;
123
+ /** Returns the same vectors for the same input. */
124
+ deterministic: boolean;
125
+ }
126
+ /** Options for an embeddings adapter's `verify()`. */
127
+ export type EmbeddingsVerifyOptions = Pick<EmbeddingProviderConformanceOptions, 'model' | 'fixtures'>;
128
+ /** Builds an embeddings adapter, verified by the embeddings conformance suite. */
129
+ export declare function defineEmbeddingsAdapter<Config, Instance extends EmbeddingsProvider>(definition: AdapterDefinition<Config, Instance, EmbeddingsAdapterCapabilities>): Adapter<Config, Instance, EmbeddingsAdapterCapabilities, EmbeddingsVerifyOptions>;
130
+ /** Options for a vector-store adapter's `verify()`. */
131
+ export interface VectorStoreVerifyOptions {
132
+ /** The embedding function the store embeds with, to compute a query vector. */
133
+ embed: EmbeddingProvider;
134
+ /** Waits for writes to become searchable, for an eventually consistent index. */
135
+ settle?: () => Promise<void>;
136
+ }
137
+ /** Builds a vector-store adapter, verified by the `VectorStore` contract. */
138
+ export declare function defineVectorStoreAdapter<Config, Instance extends VectorStore>(definition: AdapterDefinition<Config, Instance, VectorStoreCapabilities>): Adapter<Config, Instance, VectorStoreCapabilities, VectorStoreVerifyOptions>;
139
+ /** Options for a retriever adapter's `verify()`. */
140
+ export type RetrieverVerifyOptions = Omit<RetrieverContractOptions, 'capabilities'>;
141
+ /** Builds a retriever adapter, verified by the `Retriever` contract, and the `SparseRetriever` one when it takes writes. */
142
+ export declare function defineRetrieverAdapter<Config, Instance extends Retriever>(definition: AdapterDefinition<Config, Instance, RetrieverCapabilities>): Adapter<Config, Instance, RetrieverCapabilities, RetrieverVerifyOptions>;
143
+ /** Builds a long-term store adapter, verified by the `Store` contract. */
144
+ export declare function defineStoreAdapter<Config, Instance extends Store>(definition: AdapterDefinition<Config, Instance, StoreCapabilities>): Adapter<Config, Instance, StoreCapabilities, Record<string, never>>;