@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.cjs CHANGED
@@ -343,6 +343,12 @@ function isEventStreamOverflowError(error) {
343
343
  function rethrowEventStreamOverflow(error) {
344
344
  if (isEventStreamOverflowError(error)) throw error;
345
345
  }
346
+ function isUnrecoverableError(error) {
347
+ return isEventStreamOverflowError(error) || isAdmissionDeniedError(error);
348
+ }
349
+ function rethrowUnrecoverable(error) {
350
+ if (isUnrecoverableError(error)) throw error;
351
+ }
346
352
  function preserveErrorCause(error, cause) {
347
353
  if (cause === void 0 || cause === error || "cause" in error) return error;
348
354
  Object.defineProperty(error, "cause", {
@@ -352,12 +358,14 @@ function preserveErrorCause(error, cause) {
352
358
  });
353
359
  return error;
354
360
  }
361
+ var TOOL_FAILURE_MARKER = /* @__PURE__ */ Symbol.for("axl.toolFailure.v1");
355
362
  var ToolFailure = class extends AxlError {
356
363
  modelMessage;
357
364
  constructor(options) {
358
365
  super(options.code ?? "TOOL_FAILURE", options.message);
359
366
  this.name = "ToolFailure";
360
367
  this.modelMessage = options.modelMessage;
368
+ Object.defineProperty(this, TOOL_FAILURE_MARKER, { value: true });
361
369
  if (options.cause !== void 0) {
362
370
  Object.defineProperty(this, "cause", {
363
371
  configurable: true,
@@ -367,6 +375,13 @@ var ToolFailure = class extends AxlError {
367
375
  }
368
376
  }
369
377
  };
378
+ function isToolFailureError(error) {
379
+ try {
380
+ return error instanceof ToolFailure || typeof error === "object" && error !== null && error[TOOL_FAILURE_MARKER] === true && typeof error.modelMessage === "string";
381
+ } catch {
382
+ return false;
383
+ }
384
+ }
370
385
  var ToolModelOutputError = class extends AxlError {
371
386
  toolName;
372
387
  cause;
@@ -548,7 +563,7 @@ var sharedGuard = typeof installedGuard === "object" && installedGuard !== null
548
563
  var buildPath = typeof __AXL_BUILD_PATH__ === "string" ? __AXL_BUILD_PATH__ : "<source>";
549
564
  var thisCopy = {
550
565
  path: buildPath.startsWith("file:") ? (0, import_node_url.fileURLToPath)(buildPath) : buildPath,
551
- version: true ? "0.24.1" : "unknown"
566
+ version: true ? "0.24.3" : "unknown"
552
567
  };
553
568
  function warnCrossCopy(other, joined) {
554
569
  if (other === thisCopy) return;
@@ -649,10 +664,16 @@ var AccountingScope = class {
649
664
  reasons = /* @__PURE__ */ new Map();
650
665
  breakdown = { generation: 0, judging: 0, external: 0 };
651
666
  provenance = /* @__PURE__ */ new Map();
667
+ modelCalls = /* @__PURE__ */ new Map();
668
+ modelTiming = /* @__PURE__ */ new Map();
669
+ diagnosticTokens = { input: 0, output: 0, reasoning: 0 };
670
+ agentCalls = 0;
671
+ captureTimingSamples;
652
672
  constructor(options) {
653
673
  this.parent = options.parent;
654
674
  this.purpose = options.purpose;
655
675
  this.admission = options.admission;
676
+ this.captureTimingSamples = options.captureTimingSamples === true;
656
677
  }
657
678
  get isFinalized() {
658
679
  return this.finalized;
@@ -681,7 +702,7 @@ var AccountingScope = class {
681
702
  else this.byKind.delete(op.kind);
682
703
  this.denied += 1;
683
704
  }
684
- recordSettled(op, cost, provenance, usage) {
705
+ recordSettled(op, cost, provenance, usage, timing) {
685
706
  this.seen.add(op.id);
686
707
  this.openOps.delete(op.id);
687
708
  this.settled += 1;
@@ -690,13 +711,57 @@ var AccountingScope = class {
690
711
  else this.breakdown[op.purpose] += cost;
691
712
  this.provenance.set(provenance, (this.provenance.get(provenance) ?? 0) + cost);
692
713
  if (usage) this.addUsage(usage);
714
+ this.recordAgentCall(op, usage, timing);
693
715
  }
694
- recordUnknown(op, reason, usage) {
716
+ recordUnknown(op, reason, usage, timing) {
695
717
  this.seen.add(op.id);
696
718
  this.openOps.delete(op.id);
697
719
  this.unknown += 1;
698
720
  this.reasons.set(reason, (this.reasons.get(reason) ?? 0) + 1);
699
721
  if (usage) this.addUsage(usage);
722
+ this.recordAgentCall(op, usage, timing);
723
+ }
724
+ recordAgentCall(op, usage, timing) {
725
+ if (!op.agentCall) return;
726
+ this.agentCalls++;
727
+ const model = op.diagnosticModelUri ?? op.model;
728
+ if (!model) return;
729
+ this.modelCalls.set(model, (this.modelCalls.get(model) ?? 0) + 1);
730
+ if (usage) {
731
+ const finite = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
732
+ this.diagnosticTokens.input += finite(usage.inputTokens);
733
+ this.diagnosticTokens.output += finite(usage.outputTokens);
734
+ this.diagnosticTokens.reasoning += finite(usage.reasoningTokens);
735
+ }
736
+ if (!timing) return;
737
+ let bucket = this.modelTiming.get(model);
738
+ if (!bucket) {
739
+ bucket = {
740
+ calls: 0,
741
+ queuedMs: 0,
742
+ retryMs: 0,
743
+ wireMs: 0,
744
+ ...this.captureTimingSamples ? { samples: [] } : {}
745
+ };
746
+ this.modelTiming.set(model, bucket);
747
+ }
748
+ bucket.calls++;
749
+ bucket.queuedMs += timing.queuedMs;
750
+ bucket.retryMs += timing.retryMs;
751
+ bucket.wireMs += timing.wireMs;
752
+ bucket.samples?.push(timing);
753
+ if (timing.firstTokenMs != null) {
754
+ bucket.firstTokenMs = (bucket.firstTokenMs ?? 0) + timing.firstTokenMs;
755
+ bucket.firstTokenCalls = (bucket.firstTokenCalls ?? 0) + 1;
756
+ }
757
+ }
758
+ toDiagnostics() {
759
+ return {
760
+ modelCallCounts: Object.fromEntries(this.modelCalls),
761
+ tokens: { ...this.diagnosticTokens },
762
+ agentCalls: this.agentCalls,
763
+ modelTiming: Object.fromEntries(this.modelTiming)
764
+ };
700
765
  }
701
766
  hasSeen(id) {
702
767
  return this.seen.has(id);
@@ -768,14 +833,14 @@ function settleOperationUp(start, op, outcome) {
768
833
  if (scope.isFinalized) return;
769
834
  switch (outcome.outcome) {
770
835
  case "settled":
771
- scope.recordSettled(op, outcome.cost, outcome.provenance, outcome.usage);
836
+ scope.recordSettled(op, outcome.cost, outcome.provenance, outcome.usage, outcome.timing);
772
837
  if (scope.admission && !chargedControllers.has(scope.admission)) {
773
838
  chargedControllers.add(scope.admission);
774
839
  recordSpend(scope.admission, outcome.cost);
775
840
  }
776
841
  break;
777
842
  case "unknown":
778
- scope.recordUnknown(op, outcome.reason, outcome.usage);
843
+ scope.recordUnknown(op, outcome.reason, outcome.usage, outcome.timing);
779
844
  break;
780
845
  case "retracted":
781
846
  scope.recordRetracted(op);
@@ -827,6 +892,8 @@ function openOperation(descriptor) {
827
892
  kind: descriptor.kind,
828
893
  purpose: scope.purpose,
829
894
  model: descriptor.model,
895
+ diagnosticModelUri: descriptor.diagnosticModelUri,
896
+ agentCall: descriptor.agentCall === true,
830
897
  dispatched: false,
831
898
  dispatchObservable: false
832
899
  };
@@ -867,25 +934,31 @@ function openOperation(descriptor) {
867
934
  outcome: "settled",
868
935
  cost: input.cost,
869
936
  provenance: input.provenance ?? "adapter_reported",
870
- usage: input.usage
937
+ usage: input.usage,
938
+ timing: input.timing
871
939
  });
872
940
  return;
873
941
  }
874
942
  if (input.cost !== void 0 || input.usage !== void 0) {
875
- finish({ outcome: "unknown", reason: "unpriced_model", usage: input.usage });
943
+ finish({
944
+ outcome: "unknown",
945
+ reason: "unpriced_model",
946
+ usage: input.usage,
947
+ timing: input.timing
948
+ });
876
949
  return;
877
950
  }
878
- handle.settleFailure();
951
+ handle.settleFailure(void 0, input.timing);
879
952
  },
880
953
  settleUnknown(reason, usage) {
881
954
  finish({ outcome: "unknown", reason, usage });
882
955
  },
883
- settleFailure(usage) {
956
+ settleFailure(usage, timing) {
884
957
  if (op.dispatchObservable && !op.dispatched) {
885
- finish({ outcome: "settled", cost: 0, provenance: "adapter_reported", usage });
958
+ finish({ outcome: "settled", cost: 0, provenance: "adapter_reported", usage, timing });
886
959
  return;
887
960
  }
888
- finish({ outcome: "unknown", reason: "usage_missing", usage });
961
+ finish({ outcome: "unknown", reason: "usage_missing", usage, timing });
889
962
  },
890
963
  run(fn) {
891
964
  return runWithDispatchAdmission(handle.dispatchAdmission, fn);
@@ -898,7 +971,8 @@ async function runInAccountingScope(options, fn) {
898
971
  const scope = new AccountingScope({
899
972
  parent,
900
973
  purpose: options.purpose ?? parent?.purpose ?? "generation",
901
- admission: options.admission
974
+ admission: options.admission,
975
+ captureTimingSamples: options.captureTimingSamples
902
976
  });
903
977
  const inheritedGuard = sharedGuard.storage.getStore();
904
978
  const enclosingGuard = inheritedGuard && (inheritedGuard.isActive?.() ?? true) ? inheritedGuard : void 0;
@@ -916,7 +990,8 @@ async function runInAccountingScope(options, fn) {
916
990
  "The active @axlsdk/axl accounting scope uses an incompatible loaded copy; paid work was refused before dispatch."
917
991
  )
918
992
  },
919
- accounting: scope.toAccounting()
993
+ accounting: scope.toAccounting(),
994
+ diagnostics: scope.toDiagnostics()
920
995
  };
921
996
  }
922
997
  const markUninstrumented = () => {
@@ -940,10 +1015,18 @@ async function runInAccountingScope(options, fn) {
940
1015
  () => accountingStorage.run(scope, fn)
941
1016
  );
942
1017
  scope.finalize();
943
- return { outcome: { status: "fulfilled", value }, accounting: scope.toAccounting() };
1018
+ return {
1019
+ outcome: { status: "fulfilled", value },
1020
+ accounting: scope.toAccounting(),
1021
+ diagnostics: scope.toDiagnostics()
1022
+ };
944
1023
  } catch (error) {
945
1024
  scope.finalize();
946
- return { outcome: { status: "rejected", error }, accounting: scope.toAccounting() };
1025
+ return {
1026
+ outcome: { status: "rejected", error },
1027
+ accounting: scope.toAccounting(),
1028
+ diagnostics: scope.toDiagnostics()
1029
+ };
947
1030
  }
948
1031
  }
949
1032
  function validateReportedCost(amountUsd) {
@@ -1322,10 +1405,12 @@ function toolArgumentIssues(error) {
1322
1405
  }
1323
1406
  return [{ path: [], code: "invalid_arguments", message: "Tool arguments are invalid." }];
1324
1407
  }
1325
- var TOOL_INTERNALS = /* @__PURE__ */ new WeakMap();
1408
+ var TOOL_INTERNALS = /* @__PURE__ */ Symbol.for("axl.tool.internals.v1");
1326
1409
  function getToolInternals(tool2) {
1327
- const internals = TOOL_INTERNALS.get(tool2);
1328
- if (!internals) throw new Error(`Tool "${tool2.name}" was not created by tool()`);
1410
+ const internals = tool2[TOOL_INTERNALS];
1411
+ if (!internals || typeof internals.prepareInput !== "function" || typeof internals.executePrepared !== "function") {
1412
+ throw new Error(`Tool "${tool2.name}" was not created by a compatible tool()`);
1413
+ }
1329
1414
  return internals;
1330
1415
  }
1331
1416
  function prepareToolInput(tool2, input) {
@@ -1372,8 +1457,7 @@ function tool(config) {
1372
1457
  } catch (err) {
1373
1458
  if (options?.signal?.aborted) options.signal.throwIfAborted();
1374
1459
  if (isAbortError(err)) throw err;
1375
- if (isAdmissionDeniedError(err)) throw err;
1376
- rethrowEventStreamOverflow(err);
1460
+ rethrowUnrecoverable(err);
1377
1461
  lastError = err instanceof Error ? err : new Error(String(err));
1378
1462
  if (attempt === maxAttempts) break;
1379
1463
  if (retryPolicy.on && !retryPolicy.on(lastError)) {
@@ -1426,9 +1510,11 @@ function tool(config) {
1426
1510
  },
1427
1511
  _execute: execute
1428
1512
  };
1429
- TOOL_INTERNALS.set(instance, {
1430
- prepareInput,
1431
- executePrepared
1513
+ Object.defineProperty(instance, TOOL_INTERNALS, {
1514
+ value: {
1515
+ prepareInput,
1516
+ executePrepared
1517
+ }
1432
1518
  });
1433
1519
  return instance;
1434
1520
  }
@@ -1459,6 +1545,7 @@ async function resolveApiKey(source) {
1459
1545
  // src/providers/default-endpoints.ts
1460
1546
  var OPENAI_DEFAULT_BASE_URL = "https://api.openai.com/v1";
1461
1547
  var ANTHROPIC_DEFAULT_BASE_URL = "https://api.anthropic.com/v1";
1548
+ var GEMINI_DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
1462
1549
 
1463
1550
  // src/providers/rate-limiter.ts
1464
1551
  function sanitizeRateLimitConfig(config) {
@@ -1856,12 +1943,36 @@ var anthropicQuotaDialect = makeDialect(
1856
1943
  return "unknown";
1857
1944
  }
1858
1945
  );
1946
+ var geminiQuotaDialect = makeDialect([], (error) => {
1947
+ if (error.code === "quota_exceeded") return "spend_cap";
1948
+ if (Array.isArray(error.details)) {
1949
+ for (const detail of error.details) {
1950
+ if (typeof detail !== "object" || detail === null) continue;
1951
+ const violations = detail.violations;
1952
+ if (!Array.isArray(violations)) continue;
1953
+ if (violations.some((violation) => {
1954
+ if (typeof violation !== "object" || violation === null) return false;
1955
+ const id = violation.quotaId;
1956
+ return typeof id === "string" && /perday|daily|spend|billing/i.test(id);
1957
+ }))
1958
+ return "spend_cap";
1959
+ }
1960
+ }
1961
+ const message = error.message;
1962
+ if (typeof message === "string" && /monthly spending cap|spend(?:-based|ing)? (?:cap|limit)|prepay(?:ment)? credits? (?:are )?depleted/i.test(
1963
+ message
1964
+ )) {
1965
+ return "spend_cap";
1966
+ }
1967
+ return "unknown";
1968
+ });
1859
1969
  var DIALECTS = /* @__PURE__ */ new Map([
1860
1970
  ["openai", { origin: new URL(OPENAI_DEFAULT_BASE_URL).origin, dialect: openaiQuotaDialect }],
1861
1971
  [
1862
1972
  "anthropic",
1863
1973
  { origin: new URL(ANTHROPIC_DEFAULT_BASE_URL).origin, dialect: anthropicQuotaDialect }
1864
- ]
1974
+ ],
1975
+ ["google", { origin: new URL(GEMINI_DEFAULT_BASE_URL).origin, dialect: geminiQuotaDialect }]
1865
1976
  ]);
1866
1977
  function quotaDialectFor(family, origin) {
1867
1978
  const entry = DIALECTS.get(family);
@@ -6052,7 +6163,7 @@ var GEMINI_PRICING = {
6052
6163
  },
6053
6164
  "gemini-3.8-flash": { input: 75e-8, cached: 75e-9, output: 375e-8 }
6054
6165
  };
6055
- var CANONICAL_GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
6166
+ var CANONICAL_GEMINI_BASE_URL = GEMINI_DEFAULT_BASE_URL;
6056
6167
  function isValidTokenCount2(value) {
6057
6168
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
6058
6169
  }
@@ -6409,10 +6520,7 @@ var GeminiProvider = class {
6409
6520
  axlRateGovernors;
6410
6521
  constructor(options = {}) {
6411
6522
  this.apiKeySource = options.apiKey ?? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY ?? "";
6412
- this.baseUrl = (options.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta").replace(
6413
- /\/$/,
6414
- ""
6415
- );
6523
+ this.baseUrl = (options.baseUrl ?? GEMINI_DEFAULT_BASE_URL).replace(/\/$/, "");
6416
6524
  assertSafeProviderBaseUrl(
6417
6525
  this.baseUrl,
6418
6526
  "Google provider",
@@ -11611,6 +11719,34 @@ function toAccountingUsage(usage) {
11611
11719
  cacheWriteTokens: usage.cache_write_tokens ?? 0
11612
11720
  };
11613
11721
  }
11722
+ function safeTiming(response) {
11723
+ try {
11724
+ const timing = response.timing;
11725
+ if (!timing) return void 0;
11726
+ const queuedMs = timing.queuedMs;
11727
+ const retryMs = timing.retryMs;
11728
+ const wireMs = timing.wireMs;
11729
+ if (![queuedMs, retryMs, wireMs].every((value) => Number.isFinite(value) && value >= 0)) {
11730
+ return void 0;
11731
+ }
11732
+ const attempts = timing.attempts;
11733
+ const ttfbMs = timing.ttfbMs;
11734
+ const rateLimitRetries = timing.rateLimitRetries;
11735
+ const firstTokenMs = timing.firstTokenMs;
11736
+ const valid = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
11737
+ return {
11738
+ queuedMs,
11739
+ retryMs,
11740
+ wireMs,
11741
+ ...valid(attempts) ? { attempts } : {},
11742
+ ...valid(ttfbMs) ? { ttfbMs } : {},
11743
+ ...valid(rateLimitRetries) ? { rateLimitRetries } : {},
11744
+ ...valid(firstTokenMs) ? { firstTokenMs } : {}
11745
+ };
11746
+ } catch {
11747
+ return void 0;
11748
+ }
11749
+ }
11614
11750
  function composeLifecycle(caller, onDispatch) {
11615
11751
  return {
11616
11752
  onDispatch() {
@@ -11804,7 +11940,13 @@ var CaptureRecorder = class {
11804
11940
  }
11805
11941
  };
11806
11942
  function openProviderOperation(raw, kind, options) {
11807
- const handle = openOperation({ kind, model: options.model, provider: raw.name });
11943
+ const handle = openOperation({
11944
+ kind,
11945
+ model: options.model,
11946
+ diagnosticModelUri: options.accountingModelUri,
11947
+ provider: raw.name,
11948
+ agentCall: options.accountingModelUri !== void 0
11949
+ });
11808
11950
  if (handle && raw.reportsRequestLifecycle === true) handle.markDispatchObservable();
11809
11951
  return handle;
11810
11952
  }
@@ -11821,7 +11963,8 @@ function createScopedProvider(raw) {
11821
11963
  handle.settle({
11822
11964
  cost: response.cost,
11823
11965
  provenance: response.costProvenance,
11824
- usage: toAccountingUsage(response.usage)
11966
+ usage: toAccountingUsage(response.usage),
11967
+ timing: safeTiming(response)
11825
11968
  });
11826
11969
  recorder?.end({ response });
11827
11970
  return response;
@@ -11876,7 +12019,8 @@ function createScopedProvider(raw) {
11876
12019
  handle?.settle({
11877
12020
  cost: result.value.cost,
11878
12021
  provenance: result.value.costProvenance,
11879
- usage: toAccountingUsage(result.value.usage)
12022
+ usage: toAccountingUsage(result.value.usage),
12023
+ timing: safeTiming(result.value)
11880
12024
  });
11881
12025
  settled = true;
11882
12026
  recorder?.end({
@@ -12309,13 +12453,6 @@ function eventError(error) {
12309
12453
  return { name: "Error", message: "Unknown thrown value" };
12310
12454
  }
12311
12455
  }
12312
- function isToolFailure(error) {
12313
- try {
12314
- return error instanceof ToolFailure;
12315
- } catch {
12316
- return false;
12317
- }
12318
- }
12319
12456
  function cancellation(phase, error, result) {
12320
12457
  let reason;
12321
12458
  try {
@@ -12331,7 +12468,7 @@ function cancellation(phase, error, result) {
12331
12468
  }
12332
12469
  function failed(phase, error, options = {}) {
12333
12470
  let modelMessage;
12334
- if (isToolFailure(error)) {
12471
+ if (isToolFailureError(error)) {
12335
12472
  try {
12336
12473
  modelMessage = typeof error.modelMessage === "string" ? error.modelMessage : void 0;
12337
12474
  } catch {
@@ -12456,8 +12593,7 @@ async function executeAcceptedTool(options) {
12456
12593
  }
12457
12594
  }
12458
12595
  } catch (error) {
12459
- rethrowEventStreamOverflow(error);
12460
- if (isAdmissionDeniedError(error)) throw error;
12596
+ rethrowUnrecoverable(error);
12461
12597
  const abort = cancellationError(signal, error);
12462
12598
  if (abort !== void 0) return cancellation("handler", abort);
12463
12599
  return failed("handler", error, { attempts });
@@ -14019,6 +14155,7 @@ ${appendedSchemaText}`;
14019
14155
  const turnStart = Date.now();
14020
14156
  const chatOptions = {
14021
14157
  model,
14158
+ accountingModelUri: effectiveModelUri,
14022
14159
  temperature: options?.temperature ?? agent2._config.temperature,
14023
14160
  tools: toolDefs.length > 0 ? toolDefs : void 0,
14024
14161
  maxTokens: options?.maxTokens ?? agent2._config.maxTokens ?? 4096,
@@ -14722,7 +14859,8 @@ ${appendedSchemaText}`;
14722
14859
  metadata: this.metadata
14723
14860
  });
14724
14861
  } catch (err) {
14725
- rethrowEventStreamOverflow(err);
14862
+ rethrowUnrecoverable(err);
14863
+ if (this.currentSignal?.aborted) throw err;
14726
14864
  validateErr = err;
14727
14865
  const reason = err instanceof Error ? err.message : String(err);
14728
14866
  validateResult = { valid: false, reason: `Validator error: ${reason}` };
@@ -15313,15 +15451,13 @@ ${summaryResponse.content}`
15313
15451
  reject(new QuorumNotMet(quorum, successCount, results));
15314
15452
  }
15315
15453
  }).catch((err) => {
15316
- if (isEventStreamOverflowError(err)) {
15317
- if (!settled) {
15318
- settled = true;
15319
- controller.abort();
15320
- reject(err);
15321
- }
15454
+ if (settled) return;
15455
+ if (isUnrecoverableError(err) || parentSignal2?.aborted) {
15456
+ settled = true;
15457
+ controller.abort();
15458
+ reject(err);
15322
15459
  return;
15323
15460
  }
15324
- if (settled) return;
15325
15461
  const isAbort = err instanceof DOMException && err.name === "AbortError";
15326
15462
  if (isAbort) {
15327
15463
  completedCount++;
@@ -15344,7 +15480,8 @@ ${summaryResponse.content}`
15344
15480
  const parentSignal = this.currentSignal;
15345
15481
  const promises = Array.from({ length: n }, (_, i) => {
15346
15482
  const run = () => fn(i).then((value) => ({ ok: true, value })).catch((err) => {
15347
- rethrowEventStreamOverflow(err);
15483
+ rethrowUnrecoverable(err);
15484
+ if (parentSignal?.aborted) throw err;
15348
15485
  return {
15349
15486
  ok: false,
15350
15487
  error: err instanceof Error ? err.message : String(err)
@@ -15468,6 +15605,7 @@ ${summaryResponse.content}`
15468
15605
  async verify(fn, schema, options) {
15469
15606
  const maxRetries = options?.retries ?? 3;
15470
15607
  let lastRetry = void 0;
15608
+ const scopeSignal = this.currentSignal;
15471
15609
  const emitVerifyOutcome = (passed, attempts, lastError) => {
15472
15610
  this.emitEvent({
15473
15611
  type: "verify",
@@ -15490,25 +15628,24 @@ ${summaryResponse.content}`
15490
15628
  try {
15491
15629
  validateResult = await options.validate(parsed, { metadata: this.metadata });
15492
15630
  } catch (err) {
15493
- rethrowEventStreamOverflow(err);
15631
+ rethrowUnrecoverable(err);
15632
+ if (scopeSignal?.aborted) throw err;
15494
15633
  const reason = err instanceof Error ? err.message : String(err);
15495
15634
  validateResult = { valid: false, reason: `Validator error: ${reason}` };
15496
15635
  }
15497
15636
  if (!validateResult.valid) {
15637
+ scopeSignal?.throwIfAborted();
15498
15638
  const errorMsg = validateResult.reason ?? "Validation failed";
15499
15639
  lastRetry = { error: errorMsg, output: rawOutput, parsed };
15500
- if (attempt === maxRetries) {
15501
- emitVerifyOutcome(false, attempt + 1, errorMsg);
15502
- if (options?.fallback !== void 0) return options.fallback;
15503
- throw new ValidationError(parsed, errorMsg, maxRetries);
15504
- }
15640
+ if (attempt === maxRetries) throw new ValidationError(parsed, errorMsg, maxRetries);
15505
15641
  continue;
15506
15642
  }
15507
15643
  }
15508
15644
  emitVerifyOutcome(true, attempt + 1);
15509
15645
  return parsed;
15510
15646
  } catch (err) {
15511
- rethrowEventStreamOverflow(err);
15647
+ rethrowUnrecoverable(err);
15648
+ if (scopeSignal?.aborted) throw err;
15512
15649
  if (err instanceof ValidationError) {
15513
15650
  lastRetry = {
15514
15651
  error: err.reason,
@@ -15575,7 +15712,8 @@ ${summaryResponse.content}`
15575
15712
  const unpriced = this.budgetContext.unpriced;
15576
15713
  return { value, budgetExceeded: exceeded, totalCost, unpriced };
15577
15714
  } catch (err) {
15578
- rethrowEventStreamOverflow(err);
15715
+ rethrowUnrecoverable(err);
15716
+ if (parentSignal?.aborted) throw err;
15579
15717
  if (this.budgetContext.exceeded) {
15580
15718
  return {
15581
15719
  value: null,
@@ -15584,14 +15722,6 @@ ${summaryResponse.content}`
15584
15722
  unpriced: this.budgetContext.unpriced
15585
15723
  };
15586
15724
  }
15587
- if (err instanceof DOMException && err.name === "AbortError" && controller) {
15588
- return {
15589
- value: null,
15590
- budgetExceeded: true,
15591
- totalCost: this.budgetContext.totalCost,
15592
- unpriced: this.budgetContext.unpriced
15593
- };
15594
- }
15595
15725
  throw err;
15596
15726
  } finally {
15597
15727
  if (parentBudget) {
@@ -15707,7 +15837,8 @@ ${summaryResponse.content}`
15707
15837
  return;
15708
15838
  }
15709
15839
  } catch (err) {
15710
- rethrowEventStreamOverflow(err);
15840
+ rethrowUnrecoverable(err);
15841
+ if (parentSignal?.aborted) throw err;
15711
15842
  remaining--;
15712
15843
  lastError = err instanceof Error ? err : new Error(`Validator error: ${String(err)}`);
15713
15844
  if (remaining === 0 && !settled) {
@@ -15727,7 +15858,7 @@ ${summaryResponse.content}`
15727
15858
  controller.abort();
15728
15859
  resolve2(value);
15729
15860
  }).catch((err) => {
15730
- if (isEventStreamOverflowError(err)) {
15861
+ if (isUnrecoverableError(err)) {
15731
15862
  if (!settled) {
15732
15863
  settled = true;
15733
15864
  controller.abort();
@@ -15736,6 +15867,12 @@ ${summaryResponse.content}`
15736
15867
  throw err;
15737
15868
  }
15738
15869
  if (settled) return;
15870
+ if (parentSignal?.aborted) {
15871
+ settled = true;
15872
+ controller.abort();
15873
+ reject(err);
15874
+ return;
15875
+ }
15739
15876
  if (err instanceof DOMException && err.name === "AbortError") {
15740
15877
  remaining--;
15741
15878
  if (remaining === 0 && !settled) {
@@ -15794,7 +15931,7 @@ ${summaryResponse.content}`
15794
15931
  results[idx] = { ok: true, value };
15795
15932
  successCount++;
15796
15933
  } catch (err) {
15797
- if (isEventStreamOverflowError(err)) {
15934
+ if (isUnrecoverableError(err)) {
15798
15935
  settled = true;
15799
15936
  controller?.abort();
15800
15937
  reject(err);
@@ -15803,6 +15940,12 @@ ${summaryResponse.content}`
15803
15940
  if (settled) {
15804
15941
  return;
15805
15942
  }
15943
+ if (parentSignal?.aborted) {
15944
+ settled = true;
15945
+ controller?.abort();
15946
+ reject(err);
15947
+ return;
15948
+ }
15806
15949
  if (err instanceof DOMException && err.name === "AbortError" && controller?.signal.aborted) {
15807
15950
  completedCount++;
15808
15951
  return;
@@ -20764,8 +20907,9 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
20764
20907
  * new paid operations once known spend reaches the limit. Already-dispatched
20765
20908
  * work still settles and is still counted.
20766
20909
  *
20767
- * `metadata`, `modelTiming` and `traces` remain EVENT-derived and therefore
20768
- * depend on trace configuration; `accounting` never does.
20910
+ * Model metadata and timing share the settlement scope with accounting, so
20911
+ * they cross runtimes and compatible ESM/CJS copies. Workflow names and
20912
+ * captured traces still come from this runtime's events.
20769
20913
  */
20770
20914
  async trackOutcome(fn, options) {
20771
20915
  const parentScope = costScopeStorage.getStore();
@@ -20774,49 +20918,11 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
20774
20918
  trackedIds: /* @__PURE__ */ new Set(),
20775
20919
  parent: parentScope
20776
20920
  };
20777
- const modelCalls = /* @__PURE__ */ new Map();
20778
- const modelTiming = /* @__PURE__ */ new Map();
20779
- const captureTimingSamples = options?.captureTimingSamples === true;
20780
20921
  const workflowCalls = /* @__PURE__ */ new Map();
20781
- const tokens = { input: 0, output: 0, reasoning: 0 };
20782
- let agentCalls = 0;
20783
20922
  const capturedTraces = options?.captureTraces ? [] : void 0;
20784
20923
  const listener = (event) => {
20785
20924
  if (!scope.trackedIds.has(event.executionId)) return;
20786
20925
  scope.totalCost += eventCostContribution(event);
20787
- if (event.type === "agent_call_end") {
20788
- if (event.model) modelCalls.set(event.model, (modelCalls.get(event.model) ?? 0) + 1);
20789
- agentCalls++;
20790
- if (event.tokens) {
20791
- tokens.input += event.tokens.input ?? 0;
20792
- tokens.output += event.tokens.output ?? 0;
20793
- tokens.reasoning += event.tokens.reasoning ?? 0;
20794
- }
20795
- const failed2 = event.data?.error != null;
20796
- if (event.model && event.timing && !failed2) {
20797
- const t = event.timing;
20798
- let bucket = modelTiming.get(event.model);
20799
- if (!bucket) {
20800
- bucket = {
20801
- calls: 0,
20802
- queuedMs: 0,
20803
- retryMs: 0,
20804
- wireMs: 0,
20805
- ...captureTimingSamples ? { samples: [] } : {}
20806
- };
20807
- modelTiming.set(event.model, bucket);
20808
- }
20809
- bucket.calls++;
20810
- bucket.samples?.push(t);
20811
- bucket.queuedMs += t.queuedMs;
20812
- bucket.retryMs += t.retryMs;
20813
- bucket.wireMs += t.wireMs;
20814
- if (t.firstTokenMs != null) {
20815
- bucket.firstTokenMs = (bucket.firstTokenMs ?? 0) + t.firstTokenMs;
20816
- bucket.firstTokenCalls = (bucket.firstTokenCalls ?? 0) + 1;
20817
- }
20818
- }
20819
- }
20820
20926
  if (event.type === "workflow_start" && event.workflow) {
20821
20927
  workflowCalls.set(event.workflow, (workflowCalls.get(event.workflow) ?? 0) + 1);
20822
20928
  }
@@ -20842,9 +20948,14 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
20842
20948
  this.on("trace", listener);
20843
20949
  let outcome;
20844
20950
  let accounting;
20951
+ let diagnostics;
20845
20952
  try {
20846
- ({ outcome, accounting } = await runInAccountingScope(
20847
- { purpose: options?.purpose, admission: options?.admission },
20953
+ ({ outcome, accounting, diagnostics } = await runInAccountingScope(
20954
+ {
20955
+ purpose: options?.purpose,
20956
+ admission: options?.admission,
20957
+ captureTimingSamples: options?.captureTimingSamples
20958
+ },
20848
20959
  () => (
20849
20960
  // Capture wraps the cost scope rather than the other way round so a
20850
20961
  // record can never be written for an operation the accounting scope
@@ -20868,13 +20979,13 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
20868
20979
  return {
20869
20980
  ...outcome,
20870
20981
  accounting,
20871
- ...modelTiming.size > 0 ? { modelTiming: Object.fromEntries(modelTiming) } : {},
20982
+ ...Object.keys(diagnostics.modelTiming).length > 0 ? { modelTiming: diagnostics.modelTiming } : {},
20872
20983
  ...capturedTraces ? { traces: capturedTraces } : {},
20873
20984
  metadata: {
20874
- models: [...modelCalls.keys()],
20875
- modelCallCounts: modelCalls.size > 0 ? Object.fromEntries(modelCalls) : void 0,
20876
- tokens,
20877
- agentCalls,
20985
+ models: Object.keys(diagnostics.modelCallCounts),
20986
+ modelCallCounts: Object.keys(diagnostics.modelCallCounts).length > 0 ? diagnostics.modelCallCounts : void 0,
20987
+ tokens: diagnostics.tokens,
20988
+ agentCalls: diagnostics.agentCalls,
20878
20989
  workflows: [...workflowCalls.keys()],
20879
20990
  workflowCallCounts: workflowCalls.size > 0 ? Object.fromEntries(workflowCalls) : void 0
20880
20991
  }