@axlsdk/axl 0.24.0 → 0.24.2

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.
package/dist/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { createRequire as __axlCreateRequire } from 'node:module';
2
2
  const require = __axlCreateRequire(import.meta.url);
3
+ const __AXL_BUILD_PATH__ = import.meta.url;
3
4
  import {
4
5
  __require
5
- } from "./chunk-O7Y6WXE2.js";
6
+ } from "./chunk-PJROX3XU.js";
6
7
 
7
8
  // src/tool.ts
8
9
  import { ZodError } from "zod";
@@ -119,12 +120,14 @@ function preserveErrorCause(error, cause) {
119
120
  });
120
121
  return error;
121
122
  }
123
+ var TOOL_FAILURE_MARKER = /* @__PURE__ */ Symbol.for("axl.toolFailure.v1");
122
124
  var ToolFailure = class extends AxlError {
123
125
  modelMessage;
124
126
  constructor(options) {
125
127
  super(options.code ?? "TOOL_FAILURE", options.message);
126
128
  this.name = "ToolFailure";
127
129
  this.modelMessage = options.modelMessage;
130
+ Object.defineProperty(this, TOOL_FAILURE_MARKER, { value: true });
128
131
  if (options.cause !== void 0) {
129
132
  Object.defineProperty(this, "cause", {
130
133
  configurable: true,
@@ -134,6 +137,13 @@ var ToolFailure = class extends AxlError {
134
137
  }
135
138
  }
136
139
  };
140
+ function isToolFailureError(error) {
141
+ try {
142
+ return error instanceof ToolFailure || typeof error === "object" && error !== null && error[TOOL_FAILURE_MARKER] === true && typeof error.modelMessage === "string";
143
+ } catch {
144
+ return false;
145
+ }
146
+ }
137
147
  var ToolModelOutputError = class extends AxlError {
138
148
  toolName;
139
149
  cause;
@@ -277,8 +287,62 @@ var ValidationError = class extends AxlError {
277
287
  }
278
288
  };
279
289
 
280
- // src/accounting.ts
290
+ // src/shared-context.ts
281
291
  import { AsyncLocalStorage } from "async_hooks";
292
+ import { fileURLToPath } from "url";
293
+ var PROTOCOL = 1;
294
+ var CONTEXT_KEY = /* @__PURE__ */ Symbol.for(`axl.accounting.context.v${PROTOCOL}`);
295
+ var GUARD_KEY = /* @__PURE__ */ Symbol.for("axl.accounting.scopeGuard");
296
+ function createContext() {
297
+ let operationCounter = 0;
298
+ return {
299
+ protocol: PROTOCOL,
300
+ id: /* @__PURE__ */ Symbol("axl.accounting.context.instance"),
301
+ accounting: new AsyncLocalStorage(),
302
+ dispatch: new AsyncLocalStorage(),
303
+ capture: new AsyncLocalStorage(),
304
+ correlation: new AsyncLocalStorage(),
305
+ turn: new AsyncLocalStorage(),
306
+ nextOperationId: () => `op_${++operationCounter}`
307
+ };
308
+ }
309
+ function isStorage(value) {
310
+ return typeof value === "object" && value !== null && typeof value.getStore === "function" && typeof value.run === "function";
311
+ }
312
+ function isCompatible(value) {
313
+ if (typeof value !== "object" || value === null) return false;
314
+ const candidate = value;
315
+ return candidate.protocol === PROTOCOL && typeof candidate.id === "symbol" && isStorage(candidate.accounting) && isStorage(candidate.dispatch) && isStorage(candidate.capture) && isStorage(candidate.correlation) && isStorage(candidate.turn) && typeof candidate.nextOperationId === "function";
316
+ }
317
+ var registry = globalThis;
318
+ var installed = registry[CONTEXT_KEY];
319
+ var sharedContext = installed === void 0 ? registry[CONTEXT_KEY] = createContext() : isCompatible(installed) ? installed : createContext();
320
+ var installedGuard = registry[GUARD_KEY];
321
+ var sharedGuard = typeof installedGuard === "object" && installedGuard !== null && isStorage(installedGuard.storage) && installedGuard.warned instanceof WeakMap ? installedGuard : registry[GUARD_KEY] = {
322
+ storage: new AsyncLocalStorage(),
323
+ warned: /* @__PURE__ */ new WeakMap()
324
+ };
325
+ var buildPath = typeof __AXL_BUILD_PATH__ === "string" ? __AXL_BUILD_PATH__ : "<source>";
326
+ var thisCopy = {
327
+ path: buildPath.startsWith("file:") ? fileURLToPath(buildPath) : buildPath,
328
+ version: true ? "0.24.2" : "unknown"
329
+ };
330
+ function warnCrossCopy(other, joined) {
331
+ if (other === thisCopy) return;
332
+ if (sharedGuard.warned.get(other)?.has(thisCopy)) return;
333
+ const fromThis = sharedGuard.warned.get(thisCopy) ?? /* @__PURE__ */ new WeakSet();
334
+ if (fromThis.has(other)) return;
335
+ fromThis.add(other);
336
+ sharedGuard.warned.set(thisCopy, fromThis);
337
+ try {
338
+ console.warn(
339
+ `[axl] Cross-copy accounting: ${other.path} (v${other.version}) and ${thisCopy.path} (v${thisCopy.version}). ` + (joined ? "Shared accounting, admission, and request capture context joined." : "Accounting protocols could not join; paid work is refused and the active scope is incomplete.")
340
+ );
341
+ } catch {
342
+ }
343
+ }
344
+
345
+ // src/accounting.ts
282
346
  var RECORD_SPEND = /* @__PURE__ */ Symbol.for("axl.accounting.recordSpend");
283
347
  var AdmissionController = class {
284
348
  /** The configured USD threshold. Admission closes at `knownSpend >= limit`. */
@@ -340,10 +404,8 @@ function emptyUsage() {
340
404
  function isUsableCost(cost) {
341
405
  return typeof cost === "number" && Number.isFinite(cost) && cost >= 0;
342
406
  }
343
- var operationCounter = 0;
344
407
  function nextOperationId() {
345
- operationCounter += 1;
346
- return `op_${operationCounter}`;
408
+ return sharedContext.nextOperationId();
347
409
  }
348
410
  var AccountingScope = class {
349
411
  parent;
@@ -364,10 +426,16 @@ var AccountingScope = class {
364
426
  reasons = /* @__PURE__ */ new Map();
365
427
  breakdown = { generation: 0, judging: 0, external: 0 };
366
428
  provenance = /* @__PURE__ */ new Map();
429
+ modelCalls = /* @__PURE__ */ new Map();
430
+ modelTiming = /* @__PURE__ */ new Map();
431
+ diagnosticTokens = { input: 0, output: 0, reasoning: 0 };
432
+ agentCalls = 0;
433
+ captureTimingSamples;
367
434
  constructor(options) {
368
435
  this.parent = options.parent;
369
436
  this.purpose = options.purpose;
370
437
  this.admission = options.admission;
438
+ this.captureTimingSamples = options.captureTimingSamples === true;
371
439
  }
372
440
  get isFinalized() {
373
441
  return this.finalized;
@@ -381,6 +449,11 @@ var AccountingScope = class {
381
449
  recordDenied() {
382
450
  this.denied += 1;
383
451
  }
452
+ /** A foreign protocol attempted work we cannot measure. Mark every live ancestor. */
453
+ markUninstrumented() {
454
+ if (this.finalized || this.reasons.has("uninstrumented")) return;
455
+ this.reasons.set("uninstrumented", 1);
456
+ }
384
457
  /** An already-open operation refused at the dispatch check: retract it. */
385
458
  recordRetracted(op) {
386
459
  this.seen.add(op.id);
@@ -391,7 +464,7 @@ var AccountingScope = class {
391
464
  else this.byKind.delete(op.kind);
392
465
  this.denied += 1;
393
466
  }
394
- recordSettled(op, cost, provenance, usage) {
467
+ recordSettled(op, cost, provenance, usage, timing) {
395
468
  this.seen.add(op.id);
396
469
  this.openOps.delete(op.id);
397
470
  this.settled += 1;
@@ -400,13 +473,57 @@ var AccountingScope = class {
400
473
  else this.breakdown[op.purpose] += cost;
401
474
  this.provenance.set(provenance, (this.provenance.get(provenance) ?? 0) + cost);
402
475
  if (usage) this.addUsage(usage);
476
+ this.recordAgentCall(op, usage, timing);
403
477
  }
404
- recordUnknown(op, reason, usage) {
478
+ recordUnknown(op, reason, usage, timing) {
405
479
  this.seen.add(op.id);
406
480
  this.openOps.delete(op.id);
407
481
  this.unknown += 1;
408
482
  this.reasons.set(reason, (this.reasons.get(reason) ?? 0) + 1);
409
483
  if (usage) this.addUsage(usage);
484
+ this.recordAgentCall(op, usage, timing);
485
+ }
486
+ recordAgentCall(op, usage, timing) {
487
+ if (!op.agentCall) return;
488
+ this.agentCalls++;
489
+ const model = op.diagnosticModelUri ?? op.model;
490
+ if (!model) return;
491
+ this.modelCalls.set(model, (this.modelCalls.get(model) ?? 0) + 1);
492
+ if (usage) {
493
+ const finite = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
494
+ this.diagnosticTokens.input += finite(usage.inputTokens);
495
+ this.diagnosticTokens.output += finite(usage.outputTokens);
496
+ this.diagnosticTokens.reasoning += finite(usage.reasoningTokens);
497
+ }
498
+ if (!timing) return;
499
+ let bucket = this.modelTiming.get(model);
500
+ if (!bucket) {
501
+ bucket = {
502
+ calls: 0,
503
+ queuedMs: 0,
504
+ retryMs: 0,
505
+ wireMs: 0,
506
+ ...this.captureTimingSamples ? { samples: [] } : {}
507
+ };
508
+ this.modelTiming.set(model, bucket);
509
+ }
510
+ bucket.calls++;
511
+ bucket.queuedMs += timing.queuedMs;
512
+ bucket.retryMs += timing.retryMs;
513
+ bucket.wireMs += timing.wireMs;
514
+ bucket.samples?.push(timing);
515
+ if (timing.firstTokenMs != null) {
516
+ bucket.firstTokenMs = (bucket.firstTokenMs ?? 0) + timing.firstTokenMs;
517
+ bucket.firstTokenCalls = (bucket.firstTokenCalls ?? 0) + 1;
518
+ }
519
+ }
520
+ toDiagnostics() {
521
+ return {
522
+ modelCallCounts: Object.fromEntries(this.modelCalls),
523
+ tokens: { ...this.diagnosticTokens },
524
+ agentCalls: this.agentCalls,
525
+ modelTiming: Object.fromEntries(this.modelTiming)
526
+ };
410
527
  }
411
528
  hasSeen(id) {
412
529
  return this.seen.has(id);
@@ -433,7 +550,7 @@ var AccountingScope = class {
433
550
  version: 1,
434
551
  currency: "USD",
435
552
  knownCost: this.knownCost,
436
- completeness: this.unknown > 0 ? "incomplete" : "complete",
553
+ completeness: this.reasons.size > 0 ? "incomplete" : "complete",
437
554
  reasons: Object.fromEntries(this.reasons),
438
555
  usage: { ...this.usage },
439
556
  operations: {
@@ -448,8 +565,8 @@ var AccountingScope = class {
448
565
  };
449
566
  }
450
567
  };
451
- var accountingStorage = new AsyncLocalStorage();
452
- var dispatchAdmissionStorage = new AsyncLocalStorage();
568
+ var accountingStorage = sharedContext.accounting;
569
+ var dispatchAdmissionStorage = sharedContext.dispatch;
453
570
  function currentDispatchAdmission() {
454
571
  return dispatchAdmissionStorage.getStore();
455
572
  }
@@ -478,14 +595,14 @@ function settleOperationUp(start, op, outcome) {
478
595
  if (scope.isFinalized) return;
479
596
  switch (outcome.outcome) {
480
597
  case "settled":
481
- scope.recordSettled(op, outcome.cost, outcome.provenance, outcome.usage);
598
+ scope.recordSettled(op, outcome.cost, outcome.provenance, outcome.usage, outcome.timing);
482
599
  if (scope.admission && !chargedControllers.has(scope.admission)) {
483
600
  chargedControllers.add(scope.admission);
484
601
  recordSpend(scope.admission, outcome.cost);
485
602
  }
486
603
  break;
487
604
  case "unknown":
488
- scope.recordUnknown(op, outcome.reason, outcome.usage);
605
+ scope.recordUnknown(op, outcome.reason, outcome.usage, outcome.timing);
489
606
  break;
490
607
  case "retracted":
491
608
  scope.recordRetracted(op);
@@ -510,6 +627,17 @@ function admissionDenied(controller, descriptor) {
510
627
  });
511
628
  }
512
629
  function openOperation(descriptor) {
630
+ const inheritedGuard = sharedGuard.storage.getStore();
631
+ const guard = inheritedGuard && (inheritedGuard.isActive?.() ?? true) ? inheritedGuard : void 0;
632
+ if (guard && guard.contextId !== sharedContext.id) {
633
+ guard.markUninstrumented();
634
+ warnCrossCopy(guard.owner, false);
635
+ throw new AxlError(
636
+ "INCOMPATIBLE_ACCOUNTING_SCOPE",
637
+ "The active @axlsdk/axl accounting scope uses an incompatible loaded copy; paid work was refused before dispatch."
638
+ );
639
+ }
640
+ if (guard) warnCrossCopy(guard.owner, true);
513
641
  const scope = accountingStorage.getStore();
514
642
  if (!scope || scope.isFinalized) return void 0;
515
643
  const denier = denyingController(scope);
@@ -526,6 +654,8 @@ function openOperation(descriptor) {
526
654
  kind: descriptor.kind,
527
655
  purpose: scope.purpose,
528
656
  model: descriptor.model,
657
+ diagnosticModelUri: descriptor.diagnosticModelUri,
658
+ agentCall: descriptor.agentCall === true,
529
659
  dispatched: false,
530
660
  dispatchObservable: false
531
661
  };
@@ -566,25 +696,31 @@ function openOperation(descriptor) {
566
696
  outcome: "settled",
567
697
  cost: input.cost,
568
698
  provenance: input.provenance ?? "adapter_reported",
569
- usage: input.usage
699
+ usage: input.usage,
700
+ timing: input.timing
570
701
  });
571
702
  return;
572
703
  }
573
704
  if (input.cost !== void 0 || input.usage !== void 0) {
574
- finish({ outcome: "unknown", reason: "unpriced_model", usage: input.usage });
705
+ finish({
706
+ outcome: "unknown",
707
+ reason: "unpriced_model",
708
+ usage: input.usage,
709
+ timing: input.timing
710
+ });
575
711
  return;
576
712
  }
577
- handle.settleFailure();
713
+ handle.settleFailure(void 0, input.timing);
578
714
  },
579
715
  settleUnknown(reason, usage) {
580
716
  finish({ outcome: "unknown", reason, usage });
581
717
  },
582
- settleFailure(usage) {
718
+ settleFailure(usage, timing) {
583
719
  if (op.dispatchObservable && !op.dispatched) {
584
- finish({ outcome: "settled", cost: 0, provenance: "adapter_reported", usage });
720
+ finish({ outcome: "settled", cost: 0, provenance: "adapter_reported", usage, timing });
585
721
  return;
586
722
  }
587
- finish({ outcome: "unknown", reason: "usage_missing", usage });
723
+ finish({ outcome: "unknown", reason: "usage_missing", usage, timing });
588
724
  },
589
725
  run(fn) {
590
726
  return runWithDispatchAdmission(handle.dispatchAdmission, fn);
@@ -597,15 +733,62 @@ async function runInAccountingScope(options, fn) {
597
733
  const scope = new AccountingScope({
598
734
  parent,
599
735
  purpose: options.purpose ?? parent?.purpose ?? "generation",
600
- admission: options.admission
736
+ admission: options.admission,
737
+ captureTimingSamples: options.captureTimingSamples
601
738
  });
739
+ const inheritedGuard = sharedGuard.storage.getStore();
740
+ const enclosingGuard = inheritedGuard && (inheritedGuard.isActive?.() ?? true) ? inheritedGuard : void 0;
741
+ if (enclosingGuard)
742
+ warnCrossCopy(enclosingGuard.owner, enclosingGuard.contextId === sharedContext.id);
743
+ if (enclosingGuard && enclosingGuard.contextId !== sharedContext.id) {
744
+ enclosingGuard.markUninstrumented();
745
+ scope.markUninstrumented();
746
+ scope.finalize();
747
+ return {
748
+ outcome: {
749
+ status: "rejected",
750
+ error: new AxlError(
751
+ "INCOMPATIBLE_ACCOUNTING_SCOPE",
752
+ "The active @axlsdk/axl accounting scope uses an incompatible loaded copy; paid work was refused before dispatch."
753
+ )
754
+ },
755
+ accounting: scope.toAccounting(),
756
+ diagnostics: scope.toDiagnostics()
757
+ };
758
+ }
759
+ const markUninstrumented = () => {
760
+ let current = scope;
761
+ while (current) {
762
+ if (!current.isFinalized) current.markUninstrumented();
763
+ current = current.parent;
764
+ }
765
+ };
766
+ const isActive = () => {
767
+ let current = scope;
768
+ while (current) {
769
+ if (!current.isFinalized) return true;
770
+ current = current.parent;
771
+ }
772
+ return false;
773
+ };
602
774
  try {
603
- const value = await accountingStorage.run(scope, fn);
775
+ const value = await sharedGuard.storage.run(
776
+ { contextId: sharedContext.id, owner: thisCopy, markUninstrumented, isActive },
777
+ () => accountingStorage.run(scope, fn)
778
+ );
604
779
  scope.finalize();
605
- return { outcome: { status: "fulfilled", value }, accounting: scope.toAccounting() };
780
+ return {
781
+ outcome: { status: "fulfilled", value },
782
+ accounting: scope.toAccounting(),
783
+ diagnostics: scope.toDiagnostics()
784
+ };
606
785
  } catch (error) {
607
786
  scope.finalize();
608
- return { outcome: { status: "rejected", error }, accounting: scope.toAccounting() };
787
+ return {
788
+ outcome: { status: "rejected", error },
789
+ accounting: scope.toAccounting(),
790
+ diagnostics: scope.toDiagnostics()
791
+ };
609
792
  }
610
793
  }
611
794
  function validateReportedCost(amountUsd) {
@@ -984,10 +1167,12 @@ function toolArgumentIssues(error) {
984
1167
  }
985
1168
  return [{ path: [], code: "invalid_arguments", message: "Tool arguments are invalid." }];
986
1169
  }
987
- var TOOL_INTERNALS = /* @__PURE__ */ new WeakMap();
1170
+ var TOOL_INTERNALS = /* @__PURE__ */ Symbol.for("axl.tool.internals.v1");
988
1171
  function getToolInternals(tool2) {
989
- const internals = TOOL_INTERNALS.get(tool2);
990
- if (!internals) throw new Error(`Tool "${tool2.name}" was not created by tool()`);
1172
+ const internals = tool2[TOOL_INTERNALS];
1173
+ if (!internals || typeof internals.prepareInput !== "function" || typeof internals.executePrepared !== "function") {
1174
+ throw new Error(`Tool "${tool2.name}" was not created by a compatible tool()`);
1175
+ }
991
1176
  return internals;
992
1177
  }
993
1178
  function prepareToolInput(tool2, input) {
@@ -1034,7 +1219,7 @@ function tool(config) {
1034
1219
  } catch (err) {
1035
1220
  if (options?.signal?.aborted) options.signal.throwIfAborted();
1036
1221
  if (isAbortError(err)) throw err;
1037
- if (err instanceof AdmissionDeniedError) throw err;
1222
+ if (isAdmissionDeniedError(err)) throw err;
1038
1223
  rethrowEventStreamOverflow(err);
1039
1224
  lastError = err instanceof Error ? err : new Error(String(err));
1040
1225
  if (attempt === maxAttempts) break;
@@ -1088,9 +1273,11 @@ function tool(config) {
1088
1273
  },
1089
1274
  _execute: execute
1090
1275
  };
1091
- TOOL_INTERNALS.set(instance, {
1092
- prepareInput,
1093
- executePrepared
1276
+ Object.defineProperty(instance, TOOL_INTERNALS, {
1277
+ value: {
1278
+ prepareInput,
1279
+ executePrepared
1280
+ }
1094
1281
  });
1095
1282
  return instance;
1096
1283
  }
@@ -1121,6 +1308,7 @@ async function resolveApiKey(source) {
1121
1308
  // src/providers/default-endpoints.ts
1122
1309
  var OPENAI_DEFAULT_BASE_URL = "https://api.openai.com/v1";
1123
1310
  var ANTHROPIC_DEFAULT_BASE_URL = "https://api.anthropic.com/v1";
1311
+ var GEMINI_DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
1124
1312
 
1125
1313
  // src/providers/rate-limiter.ts
1126
1314
  function sanitizeRateLimitConfig(config) {
@@ -1518,12 +1706,36 @@ var anthropicQuotaDialect = makeDialect(
1518
1706
  return "unknown";
1519
1707
  }
1520
1708
  );
1709
+ var geminiQuotaDialect = makeDialect([], (error) => {
1710
+ if (error.code === "quota_exceeded") return "spend_cap";
1711
+ if (Array.isArray(error.details)) {
1712
+ for (const detail of error.details) {
1713
+ if (typeof detail !== "object" || detail === null) continue;
1714
+ const violations = detail.violations;
1715
+ if (!Array.isArray(violations)) continue;
1716
+ if (violations.some((violation) => {
1717
+ if (typeof violation !== "object" || violation === null) return false;
1718
+ const id = violation.quotaId;
1719
+ return typeof id === "string" && /perday|daily|spend|billing/i.test(id);
1720
+ }))
1721
+ return "spend_cap";
1722
+ }
1723
+ }
1724
+ const message = error.message;
1725
+ if (typeof message === "string" && /monthly spending cap|spend(?:-based|ing)? (?:cap|limit)|prepay(?:ment)? credits? (?:are )?depleted/i.test(
1726
+ message
1727
+ )) {
1728
+ return "spend_cap";
1729
+ }
1730
+ return "unknown";
1731
+ });
1521
1732
  var DIALECTS = /* @__PURE__ */ new Map([
1522
1733
  ["openai", { origin: new URL(OPENAI_DEFAULT_BASE_URL).origin, dialect: openaiQuotaDialect }],
1523
1734
  [
1524
1735
  "anthropic",
1525
1736
  { origin: new URL(ANTHROPIC_DEFAULT_BASE_URL).origin, dialect: anthropicQuotaDialect }
1526
- ]
1737
+ ],
1738
+ ["google", { origin: new URL(GEMINI_DEFAULT_BASE_URL).origin, dialect: geminiQuotaDialect }]
1527
1739
  ]);
1528
1740
  function quotaDialectFor(family, origin) {
1529
1741
  const entry = DIALECTS.get(family);
@@ -5714,7 +5926,7 @@ var GEMINI_PRICING = {
5714
5926
  },
5715
5927
  "gemini-3.8-flash": { input: 75e-8, cached: 75e-9, output: 375e-8 }
5716
5928
  };
5717
- var CANONICAL_GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
5929
+ var CANONICAL_GEMINI_BASE_URL = GEMINI_DEFAULT_BASE_URL;
5718
5930
  function isValidTokenCount2(value) {
5719
5931
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
5720
5932
  }
@@ -6071,10 +6283,7 @@ var GeminiProvider = class {
6071
6283
  axlRateGovernors;
6072
6284
  constructor(options = {}) {
6073
6285
  this.apiKeySource = options.apiKey ?? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY ?? "";
6074
- this.baseUrl = (options.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta").replace(
6075
- /\/$/,
6076
- ""
6077
- );
6286
+ this.baseUrl = (options.baseUrl ?? GEMINI_DEFAULT_BASE_URL).replace(/\/$/, "");
6078
6287
  assertSafeProviderBaseUrl(
6079
6288
  this.baseUrl,
6080
6289
  "Google provider",
@@ -8788,7 +8997,7 @@ var TranscriptionProviderRegistry = class {
8788
8997
  };
8789
8998
 
8790
8999
  // src/context.ts
8791
- import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
9000
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
8792
9001
  import { randomUUID } from "crypto";
8793
9002
  import { z, ZodError as ZodError2 } from "zod";
8794
9003
 
@@ -10973,7 +11182,6 @@ function warnDiagnosticOnce(key, message, silent, docs = DOCS_URL) {
10973
11182
  var warnSchemaDiagnosticOnce = warnDiagnosticOnce;
10974
11183
 
10975
11184
  // src/diagnostics/capture.ts
10976
- import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
10977
11185
  var DEFAULT_MAX_RECORD_BYTES = 256 * 1024;
10978
11186
  var DEFAULT_MAX_RUN_BYTES = 16 * 1024 * 1024;
10979
11187
  var DEFAULT_MAX_QUEUE_BYTES = 1024 * 1024;
@@ -11234,8 +11442,8 @@ function positive(value, fallback) {
11234
11442
  function describe(error) {
11235
11443
  return error instanceof Error ? error.message : String(error);
11236
11444
  }
11237
- var channelStorage = new AsyncLocalStorage2();
11238
- var correlationStorage = new AsyncLocalStorage2();
11445
+ var channelStorage = sharedContext.capture;
11446
+ var correlationStorage = sharedContext.correlation;
11239
11447
  function currentCaptureChannel() {
11240
11448
  return channelStorage.getStore();
11241
11449
  }
@@ -11251,7 +11459,7 @@ function runWithCaptureCorrelation(correlation, fn) {
11251
11459
  const merged = { ...correlationStorage.getStore(), ...correlation };
11252
11460
  return correlationStorage.run(merged, fn);
11253
11461
  }
11254
- var turnStorage = new AsyncLocalStorage2();
11462
+ var turnStorage = sharedContext.turn;
11255
11463
  function currentCaptureTurn() {
11256
11464
  return turnStorage.getStore()?.current;
11257
11465
  }
@@ -11274,6 +11482,34 @@ function toAccountingUsage(usage) {
11274
11482
  cacheWriteTokens: usage.cache_write_tokens ?? 0
11275
11483
  };
11276
11484
  }
11485
+ function safeTiming(response) {
11486
+ try {
11487
+ const timing = response.timing;
11488
+ if (!timing) return void 0;
11489
+ const queuedMs = timing.queuedMs;
11490
+ const retryMs = timing.retryMs;
11491
+ const wireMs = timing.wireMs;
11492
+ if (![queuedMs, retryMs, wireMs].every((value) => Number.isFinite(value) && value >= 0)) {
11493
+ return void 0;
11494
+ }
11495
+ const attempts = timing.attempts;
11496
+ const ttfbMs = timing.ttfbMs;
11497
+ const rateLimitRetries = timing.rateLimitRetries;
11498
+ const firstTokenMs = timing.firstTokenMs;
11499
+ const valid = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
11500
+ return {
11501
+ queuedMs,
11502
+ retryMs,
11503
+ wireMs,
11504
+ ...valid(attempts) ? { attempts } : {},
11505
+ ...valid(ttfbMs) ? { ttfbMs } : {},
11506
+ ...valid(rateLimitRetries) ? { rateLimitRetries } : {},
11507
+ ...valid(firstTokenMs) ? { firstTokenMs } : {}
11508
+ };
11509
+ } catch {
11510
+ return void 0;
11511
+ }
11512
+ }
11277
11513
  function composeLifecycle(caller, onDispatch) {
11278
11514
  return {
11279
11515
  onDispatch() {
@@ -11467,7 +11703,13 @@ var CaptureRecorder = class {
11467
11703
  }
11468
11704
  };
11469
11705
  function openProviderOperation(raw, kind, options) {
11470
- const handle = openOperation({ kind, model: options.model, provider: raw.name });
11706
+ const handle = openOperation({
11707
+ kind,
11708
+ model: options.model,
11709
+ diagnosticModelUri: options.accountingModelUri,
11710
+ provider: raw.name,
11711
+ agentCall: options.accountingModelUri !== void 0
11712
+ });
11471
11713
  if (handle && raw.reportsRequestLifecycle === true) handle.markDispatchObservable();
11472
11714
  return handle;
11473
11715
  }
@@ -11484,7 +11726,8 @@ function createScopedProvider(raw) {
11484
11726
  handle.settle({
11485
11727
  cost: response.cost,
11486
11728
  provenance: response.costProvenance,
11487
- usage: toAccountingUsage(response.usage)
11729
+ usage: toAccountingUsage(response.usage),
11730
+ timing: safeTiming(response)
11488
11731
  });
11489
11732
  recorder?.end({ response });
11490
11733
  return response;
@@ -11539,7 +11782,8 @@ function createScopedProvider(raw) {
11539
11782
  handle?.settle({
11540
11783
  cost: result.value.cost,
11541
11784
  provenance: result.value.costProvenance,
11542
- usage: toAccountingUsage(result.value.usage)
11785
+ usage: toAccountingUsage(result.value.usage),
11786
+ timing: safeTiming(result.value)
11543
11787
  });
11544
11788
  settled = true;
11545
11789
  recorder?.end({
@@ -11972,13 +12216,6 @@ function eventError(error) {
11972
12216
  return { name: "Error", message: "Unknown thrown value" };
11973
12217
  }
11974
12218
  }
11975
- function isToolFailure(error) {
11976
- try {
11977
- return error instanceof ToolFailure;
11978
- } catch {
11979
- return false;
11980
- }
11981
- }
11982
12219
  function cancellation(phase, error, result) {
11983
12220
  let reason;
11984
12221
  try {
@@ -11994,7 +12231,7 @@ function cancellation(phase, error, result) {
11994
12231
  }
11995
12232
  function failed(phase, error, options = {}) {
11996
12233
  let modelMessage;
11997
- if (isToolFailure(error)) {
12234
+ if (isToolFailureError(error)) {
11998
12235
  try {
11999
12236
  modelMessage = typeof error.modelMessage === "string" ? error.modelMessage : void 0;
12000
12237
  } catch {
@@ -12120,7 +12357,7 @@ async function executeAcceptedTool(options) {
12120
12357
  }
12121
12358
  } catch (error) {
12122
12359
  rethrowEventStreamOverflow(error);
12123
- if (error instanceof AdmissionDeniedError) throw error;
12360
+ if (isAdmissionDeniedError(error)) throw error;
12124
12361
  const abort = cancellationError(signal, error);
12125
12362
  if (abort !== void 0) return cancellation("handler", abort);
12126
12363
  return failed("handler", error, { attempts });
@@ -12293,9 +12530,9 @@ function recordToolSpanOutcome(span, outcome) {
12293
12530
  }
12294
12531
 
12295
12532
  // src/context.ts
12296
- var signalStorage = new AsyncLocalStorage3();
12297
- var hardSignalStorage = new AsyncLocalStorage3();
12298
- var timeoutTrackerStorage = new AsyncLocalStorage3();
12533
+ var signalStorage = new AsyncLocalStorage2();
12534
+ var hardSignalStorage = new AsyncLocalStorage2();
12535
+ var timeoutTrackerStorage = new AsyncLocalStorage2();
12299
12536
  var MAX_TIMER_DELAY_MS = 2147483647;
12300
12537
  function composeAbortSignals(...signals) {
12301
12538
  const active = signals.filter((signal) => signal !== void 0);
@@ -12400,7 +12637,7 @@ function parseHumanDecision(value) {
12400
12637
  }
12401
12638
  return invalid3();
12402
12639
  }
12403
- var askStorage = new AsyncLocalStorage3();
12640
+ var askStorage = new AsyncLocalStorage2();
12404
12641
  var inlineSchemaCache = /* @__PURE__ */ new WeakMap();
12405
12642
  function zodToJsonSchema(schema) {
12406
12643
  const cached = inlineSchemaCache.get(schema);
@@ -13682,6 +13919,7 @@ ${appendedSchemaText}`;
13682
13919
  const turnStart = Date.now();
13683
13920
  const chatOptions = {
13684
13921
  model,
13922
+ accountingModelUri: effectiveModelUri,
13685
13923
  temperature: options?.temperature ?? agent2._config.temperature,
13686
13924
  tools: toolDefs.length > 0 ? toolDefs : void 0,
13687
13925
  maxTokens: options?.maxTokens ?? agent2._config.maxTokens ?? 4096,
@@ -16235,7 +16473,7 @@ ${summaryResponse.content}`
16235
16473
  try {
16236
16474
  result = await (transcriptionOperation ? transcriptionOperation.run(() => resolved.provider.transcribe(providerRequest)) : resolved.provider.transcribe(providerRequest));
16237
16475
  } catch (error) {
16238
- if (error instanceof AdmissionDeniedError) {
16476
+ if (isAdmissionDeniedError(error)) {
16239
16477
  transcriptionOperation?.settleFailure();
16240
16478
  throw error;
16241
16479
  }
@@ -16499,13 +16737,13 @@ function agent(config) {
16499
16737
  _config: config,
16500
16738
  _name: defaultName,
16501
16739
  async ask(prompt, options) {
16502
- const registry = new ProviderRegistry();
16740
+ const registry2 = new ProviderRegistry();
16503
16741
  const transcriptionRegistry = new TranscriptionProviderRegistry();
16504
16742
  const ctx = new WorkflowContext({
16505
16743
  input: prompt,
16506
16744
  executionId: randomUUID2(),
16507
16745
  config: {},
16508
- providerRegistry: registry,
16746
+ providerRegistry: registry2,
16509
16747
  transcriptionProviderRegistry: transcriptionRegistry
16510
16748
  });
16511
16749
  return ctx.ask(agentInstance, prompt, options);
@@ -16529,7 +16767,7 @@ function workflow(config) {
16529
16767
  // src/runtime.ts
16530
16768
  import { EventEmitter as EventEmitter2 } from "events";
16531
16769
  import { randomUUID as randomUUID4 } from "crypto";
16532
- import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
16770
+ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
16533
16771
  import { appendFileSync, mkdirSync as mkdirSync2 } from "fs";
16534
16772
  import { dirname } from "path";
16535
16773
 
@@ -18491,7 +18729,7 @@ async function createSpanManager(config) {
18491
18729
  if (!config?.enabled) {
18492
18730
  return new NoopSpanManager();
18493
18731
  }
18494
- const { OTelSpanManager } = await import("./span-manager-WZHJOY7N.js");
18732
+ const { OTelSpanManager } = await import("./span-manager-L4H6ZCRQ.js");
18495
18733
  return OTelSpanManager.create(config);
18496
18734
  }
18497
18735
 
@@ -18718,7 +18956,7 @@ function assertNoRemovedObservationCallbacks(options) {
18718
18956
  `runtime.createContext() no longer accepts ${removed.join(", ")}. Observe ctx.events or use runtime.stream() instead. See docs/migration/stream-first-observation.md.`
18719
18957
  );
18720
18958
  }
18721
- var costScopeStorage = new AsyncLocalStorage4();
18959
+ var costScopeStorage = new AsyncLocalStorage3();
18722
18960
  function forwardAbortSignal(external, internal) {
18723
18961
  if (!external) return () => {
18724
18962
  };
@@ -20436,8 +20674,9 @@ var AxlRuntime = class extends EventEmitter2 {
20436
20674
  * new paid operations once known spend reaches the limit. Already-dispatched
20437
20675
  * work still settles and is still counted.
20438
20676
  *
20439
- * `metadata`, `modelTiming` and `traces` remain EVENT-derived and therefore
20440
- * depend on trace configuration; `accounting` never does.
20677
+ * Model metadata and timing share the settlement scope with accounting, so
20678
+ * they cross runtimes and compatible ESM/CJS copies. Workflow names and
20679
+ * captured traces still come from this runtime's events.
20441
20680
  */
20442
20681
  async trackOutcome(fn, options) {
20443
20682
  const parentScope = costScopeStorage.getStore();
@@ -20446,49 +20685,11 @@ var AxlRuntime = class extends EventEmitter2 {
20446
20685
  trackedIds: /* @__PURE__ */ new Set(),
20447
20686
  parent: parentScope
20448
20687
  };
20449
- const modelCalls = /* @__PURE__ */ new Map();
20450
- const modelTiming = /* @__PURE__ */ new Map();
20451
- const captureTimingSamples = options?.captureTimingSamples === true;
20452
20688
  const workflowCalls = /* @__PURE__ */ new Map();
20453
- const tokens = { input: 0, output: 0, reasoning: 0 };
20454
- let agentCalls = 0;
20455
20689
  const capturedTraces = options?.captureTraces ? [] : void 0;
20456
20690
  const listener = (event) => {
20457
20691
  if (!scope.trackedIds.has(event.executionId)) return;
20458
20692
  scope.totalCost += eventCostContribution(event);
20459
- if (event.type === "agent_call_end") {
20460
- if (event.model) modelCalls.set(event.model, (modelCalls.get(event.model) ?? 0) + 1);
20461
- agentCalls++;
20462
- if (event.tokens) {
20463
- tokens.input += event.tokens.input ?? 0;
20464
- tokens.output += event.tokens.output ?? 0;
20465
- tokens.reasoning += event.tokens.reasoning ?? 0;
20466
- }
20467
- const failed2 = event.data?.error != null;
20468
- if (event.model && event.timing && !failed2) {
20469
- const t = event.timing;
20470
- let bucket = modelTiming.get(event.model);
20471
- if (!bucket) {
20472
- bucket = {
20473
- calls: 0,
20474
- queuedMs: 0,
20475
- retryMs: 0,
20476
- wireMs: 0,
20477
- ...captureTimingSamples ? { samples: [] } : {}
20478
- };
20479
- modelTiming.set(event.model, bucket);
20480
- }
20481
- bucket.calls++;
20482
- bucket.samples?.push(t);
20483
- bucket.queuedMs += t.queuedMs;
20484
- bucket.retryMs += t.retryMs;
20485
- bucket.wireMs += t.wireMs;
20486
- if (t.firstTokenMs != null) {
20487
- bucket.firstTokenMs = (bucket.firstTokenMs ?? 0) + t.firstTokenMs;
20488
- bucket.firstTokenCalls = (bucket.firstTokenCalls ?? 0) + 1;
20489
- }
20490
- }
20491
- }
20492
20693
  if (event.type === "workflow_start" && event.workflow) {
20493
20694
  workflowCalls.set(event.workflow, (workflowCalls.get(event.workflow) ?? 0) + 1);
20494
20695
  }
@@ -20514,9 +20715,14 @@ var AxlRuntime = class extends EventEmitter2 {
20514
20715
  this.on("trace", listener);
20515
20716
  let outcome;
20516
20717
  let accounting;
20718
+ let diagnostics;
20517
20719
  try {
20518
- ({ outcome, accounting } = await runInAccountingScope(
20519
- { purpose: options?.purpose, admission: options?.admission },
20720
+ ({ outcome, accounting, diagnostics } = await runInAccountingScope(
20721
+ {
20722
+ purpose: options?.purpose,
20723
+ admission: options?.admission,
20724
+ captureTimingSamples: options?.captureTimingSamples
20725
+ },
20520
20726
  () => (
20521
20727
  // Capture wraps the cost scope rather than the other way round so a
20522
20728
  // record can never be written for an operation the accounting scope
@@ -20540,13 +20746,13 @@ var AxlRuntime = class extends EventEmitter2 {
20540
20746
  return {
20541
20747
  ...outcome,
20542
20748
  accounting,
20543
- ...modelTiming.size > 0 ? { modelTiming: Object.fromEntries(modelTiming) } : {},
20749
+ ...Object.keys(diagnostics.modelTiming).length > 0 ? { modelTiming: diagnostics.modelTiming } : {},
20544
20750
  ...capturedTraces ? { traces: capturedTraces } : {},
20545
20751
  metadata: {
20546
- models: [...modelCalls.keys()],
20547
- modelCallCounts: modelCalls.size > 0 ? Object.fromEntries(modelCalls) : void 0,
20548
- tokens,
20549
- agentCalls,
20752
+ models: Object.keys(diagnostics.modelCallCounts),
20753
+ modelCallCounts: Object.keys(diagnostics.modelCallCounts).length > 0 ? diagnostics.modelCallCounts : void 0,
20754
+ tokens: diagnostics.tokens,
20755
+ agentCalls: diagnostics.agentCalls,
20550
20756
  workflows: [...workflowCalls.keys()],
20551
20757
  workflowCallCounts: workflowCalls.size > 0 ? Object.fromEntries(workflowCalls) : void 0
20552
20758
  }