nexus-ai-pro 2.0.0 → 2.2.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 (282) hide show
  1. package/API_STABILITY.md +65 -0
  2. package/CHANGELOG.md +267 -1
  3. package/MIGRATING.md +1 -1
  4. package/README.md +17 -8
  5. package/dist/agent/create-agent.d.ts +9 -1
  6. package/dist/agent/create-agent.js +40 -1
  7. package/dist/cache/memory-cache.d.ts +8 -0
  8. package/dist/cache/memory-cache.js +9 -0
  9. package/dist/cache/semantic-cache.d.ts +6 -3
  10. package/dist/cache/semantic-cache.js +5 -2
  11. package/dist/cli/args.js +3 -0
  12. package/dist/cli/db.d.ts +16 -4
  13. package/dist/cli/db.js +169 -22
  14. package/dist/cli/doctor.d.ts +17 -0
  15. package/dist/cli/doctor.js +53 -0
  16. package/dist/cli/graph.d.ts +9 -0
  17. package/dist/cli/graph.js +47 -0
  18. package/dist/cli.js +21 -2
  19. package/dist/core/lifecycle.js +11 -6
  20. package/dist/core/nexus.d.ts +12 -2
  21. package/dist/core/nexus.js +106 -23
  22. package/dist/doctor/index.d.ts +90 -0
  23. package/dist/doctor/index.js +237 -0
  24. package/dist/evals/judge.d.ts +11 -0
  25. package/dist/evals/judge.js +33 -0
  26. package/dist/evaluate/index.d.ts +1 -0
  27. package/dist/evaluate/index.js +1 -0
  28. package/dist/evaluate/provenance.d.ts +22 -0
  29. package/dist/evaluate/provenance.js +10 -0
  30. package/dist/evaluate/run.js +20 -4
  31. package/dist/graph/durability.d.ts +27 -0
  32. package/dist/graph/durability.js +66 -0
  33. package/dist/graph/errors.d.ts +30 -4
  34. package/dist/graph/errors.js +18 -2
  35. package/dist/graph/event-stream.d.ts +75 -0
  36. package/dist/graph/event-stream.js +289 -0
  37. package/dist/graph/functional.d.ts +80 -7
  38. package/dist/graph/functional.js +138 -35
  39. package/dist/graph/graph.d.ts +56 -8
  40. package/dist/graph/graph.js +331 -49
  41. package/dist/graph/index.d.ts +4 -2
  42. package/dist/graph/index.js +3 -1
  43. package/dist/graph/lint.d.ts +57 -0
  44. package/dist/graph/lint.js +141 -0
  45. package/dist/graph/run-control.d.ts +28 -0
  46. package/dist/graph/run-control.js +26 -0
  47. package/dist/images/evals.js +4 -2
  48. package/dist/operations/adapters.d.ts +52 -4
  49. package/dist/operations/adapters.js +194 -7
  50. package/dist/operations/runner.js +14 -15
  51. package/dist/operations/stats.js +8 -1
  52. package/dist/operations/store.d.ts +5 -1
  53. package/dist/operations/store.js +7 -0
  54. package/dist/ops/circuit-breaker.d.ts +52 -0
  55. package/dist/ops/circuit-breaker.js +4 -0
  56. package/dist/ops/circuit-store.d.ts +13 -2
  57. package/dist/ops/circuit-store.js +68 -0
  58. package/dist/ops/circuit-sync.d.ts +17 -0
  59. package/dist/ops/circuit-sync.js +86 -0
  60. package/dist/ops/health.d.ts +24 -0
  61. package/dist/ops/health.js +38 -10
  62. package/dist/ops/index.d.ts +1 -1
  63. package/dist/ops/index.js +1 -1
  64. package/dist/ops/rate-limit-adapters.d.ts +23 -0
  65. package/dist/ops/rate-limit-adapters.js +34 -0
  66. package/dist/ops/rate-limiter.d.ts +14 -2
  67. package/dist/ops/rate-limiter.js +31 -0
  68. package/dist/postgres/circuits.d.ts +21 -4
  69. package/dist/postgres/circuits.js +65 -4
  70. package/dist/postgres/client.d.ts +0 -2
  71. package/dist/postgres/client.js +0 -4
  72. package/dist/postgres/evaluate.d.ts +16 -4
  73. package/dist/postgres/evaluate.js +17 -5
  74. package/dist/postgres/index.d.ts +32 -11
  75. package/dist/postgres/index.js +65 -31
  76. package/dist/postgres/migrations.d.ts +60 -0
  77. package/dist/postgres/migrations.js +121 -0
  78. package/dist/postgres/operations.d.ts +14 -3
  79. package/dist/postgres/operations.js +45 -15
  80. package/dist/postgres/prompts.d.ts +11 -2
  81. package/dist/postgres/prompts.js +14 -3
  82. package/dist/postgres/rollups.d.ts +35 -0
  83. package/dist/postgres/rollups.js +129 -0
  84. package/dist/postgres/store.d.ts +11 -2
  85. package/dist/postgres/store.js +14 -3
  86. package/dist/postgres/traces.d.ts +13 -3
  87. package/dist/postgres/traces.js +25 -3
  88. package/dist/postgres/vectors.d.ts +11 -2
  89. package/dist/postgres/vectors.js +14 -3
  90. package/dist/router/auto-router.d.ts +34 -7
  91. package/dist/router/auto-router.js +201 -68
  92. package/dist/server/assistant.d.ts +44 -0
  93. package/dist/server/assistant.js +39 -0
  94. package/dist/server/errors.d.ts +12 -0
  95. package/dist/server/errors.js +7 -0
  96. package/dist/server/index.d.ts +1 -1
  97. package/dist/server/index.js +1 -1
  98. package/dist/server/runs.d.ts +28 -1
  99. package/dist/server/runs.js +110 -14
  100. package/dist/sqlite/index.d.ts +16 -3
  101. package/dist/sqlite/index.js +16 -3
  102. package/dist/sqlite/migrations.d.ts +26 -0
  103. package/dist/sqlite/migrations.js +63 -0
  104. package/dist/sqlite/operations.d.ts +14 -3
  105. package/dist/sqlite/operations.js +42 -15
  106. package/dist/sqlite/store.d.ts +9 -2
  107. package/dist/sqlite/store.js +13 -3
  108. package/dist/sqlite/vectors.d.ts +11 -2
  109. package/dist/sqlite/vectors.js +13 -2
  110. package/dist/store/tenant.d.ts +12 -0
  111. package/dist/store/tenant.js +29 -0
  112. package/dist/tenancy/index.d.ts +113 -0
  113. package/dist/tenancy/index.js +233 -0
  114. package/dist/testing/record.d.ts +42 -0
  115. package/dist/testing/record.js +57 -5
  116. package/dist/tracing/index.d.ts +3 -3
  117. package/dist/tracing/index.js +2 -2
  118. package/dist/tracing/instrument.js +30 -0
  119. package/dist/tracing/otlp.d.ts +91 -0
  120. package/dist/tracing/otlp.js +124 -0
  121. package/dist/tracing/rollups.d.ts +100 -0
  122. package/dist/tracing/rollups.js +139 -0
  123. package/dist/tracing/stores.d.ts +17 -0
  124. package/dist/tracing/stores.js +23 -0
  125. package/dist/tracing/tracer.d.ts +50 -4
  126. package/dist/tracing/tracer.js +85 -6
  127. package/dist/types/config.d.ts +33 -2
  128. package/dist/types/evaluate.d.ts +37 -0
  129. package/dist/types/graph.d.ts +354 -9
  130. package/dist/types/graph.js +3 -1
  131. package/dist/types/messages.d.ts +5 -0
  132. package/dist/types/operations.d.ts +22 -2
  133. package/dist/types/response.d.ts +20 -0
  134. package/dist/types/server.d.ts +18 -0
  135. package/dist/types/tracing.d.ts +11 -0
  136. package/dist/utils/schema-migrations.d.ts +97 -0
  137. package/dist/utils/schema-migrations.js +91 -0
  138. package/dist/utils/signals.d.ts +17 -0
  139. package/dist/utils/signals.js +26 -0
  140. package/dist/utils/tenant.d.ts +14 -0
  141. package/dist/utils/tenant.js +16 -0
  142. package/dist/version.d.ts +5 -0
  143. package/dist/version.js +1 -0
  144. package/dist-cjs/agent/create-agent.d.ts +9 -1
  145. package/dist-cjs/agent/create-agent.js +40 -1
  146. package/dist-cjs/cache/memory-cache.d.ts +8 -0
  147. package/dist-cjs/cache/memory-cache.js +9 -0
  148. package/dist-cjs/cache/semantic-cache.d.ts +6 -3
  149. package/dist-cjs/cache/semantic-cache.js +5 -2
  150. package/dist-cjs/cli/args.js +3 -0
  151. package/dist-cjs/cli/db.d.ts +16 -4
  152. package/dist-cjs/cli/db.js +200 -20
  153. package/dist-cjs/cli/doctor.d.ts +17 -0
  154. package/dist-cjs/cli/doctor.js +56 -0
  155. package/dist-cjs/cli/graph.d.ts +9 -0
  156. package/dist-cjs/cli/graph.js +50 -0
  157. package/dist-cjs/core/lifecycle.js +11 -6
  158. package/dist-cjs/core/nexus.d.ts +12 -2
  159. package/dist-cjs/core/nexus.js +106 -23
  160. package/dist-cjs/doctor/index.d.ts +90 -0
  161. package/dist-cjs/doctor/index.js +276 -0
  162. package/dist-cjs/evals/judge.d.ts +11 -0
  163. package/dist-cjs/evals/judge.js +33 -0
  164. package/dist-cjs/evaluate/index.d.ts +1 -0
  165. package/dist-cjs/evaluate/index.js +3 -1
  166. package/dist-cjs/evaluate/provenance.d.ts +22 -0
  167. package/dist-cjs/evaluate/provenance.js +14 -0
  168. package/dist-cjs/evaluate/run.js +20 -4
  169. package/dist-cjs/graph/durability.d.ts +27 -0
  170. package/dist-cjs/graph/durability.js +70 -0
  171. package/dist-cjs/graph/errors.d.ts +30 -4
  172. package/dist-cjs/graph/errors.js +20 -3
  173. package/dist-cjs/graph/event-stream.d.ts +75 -0
  174. package/dist-cjs/graph/event-stream.js +295 -0
  175. package/dist-cjs/graph/functional.d.ts +80 -7
  176. package/dist-cjs/graph/functional.js +137 -34
  177. package/dist-cjs/graph/graph.d.ts +56 -8
  178. package/dist-cjs/graph/graph.js +330 -48
  179. package/dist-cjs/graph/index.d.ts +4 -2
  180. package/dist-cjs/graph/index.js +7 -1
  181. package/dist-cjs/graph/lint.d.ts +57 -0
  182. package/dist-cjs/graph/lint.js +144 -0
  183. package/dist-cjs/graph/run-control.d.ts +28 -0
  184. package/dist-cjs/graph/run-control.js +30 -0
  185. package/dist-cjs/images/evals.js +4 -2
  186. package/dist-cjs/operations/adapters.d.ts +52 -4
  187. package/dist-cjs/operations/adapters.js +193 -6
  188. package/dist-cjs/operations/runner.js +14 -15
  189. package/dist-cjs/operations/stats.js +8 -1
  190. package/dist-cjs/operations/store.d.ts +5 -1
  191. package/dist-cjs/operations/store.js +7 -0
  192. package/dist-cjs/ops/circuit-breaker.d.ts +52 -0
  193. package/dist-cjs/ops/circuit-breaker.js +4 -0
  194. package/dist-cjs/ops/circuit-store.d.ts +13 -2
  195. package/dist-cjs/ops/circuit-store.js +68 -0
  196. package/dist-cjs/ops/circuit-sync.d.ts +17 -0
  197. package/dist-cjs/ops/circuit-sync.js +87 -0
  198. package/dist-cjs/ops/health.d.ts +24 -0
  199. package/dist-cjs/ops/health.js +38 -10
  200. package/dist-cjs/ops/index.d.ts +1 -1
  201. package/dist-cjs/ops/rate-limit-adapters.d.ts +23 -0
  202. package/dist-cjs/ops/rate-limit-adapters.js +35 -1
  203. package/dist-cjs/ops/rate-limiter.d.ts +14 -2
  204. package/dist-cjs/ops/rate-limiter.js +32 -0
  205. package/dist-cjs/postgres/circuits.d.ts +21 -4
  206. package/dist-cjs/postgres/circuits.js +98 -3
  207. package/dist-cjs/postgres/client.d.ts +0 -2
  208. package/dist-cjs/postgres/client.js +0 -5
  209. package/dist-cjs/postgres/evaluate.d.ts +16 -4
  210. package/dist-cjs/postgres/evaluate.js +50 -4
  211. package/dist-cjs/postgres/index.d.ts +32 -11
  212. package/dist-cjs/postgres/index.js +69 -20
  213. package/dist-cjs/postgres/migrations.d.ts +60 -0
  214. package/dist-cjs/postgres/migrations.js +129 -0
  215. package/dist-cjs/postgres/operations.d.ts +14 -3
  216. package/dist-cjs/postgres/operations.js +78 -14
  217. package/dist-cjs/postgres/prompts.d.ts +11 -2
  218. package/dist-cjs/postgres/prompts.js +47 -2
  219. package/dist-cjs/postgres/rollups.d.ts +35 -0
  220. package/dist-cjs/postgres/rollups.js +168 -0
  221. package/dist-cjs/postgres/store.d.ts +11 -2
  222. package/dist-cjs/postgres/store.js +47 -2
  223. package/dist-cjs/postgres/traces.d.ts +13 -3
  224. package/dist-cjs/postgres/traces.js +58 -2
  225. package/dist-cjs/postgres/vectors.d.ts +11 -2
  226. package/dist-cjs/postgres/vectors.js +47 -2
  227. package/dist-cjs/router/auto-router.d.ts +34 -7
  228. package/dist-cjs/router/auto-router.js +201 -68
  229. package/dist-cjs/server/assistant.d.ts +44 -0
  230. package/dist-cjs/server/assistant.js +39 -0
  231. package/dist-cjs/server/errors.d.ts +12 -0
  232. package/dist-cjs/server/errors.js +9 -1
  233. package/dist-cjs/server/index.d.ts +1 -1
  234. package/dist-cjs/server/index.js +2 -1
  235. package/dist-cjs/server/runs.d.ts +28 -1
  236. package/dist-cjs/server/runs.js +109 -13
  237. package/dist-cjs/sqlite/index.d.ts +16 -3
  238. package/dist-cjs/sqlite/index.js +31 -10
  239. package/dist-cjs/sqlite/migrations.d.ts +26 -0
  240. package/dist-cjs/sqlite/migrations.js +71 -0
  241. package/dist-cjs/sqlite/operations.d.ts +14 -3
  242. package/dist-cjs/sqlite/operations.js +76 -15
  243. package/dist-cjs/sqlite/store.d.ts +9 -2
  244. package/dist-cjs/sqlite/store.js +47 -3
  245. package/dist-cjs/sqlite/vectors.d.ts +11 -2
  246. package/dist-cjs/sqlite/vectors.js +47 -2
  247. package/dist-cjs/store/tenant.d.ts +12 -0
  248. package/dist-cjs/store/tenant.js +33 -0
  249. package/dist-cjs/tenancy/index.d.ts +113 -0
  250. package/dist-cjs/tenancy/index.js +250 -0
  251. package/dist-cjs/testing/record.d.ts +42 -0
  252. package/dist-cjs/testing/record.js +57 -5
  253. package/dist-cjs/tracing/index.d.ts +3 -3
  254. package/dist-cjs/tracing/index.js +4 -1
  255. package/dist-cjs/tracing/instrument.js +30 -0
  256. package/dist-cjs/tracing/otlp.d.ts +91 -0
  257. package/dist-cjs/tracing/otlp.js +129 -0
  258. package/dist-cjs/tracing/rollups.d.ts +100 -0
  259. package/dist-cjs/tracing/rollups.js +147 -0
  260. package/dist-cjs/tracing/stores.d.ts +17 -0
  261. package/dist-cjs/tracing/stores.js +24 -0
  262. package/dist-cjs/tracing/tracer.d.ts +50 -4
  263. package/dist-cjs/tracing/tracer.js +87 -6
  264. package/dist-cjs/types/config.d.ts +33 -2
  265. package/dist-cjs/types/evaluate.d.ts +37 -0
  266. package/dist-cjs/types/graph.d.ts +354 -9
  267. package/dist-cjs/types/graph.js +3 -1
  268. package/dist-cjs/types/messages.d.ts +5 -0
  269. package/dist-cjs/types/operations.d.ts +22 -2
  270. package/dist-cjs/types/response.d.ts +20 -0
  271. package/dist-cjs/types/server.d.ts +18 -0
  272. package/dist-cjs/types/tracing.d.ts +11 -0
  273. package/dist-cjs/utils/schema-migrations.d.ts +97 -0
  274. package/dist-cjs/utils/schema-migrations.js +102 -0
  275. package/dist-cjs/utils/signals.d.ts +17 -0
  276. package/dist-cjs/utils/signals.js +29 -0
  277. package/dist-cjs/utils/tenant.d.ts +14 -0
  278. package/dist-cjs/utils/tenant.js +22 -0
  279. package/dist-cjs/version.d.ts +5 -0
  280. package/dist-cjs/version.js +4 -0
  281. package/llms.txt +4 -3
  282. package/package.json +110 -2
@@ -1,31 +1,211 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.runDbCommand = runDbCommand;
4
37
  const index_js_1 = require("../postgres/index.js");
5
38
  const args_js_1 = require("./args.js");
6
- const ADAPTERS = [
7
- 'operations',
8
- 'store',
9
- 'traces',
10
- 'evaluation',
11
- 'circuits',
12
- 'prompts',
13
- 'vectors',
14
- ];
15
- async function runDbCommand(subcommand, { flags }) {
16
- if (subcommand !== 'sql')
17
- throw new args_js_1.CliUsageError(`Unknown db command "${subcommand ?? ''}". Use sql.`);
18
- const adapters = (0, args_js_1.listFlag)(flags, 'adapters');
19
- const unknown = adapters?.filter((adapter) => !ADAPTERS.includes(adapter));
20
- if (unknown?.length) {
21
- throw new args_js_1.CliUsageError(`Unknown adapter ${unknown.join(', ')}. Choose from ${ADAPTERS.join(', ')}.`);
22
- }
39
+ const SQLITE_ADAPTERS = ['operations', 'store', 'vectors'];
40
+ async function runDbCommand(subcommand, args) {
41
+ if (subcommand === 'sql')
42
+ return printSql(args);
43
+ if (subcommand === 'status' || subcommand === 'migrate')
44
+ return withDatabase(subcommand, args);
45
+ throw new args_js_1.CliUsageError(`Unknown db command "${subcommand ?? ''}". Use sql, status, or migrate.`);
46
+ }
47
+ function printSql({ flags }) {
48
+ const adapters = postgresAdapters(flags);
23
49
  const vectorDimensions = (0, args_js_1.numberFlag)(flags, 'vector-dimensions');
24
- if (adapters?.includes('vectors') && vectorDimensions === undefined) {
25
- throw new args_js_1.CliUsageError('The vectors adapter needs --vector-dimensions.');
26
- }
27
50
  process.stdout.write((0, index_js_1.postgresMigration)({
28
51
  ...(adapters ? { adapters } : {}),
29
52
  ...(vectorDimensions === undefined ? {} : { vectorDimensions }),
53
+ ...((0, args_js_1.flagBool)(flags, 'record') ? { record: true } : {}),
30
54
  }));
31
55
  }
56
+ function postgresAdapters(flags) {
57
+ const adapters = (0, args_js_1.listFlag)(flags, 'adapters');
58
+ const unknown = adapters?.filter((adapter) => !index_js_1.POSTGRES_ADAPTERS.includes(adapter));
59
+ if (unknown?.length) {
60
+ throw new args_js_1.CliUsageError(`Unknown adapter ${unknown.join(', ')}. Choose from ${index_js_1.POSTGRES_ADAPTERS.join(', ')}.`);
61
+ }
62
+ if (adapters?.includes('vectors') && (0, args_js_1.numberFlag)(flags, 'vector-dimensions') === undefined) {
63
+ throw new args_js_1.CliUsageError('The vectors adapter needs --vector-dimensions.');
64
+ }
65
+ return adapters;
66
+ }
67
+ async function withDatabase(command, { flags }) {
68
+ const location = (0, args_js_1.stringFlag)(flags, 'client');
69
+ if (!location)
70
+ throw new args_js_1.CliUsageError(`nexus db ${command} needs --client <db.mjs>, a module exporting \`client\`.`);
71
+ const database = await openDatabase(location, (0, args_js_1.stringFlag)(flags, 'dialect'));
72
+ try {
73
+ const migrations = database.migrations ?? (await bundledMigrations(database.dialect, flags));
74
+ const table = (0, args_js_1.stringFlag)(flags, 'table');
75
+ if (command === 'status') {
76
+ const status = await statusOf(database, migrations, table);
77
+ if ((0, args_js_1.flagBool)(flags, 'json'))
78
+ (0, args_js_1.writeJson)(status);
79
+ else
80
+ printStatus(status);
81
+ if ((0, args_js_1.flagBool)(flags, 'check') && !status.current)
82
+ process.exitCode = 1;
83
+ return;
84
+ }
85
+ const result = await migrate(database, migrations, { dryRun: (0, args_js_1.flagBool)(flags, 'dry-run'), table });
86
+ if ((0, args_js_1.flagBool)(flags, 'json'))
87
+ (0, args_js_1.writeJson)(result);
88
+ else
89
+ printResult(result);
90
+ }
91
+ finally {
92
+ await database.close?.();
93
+ }
94
+ }
95
+ async function openDatabase(location, dialectFlag) {
96
+ const loaded = await (0, args_js_1.loadModule)(location);
97
+ const client = loaded.client ?? loaded.default;
98
+ if (!client || typeof client !== 'object') {
99
+ throw new args_js_1.CliUsageError(`${location} must export a database client as \`client\`.`);
100
+ }
101
+ const candidate = client;
102
+ const dialect = (dialectFlag ?? loaded.dialect);
103
+ const detected = dialect ??
104
+ (typeof candidate.query === 'function'
105
+ ? 'postgres'
106
+ : typeof candidate.prepare === 'function' || typeof candidate.exec === 'function'
107
+ ? 'sqlite'
108
+ : undefined);
109
+ if (detected !== 'postgres' && detected !== 'sqlite') {
110
+ throw new args_js_1.CliUsageError(`${location} exports a client this CLI cannot recognize. Export \`dialect\` as "postgres" or "sqlite".`);
111
+ }
112
+ return {
113
+ dialect: detected,
114
+ client,
115
+ ...(typeof loaded.transaction === 'function'
116
+ ? { transaction: loaded.transaction }
117
+ : {}),
118
+ ...(Array.isArray(loaded.migrations) ? { migrations: loaded.migrations } : {}),
119
+ ...(typeof loaded.close === 'function' ? { close: loaded.close } : {}),
120
+ };
121
+ }
122
+ async function bundledMigrations(dialect, flags) {
123
+ if (dialect === 'postgres') {
124
+ const adapters = postgresAdapters(flags);
125
+ const vectorDimensions = (0, args_js_1.numberFlag)(flags, 'vector-dimensions');
126
+ return (0, index_js_1.postgresMigrations)({
127
+ ...(adapters ? { adapters } : {}),
128
+ ...(vectorDimensions === undefined ? {} : { vectorDimensions }),
129
+ });
130
+ }
131
+ const adapters = (0, args_js_1.listFlag)(flags, 'adapters');
132
+ const unknown = adapters?.filter((adapter) => !SQLITE_ADAPTERS.includes(adapter));
133
+ if (unknown?.length) {
134
+ throw new args_js_1.CliUsageError(`Unknown SQLite adapter ${unknown.join(', ')}. Choose from ${SQLITE_ADAPTERS.join(', ')}.`);
135
+ }
136
+ const { sqliteMigrations } = await Promise.resolve().then(() => __importStar(require('../sqlite/index.js')));
137
+ return sqliteMigrations(adapters ? { adapters } : {});
138
+ }
139
+ async function statusOf(database, migrations, table) {
140
+ const options = table ? { table } : {};
141
+ if (database.dialect === 'postgres') {
142
+ const { postgresMigrationStatus } = await Promise.resolve().then(() => __importStar(require('../postgres/migrations.js')));
143
+ return postgresMigrationStatus(database.client, migrations, options);
144
+ }
145
+ const { sqliteMigrationStatus } = await Promise.resolve().then(() => __importStar(require('../sqlite/migrations.js')));
146
+ return sqliteMigrationStatus(database.client, migrations, options);
147
+ }
148
+ async function migrate(database, migrations, options) {
149
+ const common = { dryRun: options.dryRun, ...(options.table ? { table: options.table } : {}) };
150
+ if (database.dialect === 'postgres') {
151
+ const { applyPostgresMigrations } = await Promise.resolve().then(() => __importStar(require('../postgres/migrations.js')));
152
+ return applyPostgresMigrations(database.client, migrations, {
153
+ ...common,
154
+ ...(database.transaction ? { transaction: database.transaction } : {}),
155
+ });
156
+ }
157
+ const { applySqliteMigrations } = await Promise.resolve().then(() => __importStar(require('../sqlite/migrations.js')));
158
+ return applySqliteMigrations(database.client, migrations, common);
159
+ }
160
+ function printStatus(status) {
161
+ const changed = new Set(status.changed.map(({ migration }) => `${migration.component}@${migration.version}`));
162
+ const rows = [
163
+ ...status.applied
164
+ .filter((row) => !status.unknown.includes(row))
165
+ .map((row) => {
166
+ const key = `${row.component}@${row.version}`;
167
+ return {
168
+ component: row.component,
169
+ version: String(row.version),
170
+ name: row.name,
171
+ state: changed.has(key) ? 'changed' : 'applied',
172
+ applied: row.appliedAt,
173
+ };
174
+ }),
175
+ ...status.unknown.map((row) => ({
176
+ component: row.component,
177
+ version: String(row.version),
178
+ name: row.name,
179
+ state: 'newer release',
180
+ applied: row.appliedAt,
181
+ })),
182
+ ...status.pending.map((migration) => ({
183
+ component: migration.component,
184
+ version: String(migration.version),
185
+ name: migration.name,
186
+ state: 'pending',
187
+ applied: '',
188
+ })),
189
+ ];
190
+ if (rows.length === 0) {
191
+ console.log('No migrations known or recorded.');
192
+ return;
193
+ }
194
+ (0, args_js_1.printTable)(rows, ['component', 'version', 'name', 'state', 'applied']);
195
+ console.log(status.current
196
+ ? '\nUp to date.'
197
+ : `\n${status.pending.length} pending${status.changed.length ? `, ${status.changed.length} changed after they ran` : ''}. Run: nexus db migrate`);
198
+ }
199
+ function printResult(result) {
200
+ if (result.applied.length === 0) {
201
+ console.log('Nothing to apply: the database is up to date.');
202
+ return;
203
+ }
204
+ const verb = result.dryRun ? 'Would apply' : 'Applied';
205
+ for (const migration of result.applied)
206
+ console.log(`${verb} ${migration.component} v${migration.version}: ${migration.name}`);
207
+ if (result.dryRun) {
208
+ console.log('\n-- The statements, in order:\n');
209
+ console.log(`${result.statements.join(';\n\n')};`);
210
+ }
211
+ }
@@ -0,0 +1,17 @@
1
+ import { type ParsedArgs } from './args.js';
2
+ /**
3
+ * `nexus doctor` checks a deployment and exits 1 when a check fails, or with `--strict` when one
4
+ * warns. Without `--module` it checks what it can see from the shell: Node.js, packages, credentials,
5
+ * and the model registry. A module reaches the rest, exporting any of:
6
+ *
7
+ * ```js
8
+ * // doctor.mjs
9
+ * export const database = { client: pool }; // or `client` alone, as for nexus db
10
+ * export const redis = new Redis(process.env.REDIS_URL);
11
+ * export const operations = new PostgresOperationStore(pool);
12
+ * export const ai = client;
13
+ * export const deployment = { replicas: 3, stores: { checkpointer, state, tenantUsage } };
14
+ * export const close = async () => { await pool.end(); redis.disconnect(); };
15
+ * ```
16
+ */
17
+ export declare function runDoctorCommand({ flags }: ParsedArgs): Promise<void>;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runDoctorCommand = runDoctorCommand;
4
+ const index_js_1 = require("../doctor/index.js");
5
+ const args_js_1 = require("./args.js");
6
+ const LABELS = { ok: 'ok ', warn: 'warn', fail: 'FAIL', skip: 'skip' };
7
+ async function runDoctorCommand({ flags }) {
8
+ const location = (0, args_js_1.stringFlag)(flags, 'module');
9
+ const loaded = location ? await (0, args_js_1.loadModule)(location) : {};
10
+ const options = {};
11
+ const source = (0, args_js_1.isRecord)(loaded.database) ? loaded.database : loaded.client ? loaded : undefined;
12
+ if (source?.client) {
13
+ options.database = {
14
+ client: source.client,
15
+ ...(source.dialect === 'postgres' || source.dialect === 'sqlite' ? { dialect: source.dialect } : {}),
16
+ ...(Array.isArray(source.migrations) ? { migrations: source.migrations } : {}),
17
+ ...(typeof source.table === 'string' ? { table: source.table } : {}),
18
+ };
19
+ }
20
+ if (loaded.redis)
21
+ options.redis = loaded.redis;
22
+ if (loaded.operations)
23
+ options.operations = loaded.operations;
24
+ if (loaded.ai)
25
+ options.ai = loaded.ai;
26
+ if ((0, args_js_1.isRecord)(loaded.deployment))
27
+ options.deployment = loaded.deployment;
28
+ const replicas = (0, args_js_1.numberFlag)(flags, 'replicas');
29
+ if (replicas !== undefined)
30
+ options.deployment = { ...options.deployment, replicas };
31
+ const maxQueueAgeMs = (0, args_js_1.numberFlag)(flags, 'max-queue-age-ms');
32
+ if (maxQueueAgeMs !== undefined)
33
+ options.maxQueueAgeMs = maxQueueAgeMs;
34
+ let report;
35
+ try {
36
+ report = await (0, index_js_1.diagnose)(options);
37
+ }
38
+ finally {
39
+ if (typeof loaded.close === 'function')
40
+ await loaded.close();
41
+ }
42
+ if ((0, args_js_1.flagBool)(flags, 'json'))
43
+ (0, args_js_1.writeJson)(report);
44
+ else
45
+ print(report);
46
+ if (!report.ok || ((0, args_js_1.flagBool)(flags, 'strict') && report.warnings > 0))
47
+ process.exitCode = 1;
48
+ }
49
+ function print(report) {
50
+ for (const check of report.checks) {
51
+ console.log(`${LABELS[check.status]} ${check.title}: ${check.detail}`);
52
+ if (check.hint && (check.status === 'fail' || check.status === 'warn'))
53
+ console.log(` ${check.hint}`);
54
+ }
55
+ console.log(`\n${report.failures} failed, ${report.warnings} warned, ${report.checks.length - report.failures - report.warnings} passed or skipped.`);
56
+ }
@@ -0,0 +1,9 @@
1
+ import { type ParsedArgs } from './args.js';
2
+ /**
3
+ * `nexus graph lint <module>[#export]`.
4
+ *
5
+ * The module exports a compiled graph, or a description of one as `describe()` returns it, as the
6
+ * named export, as `graph`, or as its default. Exits 1 when a finding is an error, or with `--strict`
7
+ * when any finding is a warning too.
8
+ */
9
+ export declare function runGraphCommand(subcommand: string | undefined, args: ParsedArgs): Promise<void>;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runGraphCommand = runGraphCommand;
4
+ const lint_js_1 = require("../graph/lint.js");
5
+ const args_js_1 = require("./args.js");
6
+ async function runGraphCommand(subcommand, args) {
7
+ if (subcommand !== 'lint') {
8
+ throw new args_js_1.CliUsageError(`Unknown graph command "${subcommand ?? ''}". Use lint.`);
9
+ }
10
+ const [target] = args.positionals;
11
+ if (!target)
12
+ throw new args_js_1.CliUsageError('nexus graph lint needs a module: nexus graph lint ./graph.mjs[#exportName].');
13
+ const [file, exportName] = target.split('#');
14
+ const loaded = await (0, args_js_1.loadModule)(file);
15
+ const graph = (exportName ? loaded[exportName] : (loaded.graph ?? loaded.default ?? loaded));
16
+ if (!isLintable(graph)) {
17
+ throw new args_js_1.CliUsageError(`${target} must export a compiled graph, or a graph description, ${exportName ? `as "${exportName}"` : 'as `graph` or its default'}.`);
18
+ }
19
+ const findings = (0, lint_js_1.lintGraph)(graph, {
20
+ deployed: (0, args_js_1.flagBool)(args.flags, 'deployed'),
21
+ ignore: (0, args_js_1.listFlag)(args.flags, 'ignore'),
22
+ });
23
+ const failing = findings.filter((finding) => finding.severity === 'error' || ((0, args_js_1.flagBool)(args.flags, 'strict') && finding.severity === 'warning'));
24
+ if ((0, args_js_1.flagBool)(args.flags, 'json')) {
25
+ (0, args_js_1.writeJson)({ findings, failed: failing.length > 0 });
26
+ }
27
+ else if (findings.length === 0) {
28
+ console.log('No findings.');
29
+ }
30
+ else {
31
+ (0, args_js_1.printTable)(findings.map((finding) => ({
32
+ severity: finding.severity,
33
+ code: finding.code,
34
+ node: finding.node ?? '',
35
+ message: finding.message,
36
+ })), ['severity', 'code', 'node', 'message']);
37
+ console.log('');
38
+ for (const finding of findings)
39
+ console.log(`${finding.code}: ${finding.fix}`);
40
+ }
41
+ if (failing.length > 0)
42
+ process.exitCode = 1;
43
+ }
44
+ function isLintable(value) {
45
+ if (!(0, args_js_1.isRecord)(value))
46
+ return false;
47
+ if (typeof value.describe === 'function')
48
+ return true;
49
+ return Array.isArray(value.nodes) && Array.isArray(value.edges) && Array.isArray(value.dynamic);
50
+ }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OperationLifecycle = exports.BudgetExceededError = exports.OperationDeniedError = exports.LIFECYCLE_STAGES = void 0;
4
4
  const rate_limiter_js_1 = require("../ops/rate-limiter.js");
5
5
  const ids_js_1 = require("../utils/ids.js");
6
+ const signals_js_1 = require("../utils/signals.js");
6
7
  exports.LIFECYCLE_STAGES = [
7
8
  'validate',
8
9
  'authorize',
@@ -48,14 +49,15 @@ class OperationLifecycle {
48
49
  const startedAt = Date.now();
49
50
  const timeoutMs = options.timeoutMs ?? this.runtime.timeoutMs;
50
51
  const deadline = timeoutMs !== undefined && Number.isFinite(timeoutMs) && timeoutMs > 0 ? startedAt + timeoutMs : undefined;
52
+ const call = callSignal(options.signal, deadline === undefined ? undefined : deadline - startedAt);
51
53
  const context = {
52
54
  requestId: descriptor.requestId,
53
- signal: callSignal(options.signal, deadline === undefined ? undefined : deadline - startedAt),
55
+ signal: call.signal,
54
56
  ...(deadline !== undefined ? { deadline } : {}),
55
57
  ...(options.traceContext ? { traceContext: options.traceContext } : {}),
56
58
  ...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),
57
59
  };
58
- const ticket = new Ticket(this.runtime, descriptor, context, startedAt);
60
+ const ticket = new Ticket(this.runtime, descriptor, context, startedAt, call.dispose);
59
61
  try {
60
62
  await this.runtime.metrics?.recordRequest(labelsOf(descriptor));
61
63
  await this.checkRateLimit(descriptor);
@@ -110,6 +112,7 @@ class OperationLifecycle {
110
112
  const bucket = {
111
113
  model: descriptor.model ?? `${descriptor.family}:${descriptor.operation}`,
112
114
  userId: descriptor.userId,
115
+ tenantId: descriptor.tenantId,
113
116
  };
114
117
  if (rateLimit.store)
115
118
  await rateLimiter.checkAsync(bucket, rateLimit);
@@ -141,13 +144,15 @@ class Ticket {
141
144
  descriptor;
142
145
  context;
143
146
  startedAt;
147
+ release;
144
148
  reservation;
145
149
  finished = false;
146
- constructor(runtime, descriptor, context, startedAt) {
150
+ constructor(runtime, descriptor, context, startedAt, release = () => undefined) {
147
151
  this.runtime = runtime;
148
152
  this.descriptor = descriptor;
149
153
  this.context = context;
150
154
  this.startedAt = startedAt;
155
+ this.release = release;
151
156
  }
152
157
  async reserve(estimate) {
153
158
  const budget = this.runtime.config?.budget;
@@ -217,14 +222,14 @@ class Ticket {
217
222
  });
218
223
  }
219
224
  async finish(outcome) {
225
+ this.release();
220
226
  await runHook(() => this.runtime.config?.hooks?.onFinish?.(this.descriptor, outcome));
221
227
  }
222
228
  }
223
229
  function callSignal(signal, timeoutMs) {
224
230
  if (timeoutMs === undefined)
225
- return signal ?? new AbortController().signal;
226
- const timeout = AbortSignal.timeout(timeoutMs);
227
- return signal ? AbortSignal.any([signal, timeout]) : timeout;
231
+ return { signal: signal ?? new AbortController().signal, dispose: () => undefined };
232
+ return (0, signals_js_1.linkSignals)([signal, AbortSignal.timeout(timeoutMs)]);
228
233
  }
229
234
  function outcomeOf(error, signal) {
230
235
  if (error instanceof OperationDeniedError ||
@@ -248,9 +248,13 @@ export declare class NexusAI {
248
248
  /** Health of every provider seen so far, when health tracking is on. */
249
249
  getProviderHealth(): import("../ops/health.js").ProviderHealthSnapshot[];
250
250
  /**
251
- * Calls each provider's health check and records the result.
251
+ * Calls each provider's health check and records the result. With `staleOnly`, only the providers
252
+ * whose health is `unknown` are checked: never called, or not since `health.observationTtlMs`. Run
253
+ * it on a schedule to keep an idle provider's health current.
252
254
  */
253
- checkProviders(): Promise<Array<{
255
+ checkProviders(options?: {
256
+ staleOnly?: boolean;
257
+ }): Promise<Array<{
254
258
  providerName: string;
255
259
  ok: boolean;
256
260
  error?: string;
@@ -314,6 +318,12 @@ export declare class NexusAI {
314
318
  private attachTrace;
315
319
  private usesSemanticCache;
316
320
  private loadSemanticCache;
321
+ /**
322
+ * Where a request's cached response lives: its key, its namespace, and the scope a semantic match
323
+ * must share, which is always at least the request's tenant.
324
+ */
325
+ private cachePlan;
326
+ /** Looks a request up in the cache. A cache that fails is reported as an `error` outcome and treated as a miss. */
317
327
  private getCachedResponse;
318
328
  private setCachedResponse;
319
329
  private estimateRequestCost;
@@ -190,13 +190,26 @@ class NexusAI {
190
190
  }
191
191
  await this.applyBudgets(context, ticket);
192
192
  const responseFormat = context.request.responseFormat || this.config.responseFormat;
193
- const cacheKey = this.isCacheEnabled() ? (0, memory_cache_js_1.createCacheKey)({ request: context.request, responseFormat }) : undefined;
194
- if (cacheKey) {
195
- const cached = await this.pipeline.trace(context, 'cacheLookup', () => this.getCachedResponse(cacheKey, context.request));
193
+ const cachePlan = this.isCacheEnabled() ? this.cachePlan(context.request, responseFormat) : undefined;
194
+ let cacheOutcome;
195
+ if (cachePlan && !cachePlan.read) {
196
+ cacheOutcome = {
197
+ outcome: 'bypass',
198
+ reason: context.request.responseCache === 'refresh'
199
+ ? 'the request asked for a fresh answer'
200
+ : 'the request skipped the cache',
201
+ ...(cachePlan.namespace ? { namespace: cachePlan.namespace } : {}),
202
+ };
203
+ }
204
+ if (cachePlan?.read) {
205
+ const plan = cachePlan;
206
+ const lookup = await this.pipeline.trace(context, 'cacheLookup', () => this.getCachedResponse(plan, context.request));
207
+ cacheOutcome = lookup.outcome;
208
+ const cached = lookup.response;
196
209
  if (cached) {
197
210
  let cachedResponse = this.attachTrace({
198
211
  ...cached,
199
- meta: { ...cached.meta, requestId: ticket.descriptor.requestId, cacheHit: true },
212
+ meta: { ...cached.meta, requestId: ticket.descriptor.requestId, cacheHit: true, cache: lookup.outcome },
200
213
  }, this.pipeline.finish(context));
201
214
  if (this.isSecurityEnabled()) {
202
215
  const outputResult = this.security.protectOutput(cachedResponse);
@@ -258,12 +271,24 @@ class NexusAI {
258
271
  });
259
272
  context.response = formattedResponse;
260
273
  }
261
- if (cacheKey) {
274
+ if (cachePlan?.write) {
262
275
  const responseToCache = context.response;
263
276
  if (!responseToCache) {
264
277
  throw new Error('Cannot cache an empty response.');
265
278
  }
266
- await this.pipeline.trace(context, 'cacheWrite', () => this.setCachedResponse(cacheKey, context.request, responseToCache));
279
+ const plan = cachePlan;
280
+ try {
281
+ await this.pipeline.trace(context, 'cacheWrite', () => this.setCachedResponse(plan, context.request, responseToCache));
282
+ }
283
+ catch (error) {
284
+ cacheOutcome = {
285
+ ...(cacheOutcome ?? { outcome: 'miss', reason: 'no entry for this request' }),
286
+ writeError: error instanceof Error ? error.message : String(error),
287
+ };
288
+ }
289
+ }
290
+ if (cacheOutcome && context.response) {
291
+ context.response = { ...context.response, meta: { ...context.response.meta, cache: cacheOutcome } };
267
292
  }
268
293
  context = await this.pipeline.runHook('beforeReturn', context);
269
294
  let responseToReturn = context.response;
@@ -583,9 +608,11 @@ class NexusAI {
583
608
  getProviderHealth() {
584
609
  return this.health.snapshot();
585
610
  }
586
- async checkProviders() {
611
+ async checkProviders(options = {}) {
587
612
  const results = [];
588
613
  for (const [providerName, provider] of this.providers) {
614
+ if (options.staleOnly && !this.health.isUnknown(providerName))
615
+ continue;
589
616
  const started = Date.now();
590
617
  try {
591
618
  const ok = await provider.healthCheck();
@@ -828,35 +855,86 @@ class NexusAI {
828
855
  });
829
856
  return this.semanticCacheLoading;
830
857
  }
831
- async getCachedResponse(cacheKey, request) {
832
- if (!this.config.cache?.enabled)
833
- return undefined;
834
- if (this.usesSemanticCache()) {
835
- const semantic = await (await this.loadSemanticCache()).get(request);
836
- if (semantic)
837
- return semantic;
858
+ cachePlan(request, responseFormat) {
859
+ const configured = this.config.cache?.namespace;
860
+ const chosen = typeof configured === 'function' ? configured(request) : configured;
861
+ const namespace = chosen ? namespaceKey({ ...chosen, tenant: chosen.tenant ?? request.tenantId }) : undefined;
862
+ const base = (0, memory_cache_js_1.createCacheKey)({ request: { ...request, responseCache: undefined }, responseFormat });
863
+ return {
864
+ key: namespace ? `${namespace}\u0000${base}` : base,
865
+ ...(namespace ? { namespace } : {}),
866
+ scope: namespace ?? namespaceKey({ tenant: request.tenantId }),
867
+ read: request.responseCache === undefined,
868
+ write: request.responseCache !== 'bypass',
869
+ };
870
+ }
871
+ async getCachedResponse(plan, request) {
872
+ const namespace = plan.namespace ? { namespace: plan.namespace } : {};
873
+ const cache = this.config.cache ?? {};
874
+ let layer = this.usesSemanticCache() ? 'semantic' : 'exact';
875
+ try {
876
+ if (this.usesSemanticCache()) {
877
+ const semantic = await (await this.loadSemanticCache()).get(request, plan.scope);
878
+ if (semantic) {
879
+ return {
880
+ response: semantic,
881
+ outcome: { outcome: 'hit', layer: 'semantic', reason: 'a similar request was answered', ...namespace },
882
+ };
883
+ }
884
+ if (cache.strategy === 'semantic') {
885
+ return {
886
+ outcome: {
887
+ outcome: 'miss',
888
+ layer: 'semantic',
889
+ reason: 'no similar request above the threshold',
890
+ ...namespace,
891
+ },
892
+ };
893
+ }
894
+ }
895
+ layer = 'exact';
896
+ if (cache.adapter) {
897
+ const value = (await cache.adapter.get(plan.key));
898
+ return value
899
+ ? { response: value, outcome: { outcome: 'hit', layer, reason: 'this request was answered', ...namespace } }
900
+ : { outcome: { outcome: 'miss', layer, reason: 'no entry for this request', ...namespace } };
901
+ }
902
+ const { value, stale } = this.cache.lookup(plan.key);
903
+ if (value) {
904
+ return {
905
+ response: value,
906
+ outcome: { outcome: 'hit', layer, reason: 'this request was answered', ...namespace },
907
+ };
908
+ }
909
+ return stale
910
+ ? { outcome: { outcome: 'stale', layer, reason: 'the entry for this request had expired', ...namespace } }
911
+ : { outcome: { outcome: 'miss', layer, reason: 'no entry for this request', ...namespace } };
838
912
  }
839
- if (this.config.cache.strategy === 'semantic')
840
- return undefined;
841
- if (this.config.cache.adapter) {
842
- return (await this.config.cache.adapter.get(cacheKey));
913
+ catch (error) {
914
+ return {
915
+ outcome: {
916
+ outcome: 'error',
917
+ layer,
918
+ reason: error instanceof Error ? error.message : String(error),
919
+ ...namespace,
920
+ },
921
+ };
843
922
  }
844
- return this.cache.get(cacheKey);
845
923
  }
846
- async setCachedResponse(cacheKey, request, response) {
924
+ async setCachedResponse(plan, request, response) {
847
925
  if (!this.config.cache?.enabled)
848
926
  return;
849
927
  const ttl = this.config.cache.ttlSeconds || 300;
850
928
  if (this.usesSemanticCache()) {
851
- await (await this.loadSemanticCache()).set(cacheKey, request, response);
929
+ await (await this.loadSemanticCache()).set(plan.key, request, response, plan.scope);
852
930
  }
853
931
  if (this.config.cache.strategy === 'semantic')
854
932
  return;
855
933
  if (this.config.cache.adapter) {
856
- await this.config.cache.adapter.set(cacheKey, response, ttl);
934
+ await this.config.cache.adapter.set(plan.key, response, ttl);
857
935
  return;
858
936
  }
859
- this.cache.set(cacheKey, response, ttl);
937
+ this.cache.set(plan.key, response, ttl);
860
938
  }
861
939
  estimateRequestCost(request) {
862
940
  return (0, cost_js_1.estimateCost)({
@@ -891,3 +969,8 @@ function streamStopped() {
891
969
  error.name = 'AbortError';
892
970
  return error;
893
971
  }
972
+ function namespaceKey(namespace) {
973
+ return [namespace.tenant, namespace.environment, namespace.modelRevision, namespace.contextVersion]
974
+ .map((part) => encodeURIComponent(part ?? ''))
975
+ .join('/');
976
+ }