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
@@ -36,7 +36,10 @@ function recordingFetch(options) {
36
36
  const captured = await captureRequest(copyOf(input, init), options);
37
37
  const real = options.fetch ?? globalThis.fetch;
38
38
  const response = await real(input, init);
39
- const bytes = new Uint8Array(await response.arrayBuffer());
39
+ const { bytes, chunks } = options.timing
40
+ ? await readTimed(response)
41
+ : { bytes: new Uint8Array(await response.arrayBuffer()), chunks: undefined };
42
+ const contentType = response.headers.get('content-type');
40
43
  const index = counters.get(captured.key) ?? 0;
41
44
  counters.set(captured.key, index + 1);
42
45
  let exchange = {
@@ -47,7 +50,10 @@ function recordingFetch(options) {
47
50
  status: response.status,
48
51
  ...(response.statusText ? { statusText: response.statusText } : {}),
49
52
  headers: withoutTransportHeaders(redactHeaders(response.headers, options.redactHeaders)),
50
- ...encodeBody(bytes, response.headers.get('content-type')),
53
+ ...encodeBody(bytes, contentType),
54
+ ...(chunks
55
+ ? { chunks: chunks.map((chunk) => ({ atMs: chunk.atMs, ...encodeBody(chunk.bytes, contentType) })) }
56
+ : {}),
51
57
  },
52
58
  recordedAt: now().toISOString(),
53
59
  };
@@ -83,9 +89,13 @@ function replayFetch(options) {
83
89
  const position = served.get(captured.key) ?? 0;
84
90
  served.set(captured.key, position + 1);
85
91
  const exchange = recordings[Math.min(position, recordings.length - 1)];
86
- const body = exchange.response.bodyEncoding === 'base64'
87
- ? Buffer.from(exchange.response.body, 'base64')
88
- : exchange.response.body;
92
+ const pace = options.pace ?? 'instant';
93
+ const chunks = exchange.response.chunks;
94
+ const body = pace !== 'instant' && chunks?.length
95
+ ? pacedBody(chunks, pace, request.signal)
96
+ : exchange.response.bodyEncoding === 'base64'
97
+ ? Buffer.from(exchange.response.body, 'base64')
98
+ : exchange.response.body;
89
99
  return new Response(isNullBodyStatus(exchange.response.status) ? null : body, {
90
100
  status: exchange.response.status,
91
101
  statusText: exchange.response.statusText ?? '',
@@ -193,6 +203,48 @@ function encodeBody(bytes, contentType) {
193
203
  }
194
204
  return { body: Buffer.from(bytes).toString('base64'), bodyEncoding: 'base64' };
195
205
  }
206
+ async function readTimed(response) {
207
+ const chunks = [];
208
+ if (!response.body)
209
+ return { bytes: new Uint8Array(), chunks };
210
+ const started = performance.now();
211
+ const reader = response.body.getReader();
212
+ for (;;) {
213
+ const { value, done } = await reader.read();
214
+ if (done)
215
+ break;
216
+ chunks.push({ atMs: Math.round(performance.now() - started), bytes: value });
217
+ }
218
+ const bytes = new Uint8Array(chunks.reduce((total, chunk) => total + chunk.bytes.length, 0));
219
+ let offset = 0;
220
+ for (const chunk of chunks) {
221
+ bytes.set(chunk.bytes, offset);
222
+ offset += chunk.bytes.length;
223
+ }
224
+ return { bytes, chunks };
225
+ }
226
+ function pacedBody(chunks, pace, signal) {
227
+ let index = 0;
228
+ return new ReadableStream({
229
+ async pull(controller) {
230
+ const chunk = chunks[index];
231
+ if (!chunk || signal.aborted) {
232
+ controller.close();
233
+ return;
234
+ }
235
+ const delayMs = Math.max(0, chunk.atMs - (index === 0 ? chunk.atMs : chunks[index - 1].atMs));
236
+ if (typeof pace === 'function')
237
+ await pace({ index, count: chunks.length, atMs: chunk.atMs, delayMs });
238
+ else {
239
+ const wait = pace === 'original' ? delayMs : delayMs * pace;
240
+ if (wait > 0)
241
+ await new Promise((resolve) => setTimeout(resolve, wait));
242
+ }
243
+ index += 1;
244
+ controller.enqueue(chunk.bodyEncoding === 'base64' ? Buffer.from(chunk.body, 'base64') : new TextEncoder().encode(chunk.body));
245
+ },
246
+ });
247
+ }
196
248
  function isNullBodyStatus(status) {
197
249
  return status === 101 || status === 204 || status === 205 || status === 304;
198
250
  }
@@ -1,6 +1,6 @@
1
1
  export { type AlertEvent, AlertEvaluator, type AlertMetric, type AlertNotifier, type AlertRule, createWebhookNotifier, measure, type WebhookNotifierOptions, } from './alerts.js';
2
2
  export { compareTraces, formatTree, type RunDifference, type TraceComparison } from './compare.js';
3
3
  export { type GraphTracing, type GraphTracingOptions, type ModelClientLike, traceGraph, traceModelClient, } from './instrument.js';
4
- export { applyQuery, assembleTree, JsonlTraceStore, type JsonlTraceStoreOptions, MemoryTraceStore, type MemoryTraceStoreOptions, } from './stores.js';
5
- export { type FinishRunOptions, type RunHandle, type StartRunOptions, stripFields, Tracer, type TracerOptions, } from './tracer.js';
6
- export type { RedactionPolicy, Run, RunFeedback, RunKind, RunQuery, RunStatus, RunTree, SamplingPolicy, TraceStore, } from '../types/tracing.js';
4
+ export { applyQuery, assembleTree, closeAbandonedRuns, type CloseAbandonedRunsOptions, JsonlTraceStore, type JsonlTraceStoreOptions, MemoryTraceStore, type MemoryTraceStoreOptions, } from './stores.js';
5
+ export { type FinishRunOptions, type RunHandle, type StartRunOptions, stripFields, Tracer, type TracerOptions, w3cSpanId, w3cTraceId, } from './tracer.js';
6
+ export type { RedactionPolicy, Run, RunFeedback, RunKind, RunQuery, RunStatus, RunTree, SamplingPolicy, TraceExporter, TraceStore, } from '../types/tracing.js';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Tracer = exports.stripFields = exports.MemoryTraceStore = exports.JsonlTraceStore = exports.assembleTree = exports.applyQuery = exports.traceModelClient = exports.traceGraph = exports.formatTree = exports.compareTraces = exports.measure = exports.createWebhookNotifier = exports.AlertEvaluator = void 0;
3
+ exports.w3cTraceId = exports.w3cSpanId = exports.Tracer = exports.stripFields = exports.MemoryTraceStore = exports.JsonlTraceStore = exports.closeAbandonedRuns = exports.assembleTree = exports.applyQuery = exports.traceModelClient = exports.traceGraph = exports.formatTree = exports.compareTraces = exports.measure = exports.createWebhookNotifier = exports.AlertEvaluator = void 0;
4
4
  var alerts_js_1 = require("./alerts.js");
5
5
  Object.defineProperty(exports, "AlertEvaluator", { enumerable: true, get: function () { return alerts_js_1.AlertEvaluator; } });
6
6
  Object.defineProperty(exports, "createWebhookNotifier", { enumerable: true, get: function () { return alerts_js_1.createWebhookNotifier; } });
@@ -14,8 +14,11 @@ Object.defineProperty(exports, "traceModelClient", { enumerable: true, get: func
14
14
  var stores_js_1 = require("./stores.js");
15
15
  Object.defineProperty(exports, "applyQuery", { enumerable: true, get: function () { return stores_js_1.applyQuery; } });
16
16
  Object.defineProperty(exports, "assembleTree", { enumerable: true, get: function () { return stores_js_1.assembleTree; } });
17
+ Object.defineProperty(exports, "closeAbandonedRuns", { enumerable: true, get: function () { return stores_js_1.closeAbandonedRuns; } });
17
18
  Object.defineProperty(exports, "JsonlTraceStore", { enumerable: true, get: function () { return stores_js_1.JsonlTraceStore; } });
18
19
  Object.defineProperty(exports, "MemoryTraceStore", { enumerable: true, get: function () { return stores_js_1.MemoryTraceStore; } });
19
20
  var tracer_js_1 = require("./tracer.js");
20
21
  Object.defineProperty(exports, "stripFields", { enumerable: true, get: function () { return tracer_js_1.stripFields; } });
21
22
  Object.defineProperty(exports, "Tracer", { enumerable: true, get: function () { return tracer_js_1.Tracer; } });
23
+ Object.defineProperty(exports, "w3cSpanId", { enumerable: true, get: function () { return tracer_js_1.w3cSpanId; } });
24
+ Object.defineProperty(exports, "w3cTraceId", { enumerable: true, get: function () { return tracer_js_1.w3cTraceId; } });
@@ -13,6 +13,7 @@ function traceGraph(tracer, options = {}) {
13
13
  const active = new Map();
14
14
  const retries = new Map();
15
15
  const emitted = new Map();
16
+ const tools = new Map();
16
17
  const onEvent = (event) => {
17
18
  switch (event.type) {
18
19
  case 'task_start': {
@@ -29,6 +30,29 @@ function traceGraph(tracer, options = {}) {
29
30
  retries.set(event.taskId, event.attempt);
30
31
  return;
31
32
  }
33
+ case 'tool': {
34
+ const key = `${event.taskId}:${event.tool.id}`;
35
+ if (event.tool.phase === 'start') {
36
+ const parent = active.get(event.taskId) ?? root;
37
+ tools.set(key, parent.child({
38
+ name: event.tool.name,
39
+ kind: 'tool',
40
+ inputs: event.tool.args,
41
+ metadata: { callId: event.tool.id },
42
+ }));
43
+ return;
44
+ }
45
+ if (event.tool.phase === 'progress')
46
+ return;
47
+ const handle = tools.get(key);
48
+ if (!handle)
49
+ return;
50
+ tools.delete(key);
51
+ void handle.finish(event.tool.phase === 'error'
52
+ ? { error: new Error(event.tool.error ?? 'The tool failed') }
53
+ : { outputs: event.tool.result });
54
+ return;
55
+ }
32
56
  case 'custom': {
33
57
  emitted.set(event.taskId, [...(emitted.get(event.taskId) ?? []), event.data]);
34
58
  return;
@@ -57,6 +81,10 @@ function traceGraph(tracer, options = {}) {
57
81
  root,
58
82
  runFor: (node) => [...active.values()].find((handle) => handle.id && nameOf(active, handle) === node),
59
83
  async finish(result) {
84
+ for (const [key, handle] of tools) {
85
+ tools.delete(key);
86
+ await handle.finish({ metadata: { unfinished: true } });
87
+ }
60
88
  for (const [taskId, handle] of active) {
61
89
  active.delete(taskId);
62
90
  await handle.finish({ metadata: { unfinished: true } });
@@ -116,6 +144,8 @@ function traceModelClient(client, tracer, options = {}) {
116
144
  model: response.meta?.modelUsed,
117
145
  finishReason: response.finishReason,
118
146
  ...(request.metadata?.prompt === undefined ? {} : { prompt: request.metadata.prompt }),
147
+ ...(response.meta?.cache ? { cache: response.meta.cache } : {}),
148
+ ...(request.tenantId ? { tenantId: request.tenantId } : {}),
119
149
  },
120
150
  });
121
151
  return response;
@@ -0,0 +1,91 @@
1
+ import type { Run, TraceExporter } from '../types/tracing.js';
2
+ /** Options for `OtlpTraceExporter`. */
3
+ export interface OtlpTraceExporterOptions {
4
+ /**
5
+ * The collector's OTLP/HTTP base URL, such as `http://localhost:4318`. Spans are posted to
6
+ * `/v1/traces` under it, as JSON.
7
+ */
8
+ endpoint: string;
9
+ /** Headers on every request, such as an API key a hosted backend wants. */
10
+ headers?: Record<string, string>;
11
+ /** `service.name` on every span. Defaults to `nexus-ai-pro`. */
12
+ serviceName?: string;
13
+ /** More resource attributes, such as `deployment.environment.name` or `service.version`. */
14
+ resource?: Record<string, string | number | boolean>;
15
+ /** Spans sent per request. Defaults to 100. */
16
+ maxBatch?: number;
17
+ /** How long a span waits for others to batch with, in milliseconds. Defaults to 2,000. */
18
+ flushIntervalMs?: number;
19
+ /** Replaces the global `fetch`, for tests or a proxy. */
20
+ fetch?: typeof globalThis.fetch;
21
+ /** Called when a batch cannot be sent. The batch is dropped; a trace is never worth a retry storm. */
22
+ onError?: (error: unknown) => void;
23
+ }
24
+ type AttributeValue = {
25
+ stringValue: string;
26
+ } | {
27
+ intValue: string;
28
+ } | {
29
+ doubleValue: number;
30
+ } | {
31
+ boolValue: boolean;
32
+ };
33
+ /** One span in OTLP's JSON encoding. */
34
+ export interface OtlpSpan {
35
+ /** 32 hex digits. */
36
+ traceId: string;
37
+ /** 16 hex digits. */
38
+ spanId: string;
39
+ /** 16 hex digits, absent for a root span. */
40
+ parentSpanId?: string;
41
+ /** The span's name. */
42
+ name: string;
43
+ /** 1 internal, 3 client. */
44
+ kind: number;
45
+ /** Nanoseconds since the epoch, as a decimal string. */
46
+ startTimeUnixNano: string;
47
+ /** Nanoseconds since the epoch, as a decimal string. */
48
+ endTimeUnixNano: string;
49
+ /** Typed attributes. */
50
+ attributes: Array<{
51
+ key: string;
52
+ value: AttributeValue;
53
+ }>;
54
+ /** 1 ok, 2 error. */
55
+ status: {
56
+ code: number;
57
+ message?: string;
58
+ };
59
+ }
60
+ /**
61
+ * Turns one finished run into an OpenTelemetry span, with the GenAI semantic conventions.
62
+ *
63
+ * A model run is a client span named `chat <model>` with `gen_ai.operation.name`,
64
+ * `gen_ai.provider.name`, `gen_ai.request.model`, `gen_ai.response.model`, and
65
+ * `gen_ai.usage.input_tokens` and `output_tokens`. A tool run carries `gen_ai.tool.name`, an agent
66
+ * run `gen_ai.agent.name`. What only Nexus knows goes under `nexus.*`: the run kind, a graph node and
67
+ * its step, the cost, and the run id. A failed run has `error.type` and an error status.
68
+ */
69
+ export declare function runToOtlpSpan(run: Run): OtlpSpan;
70
+ /**
71
+ * Sends finished runs to any OpenTelemetry collector as OTLP/HTTP JSON, with no SDK to install.
72
+ *
73
+ * Pass it to a tracer's `exporters`, and every run the tracer keeps arrives as a span with the GenAI
74
+ * semantic conventions, so Grafana Tempo, Jaeger, Honeycomb, Datadog, New Relic, or any OTLP backend
75
+ * reads it as it reads the rest of the application. Spans are batched; `flush()` sends what is
76
+ * waiting. Trace ids are the W3C ids `RunHandle.traceparent()` propagates, so a trace that crossed
77
+ * processes arrives as one.
78
+ */
79
+ export declare class OtlpTraceExporter implements TraceExporter {
80
+ private readonly options;
81
+ private readonly queue;
82
+ private timer;
83
+ private sending;
84
+ constructor(options: OtlpTraceExporterOptions);
85
+ /** Queues a run, sending a batch when one is full. Runs still in progress are skipped. */
86
+ export(run: Run): void;
87
+ /** Sends every span waiting. */
88
+ flush(): Promise<void>;
89
+ private send;
90
+ }
91
+ export {};
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OtlpTraceExporter = void 0;
4
+ exports.runToOtlpSpan = runToOtlpSpan;
5
+ const tracer_js_1 = require("./tracer.js");
6
+ const OPERATIONS = {
7
+ model: 'chat',
8
+ embedding: 'embeddings',
9
+ tool: 'execute_tool',
10
+ agent: 'invoke_agent',
11
+ };
12
+ function runToOtlpSpan(run) {
13
+ const attributes = { 'nexus.run.kind': run.kind, 'nexus.run.id': run.id };
14
+ const operation = OPERATIONS[run.kind];
15
+ if (operation)
16
+ attributes['gen_ai.operation.name'] = operation;
17
+ if (run.provider)
18
+ attributes['gen_ai.provider.name'] = run.provider;
19
+ if (run.kind === 'model' || run.kind === 'embedding') {
20
+ const requested = run.metadata?.requestedModel;
21
+ if (typeof requested === 'string')
22
+ attributes['gen_ai.request.model'] = requested;
23
+ else if (run.model)
24
+ attributes['gen_ai.request.model'] = run.model;
25
+ if (run.model)
26
+ attributes['gen_ai.response.model'] = run.model;
27
+ const input = run.usage?.inputTokens ?? run.usage?.promptTokens;
28
+ const output = run.usage?.outputTokens ?? run.usage?.completionTokens;
29
+ if (input !== undefined)
30
+ attributes['gen_ai.usage.input_tokens'] = input;
31
+ if (output !== undefined)
32
+ attributes['gen_ai.usage.output_tokens'] = output;
33
+ }
34
+ if (run.kind === 'tool')
35
+ attributes['gen_ai.tool.name'] = run.name;
36
+ if (run.kind === 'agent')
37
+ attributes['gen_ai.agent.name'] = run.name;
38
+ if (run.kind === 'node') {
39
+ attributes['nexus.graph.node'] = run.name;
40
+ if (typeof run.metadata?.step === 'number')
41
+ attributes['nexus.graph.step'] = run.metadata.step;
42
+ }
43
+ if (run.cost !== undefined)
44
+ attributes['nexus.cost.usd'] = run.cost;
45
+ if (run.error)
46
+ attributes['error.type'] = run.error.name;
47
+ for (const tag of run.tags ?? [])
48
+ attributes[`nexus.tag.${tag}`] = true;
49
+ const started = Date.parse(run.startedAt);
50
+ const ended = run.endedAt ? Date.parse(run.endedAt) : started + (run.latencyMs ?? 0);
51
+ const name = run.kind === 'model' && run.model ? `chat ${run.model}` : operation ? `${operation} ${run.name}` : run.name;
52
+ return {
53
+ traceId: (0, tracer_js_1.w3cTraceId)(run.traceId),
54
+ spanId: (0, tracer_js_1.w3cSpanId)(run.id),
55
+ ...(run.parentId ? { parentSpanId: (0, tracer_js_1.w3cSpanId)(run.parentId) } : {}),
56
+ name,
57
+ kind: run.kind === 'model' || run.kind === 'embedding' ? 3 : 1,
58
+ startTimeUnixNano: nanos(started),
59
+ endTimeUnixNano: nanos(ended),
60
+ attributes: Object.entries(attributes).map(([key, value]) => ({ key, value: typed(value) })),
61
+ status: run.status === 'error' ? { code: 2, ...(run.error ? { message: run.error.message } : {}) } : { code: 1 },
62
+ };
63
+ }
64
+ class OtlpTraceExporter {
65
+ options;
66
+ queue = [];
67
+ timer;
68
+ sending = Promise.resolve();
69
+ constructor(options) {
70
+ this.options = options;
71
+ }
72
+ export(run) {
73
+ if (run.status === 'running')
74
+ return;
75
+ this.queue.push(runToOtlpSpan(run));
76
+ if (this.queue.length >= (this.options.maxBatch ?? 100)) {
77
+ void this.flush();
78
+ return;
79
+ }
80
+ if (!this.timer) {
81
+ this.timer = setTimeout(() => void this.flush(), this.options.flushIntervalMs ?? 2_000);
82
+ this.timer.unref?.();
83
+ }
84
+ }
85
+ async flush() {
86
+ if (this.timer)
87
+ clearTimeout(this.timer);
88
+ this.timer = undefined;
89
+ while (this.queue.length > 0) {
90
+ const batch = this.queue.splice(0, this.options.maxBatch ?? 100);
91
+ this.sending = this.sending.then(() => this.send(batch));
92
+ }
93
+ await this.sending;
94
+ }
95
+ async send(spans) {
96
+ const resource = { 'service.name': this.options.serviceName ?? 'nexus-ai-pro', ...this.options.resource };
97
+ const body = {
98
+ resourceSpans: [
99
+ {
100
+ resource: { attributes: Object.entries(resource).map(([key, value]) => ({ key, value: typed(value) })) },
101
+ scopeSpans: [{ scope: { name: 'nexus-ai-pro' }, spans }],
102
+ },
103
+ ],
104
+ };
105
+ try {
106
+ const response = await (this.options.fetch ?? globalThis.fetch)(`${this.options.endpoint.replace(/\/+$/, '')}/v1/traces`, {
107
+ method: 'POST',
108
+ headers: { 'content-type': 'application/json', ...this.options.headers },
109
+ body: JSON.stringify(body),
110
+ });
111
+ if (!response.ok)
112
+ throw new Error(`The collector answered ${response.status}`);
113
+ }
114
+ catch (error) {
115
+ this.options.onError?.(error);
116
+ }
117
+ }
118
+ }
119
+ exports.OtlpTraceExporter = OtlpTraceExporter;
120
+ function nanos(milliseconds) {
121
+ return `${BigInt(Math.round(milliseconds)) * 1000000n}`;
122
+ }
123
+ function typed(value) {
124
+ if (typeof value === 'boolean')
125
+ return { boolValue: value };
126
+ if (typeof value === 'number')
127
+ return Number.isInteger(value) ? { intValue: String(value) } : { doubleValue: value };
128
+ return { stringValue: value };
129
+ }
@@ -0,0 +1,100 @@
1
+ import type { Run, RunKind, TraceStore } from '../types/tracing.js';
2
+ /**
3
+ * Hourly totals of runs, so a dashboard reads a few rows instead of every trace.
4
+ *
5
+ * Traces are the forensic record: everything about one run. A chart of cost or errors over a week
6
+ * needs none of that, only sums, and reading 100,000 traces to draw it is how a dashboard gets slow
7
+ * as the service grows. A rollup keeps one row per hour, kind, model, provider, and tenant, updated
8
+ * as runs finish.
9
+ */
10
+ /** What one rollup row is counted by. */
11
+ export interface RollupKey {
12
+ /** The hour, as ISO-8601 truncated to the hour: `2026-10-04T13:00:00.000Z`. */
13
+ hour: string;
14
+ /** The run kind. */
15
+ kind: RunKind;
16
+ /** The run's name, for root runs, such as a graph or an assistant. Empty for others. */
17
+ name: string;
18
+ /** The model, or an empty string. */
19
+ model: string;
20
+ /** The provider, or an empty string. */
21
+ provider: string;
22
+ /** `metadata.tenantId`, or an empty string. */
23
+ tenant: string;
24
+ }
25
+ /** The totals of one rollup row. */
26
+ export interface RollupTotals {
27
+ /** Runs counted. */
28
+ runs: number;
29
+ /** Of those, runs that failed. */
30
+ errors: number;
31
+ /** Their cost. */
32
+ cost: number;
33
+ /** Input tokens reported. */
34
+ inputTokens: number;
35
+ /** Output tokens reported. */
36
+ outputTokens: number;
37
+ /** Latency counts per bucket of `ROLLUP_LATENCY_BOUNDS_MS`, for percentiles without the runs. */
38
+ latency: number[];
39
+ }
40
+ /** One rollup row: its key and its totals. */
41
+ export interface RollupRow extends RollupKey, RollupTotals {
42
+ }
43
+ /** Which rows to read. Every field narrows; the hour range is inclusive. */
44
+ export interface RollupQuery {
45
+ /** The first hour, ISO-8601. */
46
+ since?: string;
47
+ /** The last hour, ISO-8601. */
48
+ until?: string;
49
+ /** Rows of these kinds. */
50
+ kind?: RunKind | RunKind[];
51
+ /** Rows of this model. */
52
+ model?: string;
53
+ /** Rows of this provider. */
54
+ provider?: string;
55
+ /** Rows of this tenant. */
56
+ tenant?: string;
57
+ /** Rows of this name. */
58
+ name?: string;
59
+ }
60
+ /** Where rollup rows live. Adding to a row that does not exist creates it. */
61
+ export interface RollupStore {
62
+ /** Adds totals to the row for `key`. */
63
+ add(key: RollupKey, totals: RollupTotals): Promise<void> | void;
64
+ /** Rows matching a query, oldest hour first. */
65
+ query(query?: RollupQuery): Promise<RollupRow[]> | RollupRow[];
66
+ }
67
+ /**
68
+ * Upper bounds of the latency buckets, in milliseconds; a last bucket holds everything slower. A
69
+ * percentile read from them is exact to the bucket, which is close enough for a chart.
70
+ */
71
+ export declare const ROLLUP_LATENCY_BOUNDS_MS: readonly [10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 30000, 60000, 120000, 300000, 600000];
72
+ /** The latency at percentile `p`, 0 to 1, from rollup buckets: the bound of the bucket it falls in. */
73
+ export declare function rollupPercentile(latency: readonly number[], p: number): number | undefined;
74
+ /** Rows summed across their keys: a total over a day, a model, or a whole range. */
75
+ export declare function sumRollups(rows: readonly RollupTotals[]): RollupTotals;
76
+ /** Rollup rows in process memory. Rows are small, so a year of hourly rows for a few models fits easily. */
77
+ export declare class MemoryRollupStore implements RollupStore {
78
+ private readonly rows;
79
+ /** Adds totals to a row. */
80
+ add(key: RollupKey, totals: RollupTotals): void;
81
+ /** Rows matching a query, oldest hour first. */
82
+ query(query?: RollupQuery): RollupRow[];
83
+ /** Rows held. */
84
+ size(): number;
85
+ }
86
+ /** Options for `rollupTraceStore()`. */
87
+ export interface RollupTraceStoreOptions {
88
+ /** Which finished runs are counted. Defaults to every root run, and every model and embedding run. */
89
+ include?: (run: Run) => boolean;
90
+ }
91
+ /**
92
+ * Wraps a trace store so every run that finishes is also added to a rollup.
93
+ *
94
+ * Reads go to the wrapped store unchanged. A run is counted once, when it is saved finished; a save
95
+ * of a run still running, which an `incremental` tracer makes, is not counted, and neither is
96
+ * feedback added later.
97
+ */
98
+ export declare function rollupTraceStore(store: TraceStore, rollups: RollupStore, options?: RollupTraceStoreOptions): TraceStore;
99
+ /** The rollup row a finished run belongs to. */
100
+ export declare function rollupKeyOf(run: Run): RollupKey;
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryRollupStore = exports.ROLLUP_LATENCY_BOUNDS_MS = void 0;
4
+ exports.rollupPercentile = rollupPercentile;
5
+ exports.sumRollups = sumRollups;
6
+ exports.rollupTraceStore = rollupTraceStore;
7
+ exports.rollupKeyOf = rollupKeyOf;
8
+ exports.ROLLUP_LATENCY_BOUNDS_MS = [
9
+ 10, 25, 50, 100, 250, 500, 1_000, 2_500, 5_000, 10_000, 30_000, 60_000, 120_000, 300_000, 600_000,
10
+ ];
11
+ function rollupPercentile(latency, p) {
12
+ const total = latency.reduce((sum, count) => sum + count, 0);
13
+ if (total === 0)
14
+ return undefined;
15
+ const target = Math.max(1, Math.ceil(total * Math.min(1, Math.max(0, p))));
16
+ let seen = 0;
17
+ for (const [index, count] of latency.entries()) {
18
+ seen += count;
19
+ if (seen >= target)
20
+ return exports.ROLLUP_LATENCY_BOUNDS_MS[index] ?? Number.POSITIVE_INFINITY;
21
+ }
22
+ return Number.POSITIVE_INFINITY;
23
+ }
24
+ function sumRollups(rows) {
25
+ const total = emptyTotals();
26
+ for (const row of rows) {
27
+ total.runs += row.runs;
28
+ total.errors += row.errors;
29
+ total.cost += row.cost;
30
+ total.inputTokens += row.inputTokens;
31
+ total.outputTokens += row.outputTokens;
32
+ for (const [index, count] of row.latency.entries())
33
+ total.latency[index] = (total.latency[index] ?? 0) + count;
34
+ }
35
+ return total;
36
+ }
37
+ class MemoryRollupStore {
38
+ rows = new Map();
39
+ add(key, totals) {
40
+ const id = `${key.hour}|${key.kind}|${key.name}|${key.model}|${key.provider}|${key.tenant}`;
41
+ const row = this.rows.get(id);
42
+ if (!row) {
43
+ this.rows.set(id, { ...key, ...totals, latency: [...totals.latency] });
44
+ return;
45
+ }
46
+ row.runs += totals.runs;
47
+ row.errors += totals.errors;
48
+ row.cost += totals.cost;
49
+ row.inputTokens += totals.inputTokens;
50
+ row.outputTokens += totals.outputTokens;
51
+ for (const [index, count] of totals.latency.entries())
52
+ row.latency[index] = (row.latency[index] ?? 0) + count;
53
+ }
54
+ query(query = {}) {
55
+ return [...this.rows.values()]
56
+ .filter((row) => matches(row, query))
57
+ .sort((a, b) => a.hour.localeCompare(b.hour))
58
+ .map((row) => ({ ...row, latency: [...row.latency] }));
59
+ }
60
+ size() {
61
+ return this.rows.size;
62
+ }
63
+ }
64
+ exports.MemoryRollupStore = MemoryRollupStore;
65
+ function rollupTraceStore(store, rollups, options = {}) {
66
+ const include = options.include ?? ((run) => !run.parentId || run.kind === 'model' || run.kind === 'embedding');
67
+ return {
68
+ async save(run) {
69
+ await store.save(run);
70
+ if (run.status === 'running' || !include(run))
71
+ return;
72
+ await rollups.add(rollupKeyOf(run), totalsOf(run));
73
+ },
74
+ get: (runId) => store.get(runId),
75
+ query: (query) => store.query(query),
76
+ tree: (traceId) => store.tree(traceId),
77
+ async addFeedback(runId, feedback) {
78
+ if (store.addFeedback) {
79
+ await store.addFeedback(runId, feedback);
80
+ return;
81
+ }
82
+ const run = await store.get(runId);
83
+ if (run)
84
+ await store.save({ ...run, feedback: [...(run.feedback ?? []), feedback] });
85
+ },
86
+ ...(store.prune
87
+ ? { prune: (before) => store.prune(before) }
88
+ : {}),
89
+ };
90
+ }
91
+ function rollupKeyOf(run) {
92
+ const started = new Date(run.startedAt);
93
+ started.setUTCMinutes(0, 0, 0);
94
+ const tenant = run.metadata?.tenantId;
95
+ return {
96
+ hour: started.toISOString(),
97
+ kind: run.kind,
98
+ name: run.parentId ? '' : run.name,
99
+ model: run.model ?? '',
100
+ provider: run.provider ?? '',
101
+ tenant: typeof tenant === 'string' ? tenant : '',
102
+ };
103
+ }
104
+ function totalsOf(run) {
105
+ const totals = emptyTotals();
106
+ totals.runs = 1;
107
+ totals.errors = run.status === 'error' ? 1 : 0;
108
+ totals.cost = run.cost ?? 0;
109
+ totals.inputTokens = run.usage?.inputTokens ?? run.usage?.promptTokens ?? 0;
110
+ totals.outputTokens = run.usage?.outputTokens ?? run.usage?.completionTokens ?? 0;
111
+ const latency = run.latencyMs ?? 0;
112
+ const bucket = exports.ROLLUP_LATENCY_BOUNDS_MS.findIndex((bound) => latency <= bound);
113
+ totals.latency[bucket === -1 ? exports.ROLLUP_LATENCY_BOUNDS_MS.length : bucket] = 1;
114
+ return totals;
115
+ }
116
+ function emptyTotals() {
117
+ return {
118
+ runs: 0,
119
+ errors: 0,
120
+ cost: 0,
121
+ inputTokens: 0,
122
+ outputTokens: 0,
123
+ latency: Array.from({ length: exports.ROLLUP_LATENCY_BOUNDS_MS.length + 1 }, () => 0),
124
+ };
125
+ }
126
+ function matches(row, query) {
127
+ if (query.since && row.hour < hourOf(query.since))
128
+ return false;
129
+ if (query.until && row.hour > query.until)
130
+ return false;
131
+ if (query.kind !== undefined && !(Array.isArray(query.kind) ? query.kind : [query.kind]).includes(row.kind))
132
+ return false;
133
+ if (query.model !== undefined && row.model !== query.model)
134
+ return false;
135
+ if (query.provider !== undefined && row.provider !== query.provider)
136
+ return false;
137
+ if (query.tenant !== undefined && row.tenant !== query.tenant)
138
+ return false;
139
+ if (query.name !== undefined && row.name !== query.name)
140
+ return false;
141
+ return true;
142
+ }
143
+ function hourOf(iso) {
144
+ const date = new Date(iso);
145
+ date.setUTCMinutes(0, 0, 0);
146
+ return date.toISOString();
147
+ }
@@ -34,6 +34,23 @@ export declare class MemoryTraceStore implements TraceStore {
34
34
  /** Runs held. */
35
35
  size(): number;
36
36
  }
37
+ /** Options for `closeAbandonedRuns()`. */
38
+ export interface CloseAbandonedRunsOptions {
39
+ /** A run still running after this long is taken to belong to a process that ended. */
40
+ olderThanMs: number;
41
+ /** Replaces the system clock, for tests. */
42
+ now?: () => Date;
43
+ }
44
+ /**
45
+ * Closes runs a process left running when it ended, so a crash shows as a failure rather than as
46
+ * work that is still going.
47
+ *
48
+ * Only a tracer with `incremental` writes a run before it finishes, so only its runs can be left
49
+ * behind. Each one older than `olderThanMs` is saved as an error named `RunAbandoned`, with
50
+ * `metadata.abandoned`, keeping everything it recorded. Call it when a worker starts, or on a
51
+ * schedule. Returns how many runs it closed.
52
+ */
53
+ export declare function closeAbandonedRuns(store: TraceStore, options: CloseAbandonedRunsOptions): Promise<number>;
37
54
  /** Options for the JSONL trace store. */
38
55
  export interface JsonlTraceStoreOptions {
39
56
  /** File runs are appended to, one JSON object per line. */