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
@@ -0,0 +1,90 @@
1
+ import type { SchemaMigration } from '../utils/schema-migrations.js';
2
+ /** How one check came out: `fail` breaks the deployment, `warn` needs a look, `skip` had nothing to check. */
3
+ export type DoctorStatus = 'ok' | 'warn' | 'fail' | 'skip';
4
+ /** One check's result. */
5
+ export interface DoctorCheck {
6
+ /** A stable id, such as `node` or `migrations`. */
7
+ id: string;
8
+ /** What was checked, in a few words. */
9
+ title: string;
10
+ /** How it came out. */
11
+ status: DoctorStatus;
12
+ /** What was found. Never a secret's value. */
13
+ detail: string;
14
+ /** What to do about a warning or a failure. */
15
+ hint?: string;
16
+ }
17
+ /** Every check's result, and whether the deployment can run. */
18
+ export interface DoctorReport {
19
+ /** The checks, in the order they ran. */
20
+ checks: DoctorCheck[];
21
+ /** True when nothing failed. */
22
+ ok: boolean;
23
+ /** Checks that failed. */
24
+ failures: number;
25
+ /** Checks that warned. */
26
+ warnings: number;
27
+ }
28
+ /** A database for the doctor to reach, as `nexus db` reaches one. */
29
+ export interface DoctorDatabase {
30
+ /** A Postgres client (`query`) or a SQLite handle or client. */
31
+ client: unknown;
32
+ /** Which kind it is. Detected from the client when omitted. */
33
+ dialect?: 'postgres' | 'sqlite';
34
+ /** The migrations it should have. Defaults to every bundled adapter's on its default tables. */
35
+ migrations?: readonly SchemaMigration[];
36
+ /** The table that records applied migrations. */
37
+ table?: string;
38
+ }
39
+ /** What the doctor knows about how the application is deployed. */
40
+ export interface DoctorDeployment {
41
+ /** How many replicas run it. Settings that keep state in one process are checked only above 1. */
42
+ replicas: number;
43
+ /**
44
+ * The stores and shared state it uses, by any name: a checkpointer, the operation store, the
45
+ * server's state, tenant usage, a rate-limit store, a circuit store, a cache. Each is reported when
46
+ * it keeps its state in process memory, where each replica would see only its own.
47
+ */
48
+ stores?: Record<string, unknown>;
49
+ }
50
+ /** What `diagnose()` checks. Everything is optional; a check without its input is skipped. */
51
+ export interface DoctorOptions {
52
+ /** The Node.js version to check. Defaults to the running one. */
53
+ nodeVersion?: string;
54
+ /** Where credentials are read from. Defaults to `process.env`. Only names are reported. */
55
+ env?: Record<string, string | undefined>;
56
+ /** Whether a package is installed. Defaults to resolving it from the working directory. */
57
+ resolvePackage?: (name: string) => boolean;
58
+ /** A database: connectivity, and migrations pending or changed. */
59
+ database?: DoctorDatabase;
60
+ /** A Redis client to ping. */
61
+ redis?: {
62
+ ping(): unknown;
63
+ };
64
+ /** An operation store, read for leases that lapsed and a queue nobody drains. */
65
+ operations?: {
66
+ stats?(now: string): unknown;
67
+ list?(): unknown;
68
+ };
69
+ /** How long the oldest queued operation may wait before the queue counts as stalled. Defaults to 15 minutes. */
70
+ maxQueueAgeMs?: number;
71
+ /** A client, read for provider health. */
72
+ ai?: {
73
+ getProviderHealth(): ReadonlyArray<{
74
+ providerName: string;
75
+ healthy: boolean;
76
+ stale?: boolean;
77
+ }>;
78
+ };
79
+ /** How old the bundled model registry may be, in days. Defaults to the registry's own window. */
80
+ registryMaxAgeDays?: number;
81
+ /** How the application is deployed. */
82
+ deployment?: DoctorDeployment;
83
+ /** Replaces the clock, for tests. */
84
+ now?: () => Date;
85
+ }
86
+ /**
87
+ * Runs every check its options allow and reports each, never throwing: a check that cannot run is a
88
+ * failure with the reason.
89
+ */
90
+ export declare function diagnose(options?: DoctorOptions): Promise<DoctorReport>;
@@ -0,0 +1,276 @@
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
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.diagnose = diagnose;
40
+ const node_module_1 = require("node:module");
41
+ const node_path_1 = __importDefault(require("node:path"));
42
+ const PEERS = {
43
+ openai: 'the OpenAI SDK, used only where an application passes one in',
44
+ '@anthropic-ai/sdk': 'the Anthropic SDK, likewise',
45
+ ollama: 'the Ollama client',
46
+ zod: 'zod schemas for tools and structured output',
47
+ ajv: 'JSON Schema validation of structured output',
48
+ 'ajv-formats': 'formats for ajv',
49
+ };
50
+ const CREDENTIALS = {
51
+ openai: ['OPENAI_API_KEY'],
52
+ anthropic: ['ANTHROPIC_API_KEY'],
53
+ google: ['GOOGLE_API_KEY', 'GEMINI_API_KEY'],
54
+ 'azure-openai': ['AZURE_OPENAI_API_KEY'],
55
+ groq: ['GROQ_API_KEY'],
56
+ mistral: ['MISTRAL_API_KEY'],
57
+ cohere: ['COHERE_API_KEY', 'CO_API_KEY'],
58
+ deepseek: ['DEEPSEEK_API_KEY'],
59
+ openrouter: ['OPENROUTER_API_KEY'],
60
+ };
61
+ const PROCESS_LOCAL = {
62
+ MemoryGraphCheckpointer: { status: 'fail', why: 'a thread started on one replica cannot be resumed on another' },
63
+ MemoryOperationStore: { status: 'fail', why: 'operations and server runs are invisible to other replicas' },
64
+ MemoryServerStore: { status: 'fail', why: 'threads and runs exist only on the replica that created them' },
65
+ MemoryRunEventLog: { status: 'fail', why: 'a client reconnecting to another replica cannot follow its run' },
66
+ MemoryTenantUsage: { status: 'fail', why: 'each replica counts its own tenant limits, multiplying them' },
67
+ MemoryRateLimitStore: { status: 'warn', why: 'each replica enforces its own limit, multiplying it' },
68
+ MemoryCircuitStateStore: { status: 'warn', why: 'a circuit opened on one replica stays closed on the others' },
69
+ MemoryStore: { status: 'warn', why: 'long-term memory written on one replica is missing on the others' },
70
+ MemoryTraceStore: { status: 'warn', why: 'each replica keeps only its own traces' },
71
+ MemoryPromptStore: { status: 'warn', why: 'a prompt promoted on one replica is not served by the others' },
72
+ MemoryCacheAdapter: { status: 'warn', why: 'each replica caches separately' },
73
+ MemoryRollupStore: { status: 'warn', why: 'each replica sums only its own runs' },
74
+ };
75
+ async function diagnose(options = {}) {
76
+ const now = options.now ?? (() => new Date());
77
+ const checks = [];
78
+ const run = async (id, title, check) => {
79
+ try {
80
+ checks.push({ id, title, ...(await check()) });
81
+ }
82
+ catch (error) {
83
+ checks.push({ id, title, status: 'fail', detail: error instanceof Error ? error.message : String(error) });
84
+ }
85
+ };
86
+ await run('node', 'Node.js version', () => nodeCheck(options.nodeVersion ?? process.versions.node));
87
+ await run('peers', 'Optional packages', () => peersCheck(options.resolvePackage ?? defaultResolver()));
88
+ await run('credentials', 'Provider credentials', () => credentialsCheck(options.env ?? process.env));
89
+ await run('registry', 'Model registry age', () => registryCheck(now(), options.registryMaxAgeDays));
90
+ await run('database', 'Database connectivity', () => databaseCheck(options.database));
91
+ await run('migrations', 'Schema migrations', () => migrationsCheck(options.database));
92
+ await run('redis', 'Redis', () => redisCheck(options.redis));
93
+ await run('queue', 'Operation queue and leases', () => queueCheck(options.operations, now(), options.maxQueueAgeMs));
94
+ await run('health', 'Provider health', () => healthCheck(options.ai));
95
+ await run('topology', 'Settings that only work in one process', () => topologyCheck(options.deployment));
96
+ const failures = checks.filter((check) => check.status === 'fail').length;
97
+ const warnings = checks.filter((check) => check.status === 'warn').length;
98
+ return { checks, ok: failures === 0, failures, warnings };
99
+ }
100
+ function nodeCheck(version) {
101
+ const major = Number(version.replace(/^v/, '').split('.')[0]);
102
+ return major >= 22
103
+ ? { status: 'ok', detail: `Node.js ${version}` }
104
+ : { status: 'fail', detail: `Node.js ${version}; this package needs 22 or newer`, hint: 'Upgrade Node.js.' };
105
+ }
106
+ function defaultResolver() {
107
+ const resolve = (0, node_module_1.createRequire)(node_path_1.default.join(process.cwd(), 'package.json')).resolve;
108
+ return (name) => {
109
+ try {
110
+ resolve(name);
111
+ return true;
112
+ }
113
+ catch {
114
+ return false;
115
+ }
116
+ };
117
+ }
118
+ function peersCheck(resolve) {
119
+ const installed = Object.keys(PEERS).filter((name) => resolve(name));
120
+ const missing = Object.keys(PEERS).filter((name) => !installed.includes(name));
121
+ return {
122
+ status: 'ok',
123
+ detail: `Installed: ${installed.length ? installed.join(', ') : 'none'}. Not installed: ${missing.length ? missing.join(', ') : 'none'}. Every one is optional; install only what you use.`,
124
+ };
125
+ }
126
+ function credentialsCheck(env) {
127
+ const found = Object.entries(CREDENTIALS)
128
+ .filter(([, names]) => names.some((name) => Boolean(env[name]?.trim())))
129
+ .map(([provider]) => provider);
130
+ return found.length
131
+ ? { status: 'ok', detail: `Credentials found for ${found.join(', ')}.` }
132
+ : {
133
+ status: 'warn',
134
+ detail: 'No provider credential found in the environment.',
135
+ hint: 'Set the API key of each hosted provider you use, such as OPENAI_API_KEY. Local providers need none.',
136
+ };
137
+ }
138
+ async function registryCheck(now, maxAgeDays) {
139
+ const { checkRegistryFreshness } = await Promise.resolve().then(() => __importStar(require('../models/registry.js')));
140
+ const freshness = checkRegistryFreshness({ now, ...(maxAgeDays === undefined ? {} : { maxAgeDays }) });
141
+ return freshness.stale
142
+ ? {
143
+ status: 'warn',
144
+ detail: `The bundled registry was verified ${freshness.ageDays} days ago (${freshness.verifiedAt}), past its ${freshness.maxAgeDays}-day window.`,
145
+ hint: 'Upgrade nexus-ai-pro, or override stale entries with models.registry.',
146
+ }
147
+ : { status: 'ok', detail: `Verified ${freshness.ageDays} days ago (${freshness.verifiedAt}).` };
148
+ }
149
+ function dialectOf(database) {
150
+ if (database.dialect)
151
+ return database.dialect;
152
+ const client = database.client;
153
+ if (client && typeof client.query === 'function')
154
+ return 'postgres';
155
+ return 'sqlite';
156
+ }
157
+ async function databaseCheck(database) {
158
+ if (!database)
159
+ return { status: 'skip', detail: 'No database given.' };
160
+ const dialect = dialectOf(database);
161
+ if (dialect === 'postgres') {
162
+ await database.client.query('SELECT 1');
163
+ }
164
+ else {
165
+ const { toSqliteClient } = await Promise.resolve().then(() => __importStar(require('../sqlite/client.js')));
166
+ await toSqliteClient(database.client).all('SELECT 1', []);
167
+ }
168
+ return { status: 'ok', detail: `Reached the ${dialect === 'postgres' ? 'Postgres' : 'SQLite'} database.` };
169
+ }
170
+ async function migrationsCheck(database) {
171
+ if (!database)
172
+ return { status: 'skip', detail: 'No database given.' };
173
+ const dialect = dialectOf(database);
174
+ const options = database.table ? { table: database.table } : {};
175
+ let status;
176
+ if (dialect === 'postgres') {
177
+ const [{ postgresMigrations }, { postgresMigrationStatus }] = await Promise.all([
178
+ Promise.resolve().then(() => __importStar(require('../postgres/index.js'))),
179
+ Promise.resolve().then(() => __importStar(require('../postgres/migrations.js'))),
180
+ ]);
181
+ status = await postgresMigrationStatus(database.client, database.migrations ?? postgresMigrations(), options);
182
+ }
183
+ else {
184
+ const [{ sqliteMigrations }, { sqliteMigrationStatus }] = await Promise.all([
185
+ Promise.resolve().then(() => __importStar(require('../sqlite/index.js'))),
186
+ Promise.resolve().then(() => __importStar(require('../sqlite/migrations.js'))),
187
+ ]);
188
+ status = await sqliteMigrationStatus(database.client, database.migrations ?? sqliteMigrations(), options);
189
+ }
190
+ if (status.changed.length) {
191
+ return {
192
+ status: 'fail',
193
+ detail: `${status.changed.length} migration${status.changed.length === 1 ? '' : 's'} changed after running: ${status.changed.map(({ migration }) => `${migration.component} v${migration.version}`).join(', ')}.`,
194
+ hint: 'A migration never changes once applied. Check the adapter options match those it ran with.',
195
+ };
196
+ }
197
+ if (status.pending.length) {
198
+ return {
199
+ status: 'fail',
200
+ detail: `${status.pending.length} pending: ${status.pending.map((migration) => `${migration.component} v${migration.version}`).join(', ')}.`,
201
+ hint: 'Run: nexus db migrate --client db.mjs',
202
+ };
203
+ }
204
+ const newer = status.unknown.length ? ` ${status.unknown.length} recorded by a newer release.` : '';
205
+ return { status: 'ok', detail: `${status.applied.length} applied, none pending.${newer}` };
206
+ }
207
+ async function redisCheck(redis) {
208
+ if (!redis)
209
+ return { status: 'skip', detail: 'No Redis client given.' };
210
+ const reply = await redis.ping();
211
+ return { status: 'ok', detail: `PING answered ${String(reply)}.` };
212
+ }
213
+ async function queueCheck(operations, now, maxQueueAgeMs = 15 * 60_000) {
214
+ if (!operations)
215
+ return { status: 'skip', detail: 'No operation store given.' };
216
+ const { operationStats } = await Promise.resolve().then(() => __importStar(require('../operations/stats.js')));
217
+ const stats = await operationStats(operations, { now });
218
+ const problems = [];
219
+ if (stats.lapsedLeases > 0) {
220
+ problems.push(`${stats.lapsedLeases} running operation${stats.lapsedLeases === 1 ? '' : 's'} hold a lease that lapsed`);
221
+ }
222
+ const age = stats.oldestQueuedAt ? now.getTime() - Date.parse(stats.oldestQueuedAt) : 0;
223
+ if (age > maxQueueAgeMs)
224
+ problems.push(`the oldest queued operation has waited ${Math.round(age / 60_000)} minutes`);
225
+ const counts = Object.entries(stats.byStatus)
226
+ .map(([status, count]) => `${count} ${status}`)
227
+ .join(', ');
228
+ return problems.length
229
+ ? {
230
+ status: 'warn',
231
+ detail: `${problems.join('; ')}. (${counts || 'nothing unfinished'})`,
232
+ hint: 'Run recovery — OperationRunner.recover(), or the agent server with recoverEveryMs — and a worker that claims the queue.',
233
+ }
234
+ : { status: 'ok', detail: counts ? `Unfinished: ${counts}. No lapsed lease.` : 'Nothing unfinished.' };
235
+ }
236
+ function healthCheck(ai) {
237
+ if (!ai)
238
+ return { status: 'skip', detail: 'No client given.' };
239
+ const snapshots = ai.getProviderHealth();
240
+ if (snapshots.length === 0)
241
+ return { status: 'skip', detail: 'Health tracking is off, or no provider has been called.' };
242
+ const stale = snapshots.filter((item) => item.stale).map((item) => item.providerName);
243
+ const unhealthy = snapshots.filter((item) => !item.healthy && !item.stale).map((item) => item.providerName);
244
+ const issues = [
245
+ ...(stale.length ? [`stale: ${stale.join(', ')}`] : []),
246
+ ...(unhealthy.length ? [`unhealthy: ${unhealthy.join(', ')}`] : []),
247
+ ];
248
+ return issues.length
249
+ ? {
250
+ status: 'warn',
251
+ detail: `${issues.join('; ')}.`,
252
+ hint: 'Refresh stale entries with ai.checkProviders({ staleOnly: true }).',
253
+ }
254
+ : { status: 'ok', detail: `${snapshots.length} provider${snapshots.length === 1 ? '' : 's'} healthy.` };
255
+ }
256
+ function topologyCheck(deployment) {
257
+ if (!deployment)
258
+ return { status: 'skip', detail: 'No deployment described.' };
259
+ if (deployment.replicas <= 1)
260
+ return { status: 'ok', detail: 'One replica: in-process state is shared by everything.' };
261
+ const findings = [];
262
+ for (const [name, value] of Object.entries(deployment.stores ?? {})) {
263
+ const kind = value?.constructor?.name;
264
+ const known = kind ? PROCESS_LOCAL[kind] : undefined;
265
+ if (known)
266
+ findings.push({ status: known.status, text: `${name} is a ${kind}: ${known.why}` });
267
+ }
268
+ if (findings.length === 0) {
269
+ return { status: 'ok', detail: `${deployment.replicas} replicas, and nothing keeps its state in one process.` };
270
+ }
271
+ return {
272
+ status: findings.some((finding) => finding.status === 'fail') ? 'fail' : 'warn',
273
+ detail: `${deployment.replicas} replicas: ${findings.map((finding) => finding.text).join('; ')}.`,
274
+ hint: 'Use the Redis, Postgres, or SQLite adapter for each, or run one replica.',
275
+ };
276
+ }
@@ -1,3 +1,4 @@
1
+ import type { EvaluationContext, Evaluator } from '../types/evaluate.js';
1
2
  import type { CompletionRequest } from '../types/messages.js';
2
3
  import type { NexusResponse } from '../types/response.js';
3
4
  import type { EvalCase, EvalJudge } from './runner.js';
@@ -75,6 +76,16 @@ export declare class LLMJudge<Response = NexusResponse> {
75
76
  constructor(options: LLMJudgeOptions<Response>);
76
77
  /** Judges one answer. */
77
78
  evaluate(input: LLMJudgeInput): Promise<LLMJudgeResult>;
79
+ /**
80
+ * The judge as an evaluator for `evaluate()`, scoring each output under `key` (`judge` by
81
+ * default). `input` turns an evaluation context into what the judge sees; by default the output is
82
+ * the answer, the example's expected output the reference, and its inputs the question. The
83
+ * experiment records the judge's model, temperature, rubric, and a hash of its instructions.
84
+ */
85
+ asEvaluator(options?: {
86
+ key?: string;
87
+ input?: (context: EvaluationContext) => LLMJudgeInput;
88
+ }): Evaluator;
78
89
  /**
79
90
  * Adapts the judge to an `EvalRunner` case, optionally with a mapper that picks what the judge
80
91
  * sees.
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LLMJudge = void 0;
4
4
  exports.createLLMJudgeEval = createLLMJudgeEval;
5
5
  exports.parseJudgeResponse = parseJudgeResponse;
6
+ const node_crypto_1 = require("node:crypto");
7
+ const provenance_js_1 = require("../evaluate/provenance.js");
6
8
  const DEFAULT_SYSTEM_PROMPT = [
7
9
  'You are a careful evaluation judge.',
8
10
  'Return only JSON with these fields: score, passed, rationale, labels.',
@@ -48,6 +50,34 @@ class LLMJudge {
48
50
  modelUsed: modelOf(response),
49
51
  });
50
52
  }
53
+ asEvaluator(options = {}) {
54
+ const evaluator = async (context) => {
55
+ const input = options.input?.(context) ?? {
56
+ actual: text(context.output),
57
+ ...(context.example.expected === undefined ? {} : { expected: text(context.example.expected) }),
58
+ query: text(context.example.inputs),
59
+ };
60
+ const verdict = await this.evaluate(input);
61
+ return {
62
+ key: options.key ?? 'judge',
63
+ score: verdict.score,
64
+ passed: verdict.passed,
65
+ ...(verdict.rationale ? { comment: verdict.rationale } : {}),
66
+ };
67
+ };
68
+ return (0, provenance_js_1.withProvenance)(evaluator, {
69
+ name: options.key ?? 'judge',
70
+ judge: {
71
+ model: this.options.model,
72
+ promptVersion: (0, node_crypto_1.createHash)('sha256')
73
+ .update(this.options.systemPrompt ?? 'default')
74
+ .digest('hex')
75
+ .slice(0, 12),
76
+ temperature: this.options.temperature ?? 0,
77
+ },
78
+ ...(this.options.rubric ? { rubric: this.options.rubric } : {}),
79
+ });
80
+ }
51
81
  asEvalJudge(mapper) {
52
82
  return async (response, testCase) => {
53
83
  const input = mapper
@@ -156,3 +186,6 @@ function clamp(value, [min, max]) {
156
186
  function midpoint([min, max]) {
157
187
  return min + (max - min) / 2;
158
188
  }
189
+ function text(value) {
190
+ return typeof value === 'string' ? value : JSON.stringify(value ?? '');
191
+ }
@@ -5,3 +5,4 @@ export { AnnotationQueue, type AnnotationQueueOptions, evaluateOnline, type Onli
5
5
  export { evaluate, type EvaluateOptions, type EvaluationTarget, stats, summarize } from './run.js';
6
6
  export { type CachedOutput, type EvaluationCache, evaluationCacheKey, FileEvaluationCache, fingerprintOf, MemoryEvaluationCache, type MemoryEvaluationCacheOptions, } from './cache.js';
7
7
  export type { Dataset, DatasetExample, DatasetStore, EvaluationContext, EvaluationScore, Evaluator, ExampleResult, Experiment, ExperimentStore, MetricSummary, SummaryEvaluator, } from '../types/evaluate.js';
8
+ export { withProvenance } from './provenance.js';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MemoryEvaluationCache = exports.fingerprintOf = exports.FileEvaluationCache = exports.evaluationCacheKey = exports.summarize = exports.stats = exports.evaluate = exports.evaluateOnline = exports.AnnotationQueue = exports.underLatency = exports.underCost = exports.trajectory = exports.totalCost = exports.reciprocalRank = exports.recallAtK = exports.passRate = exports.pairwise = exports.mustNotMatch = exports.exactMatch = exports.embeddingSimilarity = exports.contains = exports.completed = exports.splitOf = exports.readExperiment = exports.MemoryExperimentStore = exports.MemoryDatasetStore = exports.FileExperimentStore = exports.FileDatasetStore = exports.datasetFromTraces = exports.createDataset = exports.contentVersion = exports.scoreMap = exports.formatComparison = exports.compareExperiments = void 0;
3
+ exports.withProvenance = exports.MemoryEvaluationCache = exports.fingerprintOf = exports.FileEvaluationCache = exports.evaluationCacheKey = exports.summarize = exports.stats = exports.evaluate = exports.evaluateOnline = exports.AnnotationQueue = exports.underLatency = exports.underCost = exports.trajectory = exports.totalCost = exports.reciprocalRank = exports.recallAtK = exports.passRate = exports.pairwise = exports.mustNotMatch = exports.exactMatch = exports.embeddingSimilarity = exports.contains = exports.completed = exports.splitOf = exports.readExperiment = exports.MemoryExperimentStore = exports.MemoryDatasetStore = exports.FileExperimentStore = exports.FileDatasetStore = exports.datasetFromTraces = exports.createDataset = exports.contentVersion = exports.scoreMap = exports.formatComparison = exports.compareExperiments = void 0;
4
4
  var compare_js_1 = require("./compare.js");
5
5
  Object.defineProperty(exports, "compareExperiments", { enumerable: true, get: function () { return compare_js_1.compareExperiments; } });
6
6
  Object.defineProperty(exports, "formatComparison", { enumerable: true, get: function () { return compare_js_1.formatComparison; } });
@@ -41,3 +41,5 @@ Object.defineProperty(exports, "evaluationCacheKey", { enumerable: true, get: fu
41
41
  Object.defineProperty(exports, "FileEvaluationCache", { enumerable: true, get: function () { return cache_js_1.FileEvaluationCache; } });
42
42
  Object.defineProperty(exports, "fingerprintOf", { enumerable: true, get: function () { return cache_js_1.fingerprintOf; } });
43
43
  Object.defineProperty(exports, "MemoryEvaluationCache", { enumerable: true, get: function () { return cache_js_1.MemoryEvaluationCache; } });
44
+ var provenance_js_1 = require("./provenance.js");
45
+ Object.defineProperty(exports, "withProvenance", { enumerable: true, get: function () { return provenance_js_1.withProvenance; } });
@@ -0,0 +1,22 @@
1
+ import type { Evaluator, EvaluatorProvenance } from '../types/evaluate.js';
2
+ /**
3
+ * Says where an evaluator's scores come from: its name and version, the judge model and prompt
4
+ * version behind it, and its rubric. `evaluate()` records it on the experiment, so a score of 0.87
5
+ * can be traced to exactly what produced it, and two experiments scored by different judges are not
6
+ * compared as if they were alike.
7
+ *
8
+ * Returns the same evaluator, marked; it runs exactly as before.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const grounded = withProvenance(groundedness, {
13
+ * name: 'groundedness',
14
+ * version: '3',
15
+ * judge: { model: 'claude-sonnet-5-5', promptVersion: 'grounded-v3', temperature: 0 },
16
+ * rubric: 'Every claim is supported by a cited passage.',
17
+ * });
18
+ * ```
19
+ */
20
+ export declare function withProvenance<I = unknown, O = unknown>(evaluator: Evaluator<I, O>, provenance: Omit<EvaluatorProvenance, 'keys' | 'position'>): Evaluator<I, O>;
21
+ /** What an evaluator declared through `withProvenance()`, if anything. */
22
+ export declare function declaredProvenance(evaluator: unknown): Omit<EvaluatorProvenance, 'keys' | 'position'> | undefined;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withProvenance = withProvenance;
4
+ exports.declaredProvenance = declaredProvenance;
5
+ const PROVENANCE = Symbol('nexus.evaluate.provenance');
6
+ function withProvenance(evaluator, provenance) {
7
+ Object.defineProperty(evaluator, PROVENANCE, { value: provenance, configurable: true });
8
+ return evaluator;
9
+ }
10
+ function declaredProvenance(evaluator) {
11
+ if (typeof evaluator !== 'function')
12
+ return undefined;
13
+ return evaluator[PROVENANCE];
14
+ }
@@ -4,7 +4,9 @@ exports.evaluate = evaluate;
4
4
  exports.summarize = summarize;
5
5
  exports.stats = stats;
6
6
  const node_crypto_1 = require("node:crypto");
7
+ const version_js_1 = require("../version.js");
7
8
  const cache_js_1 = require("./cache.js");
9
+ const provenance_js_1 = require("./provenance.js");
8
10
  async function evaluate(target, dataset, evaluators, options = {}) {
9
11
  if (dataset.examples.length === 0)
10
12
  throw new RangeError(`Dataset "${dataset.name}" has no examples`);
@@ -19,8 +21,9 @@ async function evaluate(target, dataset, evaluators, options = {}) {
19
21
  example,
20
22
  run,
21
23
  })));
24
+ const produced = evaluators.map(() => new Set());
22
25
  await runConcurrently(jobs, options.concurrency ?? 4, async ({ example, run }) => {
23
- const result = await evaluateOne(target, example, run, evaluators, options);
26
+ const result = await evaluateOne(target, example, run, evaluators, options, produced);
24
27
  results.push(result);
25
28
  options.onResult?.(result);
26
29
  }, options.signal);
@@ -40,6 +43,16 @@ async function evaluate(target, dataset, evaluators, options = {}) {
40
43
  results,
41
44
  metrics: summarize(results),
42
45
  summary,
46
+ evaluators: evaluators.map((evaluator, position) => {
47
+ const declared = (0, provenance_js_1.declaredProvenance)(evaluator);
48
+ return {
49
+ ...declared,
50
+ position,
51
+ name: declared?.name ?? (evaluator.name || `evaluator-${position}`),
52
+ keys: [...(produced[position] ?? [])],
53
+ };
54
+ }),
55
+ framework: { name: 'nexus-ai-pro', version: version_js_1.NEXUS_VERSION },
43
56
  ...(options.metadata ? { metadata: options.metadata } : {}),
44
57
  ...(options.cache
45
58
  ? {
@@ -62,7 +75,7 @@ function repetitionsOf(example, setting) {
62
75
  }
63
76
  return count;
64
77
  }
65
- async function evaluateOne(target, example, run, evaluators, options) {
78
+ async function evaluateOne(target, example, run, evaluators, options, produced) {
66
79
  const started = Date.now();
67
80
  let output;
68
81
  let error;
@@ -102,9 +115,12 @@ async function evaluateOne(target, example, run, evaluators, options) {
102
115
  });
103
116
  }
104
117
  const scores = [];
105
- for (const evaluator of evaluators) {
118
+ for (const [position, evaluator] of evaluators.entries()) {
106
119
  try {
107
- scores.push(...normalizeScores(await evaluator(context), evaluator));
120
+ const made = normalizeScores(await evaluator(context), evaluator);
121
+ for (const score of made)
122
+ produced[position]?.add(score.key);
123
+ scores.push(...made);
108
124
  }
109
125
  catch (caught) {
110
126
  scores.push({
@@ -0,0 +1,27 @@
1
+ import type { DurabilityMode } from '../types/graph.js';
2
+ /**
3
+ * Writes one run's checkpoints under a durability mode.
4
+ *
5
+ * `sync` writes each one before returning. `async` chains writes behind each other, so they land in
6
+ * order, and makes the run wait only when `maxPending` are already in flight. `exit` keeps only the
7
+ * latest and writes it when the run stops. A write that fails is kept and thrown from the next call,
8
+ * so a background failure still fails the run.
9
+ */
10
+ export declare class CheckpointWriter<T> {
11
+ private readonly write;
12
+ private readonly mode;
13
+ private readonly maxPending;
14
+ private chain;
15
+ private readonly inFlight;
16
+ private failure;
17
+ private unwritten;
18
+ constructor(write: (checkpoint: T) => Promise<void>, mode: DurabilityMode, maxPending?: number);
19
+ /**
20
+ * Hands over a checkpoint. A `durable` one is persisted, with everything before it, before this
21
+ * resolves, whatever the mode: a pause, a failure, the end of a run, or a recovery decision.
22
+ */
23
+ persist(checkpoint: T, durable?: boolean): Promise<void>;
24
+ /** Waits for every write handed over so far, and writes what `exit` mode was holding. */
25
+ flush(): Promise<void>;
26
+ private throwFailure;
27
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CheckpointWriter = void 0;
4
+ const DEFAULT_MAX_PENDING = 8;
5
+ class CheckpointWriter {
6
+ write;
7
+ mode;
8
+ maxPending;
9
+ chain = Promise.resolve();
10
+ inFlight = [];
11
+ failure;
12
+ unwritten;
13
+ constructor(write, mode, maxPending = DEFAULT_MAX_PENDING) {
14
+ this.write = write;
15
+ this.mode = mode;
16
+ this.maxPending = maxPending;
17
+ }
18
+ async persist(checkpoint, durable = false) {
19
+ this.throwFailure();
20
+ if (this.mode === 'sync') {
21
+ await this.write(checkpoint);
22
+ return;
23
+ }
24
+ if (this.mode === 'exit' && !durable) {
25
+ this.unwritten = checkpoint;
26
+ return;
27
+ }
28
+ if (this.mode === 'exit') {
29
+ this.unwritten = undefined;
30
+ await this.write(checkpoint);
31
+ return;
32
+ }
33
+ const link = this.chain
34
+ .then(() => this.write(checkpoint))
35
+ .catch((error) => {
36
+ this.failure ??= { error };
37
+ })
38
+ .finally(() => {
39
+ const index = this.inFlight.indexOf(link);
40
+ if (index >= 0)
41
+ this.inFlight.splice(index, 1);
42
+ });
43
+ this.chain = link;
44
+ this.inFlight.push(link);
45
+ if (durable) {
46
+ await this.flush();
47
+ return;
48
+ }
49
+ while (this.inFlight.length > Math.max(1, this.maxPending))
50
+ await this.inFlight[0];
51
+ this.throwFailure();
52
+ }
53
+ async flush() {
54
+ await this.chain;
55
+ if (this.unwritten !== undefined) {
56
+ const latest = this.unwritten;
57
+ this.unwritten = undefined;
58
+ await this.write(latest);
59
+ }
60
+ this.throwFailure();
61
+ }
62
+ throwFailure() {
63
+ if (!this.failure)
64
+ return;
65
+ const { error } = this.failure;
66
+ this.failure = undefined;
67
+ throw error;
68
+ }
69
+ }
70
+ exports.CheckpointWriter = CheckpointWriter;