@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.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ const __AXL_BUILD_PATH__ = __filename;
1
2
  "use strict";
2
3
  var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
@@ -351,12 +352,14 @@ function preserveErrorCause(error, cause) {
351
352
  });
352
353
  return error;
353
354
  }
355
+ var TOOL_FAILURE_MARKER = /* @__PURE__ */ Symbol.for("axl.toolFailure.v1");
354
356
  var ToolFailure = class extends AxlError {
355
357
  modelMessage;
356
358
  constructor(options) {
357
359
  super(options.code ?? "TOOL_FAILURE", options.message);
358
360
  this.name = "ToolFailure";
359
361
  this.modelMessage = options.modelMessage;
362
+ Object.defineProperty(this, TOOL_FAILURE_MARKER, { value: true });
360
363
  if (options.cause !== void 0) {
361
364
  Object.defineProperty(this, "cause", {
362
365
  configurable: true,
@@ -366,6 +369,13 @@ var ToolFailure = class extends AxlError {
366
369
  }
367
370
  }
368
371
  };
372
+ function isToolFailureError(error) {
373
+ try {
374
+ return error instanceof ToolFailure || typeof error === "object" && error !== null && error[TOOL_FAILURE_MARKER] === true && typeof error.modelMessage === "string";
375
+ } catch {
376
+ return false;
377
+ }
378
+ }
369
379
  var ToolModelOutputError = class extends AxlError {
370
380
  toolName;
371
381
  cause;
@@ -509,8 +519,62 @@ var ValidationError = class extends AxlError {
509
519
  }
510
520
  };
511
521
 
512
- // src/accounting.ts
522
+ // src/shared-context.ts
513
523
  var import_node_async_hooks = require("async_hooks");
524
+ var import_node_url = require("url");
525
+ var PROTOCOL = 1;
526
+ var CONTEXT_KEY = /* @__PURE__ */ Symbol.for(`axl.accounting.context.v${PROTOCOL}`);
527
+ var GUARD_KEY = /* @__PURE__ */ Symbol.for("axl.accounting.scopeGuard");
528
+ function createContext() {
529
+ let operationCounter = 0;
530
+ return {
531
+ protocol: PROTOCOL,
532
+ id: /* @__PURE__ */ Symbol("axl.accounting.context.instance"),
533
+ accounting: new import_node_async_hooks.AsyncLocalStorage(),
534
+ dispatch: new import_node_async_hooks.AsyncLocalStorage(),
535
+ capture: new import_node_async_hooks.AsyncLocalStorage(),
536
+ correlation: new import_node_async_hooks.AsyncLocalStorage(),
537
+ turn: new import_node_async_hooks.AsyncLocalStorage(),
538
+ nextOperationId: () => `op_${++operationCounter}`
539
+ };
540
+ }
541
+ function isStorage(value) {
542
+ return typeof value === "object" && value !== null && typeof value.getStore === "function" && typeof value.run === "function";
543
+ }
544
+ function isCompatible(value) {
545
+ if (typeof value !== "object" || value === null) return false;
546
+ const candidate = value;
547
+ 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";
548
+ }
549
+ var registry = globalThis;
550
+ var installed = registry[CONTEXT_KEY];
551
+ var sharedContext = installed === void 0 ? registry[CONTEXT_KEY] = createContext() : isCompatible(installed) ? installed : createContext();
552
+ var installedGuard = registry[GUARD_KEY];
553
+ var sharedGuard = typeof installedGuard === "object" && installedGuard !== null && isStorage(installedGuard.storage) && installedGuard.warned instanceof WeakMap ? installedGuard : registry[GUARD_KEY] = {
554
+ storage: new import_node_async_hooks.AsyncLocalStorage(),
555
+ warned: /* @__PURE__ */ new WeakMap()
556
+ };
557
+ var buildPath = typeof __AXL_BUILD_PATH__ === "string" ? __AXL_BUILD_PATH__ : "<source>";
558
+ var thisCopy = {
559
+ path: buildPath.startsWith("file:") ? (0, import_node_url.fileURLToPath)(buildPath) : buildPath,
560
+ version: true ? "0.24.2" : "unknown"
561
+ };
562
+ function warnCrossCopy(other, joined) {
563
+ if (other === thisCopy) return;
564
+ if (sharedGuard.warned.get(other)?.has(thisCopy)) return;
565
+ const fromThis = sharedGuard.warned.get(thisCopy) ?? /* @__PURE__ */ new WeakSet();
566
+ if (fromThis.has(other)) return;
567
+ fromThis.add(other);
568
+ sharedGuard.warned.set(thisCopy, fromThis);
569
+ try {
570
+ console.warn(
571
+ `[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.")
572
+ );
573
+ } catch {
574
+ }
575
+ }
576
+
577
+ // src/accounting.ts
514
578
  var RECORD_SPEND = /* @__PURE__ */ Symbol.for("axl.accounting.recordSpend");
515
579
  var AdmissionController = class {
516
580
  /** The configured USD threshold. Admission closes at `knownSpend >= limit`. */
@@ -572,10 +636,8 @@ function emptyUsage() {
572
636
  function isUsableCost(cost) {
573
637
  return typeof cost === "number" && Number.isFinite(cost) && cost >= 0;
574
638
  }
575
- var operationCounter = 0;
576
639
  function nextOperationId() {
577
- operationCounter += 1;
578
- return `op_${operationCounter}`;
640
+ return sharedContext.nextOperationId();
579
641
  }
580
642
  var AccountingScope = class {
581
643
  parent;
@@ -596,10 +658,16 @@ var AccountingScope = class {
596
658
  reasons = /* @__PURE__ */ new Map();
597
659
  breakdown = { generation: 0, judging: 0, external: 0 };
598
660
  provenance = /* @__PURE__ */ new Map();
661
+ modelCalls = /* @__PURE__ */ new Map();
662
+ modelTiming = /* @__PURE__ */ new Map();
663
+ diagnosticTokens = { input: 0, output: 0, reasoning: 0 };
664
+ agentCalls = 0;
665
+ captureTimingSamples;
599
666
  constructor(options) {
600
667
  this.parent = options.parent;
601
668
  this.purpose = options.purpose;
602
669
  this.admission = options.admission;
670
+ this.captureTimingSamples = options.captureTimingSamples === true;
603
671
  }
604
672
  get isFinalized() {
605
673
  return this.finalized;
@@ -613,6 +681,11 @@ var AccountingScope = class {
613
681
  recordDenied() {
614
682
  this.denied += 1;
615
683
  }
684
+ /** A foreign protocol attempted work we cannot measure. Mark every live ancestor. */
685
+ markUninstrumented() {
686
+ if (this.finalized || this.reasons.has("uninstrumented")) return;
687
+ this.reasons.set("uninstrumented", 1);
688
+ }
616
689
  /** An already-open operation refused at the dispatch check: retract it. */
617
690
  recordRetracted(op) {
618
691
  this.seen.add(op.id);
@@ -623,7 +696,7 @@ var AccountingScope = class {
623
696
  else this.byKind.delete(op.kind);
624
697
  this.denied += 1;
625
698
  }
626
- recordSettled(op, cost, provenance, usage) {
699
+ recordSettled(op, cost, provenance, usage, timing) {
627
700
  this.seen.add(op.id);
628
701
  this.openOps.delete(op.id);
629
702
  this.settled += 1;
@@ -632,13 +705,57 @@ var AccountingScope = class {
632
705
  else this.breakdown[op.purpose] += cost;
633
706
  this.provenance.set(provenance, (this.provenance.get(provenance) ?? 0) + cost);
634
707
  if (usage) this.addUsage(usage);
708
+ this.recordAgentCall(op, usage, timing);
635
709
  }
636
- recordUnknown(op, reason, usage) {
710
+ recordUnknown(op, reason, usage, timing) {
637
711
  this.seen.add(op.id);
638
712
  this.openOps.delete(op.id);
639
713
  this.unknown += 1;
640
714
  this.reasons.set(reason, (this.reasons.get(reason) ?? 0) + 1);
641
715
  if (usage) this.addUsage(usage);
716
+ this.recordAgentCall(op, usage, timing);
717
+ }
718
+ recordAgentCall(op, usage, timing) {
719
+ if (!op.agentCall) return;
720
+ this.agentCalls++;
721
+ const model = op.diagnosticModelUri ?? op.model;
722
+ if (!model) return;
723
+ this.modelCalls.set(model, (this.modelCalls.get(model) ?? 0) + 1);
724
+ if (usage) {
725
+ const finite = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
726
+ this.diagnosticTokens.input += finite(usage.inputTokens);
727
+ this.diagnosticTokens.output += finite(usage.outputTokens);
728
+ this.diagnosticTokens.reasoning += finite(usage.reasoningTokens);
729
+ }
730
+ if (!timing) return;
731
+ let bucket = this.modelTiming.get(model);
732
+ if (!bucket) {
733
+ bucket = {
734
+ calls: 0,
735
+ queuedMs: 0,
736
+ retryMs: 0,
737
+ wireMs: 0,
738
+ ...this.captureTimingSamples ? { samples: [] } : {}
739
+ };
740
+ this.modelTiming.set(model, bucket);
741
+ }
742
+ bucket.calls++;
743
+ bucket.queuedMs += timing.queuedMs;
744
+ bucket.retryMs += timing.retryMs;
745
+ bucket.wireMs += timing.wireMs;
746
+ bucket.samples?.push(timing);
747
+ if (timing.firstTokenMs != null) {
748
+ bucket.firstTokenMs = (bucket.firstTokenMs ?? 0) + timing.firstTokenMs;
749
+ bucket.firstTokenCalls = (bucket.firstTokenCalls ?? 0) + 1;
750
+ }
751
+ }
752
+ toDiagnostics() {
753
+ return {
754
+ modelCallCounts: Object.fromEntries(this.modelCalls),
755
+ tokens: { ...this.diagnosticTokens },
756
+ agentCalls: this.agentCalls,
757
+ modelTiming: Object.fromEntries(this.modelTiming)
758
+ };
642
759
  }
643
760
  hasSeen(id) {
644
761
  return this.seen.has(id);
@@ -665,7 +782,7 @@ var AccountingScope = class {
665
782
  version: 1,
666
783
  currency: "USD",
667
784
  knownCost: this.knownCost,
668
- completeness: this.unknown > 0 ? "incomplete" : "complete",
785
+ completeness: this.reasons.size > 0 ? "incomplete" : "complete",
669
786
  reasons: Object.fromEntries(this.reasons),
670
787
  usage: { ...this.usage },
671
788
  operations: {
@@ -680,8 +797,8 @@ var AccountingScope = class {
680
797
  };
681
798
  }
682
799
  };
683
- var accountingStorage = new import_node_async_hooks.AsyncLocalStorage();
684
- var dispatchAdmissionStorage = new import_node_async_hooks.AsyncLocalStorage();
800
+ var accountingStorage = sharedContext.accounting;
801
+ var dispatchAdmissionStorage = sharedContext.dispatch;
685
802
  function currentDispatchAdmission() {
686
803
  return dispatchAdmissionStorage.getStore();
687
804
  }
@@ -710,14 +827,14 @@ function settleOperationUp(start, op, outcome) {
710
827
  if (scope.isFinalized) return;
711
828
  switch (outcome.outcome) {
712
829
  case "settled":
713
- scope.recordSettled(op, outcome.cost, outcome.provenance, outcome.usage);
830
+ scope.recordSettled(op, outcome.cost, outcome.provenance, outcome.usage, outcome.timing);
714
831
  if (scope.admission && !chargedControllers.has(scope.admission)) {
715
832
  chargedControllers.add(scope.admission);
716
833
  recordSpend(scope.admission, outcome.cost);
717
834
  }
718
835
  break;
719
836
  case "unknown":
720
- scope.recordUnknown(op, outcome.reason, outcome.usage);
837
+ scope.recordUnknown(op, outcome.reason, outcome.usage, outcome.timing);
721
838
  break;
722
839
  case "retracted":
723
840
  scope.recordRetracted(op);
@@ -742,6 +859,17 @@ function admissionDenied(controller, descriptor) {
742
859
  });
743
860
  }
744
861
  function openOperation(descriptor) {
862
+ const inheritedGuard = sharedGuard.storage.getStore();
863
+ const guard = inheritedGuard && (inheritedGuard.isActive?.() ?? true) ? inheritedGuard : void 0;
864
+ if (guard && guard.contextId !== sharedContext.id) {
865
+ guard.markUninstrumented();
866
+ warnCrossCopy(guard.owner, false);
867
+ throw new AxlError(
868
+ "INCOMPATIBLE_ACCOUNTING_SCOPE",
869
+ "The active @axlsdk/axl accounting scope uses an incompatible loaded copy; paid work was refused before dispatch."
870
+ );
871
+ }
872
+ if (guard) warnCrossCopy(guard.owner, true);
745
873
  const scope = accountingStorage.getStore();
746
874
  if (!scope || scope.isFinalized) return void 0;
747
875
  const denier = denyingController(scope);
@@ -758,6 +886,8 @@ function openOperation(descriptor) {
758
886
  kind: descriptor.kind,
759
887
  purpose: scope.purpose,
760
888
  model: descriptor.model,
889
+ diagnosticModelUri: descriptor.diagnosticModelUri,
890
+ agentCall: descriptor.agentCall === true,
761
891
  dispatched: false,
762
892
  dispatchObservable: false
763
893
  };
@@ -798,25 +928,31 @@ function openOperation(descriptor) {
798
928
  outcome: "settled",
799
929
  cost: input.cost,
800
930
  provenance: input.provenance ?? "adapter_reported",
801
- usage: input.usage
931
+ usage: input.usage,
932
+ timing: input.timing
802
933
  });
803
934
  return;
804
935
  }
805
936
  if (input.cost !== void 0 || input.usage !== void 0) {
806
- finish({ outcome: "unknown", reason: "unpriced_model", usage: input.usage });
937
+ finish({
938
+ outcome: "unknown",
939
+ reason: "unpriced_model",
940
+ usage: input.usage,
941
+ timing: input.timing
942
+ });
807
943
  return;
808
944
  }
809
- handle.settleFailure();
945
+ handle.settleFailure(void 0, input.timing);
810
946
  },
811
947
  settleUnknown(reason, usage) {
812
948
  finish({ outcome: "unknown", reason, usage });
813
949
  },
814
- settleFailure(usage) {
950
+ settleFailure(usage, timing) {
815
951
  if (op.dispatchObservable && !op.dispatched) {
816
- finish({ outcome: "settled", cost: 0, provenance: "adapter_reported", usage });
952
+ finish({ outcome: "settled", cost: 0, provenance: "adapter_reported", usage, timing });
817
953
  return;
818
954
  }
819
- finish({ outcome: "unknown", reason: "usage_missing", usage });
955
+ finish({ outcome: "unknown", reason: "usage_missing", usage, timing });
820
956
  },
821
957
  run(fn) {
822
958
  return runWithDispatchAdmission(handle.dispatchAdmission, fn);
@@ -829,15 +965,62 @@ async function runInAccountingScope(options, fn) {
829
965
  const scope = new AccountingScope({
830
966
  parent,
831
967
  purpose: options.purpose ?? parent?.purpose ?? "generation",
832
- admission: options.admission
968
+ admission: options.admission,
969
+ captureTimingSamples: options.captureTimingSamples
833
970
  });
971
+ const inheritedGuard = sharedGuard.storage.getStore();
972
+ const enclosingGuard = inheritedGuard && (inheritedGuard.isActive?.() ?? true) ? inheritedGuard : void 0;
973
+ if (enclosingGuard)
974
+ warnCrossCopy(enclosingGuard.owner, enclosingGuard.contextId === sharedContext.id);
975
+ if (enclosingGuard && enclosingGuard.contextId !== sharedContext.id) {
976
+ enclosingGuard.markUninstrumented();
977
+ scope.markUninstrumented();
978
+ scope.finalize();
979
+ return {
980
+ outcome: {
981
+ status: "rejected",
982
+ error: new AxlError(
983
+ "INCOMPATIBLE_ACCOUNTING_SCOPE",
984
+ "The active @axlsdk/axl accounting scope uses an incompatible loaded copy; paid work was refused before dispatch."
985
+ )
986
+ },
987
+ accounting: scope.toAccounting(),
988
+ diagnostics: scope.toDiagnostics()
989
+ };
990
+ }
991
+ const markUninstrumented = () => {
992
+ let current = scope;
993
+ while (current) {
994
+ if (!current.isFinalized) current.markUninstrumented();
995
+ current = current.parent;
996
+ }
997
+ };
998
+ const isActive = () => {
999
+ let current = scope;
1000
+ while (current) {
1001
+ if (!current.isFinalized) return true;
1002
+ current = current.parent;
1003
+ }
1004
+ return false;
1005
+ };
834
1006
  try {
835
- const value = await accountingStorage.run(scope, fn);
1007
+ const value = await sharedGuard.storage.run(
1008
+ { contextId: sharedContext.id, owner: thisCopy, markUninstrumented, isActive },
1009
+ () => accountingStorage.run(scope, fn)
1010
+ );
836
1011
  scope.finalize();
837
- return { outcome: { status: "fulfilled", value }, accounting: scope.toAccounting() };
1012
+ return {
1013
+ outcome: { status: "fulfilled", value },
1014
+ accounting: scope.toAccounting(),
1015
+ diagnostics: scope.toDiagnostics()
1016
+ };
838
1017
  } catch (error) {
839
1018
  scope.finalize();
840
- return { outcome: { status: "rejected", error }, accounting: scope.toAccounting() };
1019
+ return {
1020
+ outcome: { status: "rejected", error },
1021
+ accounting: scope.toAccounting(),
1022
+ diagnostics: scope.toDiagnostics()
1023
+ };
841
1024
  }
842
1025
  }
843
1026
  function validateReportedCost(amountUsd) {
@@ -1216,10 +1399,12 @@ function toolArgumentIssues(error) {
1216
1399
  }
1217
1400
  return [{ path: [], code: "invalid_arguments", message: "Tool arguments are invalid." }];
1218
1401
  }
1219
- var TOOL_INTERNALS = /* @__PURE__ */ new WeakMap();
1402
+ var TOOL_INTERNALS = /* @__PURE__ */ Symbol.for("axl.tool.internals.v1");
1220
1403
  function getToolInternals(tool2) {
1221
- const internals = TOOL_INTERNALS.get(tool2);
1222
- if (!internals) throw new Error(`Tool "${tool2.name}" was not created by tool()`);
1404
+ const internals = tool2[TOOL_INTERNALS];
1405
+ if (!internals || typeof internals.prepareInput !== "function" || typeof internals.executePrepared !== "function") {
1406
+ throw new Error(`Tool "${tool2.name}" was not created by a compatible tool()`);
1407
+ }
1223
1408
  return internals;
1224
1409
  }
1225
1410
  function prepareToolInput(tool2, input) {
@@ -1266,7 +1451,7 @@ function tool(config) {
1266
1451
  } catch (err) {
1267
1452
  if (options?.signal?.aborted) options.signal.throwIfAborted();
1268
1453
  if (isAbortError(err)) throw err;
1269
- if (err instanceof AdmissionDeniedError) throw err;
1454
+ if (isAdmissionDeniedError(err)) throw err;
1270
1455
  rethrowEventStreamOverflow(err);
1271
1456
  lastError = err instanceof Error ? err : new Error(String(err));
1272
1457
  if (attempt === maxAttempts) break;
@@ -1320,9 +1505,11 @@ function tool(config) {
1320
1505
  },
1321
1506
  _execute: execute
1322
1507
  };
1323
- TOOL_INTERNALS.set(instance, {
1324
- prepareInput,
1325
- executePrepared
1508
+ Object.defineProperty(instance, TOOL_INTERNALS, {
1509
+ value: {
1510
+ prepareInput,
1511
+ executePrepared
1512
+ }
1326
1513
  });
1327
1514
  return instance;
1328
1515
  }
@@ -1353,6 +1540,7 @@ async function resolveApiKey(source) {
1353
1540
  // src/providers/default-endpoints.ts
1354
1541
  var OPENAI_DEFAULT_BASE_URL = "https://api.openai.com/v1";
1355
1542
  var ANTHROPIC_DEFAULT_BASE_URL = "https://api.anthropic.com/v1";
1543
+ var GEMINI_DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
1356
1544
 
1357
1545
  // src/providers/rate-limiter.ts
1358
1546
  function sanitizeRateLimitConfig(config) {
@@ -1750,12 +1938,36 @@ var anthropicQuotaDialect = makeDialect(
1750
1938
  return "unknown";
1751
1939
  }
1752
1940
  );
1941
+ var geminiQuotaDialect = makeDialect([], (error) => {
1942
+ if (error.code === "quota_exceeded") return "spend_cap";
1943
+ if (Array.isArray(error.details)) {
1944
+ for (const detail of error.details) {
1945
+ if (typeof detail !== "object" || detail === null) continue;
1946
+ const violations = detail.violations;
1947
+ if (!Array.isArray(violations)) continue;
1948
+ if (violations.some((violation) => {
1949
+ if (typeof violation !== "object" || violation === null) return false;
1950
+ const id = violation.quotaId;
1951
+ return typeof id === "string" && /perday|daily|spend|billing/i.test(id);
1952
+ }))
1953
+ return "spend_cap";
1954
+ }
1955
+ }
1956
+ const message = error.message;
1957
+ if (typeof message === "string" && /monthly spending cap|spend(?:-based|ing)? (?:cap|limit)|prepay(?:ment)? credits? (?:are )?depleted/i.test(
1958
+ message
1959
+ )) {
1960
+ return "spend_cap";
1961
+ }
1962
+ return "unknown";
1963
+ });
1753
1964
  var DIALECTS = /* @__PURE__ */ new Map([
1754
1965
  ["openai", { origin: new URL(OPENAI_DEFAULT_BASE_URL).origin, dialect: openaiQuotaDialect }],
1755
1966
  [
1756
1967
  "anthropic",
1757
1968
  { origin: new URL(ANTHROPIC_DEFAULT_BASE_URL).origin, dialect: anthropicQuotaDialect }
1758
- ]
1969
+ ],
1970
+ ["google", { origin: new URL(GEMINI_DEFAULT_BASE_URL).origin, dialect: geminiQuotaDialect }]
1759
1971
  ]);
1760
1972
  function quotaDialectFor(family, origin) {
1761
1973
  const entry = DIALECTS.get(family);
@@ -5946,7 +6158,7 @@ var GEMINI_PRICING = {
5946
6158
  },
5947
6159
  "gemini-3.8-flash": { input: 75e-8, cached: 75e-9, output: 375e-8 }
5948
6160
  };
5949
- var CANONICAL_GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
6161
+ var CANONICAL_GEMINI_BASE_URL = GEMINI_DEFAULT_BASE_URL;
5950
6162
  function isValidTokenCount2(value) {
5951
6163
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
5952
6164
  }
@@ -6303,10 +6515,7 @@ var GeminiProvider = class {
6303
6515
  axlRateGovernors;
6304
6516
  constructor(options = {}) {
6305
6517
  this.apiKeySource = options.apiKey ?? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY ?? "";
6306
- this.baseUrl = (options.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta").replace(
6307
- /\/$/,
6308
- ""
6309
- );
6518
+ this.baseUrl = (options.baseUrl ?? GEMINI_DEFAULT_BASE_URL).replace(/\/$/, "");
6310
6519
  assertSafeProviderBaseUrl(
6311
6520
  this.baseUrl,
6312
6521
  "Google provider",
@@ -9020,7 +9229,7 @@ var TranscriptionProviderRegistry = class {
9020
9229
  };
9021
9230
 
9022
9231
  // src/context.ts
9023
- var import_node_async_hooks3 = require("async_hooks");
9232
+ var import_node_async_hooks2 = require("async_hooks");
9024
9233
  var import_node_crypto = require("crypto");
9025
9234
  var import_zod2 = require("zod");
9026
9235
 
@@ -11205,7 +11414,6 @@ function warnDiagnosticOnce(key, message, silent, docs = DOCS_URL) {
11205
11414
  var warnSchemaDiagnosticOnce = warnDiagnosticOnce;
11206
11415
 
11207
11416
  // src/diagnostics/capture.ts
11208
- var import_node_async_hooks2 = require("async_hooks");
11209
11417
  var DEFAULT_MAX_RECORD_BYTES = 256 * 1024;
11210
11418
  var DEFAULT_MAX_RUN_BYTES = 16 * 1024 * 1024;
11211
11419
  var DEFAULT_MAX_QUEUE_BYTES = 1024 * 1024;
@@ -11466,8 +11674,8 @@ function positive(value, fallback) {
11466
11674
  function describe(error) {
11467
11675
  return error instanceof Error ? error.message : String(error);
11468
11676
  }
11469
- var channelStorage = new import_node_async_hooks2.AsyncLocalStorage();
11470
- var correlationStorage = new import_node_async_hooks2.AsyncLocalStorage();
11677
+ var channelStorage = sharedContext.capture;
11678
+ var correlationStorage = sharedContext.correlation;
11471
11679
  function currentCaptureChannel() {
11472
11680
  return channelStorage.getStore();
11473
11681
  }
@@ -11483,7 +11691,7 @@ function runWithCaptureCorrelation(correlation, fn) {
11483
11691
  const merged = { ...correlationStorage.getStore(), ...correlation };
11484
11692
  return correlationStorage.run(merged, fn);
11485
11693
  }
11486
- var turnStorage = new import_node_async_hooks2.AsyncLocalStorage();
11694
+ var turnStorage = sharedContext.turn;
11487
11695
  function currentCaptureTurn() {
11488
11696
  return turnStorage.getStore()?.current;
11489
11697
  }
@@ -11506,6 +11714,34 @@ function toAccountingUsage(usage) {
11506
11714
  cacheWriteTokens: usage.cache_write_tokens ?? 0
11507
11715
  };
11508
11716
  }
11717
+ function safeTiming(response) {
11718
+ try {
11719
+ const timing = response.timing;
11720
+ if (!timing) return void 0;
11721
+ const queuedMs = timing.queuedMs;
11722
+ const retryMs = timing.retryMs;
11723
+ const wireMs = timing.wireMs;
11724
+ if (![queuedMs, retryMs, wireMs].every((value) => Number.isFinite(value) && value >= 0)) {
11725
+ return void 0;
11726
+ }
11727
+ const attempts = timing.attempts;
11728
+ const ttfbMs = timing.ttfbMs;
11729
+ const rateLimitRetries = timing.rateLimitRetries;
11730
+ const firstTokenMs = timing.firstTokenMs;
11731
+ const valid = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
11732
+ return {
11733
+ queuedMs,
11734
+ retryMs,
11735
+ wireMs,
11736
+ ...valid(attempts) ? { attempts } : {},
11737
+ ...valid(ttfbMs) ? { ttfbMs } : {},
11738
+ ...valid(rateLimitRetries) ? { rateLimitRetries } : {},
11739
+ ...valid(firstTokenMs) ? { firstTokenMs } : {}
11740
+ };
11741
+ } catch {
11742
+ return void 0;
11743
+ }
11744
+ }
11509
11745
  function composeLifecycle(caller, onDispatch) {
11510
11746
  return {
11511
11747
  onDispatch() {
@@ -11699,7 +11935,13 @@ var CaptureRecorder = class {
11699
11935
  }
11700
11936
  };
11701
11937
  function openProviderOperation(raw, kind, options) {
11702
- const handle = openOperation({ kind, model: options.model, provider: raw.name });
11938
+ const handle = openOperation({
11939
+ kind,
11940
+ model: options.model,
11941
+ diagnosticModelUri: options.accountingModelUri,
11942
+ provider: raw.name,
11943
+ agentCall: options.accountingModelUri !== void 0
11944
+ });
11703
11945
  if (handle && raw.reportsRequestLifecycle === true) handle.markDispatchObservable();
11704
11946
  return handle;
11705
11947
  }
@@ -11716,7 +11958,8 @@ function createScopedProvider(raw) {
11716
11958
  handle.settle({
11717
11959
  cost: response.cost,
11718
11960
  provenance: response.costProvenance,
11719
- usage: toAccountingUsage(response.usage)
11961
+ usage: toAccountingUsage(response.usage),
11962
+ timing: safeTiming(response)
11720
11963
  });
11721
11964
  recorder?.end({ response });
11722
11965
  return response;
@@ -11771,7 +12014,8 @@ function createScopedProvider(raw) {
11771
12014
  handle?.settle({
11772
12015
  cost: result.value.cost,
11773
12016
  provenance: result.value.costProvenance,
11774
- usage: toAccountingUsage(result.value.usage)
12017
+ usage: toAccountingUsage(result.value.usage),
12018
+ timing: safeTiming(result.value)
11775
12019
  });
11776
12020
  settled = true;
11777
12021
  recorder?.end({
@@ -12204,13 +12448,6 @@ function eventError(error) {
12204
12448
  return { name: "Error", message: "Unknown thrown value" };
12205
12449
  }
12206
12450
  }
12207
- function isToolFailure(error) {
12208
- try {
12209
- return error instanceof ToolFailure;
12210
- } catch {
12211
- return false;
12212
- }
12213
- }
12214
12451
  function cancellation(phase, error, result) {
12215
12452
  let reason;
12216
12453
  try {
@@ -12226,7 +12463,7 @@ function cancellation(phase, error, result) {
12226
12463
  }
12227
12464
  function failed(phase, error, options = {}) {
12228
12465
  let modelMessage;
12229
- if (isToolFailure(error)) {
12466
+ if (isToolFailureError(error)) {
12230
12467
  try {
12231
12468
  modelMessage = typeof error.modelMessage === "string" ? error.modelMessage : void 0;
12232
12469
  } catch {
@@ -12352,7 +12589,7 @@ async function executeAcceptedTool(options) {
12352
12589
  }
12353
12590
  } catch (error) {
12354
12591
  rethrowEventStreamOverflow(error);
12355
- if (error instanceof AdmissionDeniedError) throw error;
12592
+ if (isAdmissionDeniedError(error)) throw error;
12356
12593
  const abort = cancellationError(signal, error);
12357
12594
  if (abort !== void 0) return cancellation("handler", abort);
12358
12595
  return failed("handler", error, { attempts });
@@ -12525,9 +12762,9 @@ function recordToolSpanOutcome(span, outcome) {
12525
12762
  }
12526
12763
 
12527
12764
  // src/context.ts
12528
- var signalStorage = new import_node_async_hooks3.AsyncLocalStorage();
12529
- var hardSignalStorage = new import_node_async_hooks3.AsyncLocalStorage();
12530
- var timeoutTrackerStorage = new import_node_async_hooks3.AsyncLocalStorage();
12765
+ var signalStorage = new import_node_async_hooks2.AsyncLocalStorage();
12766
+ var hardSignalStorage = new import_node_async_hooks2.AsyncLocalStorage();
12767
+ var timeoutTrackerStorage = new import_node_async_hooks2.AsyncLocalStorage();
12531
12768
  var MAX_TIMER_DELAY_MS = 2147483647;
12532
12769
  function composeAbortSignals(...signals) {
12533
12770
  const active = signals.filter((signal) => signal !== void 0);
@@ -12632,7 +12869,7 @@ function parseHumanDecision(value) {
12632
12869
  }
12633
12870
  return invalid3();
12634
12871
  }
12635
- var askStorage = new import_node_async_hooks3.AsyncLocalStorage();
12872
+ var askStorage = new import_node_async_hooks2.AsyncLocalStorage();
12636
12873
  var inlineSchemaCache = /* @__PURE__ */ new WeakMap();
12637
12874
  function zodToJsonSchema(schema) {
12638
12875
  const cached = inlineSchemaCache.get(schema);
@@ -13914,6 +14151,7 @@ ${appendedSchemaText}`;
13914
14151
  const turnStart = Date.now();
13915
14152
  const chatOptions = {
13916
14153
  model,
14154
+ accountingModelUri: effectiveModelUri,
13917
14155
  temperature: options?.temperature ?? agent2._config.temperature,
13918
14156
  tools: toolDefs.length > 0 ? toolDefs : void 0,
13919
14157
  maxTokens: options?.maxTokens ?? agent2._config.maxTokens ?? 4096,
@@ -16467,7 +16705,7 @@ ${summaryResponse.content}`
16467
16705
  try {
16468
16706
  result = await (transcriptionOperation ? transcriptionOperation.run(() => resolved.provider.transcribe(providerRequest)) : resolved.provider.transcribe(providerRequest));
16469
16707
  } catch (error) {
16470
- if (error instanceof AdmissionDeniedError) {
16708
+ if (isAdmissionDeniedError(error)) {
16471
16709
  transcriptionOperation?.settleFailure();
16472
16710
  throw error;
16473
16711
  }
@@ -16731,13 +16969,13 @@ function agent(config) {
16731
16969
  _config: config,
16732
16970
  _name: defaultName,
16733
16971
  async ask(prompt, options) {
16734
- const registry = new ProviderRegistry();
16972
+ const registry2 = new ProviderRegistry();
16735
16973
  const transcriptionRegistry = new TranscriptionProviderRegistry();
16736
16974
  const ctx = new WorkflowContext({
16737
16975
  input: prompt,
16738
16976
  executionId: (0, import_node_crypto2.randomUUID)(),
16739
16977
  config: {},
16740
- providerRegistry: registry,
16978
+ providerRegistry: registry2,
16741
16979
  transcriptionProviderRegistry: transcriptionRegistry
16742
16980
  });
16743
16981
  return ctx.ask(agentInstance, prompt, options);
@@ -16761,7 +16999,7 @@ function workflow(config) {
16761
16999
  // src/runtime.ts
16762
17000
  var import_node_events2 = require("events");
16763
17001
  var import_node_crypto4 = require("crypto");
16764
- var import_node_async_hooks4 = require("async_hooks");
17002
+ var import_node_async_hooks3 = require("async_hooks");
16765
17003
  var import_node_fs3 = require("fs");
16766
17004
  var import_node_path3 = require("path");
16767
17005
 
@@ -18941,7 +19179,7 @@ function assertNoRemovedObservationCallbacks(options) {
18941
19179
  `runtime.createContext() no longer accepts ${removed.join(", ")}. Observe ctx.events or use runtime.stream() instead. See docs/migration/stream-first-observation.md.`
18942
19180
  );
18943
19181
  }
18944
- var costScopeStorage = new import_node_async_hooks4.AsyncLocalStorage();
19182
+ var costScopeStorage = new import_node_async_hooks3.AsyncLocalStorage();
18945
19183
  function forwardAbortSignal(external, internal) {
18946
19184
  if (!external) return () => {
18947
19185
  };
@@ -20659,8 +20897,9 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
20659
20897
  * new paid operations once known spend reaches the limit. Already-dispatched
20660
20898
  * work still settles and is still counted.
20661
20899
  *
20662
- * `metadata`, `modelTiming` and `traces` remain EVENT-derived and therefore
20663
- * depend on trace configuration; `accounting` never does.
20900
+ * Model metadata and timing share the settlement scope with accounting, so
20901
+ * they cross runtimes and compatible ESM/CJS copies. Workflow names and
20902
+ * captured traces still come from this runtime's events.
20664
20903
  */
20665
20904
  async trackOutcome(fn, options) {
20666
20905
  const parentScope = costScopeStorage.getStore();
@@ -20669,49 +20908,11 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
20669
20908
  trackedIds: /* @__PURE__ */ new Set(),
20670
20909
  parent: parentScope
20671
20910
  };
20672
- const modelCalls = /* @__PURE__ */ new Map();
20673
- const modelTiming = /* @__PURE__ */ new Map();
20674
- const captureTimingSamples = options?.captureTimingSamples === true;
20675
20911
  const workflowCalls = /* @__PURE__ */ new Map();
20676
- const tokens = { input: 0, output: 0, reasoning: 0 };
20677
- let agentCalls = 0;
20678
20912
  const capturedTraces = options?.captureTraces ? [] : void 0;
20679
20913
  const listener = (event) => {
20680
20914
  if (!scope.trackedIds.has(event.executionId)) return;
20681
20915
  scope.totalCost += eventCostContribution(event);
20682
- if (event.type === "agent_call_end") {
20683
- if (event.model) modelCalls.set(event.model, (modelCalls.get(event.model) ?? 0) + 1);
20684
- agentCalls++;
20685
- if (event.tokens) {
20686
- tokens.input += event.tokens.input ?? 0;
20687
- tokens.output += event.tokens.output ?? 0;
20688
- tokens.reasoning += event.tokens.reasoning ?? 0;
20689
- }
20690
- const failed2 = event.data?.error != null;
20691
- if (event.model && event.timing && !failed2) {
20692
- const t = event.timing;
20693
- let bucket = modelTiming.get(event.model);
20694
- if (!bucket) {
20695
- bucket = {
20696
- calls: 0,
20697
- queuedMs: 0,
20698
- retryMs: 0,
20699
- wireMs: 0,
20700
- ...captureTimingSamples ? { samples: [] } : {}
20701
- };
20702
- modelTiming.set(event.model, bucket);
20703
- }
20704
- bucket.calls++;
20705
- bucket.samples?.push(t);
20706
- bucket.queuedMs += t.queuedMs;
20707
- bucket.retryMs += t.retryMs;
20708
- bucket.wireMs += t.wireMs;
20709
- if (t.firstTokenMs != null) {
20710
- bucket.firstTokenMs = (bucket.firstTokenMs ?? 0) + t.firstTokenMs;
20711
- bucket.firstTokenCalls = (bucket.firstTokenCalls ?? 0) + 1;
20712
- }
20713
- }
20714
- }
20715
20916
  if (event.type === "workflow_start" && event.workflow) {
20716
20917
  workflowCalls.set(event.workflow, (workflowCalls.get(event.workflow) ?? 0) + 1);
20717
20918
  }
@@ -20737,9 +20938,14 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
20737
20938
  this.on("trace", listener);
20738
20939
  let outcome;
20739
20940
  let accounting;
20941
+ let diagnostics;
20740
20942
  try {
20741
- ({ outcome, accounting } = await runInAccountingScope(
20742
- { purpose: options?.purpose, admission: options?.admission },
20943
+ ({ outcome, accounting, diagnostics } = await runInAccountingScope(
20944
+ {
20945
+ purpose: options?.purpose,
20946
+ admission: options?.admission,
20947
+ captureTimingSamples: options?.captureTimingSamples
20948
+ },
20743
20949
  () => (
20744
20950
  // Capture wraps the cost scope rather than the other way round so a
20745
20951
  // record can never be written for an operation the accounting scope
@@ -20763,13 +20969,13 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
20763
20969
  return {
20764
20970
  ...outcome,
20765
20971
  accounting,
20766
- ...modelTiming.size > 0 ? { modelTiming: Object.fromEntries(modelTiming) } : {},
20972
+ ...Object.keys(diagnostics.modelTiming).length > 0 ? { modelTiming: diagnostics.modelTiming } : {},
20767
20973
  ...capturedTraces ? { traces: capturedTraces } : {},
20768
20974
  metadata: {
20769
- models: [...modelCalls.keys()],
20770
- modelCallCounts: modelCalls.size > 0 ? Object.fromEntries(modelCalls) : void 0,
20771
- tokens,
20772
- agentCalls,
20975
+ models: Object.keys(diagnostics.modelCallCounts),
20976
+ modelCallCounts: Object.keys(diagnostics.modelCallCounts).length > 0 ? diagnostics.modelCallCounts : void 0,
20977
+ tokens: diagnostics.tokens,
20978
+ agentCalls: diagnostics.agentCalls,
20773
20979
  workflows: [...workflowCalls.keys()],
20774
20980
  workflowCallCounts: workflowCalls.size > 0 ? Object.fromEntries(workflowCalls) : void 0
20775
20981
  }
@@ -20979,7 +21185,7 @@ function pickExport(mod, key) {
20979
21185
  // src/cli-internals.ts
20980
21186
  var import_node_path4 = require("path");
20981
21187
  var import_node_fs4 = require("fs");
20982
- var import_node_url = require("url");
21188
+ var import_node_url2 = require("url");
20983
21189
  var CONFIG_CANDIDATES = [
20984
21190
  "axl.config.mts",
20985
21191
  "axl.config.ts",
@@ -21021,7 +21227,7 @@ async function ensureTsxRegistered() {
21021
21227
  return tsxRegistered;
21022
21228
  }
21023
21229
  async function importModule(filePath, _parentURL) {
21024
- const url = (0, import_node_url.pathToFileURL)(filePath).href;
21230
+ const url = (0, import_node_url2.pathToFileURL)(filePath).href;
21025
21231
  if (!needsTsxLoader(filePath)) {
21026
21232
  return await import(url);
21027
21233
  }