@axlsdk/axl 0.24.1 → 0.24.3

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
@@ -111,6 +111,12 @@ function isEventStreamOverflowError(error) {
111
111
  function rethrowEventStreamOverflow(error) {
112
112
  if (isEventStreamOverflowError(error)) throw error;
113
113
  }
114
+ function isUnrecoverableError(error) {
115
+ return isEventStreamOverflowError(error) || isAdmissionDeniedError(error);
116
+ }
117
+ function rethrowUnrecoverable(error) {
118
+ if (isUnrecoverableError(error)) throw error;
119
+ }
114
120
  function preserveErrorCause(error, cause) {
115
121
  if (cause === void 0 || cause === error || "cause" in error) return error;
116
122
  Object.defineProperty(error, "cause", {
@@ -120,12 +126,14 @@ function preserveErrorCause(error, cause) {
120
126
  });
121
127
  return error;
122
128
  }
129
+ var TOOL_FAILURE_MARKER = /* @__PURE__ */ Symbol.for("axl.toolFailure.v1");
123
130
  var ToolFailure = class extends AxlError {
124
131
  modelMessage;
125
132
  constructor(options) {
126
133
  super(options.code ?? "TOOL_FAILURE", options.message);
127
134
  this.name = "ToolFailure";
128
135
  this.modelMessage = options.modelMessage;
136
+ Object.defineProperty(this, TOOL_FAILURE_MARKER, { value: true });
129
137
  if (options.cause !== void 0) {
130
138
  Object.defineProperty(this, "cause", {
131
139
  configurable: true,
@@ -135,6 +143,13 @@ var ToolFailure = class extends AxlError {
135
143
  }
136
144
  }
137
145
  };
146
+ function isToolFailureError(error) {
147
+ try {
148
+ return error instanceof ToolFailure || typeof error === "object" && error !== null && error[TOOL_FAILURE_MARKER] === true && typeof error.modelMessage === "string";
149
+ } catch {
150
+ return false;
151
+ }
152
+ }
138
153
  var ToolModelOutputError = class extends AxlError {
139
154
  toolName;
140
155
  cause;
@@ -316,7 +331,7 @@ var sharedGuard = typeof installedGuard === "object" && installedGuard !== null
316
331
  var buildPath = typeof __AXL_BUILD_PATH__ === "string" ? __AXL_BUILD_PATH__ : "<source>";
317
332
  var thisCopy = {
318
333
  path: buildPath.startsWith("file:") ? fileURLToPath(buildPath) : buildPath,
319
- version: true ? "0.24.1" : "unknown"
334
+ version: true ? "0.24.3" : "unknown"
320
335
  };
321
336
  function warnCrossCopy(other, joined) {
322
337
  if (other === thisCopy) return;
@@ -417,10 +432,16 @@ var AccountingScope = class {
417
432
  reasons = /* @__PURE__ */ new Map();
418
433
  breakdown = { generation: 0, judging: 0, external: 0 };
419
434
  provenance = /* @__PURE__ */ new Map();
435
+ modelCalls = /* @__PURE__ */ new Map();
436
+ modelTiming = /* @__PURE__ */ new Map();
437
+ diagnosticTokens = { input: 0, output: 0, reasoning: 0 };
438
+ agentCalls = 0;
439
+ captureTimingSamples;
420
440
  constructor(options) {
421
441
  this.parent = options.parent;
422
442
  this.purpose = options.purpose;
423
443
  this.admission = options.admission;
444
+ this.captureTimingSamples = options.captureTimingSamples === true;
424
445
  }
425
446
  get isFinalized() {
426
447
  return this.finalized;
@@ -449,7 +470,7 @@ var AccountingScope = class {
449
470
  else this.byKind.delete(op.kind);
450
471
  this.denied += 1;
451
472
  }
452
- recordSettled(op, cost, provenance, usage) {
473
+ recordSettled(op, cost, provenance, usage, timing) {
453
474
  this.seen.add(op.id);
454
475
  this.openOps.delete(op.id);
455
476
  this.settled += 1;
@@ -458,13 +479,57 @@ var AccountingScope = class {
458
479
  else this.breakdown[op.purpose] += cost;
459
480
  this.provenance.set(provenance, (this.provenance.get(provenance) ?? 0) + cost);
460
481
  if (usage) this.addUsage(usage);
482
+ this.recordAgentCall(op, usage, timing);
461
483
  }
462
- recordUnknown(op, reason, usage) {
484
+ recordUnknown(op, reason, usage, timing) {
463
485
  this.seen.add(op.id);
464
486
  this.openOps.delete(op.id);
465
487
  this.unknown += 1;
466
488
  this.reasons.set(reason, (this.reasons.get(reason) ?? 0) + 1);
467
489
  if (usage) this.addUsage(usage);
490
+ this.recordAgentCall(op, usage, timing);
491
+ }
492
+ recordAgentCall(op, usage, timing) {
493
+ if (!op.agentCall) return;
494
+ this.agentCalls++;
495
+ const model = op.diagnosticModelUri ?? op.model;
496
+ if (!model) return;
497
+ this.modelCalls.set(model, (this.modelCalls.get(model) ?? 0) + 1);
498
+ if (usage) {
499
+ const finite = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
500
+ this.diagnosticTokens.input += finite(usage.inputTokens);
501
+ this.diagnosticTokens.output += finite(usage.outputTokens);
502
+ this.diagnosticTokens.reasoning += finite(usage.reasoningTokens);
503
+ }
504
+ if (!timing) return;
505
+ let bucket = this.modelTiming.get(model);
506
+ if (!bucket) {
507
+ bucket = {
508
+ calls: 0,
509
+ queuedMs: 0,
510
+ retryMs: 0,
511
+ wireMs: 0,
512
+ ...this.captureTimingSamples ? { samples: [] } : {}
513
+ };
514
+ this.modelTiming.set(model, bucket);
515
+ }
516
+ bucket.calls++;
517
+ bucket.queuedMs += timing.queuedMs;
518
+ bucket.retryMs += timing.retryMs;
519
+ bucket.wireMs += timing.wireMs;
520
+ bucket.samples?.push(timing);
521
+ if (timing.firstTokenMs != null) {
522
+ bucket.firstTokenMs = (bucket.firstTokenMs ?? 0) + timing.firstTokenMs;
523
+ bucket.firstTokenCalls = (bucket.firstTokenCalls ?? 0) + 1;
524
+ }
525
+ }
526
+ toDiagnostics() {
527
+ return {
528
+ modelCallCounts: Object.fromEntries(this.modelCalls),
529
+ tokens: { ...this.diagnosticTokens },
530
+ agentCalls: this.agentCalls,
531
+ modelTiming: Object.fromEntries(this.modelTiming)
532
+ };
468
533
  }
469
534
  hasSeen(id) {
470
535
  return this.seen.has(id);
@@ -536,14 +601,14 @@ function settleOperationUp(start, op, outcome) {
536
601
  if (scope.isFinalized) return;
537
602
  switch (outcome.outcome) {
538
603
  case "settled":
539
- scope.recordSettled(op, outcome.cost, outcome.provenance, outcome.usage);
604
+ scope.recordSettled(op, outcome.cost, outcome.provenance, outcome.usage, outcome.timing);
540
605
  if (scope.admission && !chargedControllers.has(scope.admission)) {
541
606
  chargedControllers.add(scope.admission);
542
607
  recordSpend(scope.admission, outcome.cost);
543
608
  }
544
609
  break;
545
610
  case "unknown":
546
- scope.recordUnknown(op, outcome.reason, outcome.usage);
611
+ scope.recordUnknown(op, outcome.reason, outcome.usage, outcome.timing);
547
612
  break;
548
613
  case "retracted":
549
614
  scope.recordRetracted(op);
@@ -595,6 +660,8 @@ function openOperation(descriptor) {
595
660
  kind: descriptor.kind,
596
661
  purpose: scope.purpose,
597
662
  model: descriptor.model,
663
+ diagnosticModelUri: descriptor.diagnosticModelUri,
664
+ agentCall: descriptor.agentCall === true,
598
665
  dispatched: false,
599
666
  dispatchObservable: false
600
667
  };
@@ -635,25 +702,31 @@ function openOperation(descriptor) {
635
702
  outcome: "settled",
636
703
  cost: input.cost,
637
704
  provenance: input.provenance ?? "adapter_reported",
638
- usage: input.usage
705
+ usage: input.usage,
706
+ timing: input.timing
639
707
  });
640
708
  return;
641
709
  }
642
710
  if (input.cost !== void 0 || input.usage !== void 0) {
643
- finish({ outcome: "unknown", reason: "unpriced_model", usage: input.usage });
711
+ finish({
712
+ outcome: "unknown",
713
+ reason: "unpriced_model",
714
+ usage: input.usage,
715
+ timing: input.timing
716
+ });
644
717
  return;
645
718
  }
646
- handle.settleFailure();
719
+ handle.settleFailure(void 0, input.timing);
647
720
  },
648
721
  settleUnknown(reason, usage) {
649
722
  finish({ outcome: "unknown", reason, usage });
650
723
  },
651
- settleFailure(usage) {
724
+ settleFailure(usage, timing) {
652
725
  if (op.dispatchObservable && !op.dispatched) {
653
- finish({ outcome: "settled", cost: 0, provenance: "adapter_reported", usage });
726
+ finish({ outcome: "settled", cost: 0, provenance: "adapter_reported", usage, timing });
654
727
  return;
655
728
  }
656
- finish({ outcome: "unknown", reason: "usage_missing", usage });
729
+ finish({ outcome: "unknown", reason: "usage_missing", usage, timing });
657
730
  },
658
731
  run(fn) {
659
732
  return runWithDispatchAdmission(handle.dispatchAdmission, fn);
@@ -666,7 +739,8 @@ async function runInAccountingScope(options, fn) {
666
739
  const scope = new AccountingScope({
667
740
  parent,
668
741
  purpose: options.purpose ?? parent?.purpose ?? "generation",
669
- admission: options.admission
742
+ admission: options.admission,
743
+ captureTimingSamples: options.captureTimingSamples
670
744
  });
671
745
  const inheritedGuard = sharedGuard.storage.getStore();
672
746
  const enclosingGuard = inheritedGuard && (inheritedGuard.isActive?.() ?? true) ? inheritedGuard : void 0;
@@ -684,7 +758,8 @@ async function runInAccountingScope(options, fn) {
684
758
  "The active @axlsdk/axl accounting scope uses an incompatible loaded copy; paid work was refused before dispatch."
685
759
  )
686
760
  },
687
- accounting: scope.toAccounting()
761
+ accounting: scope.toAccounting(),
762
+ diagnostics: scope.toDiagnostics()
688
763
  };
689
764
  }
690
765
  const markUninstrumented = () => {
@@ -708,10 +783,18 @@ async function runInAccountingScope(options, fn) {
708
783
  () => accountingStorage.run(scope, fn)
709
784
  );
710
785
  scope.finalize();
711
- return { outcome: { status: "fulfilled", value }, accounting: scope.toAccounting() };
786
+ return {
787
+ outcome: { status: "fulfilled", value },
788
+ accounting: scope.toAccounting(),
789
+ diagnostics: scope.toDiagnostics()
790
+ };
712
791
  } catch (error) {
713
792
  scope.finalize();
714
- return { outcome: { status: "rejected", error }, accounting: scope.toAccounting() };
793
+ return {
794
+ outcome: { status: "rejected", error },
795
+ accounting: scope.toAccounting(),
796
+ diagnostics: scope.toDiagnostics()
797
+ };
715
798
  }
716
799
  }
717
800
  function validateReportedCost(amountUsd) {
@@ -1090,10 +1173,12 @@ function toolArgumentIssues(error) {
1090
1173
  }
1091
1174
  return [{ path: [], code: "invalid_arguments", message: "Tool arguments are invalid." }];
1092
1175
  }
1093
- var TOOL_INTERNALS = /* @__PURE__ */ new WeakMap();
1176
+ var TOOL_INTERNALS = /* @__PURE__ */ Symbol.for("axl.tool.internals.v1");
1094
1177
  function getToolInternals(tool2) {
1095
- const internals = TOOL_INTERNALS.get(tool2);
1096
- if (!internals) throw new Error(`Tool "${tool2.name}" was not created by tool()`);
1178
+ const internals = tool2[TOOL_INTERNALS];
1179
+ if (!internals || typeof internals.prepareInput !== "function" || typeof internals.executePrepared !== "function") {
1180
+ throw new Error(`Tool "${tool2.name}" was not created by a compatible tool()`);
1181
+ }
1097
1182
  return internals;
1098
1183
  }
1099
1184
  function prepareToolInput(tool2, input) {
@@ -1140,8 +1225,7 @@ function tool(config) {
1140
1225
  } catch (err) {
1141
1226
  if (options?.signal?.aborted) options.signal.throwIfAborted();
1142
1227
  if (isAbortError(err)) throw err;
1143
- if (isAdmissionDeniedError(err)) throw err;
1144
- rethrowEventStreamOverflow(err);
1228
+ rethrowUnrecoverable(err);
1145
1229
  lastError = err instanceof Error ? err : new Error(String(err));
1146
1230
  if (attempt === maxAttempts) break;
1147
1231
  if (retryPolicy.on && !retryPolicy.on(lastError)) {
@@ -1194,9 +1278,11 @@ function tool(config) {
1194
1278
  },
1195
1279
  _execute: execute
1196
1280
  };
1197
- TOOL_INTERNALS.set(instance, {
1198
- prepareInput,
1199
- executePrepared
1281
+ Object.defineProperty(instance, TOOL_INTERNALS, {
1282
+ value: {
1283
+ prepareInput,
1284
+ executePrepared
1285
+ }
1200
1286
  });
1201
1287
  return instance;
1202
1288
  }
@@ -1227,6 +1313,7 @@ async function resolveApiKey(source) {
1227
1313
  // src/providers/default-endpoints.ts
1228
1314
  var OPENAI_DEFAULT_BASE_URL = "https://api.openai.com/v1";
1229
1315
  var ANTHROPIC_DEFAULT_BASE_URL = "https://api.anthropic.com/v1";
1316
+ var GEMINI_DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
1230
1317
 
1231
1318
  // src/providers/rate-limiter.ts
1232
1319
  function sanitizeRateLimitConfig(config) {
@@ -1624,12 +1711,36 @@ var anthropicQuotaDialect = makeDialect(
1624
1711
  return "unknown";
1625
1712
  }
1626
1713
  );
1714
+ var geminiQuotaDialect = makeDialect([], (error) => {
1715
+ if (error.code === "quota_exceeded") return "spend_cap";
1716
+ if (Array.isArray(error.details)) {
1717
+ for (const detail of error.details) {
1718
+ if (typeof detail !== "object" || detail === null) continue;
1719
+ const violations = detail.violations;
1720
+ if (!Array.isArray(violations)) continue;
1721
+ if (violations.some((violation) => {
1722
+ if (typeof violation !== "object" || violation === null) return false;
1723
+ const id = violation.quotaId;
1724
+ return typeof id === "string" && /perday|daily|spend|billing/i.test(id);
1725
+ }))
1726
+ return "spend_cap";
1727
+ }
1728
+ }
1729
+ const message = error.message;
1730
+ if (typeof message === "string" && /monthly spending cap|spend(?:-based|ing)? (?:cap|limit)|prepay(?:ment)? credits? (?:are )?depleted/i.test(
1731
+ message
1732
+ )) {
1733
+ return "spend_cap";
1734
+ }
1735
+ return "unknown";
1736
+ });
1627
1737
  var DIALECTS = /* @__PURE__ */ new Map([
1628
1738
  ["openai", { origin: new URL(OPENAI_DEFAULT_BASE_URL).origin, dialect: openaiQuotaDialect }],
1629
1739
  [
1630
1740
  "anthropic",
1631
1741
  { origin: new URL(ANTHROPIC_DEFAULT_BASE_URL).origin, dialect: anthropicQuotaDialect }
1632
- ]
1742
+ ],
1743
+ ["google", { origin: new URL(GEMINI_DEFAULT_BASE_URL).origin, dialect: geminiQuotaDialect }]
1633
1744
  ]);
1634
1745
  function quotaDialectFor(family, origin) {
1635
1746
  const entry = DIALECTS.get(family);
@@ -5820,7 +5931,7 @@ var GEMINI_PRICING = {
5820
5931
  },
5821
5932
  "gemini-3.8-flash": { input: 75e-8, cached: 75e-9, output: 375e-8 }
5822
5933
  };
5823
- var CANONICAL_GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
5934
+ var CANONICAL_GEMINI_BASE_URL = GEMINI_DEFAULT_BASE_URL;
5824
5935
  function isValidTokenCount2(value) {
5825
5936
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
5826
5937
  }
@@ -6177,10 +6288,7 @@ var GeminiProvider = class {
6177
6288
  axlRateGovernors;
6178
6289
  constructor(options = {}) {
6179
6290
  this.apiKeySource = options.apiKey ?? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY ?? "";
6180
- this.baseUrl = (options.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta").replace(
6181
- /\/$/,
6182
- ""
6183
- );
6291
+ this.baseUrl = (options.baseUrl ?? GEMINI_DEFAULT_BASE_URL).replace(/\/$/, "");
6184
6292
  assertSafeProviderBaseUrl(
6185
6293
  this.baseUrl,
6186
6294
  "Google provider",
@@ -11379,6 +11487,34 @@ function toAccountingUsage(usage) {
11379
11487
  cacheWriteTokens: usage.cache_write_tokens ?? 0
11380
11488
  };
11381
11489
  }
11490
+ function safeTiming(response) {
11491
+ try {
11492
+ const timing = response.timing;
11493
+ if (!timing) return void 0;
11494
+ const queuedMs = timing.queuedMs;
11495
+ const retryMs = timing.retryMs;
11496
+ const wireMs = timing.wireMs;
11497
+ if (![queuedMs, retryMs, wireMs].every((value) => Number.isFinite(value) && value >= 0)) {
11498
+ return void 0;
11499
+ }
11500
+ const attempts = timing.attempts;
11501
+ const ttfbMs = timing.ttfbMs;
11502
+ const rateLimitRetries = timing.rateLimitRetries;
11503
+ const firstTokenMs = timing.firstTokenMs;
11504
+ const valid = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
11505
+ return {
11506
+ queuedMs,
11507
+ retryMs,
11508
+ wireMs,
11509
+ ...valid(attempts) ? { attempts } : {},
11510
+ ...valid(ttfbMs) ? { ttfbMs } : {},
11511
+ ...valid(rateLimitRetries) ? { rateLimitRetries } : {},
11512
+ ...valid(firstTokenMs) ? { firstTokenMs } : {}
11513
+ };
11514
+ } catch {
11515
+ return void 0;
11516
+ }
11517
+ }
11382
11518
  function composeLifecycle(caller, onDispatch) {
11383
11519
  return {
11384
11520
  onDispatch() {
@@ -11572,7 +11708,13 @@ var CaptureRecorder = class {
11572
11708
  }
11573
11709
  };
11574
11710
  function openProviderOperation(raw, kind, options) {
11575
- const handle = openOperation({ kind, model: options.model, provider: raw.name });
11711
+ const handle = openOperation({
11712
+ kind,
11713
+ model: options.model,
11714
+ diagnosticModelUri: options.accountingModelUri,
11715
+ provider: raw.name,
11716
+ agentCall: options.accountingModelUri !== void 0
11717
+ });
11576
11718
  if (handle && raw.reportsRequestLifecycle === true) handle.markDispatchObservable();
11577
11719
  return handle;
11578
11720
  }
@@ -11589,7 +11731,8 @@ function createScopedProvider(raw) {
11589
11731
  handle.settle({
11590
11732
  cost: response.cost,
11591
11733
  provenance: response.costProvenance,
11592
- usage: toAccountingUsage(response.usage)
11734
+ usage: toAccountingUsage(response.usage),
11735
+ timing: safeTiming(response)
11593
11736
  });
11594
11737
  recorder?.end({ response });
11595
11738
  return response;
@@ -11644,7 +11787,8 @@ function createScopedProvider(raw) {
11644
11787
  handle?.settle({
11645
11788
  cost: result.value.cost,
11646
11789
  provenance: result.value.costProvenance,
11647
- usage: toAccountingUsage(result.value.usage)
11790
+ usage: toAccountingUsage(result.value.usage),
11791
+ timing: safeTiming(result.value)
11648
11792
  });
11649
11793
  settled = true;
11650
11794
  recorder?.end({
@@ -12077,13 +12221,6 @@ function eventError(error) {
12077
12221
  return { name: "Error", message: "Unknown thrown value" };
12078
12222
  }
12079
12223
  }
12080
- function isToolFailure(error) {
12081
- try {
12082
- return error instanceof ToolFailure;
12083
- } catch {
12084
- return false;
12085
- }
12086
- }
12087
12224
  function cancellation(phase, error, result) {
12088
12225
  let reason;
12089
12226
  try {
@@ -12099,7 +12236,7 @@ function cancellation(phase, error, result) {
12099
12236
  }
12100
12237
  function failed(phase, error, options = {}) {
12101
12238
  let modelMessage;
12102
- if (isToolFailure(error)) {
12239
+ if (isToolFailureError(error)) {
12103
12240
  try {
12104
12241
  modelMessage = typeof error.modelMessage === "string" ? error.modelMessage : void 0;
12105
12242
  } catch {
@@ -12224,8 +12361,7 @@ async function executeAcceptedTool(options) {
12224
12361
  }
12225
12362
  }
12226
12363
  } catch (error) {
12227
- rethrowEventStreamOverflow(error);
12228
- if (isAdmissionDeniedError(error)) throw error;
12364
+ rethrowUnrecoverable(error);
12229
12365
  const abort = cancellationError(signal, error);
12230
12366
  if (abort !== void 0) return cancellation("handler", abort);
12231
12367
  return failed("handler", error, { attempts });
@@ -13787,6 +13923,7 @@ ${appendedSchemaText}`;
13787
13923
  const turnStart = Date.now();
13788
13924
  const chatOptions = {
13789
13925
  model,
13926
+ accountingModelUri: effectiveModelUri,
13790
13927
  temperature: options?.temperature ?? agent2._config.temperature,
13791
13928
  tools: toolDefs.length > 0 ? toolDefs : void 0,
13792
13929
  maxTokens: options?.maxTokens ?? agent2._config.maxTokens ?? 4096,
@@ -14490,7 +14627,8 @@ ${appendedSchemaText}`;
14490
14627
  metadata: this.metadata
14491
14628
  });
14492
14629
  } catch (err) {
14493
- rethrowEventStreamOverflow(err);
14630
+ rethrowUnrecoverable(err);
14631
+ if (this.currentSignal?.aborted) throw err;
14494
14632
  validateErr = err;
14495
14633
  const reason = err instanceof Error ? err.message : String(err);
14496
14634
  validateResult = { valid: false, reason: `Validator error: ${reason}` };
@@ -15081,15 +15219,13 @@ ${summaryResponse.content}`
15081
15219
  reject(new QuorumNotMet(quorum, successCount, results));
15082
15220
  }
15083
15221
  }).catch((err) => {
15084
- if (isEventStreamOverflowError(err)) {
15085
- if (!settled) {
15086
- settled = true;
15087
- controller.abort();
15088
- reject(err);
15089
- }
15222
+ if (settled) return;
15223
+ if (isUnrecoverableError(err) || parentSignal2?.aborted) {
15224
+ settled = true;
15225
+ controller.abort();
15226
+ reject(err);
15090
15227
  return;
15091
15228
  }
15092
- if (settled) return;
15093
15229
  const isAbort = err instanceof DOMException && err.name === "AbortError";
15094
15230
  if (isAbort) {
15095
15231
  completedCount++;
@@ -15112,7 +15248,8 @@ ${summaryResponse.content}`
15112
15248
  const parentSignal = this.currentSignal;
15113
15249
  const promises = Array.from({ length: n }, (_, i) => {
15114
15250
  const run = () => fn(i).then((value) => ({ ok: true, value })).catch((err) => {
15115
- rethrowEventStreamOverflow(err);
15251
+ rethrowUnrecoverable(err);
15252
+ if (parentSignal?.aborted) throw err;
15116
15253
  return {
15117
15254
  ok: false,
15118
15255
  error: err instanceof Error ? err.message : String(err)
@@ -15236,6 +15373,7 @@ ${summaryResponse.content}`
15236
15373
  async verify(fn, schema, options) {
15237
15374
  const maxRetries = options?.retries ?? 3;
15238
15375
  let lastRetry = void 0;
15376
+ const scopeSignal = this.currentSignal;
15239
15377
  const emitVerifyOutcome = (passed, attempts, lastError) => {
15240
15378
  this.emitEvent({
15241
15379
  type: "verify",
@@ -15258,25 +15396,24 @@ ${summaryResponse.content}`
15258
15396
  try {
15259
15397
  validateResult = await options.validate(parsed, { metadata: this.metadata });
15260
15398
  } catch (err) {
15261
- rethrowEventStreamOverflow(err);
15399
+ rethrowUnrecoverable(err);
15400
+ if (scopeSignal?.aborted) throw err;
15262
15401
  const reason = err instanceof Error ? err.message : String(err);
15263
15402
  validateResult = { valid: false, reason: `Validator error: ${reason}` };
15264
15403
  }
15265
15404
  if (!validateResult.valid) {
15405
+ scopeSignal?.throwIfAborted();
15266
15406
  const errorMsg = validateResult.reason ?? "Validation failed";
15267
15407
  lastRetry = { error: errorMsg, output: rawOutput, parsed };
15268
- if (attempt === maxRetries) {
15269
- emitVerifyOutcome(false, attempt + 1, errorMsg);
15270
- if (options?.fallback !== void 0) return options.fallback;
15271
- throw new ValidationError(parsed, errorMsg, maxRetries);
15272
- }
15408
+ if (attempt === maxRetries) throw new ValidationError(parsed, errorMsg, maxRetries);
15273
15409
  continue;
15274
15410
  }
15275
15411
  }
15276
15412
  emitVerifyOutcome(true, attempt + 1);
15277
15413
  return parsed;
15278
15414
  } catch (err) {
15279
- rethrowEventStreamOverflow(err);
15415
+ rethrowUnrecoverable(err);
15416
+ if (scopeSignal?.aborted) throw err;
15280
15417
  if (err instanceof ValidationError) {
15281
15418
  lastRetry = {
15282
15419
  error: err.reason,
@@ -15343,7 +15480,8 @@ ${summaryResponse.content}`
15343
15480
  const unpriced = this.budgetContext.unpriced;
15344
15481
  return { value, budgetExceeded: exceeded, totalCost, unpriced };
15345
15482
  } catch (err) {
15346
- rethrowEventStreamOverflow(err);
15483
+ rethrowUnrecoverable(err);
15484
+ if (parentSignal?.aborted) throw err;
15347
15485
  if (this.budgetContext.exceeded) {
15348
15486
  return {
15349
15487
  value: null,
@@ -15352,14 +15490,6 @@ ${summaryResponse.content}`
15352
15490
  unpriced: this.budgetContext.unpriced
15353
15491
  };
15354
15492
  }
15355
- if (err instanceof DOMException && err.name === "AbortError" && controller) {
15356
- return {
15357
- value: null,
15358
- budgetExceeded: true,
15359
- totalCost: this.budgetContext.totalCost,
15360
- unpriced: this.budgetContext.unpriced
15361
- };
15362
- }
15363
15493
  throw err;
15364
15494
  } finally {
15365
15495
  if (parentBudget) {
@@ -15475,7 +15605,8 @@ ${summaryResponse.content}`
15475
15605
  return;
15476
15606
  }
15477
15607
  } catch (err) {
15478
- rethrowEventStreamOverflow(err);
15608
+ rethrowUnrecoverable(err);
15609
+ if (parentSignal?.aborted) throw err;
15479
15610
  remaining--;
15480
15611
  lastError = err instanceof Error ? err : new Error(`Validator error: ${String(err)}`);
15481
15612
  if (remaining === 0 && !settled) {
@@ -15495,7 +15626,7 @@ ${summaryResponse.content}`
15495
15626
  controller.abort();
15496
15627
  resolve2(value);
15497
15628
  }).catch((err) => {
15498
- if (isEventStreamOverflowError(err)) {
15629
+ if (isUnrecoverableError(err)) {
15499
15630
  if (!settled) {
15500
15631
  settled = true;
15501
15632
  controller.abort();
@@ -15504,6 +15635,12 @@ ${summaryResponse.content}`
15504
15635
  throw err;
15505
15636
  }
15506
15637
  if (settled) return;
15638
+ if (parentSignal?.aborted) {
15639
+ settled = true;
15640
+ controller.abort();
15641
+ reject(err);
15642
+ return;
15643
+ }
15507
15644
  if (err instanceof DOMException && err.name === "AbortError") {
15508
15645
  remaining--;
15509
15646
  if (remaining === 0 && !settled) {
@@ -15562,7 +15699,7 @@ ${summaryResponse.content}`
15562
15699
  results[idx] = { ok: true, value };
15563
15700
  successCount++;
15564
15701
  } catch (err) {
15565
- if (isEventStreamOverflowError(err)) {
15702
+ if (isUnrecoverableError(err)) {
15566
15703
  settled = true;
15567
15704
  controller?.abort();
15568
15705
  reject(err);
@@ -15571,6 +15708,12 @@ ${summaryResponse.content}`
15571
15708
  if (settled) {
15572
15709
  return;
15573
15710
  }
15711
+ if (parentSignal?.aborted) {
15712
+ settled = true;
15713
+ controller?.abort();
15714
+ reject(err);
15715
+ return;
15716
+ }
15574
15717
  if (err instanceof DOMException && err.name === "AbortError" && controller?.signal.aborted) {
15575
15718
  completedCount++;
15576
15719
  return;
@@ -20541,8 +20684,9 @@ var AxlRuntime = class extends EventEmitter2 {
20541
20684
  * new paid operations once known spend reaches the limit. Already-dispatched
20542
20685
  * work still settles and is still counted.
20543
20686
  *
20544
- * `metadata`, `modelTiming` and `traces` remain EVENT-derived and therefore
20545
- * depend on trace configuration; `accounting` never does.
20687
+ * Model metadata and timing share the settlement scope with accounting, so
20688
+ * they cross runtimes and compatible ESM/CJS copies. Workflow names and
20689
+ * captured traces still come from this runtime's events.
20546
20690
  */
20547
20691
  async trackOutcome(fn, options) {
20548
20692
  const parentScope = costScopeStorage.getStore();
@@ -20551,49 +20695,11 @@ var AxlRuntime = class extends EventEmitter2 {
20551
20695
  trackedIds: /* @__PURE__ */ new Set(),
20552
20696
  parent: parentScope
20553
20697
  };
20554
- const modelCalls = /* @__PURE__ */ new Map();
20555
- const modelTiming = /* @__PURE__ */ new Map();
20556
- const captureTimingSamples = options?.captureTimingSamples === true;
20557
20698
  const workflowCalls = /* @__PURE__ */ new Map();
20558
- const tokens = { input: 0, output: 0, reasoning: 0 };
20559
- let agentCalls = 0;
20560
20699
  const capturedTraces = options?.captureTraces ? [] : void 0;
20561
20700
  const listener = (event) => {
20562
20701
  if (!scope.trackedIds.has(event.executionId)) return;
20563
20702
  scope.totalCost += eventCostContribution(event);
20564
- if (event.type === "agent_call_end") {
20565
- if (event.model) modelCalls.set(event.model, (modelCalls.get(event.model) ?? 0) + 1);
20566
- agentCalls++;
20567
- if (event.tokens) {
20568
- tokens.input += event.tokens.input ?? 0;
20569
- tokens.output += event.tokens.output ?? 0;
20570
- tokens.reasoning += event.tokens.reasoning ?? 0;
20571
- }
20572
- const failed2 = event.data?.error != null;
20573
- if (event.model && event.timing && !failed2) {
20574
- const t = event.timing;
20575
- let bucket = modelTiming.get(event.model);
20576
- if (!bucket) {
20577
- bucket = {
20578
- calls: 0,
20579
- queuedMs: 0,
20580
- retryMs: 0,
20581
- wireMs: 0,
20582
- ...captureTimingSamples ? { samples: [] } : {}
20583
- };
20584
- modelTiming.set(event.model, bucket);
20585
- }
20586
- bucket.calls++;
20587
- bucket.samples?.push(t);
20588
- bucket.queuedMs += t.queuedMs;
20589
- bucket.retryMs += t.retryMs;
20590
- bucket.wireMs += t.wireMs;
20591
- if (t.firstTokenMs != null) {
20592
- bucket.firstTokenMs = (bucket.firstTokenMs ?? 0) + t.firstTokenMs;
20593
- bucket.firstTokenCalls = (bucket.firstTokenCalls ?? 0) + 1;
20594
- }
20595
- }
20596
- }
20597
20703
  if (event.type === "workflow_start" && event.workflow) {
20598
20704
  workflowCalls.set(event.workflow, (workflowCalls.get(event.workflow) ?? 0) + 1);
20599
20705
  }
@@ -20619,9 +20725,14 @@ var AxlRuntime = class extends EventEmitter2 {
20619
20725
  this.on("trace", listener);
20620
20726
  let outcome;
20621
20727
  let accounting;
20728
+ let diagnostics;
20622
20729
  try {
20623
- ({ outcome, accounting } = await runInAccountingScope(
20624
- { purpose: options?.purpose, admission: options?.admission },
20730
+ ({ outcome, accounting, diagnostics } = await runInAccountingScope(
20731
+ {
20732
+ purpose: options?.purpose,
20733
+ admission: options?.admission,
20734
+ captureTimingSamples: options?.captureTimingSamples
20735
+ },
20625
20736
  () => (
20626
20737
  // Capture wraps the cost scope rather than the other way round so a
20627
20738
  // record can never be written for an operation the accounting scope
@@ -20645,13 +20756,13 @@ var AxlRuntime = class extends EventEmitter2 {
20645
20756
  return {
20646
20757
  ...outcome,
20647
20758
  accounting,
20648
- ...modelTiming.size > 0 ? { modelTiming: Object.fromEntries(modelTiming) } : {},
20759
+ ...Object.keys(diagnostics.modelTiming).length > 0 ? { modelTiming: diagnostics.modelTiming } : {},
20649
20760
  ...capturedTraces ? { traces: capturedTraces } : {},
20650
20761
  metadata: {
20651
- models: [...modelCalls.keys()],
20652
- modelCallCounts: modelCalls.size > 0 ? Object.fromEntries(modelCalls) : void 0,
20653
- tokens,
20654
- agentCalls,
20762
+ models: Object.keys(diagnostics.modelCallCounts),
20763
+ modelCallCounts: Object.keys(diagnostics.modelCallCounts).length > 0 ? diagnostics.modelCallCounts : void 0,
20764
+ tokens: diagnostics.tokens,
20765
+ agentCalls: diagnostics.agentCalls,
20655
20766
  workflows: [...workflowCalls.keys()],
20656
20767
  workflowCallCounts: workflowCalls.size > 0 ? Object.fromEntries(workflowCalls) : void 0
20657
20768
  }