@axlsdk/axl 0.24.2 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -2161,6 +2161,8 @@ declare class NoConsensus extends AxlError {
2161
2161
  type TimeoutBreakdown = {
2162
2162
  /** Wall clock consumed by the operation when it timed out. */
2163
2163
  elapsedMs: number;
2164
+ /** Graceful work budget consumed after human and observed governor waits. */
2165
+ chargedMs: number;
2164
2166
  /** Sum of `CallTiming.queuedMs` — self-imposed rate-limiter wait. */
2165
2167
  queuedMs: number;
2166
2168
  /** Sum of `CallTiming.retryMs` — failed provider attempts and their backoff. */
package/dist/index.d.ts CHANGED
@@ -2161,6 +2161,8 @@ declare class NoConsensus extends AxlError {
2161
2161
  type TimeoutBreakdown = {
2162
2162
  /** Wall clock consumed by the operation when it timed out. */
2163
2163
  elapsedMs: number;
2164
+ /** Graceful work budget consumed after human and observed governor waits. */
2165
+ chargedMs: number;
2164
2166
  /** Sum of `CallTiming.queuedMs` — self-imposed rate-limiter wait. */
2165
2167
  queuedMs: number;
2166
2168
  /** Sum of `CallTiming.retryMs` — failed provider attempts and their backoff. */
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", {
@@ -187,7 +193,7 @@ var TimeoutError = class extends AxlError {
187
193
  constructor(operation, timeoutMs, breakdown, agent2) {
188
194
  super(
189
195
  "TIMEOUT",
190
- `${operation} exceeded timeout of ${timeoutMs}ms` + (breakdown ? ` (elapsed ${breakdown.elapsedMs}ms: queued ${breakdown.queuedMs}ms, retries ${breakdown.retryMs}ms, wire ${breakdown.wireMs}ms, other ${breakdown.otherMs}ms)` : "") + (agent2 ? ` for agent '${agent2}'` : "")
196
+ `${operation} exceeded timeout of ${timeoutMs}ms` + (breakdown ? ` (elapsed ${breakdown.elapsedMs}ms: queued ${breakdown.queuedMs}ms, retries ${breakdown.retryMs}ms, wire ${breakdown.wireMs}ms, other ${breakdown.otherMs}ms, charged ${breakdown.chargedMs}ms)` : "") + (agent2 ? ` for agent '${agent2}'` : "")
191
197
  );
192
198
  this.name = "TimeoutError";
193
199
  if (breakdown) this.breakdown = breakdown;
@@ -325,7 +331,7 @@ var sharedGuard = typeof installedGuard === "object" && installedGuard !== null
325
331
  var buildPath = typeof __AXL_BUILD_PATH__ === "string" ? __AXL_BUILD_PATH__ : "<source>";
326
332
  var thisCopy = {
327
333
  path: buildPath.startsWith("file:") ? fileURLToPath(buildPath) : buildPath,
328
- version: true ? "0.24.2" : "unknown"
334
+ version: true ? "0.25.0" : "unknown"
329
335
  };
330
336
  function warnCrossCopy(other, joined) {
331
337
  if (other === thisCopy) return;
@@ -1219,8 +1225,7 @@ function tool(config) {
1219
1225
  } catch (err) {
1220
1226
  if (options?.signal?.aborted) options.signal.throwIfAborted();
1221
1227
  if (isAbortError(err)) throw err;
1222
- if (isAdmissionDeniedError(err)) throw err;
1223
- rethrowEventStreamOverflow(err);
1228
+ rethrowUnrecoverable(err);
1224
1229
  lastError = err instanceof Error ? err : new Error(String(err));
1225
1230
  if (attempt === maxAttempts) break;
1226
1231
  if (retryPolicy.on && !retryPolicy.on(lastError)) {
@@ -1310,6 +1315,41 @@ var OPENAI_DEFAULT_BASE_URL = "https://api.openai.com/v1";
1310
1315
  var ANTHROPIC_DEFAULT_BASE_URL = "https://api.anthropic.com/v1";
1311
1316
  var GEMINI_DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
1312
1317
 
1318
+ // src/ask-clock.ts
1319
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
1320
+ var askClockStorage = new AsyncLocalStorage2();
1321
+ function runWithAskClock(fn) {
1322
+ const tracker = { pausedMs: 0, activePauses: 0 };
1323
+ const enclosing = askClockStorage.getStore() ?? [];
1324
+ return askClockStorage.run([...enclosing, tracker], fn);
1325
+ }
1326
+ function currentAskClock() {
1327
+ return askClockStorage.getStore()?.at(-1);
1328
+ }
1329
+ function pausedAskClockMs(tracker, now = Date.now()) {
1330
+ return tracker.pausedMs + (tracker.activePauses > 0 && tracker.pauseStartedAt !== void 0 ? now - tracker.pauseStartedAt : 0);
1331
+ }
1332
+ async function pauseAskClocks(wait) {
1333
+ const trackers = askClockStorage.getStore();
1334
+ if (!trackers || trackers.length === 0) return wait();
1335
+ const startedAt = Date.now();
1336
+ for (const tracker of trackers) {
1337
+ if (tracker.activePauses++ === 0) tracker.pauseStartedAt = startedAt;
1338
+ }
1339
+ try {
1340
+ return await wait();
1341
+ } finally {
1342
+ const finishedAt = Date.now();
1343
+ for (const tracker of trackers) {
1344
+ tracker.activePauses--;
1345
+ if (tracker.activePauses === 0 && tracker.pauseStartedAt !== void 0) {
1346
+ tracker.pausedMs += finishedAt - tracker.pauseStartedAt;
1347
+ tracker.pauseStartedAt = void 0;
1348
+ }
1349
+ }
1350
+ }
1351
+ }
1352
+
1313
1353
  // src/providers/rate-limiter.ts
1314
1354
  function sanitizeRateLimitConfig(config) {
1315
1355
  const limits = {};
@@ -2372,7 +2412,7 @@ async function fetchWithRetry(input, init, opts) {
2372
2412
  let headersAt = 0;
2373
2413
  const waitSelfImposed = async (wait) => {
2374
2414
  const start = Date.now();
2375
- await wait();
2415
+ await pauseAskClocks(wait);
2376
2416
  const waited = Date.now() - start;
2377
2417
  queuedMs += waited;
2378
2418
  if (dispatches > 0) queuedAfterFirstDispatchMs += waited;
@@ -8997,7 +9037,7 @@ var TranscriptionProviderRegistry = class {
8997
9037
  };
8998
9038
 
8999
9039
  // src/context.ts
9000
- import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
9040
+ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
9001
9041
  import { randomUUID } from "crypto";
9002
9042
  import { z, ZodError as ZodError2 } from "zod";
9003
9043
 
@@ -12356,8 +12396,7 @@ async function executeAcceptedTool(options) {
12356
12396
  }
12357
12397
  }
12358
12398
  } catch (error) {
12359
- rethrowEventStreamOverflow(error);
12360
- if (isAdmissionDeniedError(error)) throw error;
12399
+ rethrowUnrecoverable(error);
12361
12400
  const abort = cancellationError(signal, error);
12362
12401
  if (abort !== void 0) return cancellation("handler", abort);
12363
12402
  return failed("handler", error, { attempts });
@@ -12530,9 +12569,8 @@ function recordToolSpanOutcome(span, outcome) {
12530
12569
  }
12531
12570
 
12532
12571
  // src/context.ts
12533
- var signalStorage = new AsyncLocalStorage2();
12534
- var hardSignalStorage = new AsyncLocalStorage2();
12535
- var timeoutTrackerStorage = new AsyncLocalStorage2();
12572
+ var signalStorage = new AsyncLocalStorage3();
12573
+ var hardSignalStorage = new AsyncLocalStorage3();
12536
12574
  var MAX_TIMER_DELAY_MS = 2147483647;
12537
12575
  function composeAbortSignals(...signals) {
12538
12576
  const active = signals.filter((signal) => signal !== void 0);
@@ -12564,9 +12602,6 @@ function raceWithAbortSignal(promise, signal) {
12564
12602
  );
12565
12603
  });
12566
12604
  }
12567
- function pausedHumanWaitMs(tracker, now = Date.now()) {
12568
- return tracker.humanWaitMs + (tracker.activeHumanWaits > 0 && tracker.humanWaitStartedAt !== void 0 ? now - tracker.humanWaitStartedAt : 0);
12569
- }
12570
12605
  var DEFAULT_BRANCH_DRAIN_TIMEOUT_MS = 5e3;
12571
12606
  function resolveBranchDrainTimeoutMs(value) {
12572
12607
  const resolved = value === void 0 ? DEFAULT_BRANCH_DRAIN_TIMEOUT_MS : value;
@@ -12637,7 +12672,7 @@ function parseHumanDecision(value) {
12637
12672
  }
12638
12673
  return invalid3();
12639
12674
  }
12640
- var askStorage = new AsyncLocalStorage2();
12675
+ var askStorage = new AsyncLocalStorage3();
12641
12676
  var inlineSchemaCache = /* @__PURE__ */ new WeakMap();
12642
12677
  function zodToJsonSchema(schema) {
12643
12678
  const cached = inlineSchemaCache.get(schema);
@@ -13574,10 +13609,7 @@ var WorkflowContext = class _WorkflowContext {
13574
13609
  return hardAskSignal ? hardSignalStorage.run(hardAskSignal, runWithAskSignal) : runWithAskSignal();
13575
13610
  }
13576
13611
  async executeAgentCall(agent2, input, options, handoffMessages, usageCapture, sessionHistory = normalizeSessionHistory(this.sessionHistory)) {
13577
- const tracker = { humanWaitMs: 0, activeHumanWaits: 0 };
13578
- const enclosing = timeoutTrackerStorage.getStore() ?? [];
13579
- return timeoutTrackerStorage.run(
13580
- [...enclosing, tracker],
13612
+ return runWithAskClock(
13581
13613
  () => this.executeAgentCallImpl(
13582
13614
  agent2,
13583
13615
  input,
@@ -13852,7 +13884,7 @@ ${appendedSchemaText}`;
13852
13884
  );
13853
13885
  }
13854
13886
  const startTime = Date.now();
13855
- const timeoutTracker = timeoutTrackerStorage.getStore()?.at(-1);
13887
+ const askClock = currentAskClock();
13856
13888
  const timingTotals = { turns: 0, queuedMs: 0, retryMs: 0, wireMs: 0 };
13857
13889
  const currentMessages = [...messages];
13858
13890
  let turns = 0;
@@ -13869,13 +13901,16 @@ ${appendedSchemaText}`;
13869
13901
  while (turns < maxTurns) {
13870
13902
  this.currentSignal?.throwIfAborted();
13871
13903
  const now = Date.now();
13872
- const elapsedMs = now - startTime - (timeoutTracker ? pausedHumanWaitMs(timeoutTracker, now) : 0);
13873
- if (elapsedMs > timeoutMs) {
13904
+ const elapsedMs = now - startTime;
13905
+ const pausedMs = askClock ? pausedAskClockMs(askClock, now) : 0;
13906
+ const chargedMs = Math.max(0, elapsedMs - Math.min(pausedMs, Math.max(0, elapsedMs)));
13907
+ if (chargedMs > timeoutMs) {
13874
13908
  throw new TimeoutError(
13875
13909
  "ctx.ask()",
13876
13910
  timeoutMs,
13877
13911
  timingTotals.turns > 0 ? {
13878
13912
  elapsedMs,
13913
+ chargedMs,
13879
13914
  queuedMs: timingTotals.queuedMs,
13880
13915
  retryMs: timingTotals.retryMs,
13881
13916
  wireMs: timingTotals.wireMs,
@@ -14623,7 +14658,8 @@ ${appendedSchemaText}`;
14623
14658
  metadata: this.metadata
14624
14659
  });
14625
14660
  } catch (err) {
14626
- rethrowEventStreamOverflow(err);
14661
+ rethrowUnrecoverable(err);
14662
+ if (this.currentSignal?.aborted) throw err;
14627
14663
  validateErr = err;
14628
14664
  const reason = err instanceof Error ? err.message : String(err);
14629
14665
  validateResult = { valid: false, reason: `Validator error: ${reason}` };
@@ -15214,15 +15250,13 @@ ${summaryResponse.content}`
15214
15250
  reject(new QuorumNotMet(quorum, successCount, results));
15215
15251
  }
15216
15252
  }).catch((err) => {
15217
- if (isEventStreamOverflowError(err)) {
15218
- if (!settled) {
15219
- settled = true;
15220
- controller.abort();
15221
- reject(err);
15222
- }
15253
+ if (settled) return;
15254
+ if (isUnrecoverableError(err) || parentSignal2?.aborted) {
15255
+ settled = true;
15256
+ controller.abort();
15257
+ reject(err);
15223
15258
  return;
15224
15259
  }
15225
- if (settled) return;
15226
15260
  const isAbort = err instanceof DOMException && err.name === "AbortError";
15227
15261
  if (isAbort) {
15228
15262
  completedCount++;
@@ -15245,7 +15279,8 @@ ${summaryResponse.content}`
15245
15279
  const parentSignal = this.currentSignal;
15246
15280
  const promises = Array.from({ length: n }, (_, i) => {
15247
15281
  const run = () => fn(i).then((value) => ({ ok: true, value })).catch((err) => {
15248
- rethrowEventStreamOverflow(err);
15282
+ rethrowUnrecoverable(err);
15283
+ if (parentSignal?.aborted) throw err;
15249
15284
  return {
15250
15285
  ok: false,
15251
15286
  error: err instanceof Error ? err.message : String(err)
@@ -15369,6 +15404,7 @@ ${summaryResponse.content}`
15369
15404
  async verify(fn, schema, options) {
15370
15405
  const maxRetries = options?.retries ?? 3;
15371
15406
  let lastRetry = void 0;
15407
+ const scopeSignal = this.currentSignal;
15372
15408
  const emitVerifyOutcome = (passed, attempts, lastError) => {
15373
15409
  this.emitEvent({
15374
15410
  type: "verify",
@@ -15391,25 +15427,24 @@ ${summaryResponse.content}`
15391
15427
  try {
15392
15428
  validateResult = await options.validate(parsed, { metadata: this.metadata });
15393
15429
  } catch (err) {
15394
- rethrowEventStreamOverflow(err);
15430
+ rethrowUnrecoverable(err);
15431
+ if (scopeSignal?.aborted) throw err;
15395
15432
  const reason = err instanceof Error ? err.message : String(err);
15396
15433
  validateResult = { valid: false, reason: `Validator error: ${reason}` };
15397
15434
  }
15398
15435
  if (!validateResult.valid) {
15436
+ scopeSignal?.throwIfAborted();
15399
15437
  const errorMsg = validateResult.reason ?? "Validation failed";
15400
15438
  lastRetry = { error: errorMsg, output: rawOutput, parsed };
15401
- if (attempt === maxRetries) {
15402
- emitVerifyOutcome(false, attempt + 1, errorMsg);
15403
- if (options?.fallback !== void 0) return options.fallback;
15404
- throw new ValidationError(parsed, errorMsg, maxRetries);
15405
- }
15439
+ if (attempt === maxRetries) throw new ValidationError(parsed, errorMsg, maxRetries);
15406
15440
  continue;
15407
15441
  }
15408
15442
  }
15409
15443
  emitVerifyOutcome(true, attempt + 1);
15410
15444
  return parsed;
15411
15445
  } catch (err) {
15412
- rethrowEventStreamOverflow(err);
15446
+ rethrowUnrecoverable(err);
15447
+ if (scopeSignal?.aborted) throw err;
15413
15448
  if (err instanceof ValidationError) {
15414
15449
  lastRetry = {
15415
15450
  error: err.reason,
@@ -15476,7 +15511,8 @@ ${summaryResponse.content}`
15476
15511
  const unpriced = this.budgetContext.unpriced;
15477
15512
  return { value, budgetExceeded: exceeded, totalCost, unpriced };
15478
15513
  } catch (err) {
15479
- rethrowEventStreamOverflow(err);
15514
+ rethrowUnrecoverable(err);
15515
+ if (parentSignal?.aborted) throw err;
15480
15516
  if (this.budgetContext.exceeded) {
15481
15517
  return {
15482
15518
  value: null,
@@ -15485,14 +15521,6 @@ ${summaryResponse.content}`
15485
15521
  unpriced: this.budgetContext.unpriced
15486
15522
  };
15487
15523
  }
15488
- if (err instanceof DOMException && err.name === "AbortError" && controller) {
15489
- return {
15490
- value: null,
15491
- budgetExceeded: true,
15492
- totalCost: this.budgetContext.totalCost,
15493
- unpriced: this.budgetContext.unpriced
15494
- };
15495
- }
15496
15524
  throw err;
15497
15525
  } finally {
15498
15526
  if (parentBudget) {
@@ -15608,7 +15636,8 @@ ${summaryResponse.content}`
15608
15636
  return;
15609
15637
  }
15610
15638
  } catch (err) {
15611
- rethrowEventStreamOverflow(err);
15639
+ rethrowUnrecoverable(err);
15640
+ if (parentSignal?.aborted) throw err;
15612
15641
  remaining--;
15613
15642
  lastError = err instanceof Error ? err : new Error(`Validator error: ${String(err)}`);
15614
15643
  if (remaining === 0 && !settled) {
@@ -15628,7 +15657,7 @@ ${summaryResponse.content}`
15628
15657
  controller.abort();
15629
15658
  resolve2(value);
15630
15659
  }).catch((err) => {
15631
- if (isEventStreamOverflowError(err)) {
15660
+ if (isUnrecoverableError(err)) {
15632
15661
  if (!settled) {
15633
15662
  settled = true;
15634
15663
  controller.abort();
@@ -15637,6 +15666,12 @@ ${summaryResponse.content}`
15637
15666
  throw err;
15638
15667
  }
15639
15668
  if (settled) return;
15669
+ if (parentSignal?.aborted) {
15670
+ settled = true;
15671
+ controller.abort();
15672
+ reject(err);
15673
+ return;
15674
+ }
15640
15675
  if (err instanceof DOMException && err.name === "AbortError") {
15641
15676
  remaining--;
15642
15677
  if (remaining === 0 && !settled) {
@@ -15695,7 +15730,7 @@ ${summaryResponse.content}`
15695
15730
  results[idx] = { ok: true, value };
15696
15731
  successCount++;
15697
15732
  } catch (err) {
15698
- if (isEventStreamOverflowError(err)) {
15733
+ if (isUnrecoverableError(err)) {
15699
15734
  settled = true;
15700
15735
  controller?.abort();
15701
15736
  reject(err);
@@ -15704,6 +15739,12 @@ ${summaryResponse.content}`
15704
15739
  if (settled) {
15705
15740
  return;
15706
15741
  }
15742
+ if (parentSignal?.aborted) {
15743
+ settled = true;
15744
+ controller?.abort();
15745
+ reject(err);
15746
+ return;
15747
+ }
15707
15748
  if (err instanceof DOMException && err.name === "AbortError" && controller?.signal.aborted) {
15708
15749
  completedCount++;
15709
15750
  return;
@@ -15739,12 +15780,7 @@ ${summaryResponse.content}`
15739
15780
  }
15740
15781
  // ── ctx.awaitHuman() ──────────────────────────────────────────────────
15741
15782
  async awaitHuman(options) {
15742
- const trackers = timeoutTrackerStorage.getStore();
15743
- const startedAt = Date.now();
15744
- for (const tracker of trackers ?? []) {
15745
- if (tracker.activeHumanWaits++ === 0) tracker.humanWaitStartedAt = startedAt;
15746
- }
15747
- try {
15783
+ return pauseAskClocks(async () => {
15748
15784
  if (this.spanManager) {
15749
15785
  return await this.spanManager.withSpanAsync(
15750
15786
  "axl.ctx.awaitHuman",
@@ -15761,16 +15797,7 @@ ${summaryResponse.content}`
15761
15797
  );
15762
15798
  }
15763
15799
  return await this._awaitHumanImpl(options);
15764
- } finally {
15765
- const finishedAt = Date.now();
15766
- for (const tracker of trackers ?? []) {
15767
- tracker.activeHumanWaits--;
15768
- if (tracker.activeHumanWaits === 0 && tracker.humanWaitStartedAt !== void 0) {
15769
- tracker.humanWaitMs += finishedAt - tracker.humanWaitStartedAt;
15770
- tracker.humanWaitStartedAt = void 0;
15771
- }
15772
- }
15773
- }
15800
+ });
15774
15801
  }
15775
15802
  async _awaitHumanImpl(options) {
15776
15803
  const branchSignal = this.currentSignal;
@@ -16767,7 +16794,7 @@ function workflow(config) {
16767
16794
  // src/runtime.ts
16768
16795
  import { EventEmitter as EventEmitter2 } from "events";
16769
16796
  import { randomUUID as randomUUID4 } from "crypto";
16770
- import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
16797
+ import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
16771
16798
  import { appendFileSync, mkdirSync as mkdirSync2 } from "fs";
16772
16799
  import { dirname } from "path";
16773
16800
 
@@ -18956,7 +18983,7 @@ function assertNoRemovedObservationCallbacks(options) {
18956
18983
  `runtime.createContext() no longer accepts ${removed.join(", ")}. Observe ctx.events or use runtime.stream() instead. See docs/migration/stream-first-observation.md.`
18957
18984
  );
18958
18985
  }
18959
- var costScopeStorage = new AsyncLocalStorage3();
18986
+ var costScopeStorage = new AsyncLocalStorage4();
18960
18987
  function forwardAbortSignal(external, internal) {
18961
18988
  if (!external) return () => {
18962
18989
  };