@adaptic/utils 0.0.1034 → 0.0.1035

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.mjs CHANGED
@@ -7484,6 +7484,68 @@ class DuplicateClientOrderIdError extends AlpacaApiError {
7484
7484
  this.wasDerived = wasDerived;
7485
7485
  }
7486
7486
  }
7487
+ /** Stable `code` carried by {@link PendingCancelError}. */
7488
+ const PENDING_CANCEL_ERROR_CODE = "ORDER_PENDING_CANCEL";
7489
+ /**
7490
+ * Alpaca's numeric code for an unprocessable order request. It is shared by
7491
+ * many distinct reasons (pending cancel, not cancelable, bad replace), so the
7492
+ * reason text is what separates them.
7493
+ */
7494
+ const ALPACA_UNPROCESSABLE_BROKER_CODE = 42210000;
7495
+ /** HTTP status Alpaca answers an unprocessable order request on. */
7496
+ const HTTP_UNPROCESSABLE_STATUS = 422;
7497
+ /** Alpaca's reason text for an order it already holds in `pending_cancel`. */
7498
+ const PENDING_CANCEL_REASON = /pending[ _]cancel/i;
7499
+ /**
7500
+ * Alpaca refused a DELETE because it has already accepted a cancel for the
7501
+ * order and holds it in `pending_cancel` until the venue confirms (HTTP 422,
7502
+ * code 42210000, "order pending cancel").
7503
+ *
7504
+ * This is a distinct broker answer from "not cancelable" (the order already
7505
+ * filled or is otherwise terminal): the order is being torn down, so it is
7506
+ * NOT live protection, and a second DELETE only returns the same 422. A caller
7507
+ * must treat the order as dying and wait for terminal truth (a `canceled` or
7508
+ * `filled` trade update, or a GET), never re-cancel it and never count it as
7509
+ * kept.
7510
+ *
7511
+ * The `message` is kept byte-identical to the generic rewrite
7512
+ * ("Order X is not cancelable") so consumers that still match that text keep
7513
+ * their behaviour; new consumers branch on the type, on
7514
+ * {@link PENDING_CANCEL_ERROR_CODE}, or on {@link isPendingCancelRejection}.
7515
+ * Never retryable.
7516
+ */
7517
+ class PendingCancelError extends AlpacaApiError {
7518
+ orderId;
7519
+ constructor(message,
7520
+ /** The broker order id the DELETE targeted. */
7521
+ orderId, cause) {
7522
+ super(message, PENDING_CANCEL_ERROR_CODE, HTTP_UNPROCESSABLE_STATUS, cause, extractAlpacaBrokerError(cause));
7523
+ this.orderId = orderId;
7524
+ }
7525
+ }
7526
+ /**
7527
+ * Did Alpaca answer that the order is already pending cancel?
7528
+ *
7529
+ * True for a {@link PendingCancelError}, and for any thrown value whose broker
7530
+ * payload (on the value or its `cause` chain) is a 422 / 42210000 whose reason
7531
+ * names `pending cancel` / `pending_cancel`. A payload with no reason text is
7532
+ * never read as pending cancel: absence stays unknown.
7533
+ *
7534
+ * @param error - The thrown value.
7535
+ * @returns Whether the broker holds the order in `pending_cancel`.
7536
+ */
7537
+ function isPendingCancelRejection(error) {
7538
+ if (error instanceof PendingCancelError) {
7539
+ return true;
7540
+ }
7541
+ const detail = extractAlpacaBrokerError(error);
7542
+ if (detail === undefined || detail.brokerMessage === null) {
7543
+ return false;
7544
+ }
7545
+ const unprocessable = detail.statusCode === HTTP_UNPROCESSABLE_STATUS ||
7546
+ detail.brokerCode === ALPACA_UNPROCESSABLE_BROKER_CODE;
7547
+ return unprocessable && PENDING_CANCEL_REASON.test(detail.brokerMessage);
7548
+ }
7487
7549
  /** Max depth walked along the `error.cause` chain when locating a broker payload. */
7488
7550
  const MAX_BROKER_ERROR_CAUSE_DEPTH = 6;
7489
7551
  /**
@@ -11004,6 +11066,9 @@ class AlpacaTradingAPI {
11004
11066
  /**
11005
11067
  * Cancel a specific order by its ID
11006
11068
  * @param orderId The id of the order to cancel
11069
+ * @throws PendingCancelError if the broker already holds the order in
11070
+ * `pending_cancel` (422 / 42210000 "order pending cancel"): the order is
11071
+ * being torn down, so it is not live and must not be cancelled again
11007
11072
  * @throws Error if the order is not cancelable (status 422) or if the order doesn't exist
11008
11073
  * @returns Promise that resolves when the order is successfully canceled
11009
11074
  */
@@ -11014,6 +11079,12 @@ class AlpacaTradingAPI {
11014
11079
  this.log(`Successfully canceled order ${orderId}`);
11015
11080
  }
11016
11081
  catch (error) {
11082
+ // A cancel the broker already accepted: the order is dying, not kept.
11083
+ // Typed so callers stop reading it as "not cancelable, still live".
11084
+ if (isPendingCancelRejection(error)) {
11085
+ this.log(`Order ${orderId} is already pending cancel at the broker; not re-cancelling`, { type: "warn" });
11086
+ throw new PendingCancelError(`Order ${orderId} is not cancelable`, orderId, error);
11087
+ }
11017
11088
  // If the error is a 422, it means the order is not cancelable
11018
11089
  if (error instanceof Error && error.message.includes("422")) {
11019
11090
  this.log(`Order ${orderId} is not cancelable`, {
@@ -12293,6 +12364,9 @@ async function replaceOrder$1(auth, orderId, params) {
12293
12364
  * @param auth - The authentication details for Alpaca
12294
12365
  * @param orderId - The ID of the order to cancel
12295
12366
  * @returns Success status and optional message if order not found
12367
+ * @throws PendingCancelError if the broker already holds the order in
12368
+ * `pending_cancel` (the order is dying; never cancel it again). The message
12369
+ * is the same "Failed to cancel order: ..." text as any other refusal.
12296
12370
  */
12297
12371
  async function cancelOrder$1(auth, orderId) {
12298
12372
  try {
@@ -12312,7 +12386,13 @@ async function cancelOrder$1(auth, orderId) {
12312
12386
  return { success: false, message: `Order not found: ${orderId}` };
12313
12387
  }
12314
12388
  else {
12315
- throw alpacaHttpError(`Failed to cancel order: ${response.status} ${response.statusText} ${errorText}`, response.status, errorText);
12389
+ const httpError = alpacaHttpError(`Failed to cancel order: ${response.status} ${response.statusText} ${errorText}`, response.status, errorText);
12390
+ // A cancel the broker already accepted: the order is dying, not kept,
12391
+ // and a second DELETE only returns the same 422.
12392
+ if (isPendingCancelRejection(httpError)) {
12393
+ throw new PendingCancelError(httpError.message, orderId, httpError);
12394
+ }
12395
+ throw httpError;
12316
12396
  }
12317
12397
  }
12318
12398
  return { success: true };
@@ -68316,6 +68396,8 @@ async function getOrders(client, params = {}) {
68316
68396
  *
68317
68397
  * @param client - The AlpacaClient instance
68318
68398
  * @param orderId - The unique identifier of the order to cancel
68399
+ * @throws PendingCancelError if the broker already holds the order in
68400
+ * `pending_cancel` (the order is dying; never cancel it again)
68319
68401
  * @throws Error if order cannot be canceled (e.g., already filled or canceled)
68320
68402
  *
68321
68403
  * @example
@@ -68331,6 +68413,11 @@ async function cancelOrder(client, orderId) {
68331
68413
  }
68332
68414
  catch (error) {
68333
68415
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
68416
+ // A cancel the broker already accepted: the order is dying, not kept.
68417
+ if (isPendingCancelRejection(error)) {
68418
+ log$6(`Order ${orderId} is already pending cancel at the broker; not re-cancelling`, { type: "warn" });
68419
+ throw new PendingCancelError(`Order ${orderId} is not cancelable`, orderId, error);
68420
+ }
68334
68421
  // Check for specific error conditions
68335
68422
  if (errorMessage.includes("422") ||
68336
68423
  errorMessage.includes("not cancelable")) {
@@ -72473,6 +72560,10 @@ const MAX_TOKENS_PARAM_BY_API_STYLE = {
72473
72560
  };
72474
72561
  /** The response-format key an OpenAI-compatible provider expects. */
72475
72562
  const RESPONSE_FORMAT_KEY = "response_format";
72563
+ /** The tool-choice key, in the OpenAI-compatible vocabulary the gateway speaks. */
72564
+ const TOOL_CHOICE_KEY = "tool_choice";
72565
+ /** The mandatory tool-choice value: the answer must be a tool call. */
72566
+ const TOOL_CHOICE_REQUIRED = "required";
72476
72567
  /**
72477
72568
  * Normalise a caller's options into the exact parameter set one leg accepts.
72478
72569
  *
@@ -72520,12 +72611,91 @@ function normaliseParams(options, route, responseFormat) {
72520
72611
  // tool calls concurrently reorders its own effects, and ordering is part
72521
72612
  // of the meaning of a sequence of trading actions.
72522
72613
  params.parallel_tool_calls = false;
72614
+ const toolChoice = normaliseToolChoice(options.toolChoice, route);
72615
+ if (toolChoice !== undefined) {
72616
+ params[TOOL_CHOICE_KEY] = toolChoice;
72617
+ }
72523
72618
  }
72524
72619
  if (options.metadata !== undefined) {
72525
72620
  params.metadata = { ...options.metadata };
72526
72621
  }
72527
72622
  return params;
72528
72623
  }
72624
+ /**
72625
+ * Translate the caller's tool-choice policy into the value one leg is sent.
72626
+ *
72627
+ * `"auto"` is every provider's default once tools are present, so it is never
72628
+ * sent. `"required"` is sent only to a route MEASURED to honour it: serving
72629
+ * stacks accept the parameter and silently answer in prose anyway, so sending
72630
+ * it to an unmeasured route would assert a constraint nobody checked. Omitting
72631
+ * it there leaves that leg exactly as it was, with the caller's own validation
72632
+ * as the guard.
72633
+ *
72634
+ * @param toolChoice The caller's policy, if any.
72635
+ * @param route The leg being prepared.
72636
+ * @returns The value to send, or undefined to omit the parameter.
72637
+ */
72638
+ function normaliseToolChoice(toolChoice, route) {
72639
+ if (toolChoice !== TOOL_CHOICE_REQUIRED) {
72640
+ return undefined;
72641
+ }
72642
+ return route.params.supports_tool_choice === true ? TOOL_CHOICE_REQUIRED : undefined;
72643
+ }
72644
+ /**
72645
+ * Fail a leg that was sent a mandatory tool choice and answered without a tool call.
72646
+ *
72647
+ * The parameter is sent only to a route declared to honour it, so an answer
72648
+ * with no tool call means the declaration no longer holds for this leg — a
72649
+ * serving-stack upgrade can drop the constraint without an error. Returning
72650
+ * that answer would hand a caller prose where it demanded an action; failing
72651
+ * the leg lets the chain advance and names the broken declaration.
72652
+ *
72653
+ * @param route The leg that answered.
72654
+ * @param params The parameters it was sent.
72655
+ * @param response The leg's answer: its tool calls, and the usage and serving
72656
+ * model the error carries so the leg's spend and attribution are not lost.
72657
+ * @throws {ToolChoiceIgnoredError} When a mandatory choice produced no tool call.
72658
+ */
72659
+ function assertToolChoiceHonoured(route, params, response) {
72660
+ if (params[TOOL_CHOICE_KEY] !== TOOL_CHOICE_REQUIRED) {
72661
+ return;
72662
+ }
72663
+ if (response.tool_calls !== undefined && response.tool_calls.length > 0) {
72664
+ return;
72665
+ }
72666
+ throw new ToolChoiceIgnoredError(route, response.usage, response.servedModel ?? null);
72667
+ }
72668
+ /**
72669
+ * Thrown when a route declared to honour a mandatory tool choice answered
72670
+ * without a tool call.
72671
+ *
72672
+ * Distinct from a provider outage: the route answered, but not in the form it
72673
+ * is declared to guarantee. The chain advances, and the route's health is not
72674
+ * charged, because the fault is in the declaration, not in availability. The
72675
+ * provider still billed for the answer, so the error carries the leg's usage:
72676
+ * the spend belongs in the chain's total whether or not a later leg serves.
72677
+ */
72678
+ class ToolChoiceIgnoredError extends Error {
72679
+ /** The leg that ignored the choice. */
72680
+ routeKey;
72681
+ /** What the provider billed for the answer that carried no tool call. */
72682
+ usage;
72683
+ /** The model the provider reports as having answered, or `null` when unreported. */
72684
+ servedModel;
72685
+ /**
72686
+ * @param route The leg.
72687
+ * @param usage What the provider billed for the answer.
72688
+ * @param servedModel The provider-reported serving model, or `null`.
72689
+ */
72690
+ constructor(route, usage, servedModel) {
72691
+ super(`route ${route.routeKey} (${route.providerName}/${route.modelId}) was sent tool_choice "required" ` +
72692
+ "and answered without a tool call; its supports_tool_choice declaration no longer holds");
72693
+ this.name = "ToolChoiceIgnoredError";
72694
+ this.routeKey = route.routeKey;
72695
+ this.usage = usage;
72696
+ this.servedModel = servedModel;
72697
+ }
72698
+ }
72529
72699
  /**
72530
72700
  * Translate the requested response shape into the parameter a route accepts.
72531
72701
  *
@@ -73227,6 +73397,9 @@ class ChainExhaustedError extends Error {
73227
73397
  * understate spend by exactly the amount the failures cost — which is the
73228
73398
  * amount a fallback chain is most likely to run up.
73229
73399
  *
73400
+ * A count one attempt did not report makes the total for that count unknown
73401
+ * (`null`): a sum that skipped it would present a partial figure as complete.
73402
+ *
73230
73403
  * @param a The running total.
73231
73404
  * @param b The attempt to add, if any.
73232
73405
  * @returns The combined usage.
@@ -73236,8 +73409,8 @@ function sumUsage(a, b) {
73236
73409
  return a;
73237
73410
  }
73238
73411
  return {
73239
- prompt_tokens: a.prompt_tokens + b.prompt_tokens,
73240
- completion_tokens: a.completion_tokens + b.completion_tokens,
73412
+ prompt_tokens: addKnown(a.prompt_tokens, b.prompt_tokens),
73413
+ completion_tokens: addKnown(a.completion_tokens, b.completion_tokens),
73241
73414
  reasoning_tokens: a.reasoning_tokens === undefined && b.reasoning_tokens === undefined
73242
73415
  ? undefined
73243
73416
  : (a.reasoning_tokens ?? 0) + (b.reasoning_tokens ?? 0),
@@ -73246,9 +73419,38 @@ function sumUsage(a, b) {
73246
73419
  : (a.cached_tokens ?? 0) + (b.cached_tokens ?? 0),
73247
73420
  provider: b.provider,
73248
73421
  model: b.model,
73249
- cost: a.cost + b.cost,
73422
+ cost: addKnown(a.cost, b.cost),
73250
73423
  };
73251
73424
  }
73425
+ /**
73426
+ * Add two measured quantities, either of which may be unreported.
73427
+ *
73428
+ * @param a The running total, or null when already unknown.
73429
+ * @param b The value to add, or null when unreported.
73430
+ * @returns The sum, or null when either side is unknown.
73431
+ */
73432
+ function addKnown(a, b) {
73433
+ return a === null || b === null ? null : a + b;
73434
+ }
73435
+ /**
73436
+ * The budget one leg may spend.
73437
+ *
73438
+ * The route's own budget, cut to what remains of the caller's deadline. The
73439
+ * route budget is never widened, so a caller with a long deadline still has a
73440
+ * slow leg abandoned in time for the next one to run; and the deadline is never
73441
+ * exceeded, so the last leg reached ends when the caller stops waiting.
73442
+ *
73443
+ * @param routeBudgetMs The leg's route budget.
73444
+ * @param deadlineAtMs The caller's deadline instant, if any.
73445
+ * @param nowMs The current instant on the same clock.
73446
+ * @returns The leg's budget in milliseconds; zero or less means none is left.
73447
+ */
73448
+ function legBudgetMs(routeBudgetMs, deadlineAtMs, nowMs) {
73449
+ if (deadlineAtMs === undefined) {
73450
+ return routeBudgetMs;
73451
+ }
73452
+ return Math.min(routeBudgetMs, deadlineAtMs - nowMs);
73453
+ }
73252
73454
  /** Raised internally when a leg exceeds its budget. */
73253
73455
  class LegTimeoutError extends Error {
73254
73456
  /**
@@ -73270,11 +73472,11 @@ class LegTimeoutError extends Error {
73270
73472
  * @param leg The leg to run.
73271
73473
  * @param params Normalised parameters for this leg.
73272
73474
  * @param execution The call context.
73475
+ * @param budgetMs The leg's budget: its route budget cut to the caller's deadline.
73273
73476
  * @returns The provider's answer.
73274
73477
  */
73275
- async function runLeg(leg, params, execution) {
73478
+ async function runLeg(leg, params, execution, budgetMs) {
73276
73479
  const controller = new AbortController();
73277
- const budgetMs = leg.route.timeoutMs;
73278
73480
  const timer = setTimeout(() => {
73279
73481
  controller.abort(new LegTimeoutError(leg.route.routeKey, budgetMs));
73280
73482
  }, budgetMs);
@@ -73296,7 +73498,7 @@ async function runLeg(leg, params, execution) {
73296
73498
  // provider, and only one clock should govern both. The leg's own signal is
73297
73499
  // handed to the guard as well, so a leg whose budget or caller is gone
73298
73500
  // leaves the queue at once instead of holding its place in it.
73299
- return await withProviderGuards(leg.route.providerName, () => leg.transport.execute({
73501
+ const response = await withProviderGuards(leg.route.providerName, () => leg.transport.execute({
73300
73502
  route: leg.route,
73301
73503
  content: execution.content,
73302
73504
  responseFormat: execution.responseFormat,
@@ -73306,6 +73508,8 @@ async function runLeg(leg, params, execution) {
73306
73508
  signal: controller.signal,
73307
73509
  correlationId: execution.correlationId,
73308
73510
  }), budgetMs, { modelId: leg.route.modelId, signal: controller.signal });
73511
+ assertToolChoiceHonoured(leg.route, params, response);
73512
+ return response;
73309
73513
  }
73310
73514
  finally {
73311
73515
  clearTimeout(timer);
@@ -73338,6 +73542,11 @@ function classify(error, callerSignal) {
73338
73542
  if (error instanceof UnsupportedCapabilityError) {
73339
73543
  return { outcome: "skipped", reason: error.message, countsAgainstHealth: false };
73340
73544
  }
73545
+ if (error instanceof ToolChoiceIgnoredError) {
73546
+ // The route answered; it broke a declared guarantee rather than failing to
73547
+ // be available, so its breaker is not charged for it.
73548
+ return { outcome: "error", reason: error.message, countsAgainstHealth: false };
73549
+ }
73341
73550
  if (error instanceof RateGuardTimeoutError) {
73342
73551
  // Self-inflicted pacing, not provider ill-health. Counting it would let the
73343
73552
  // client's own throttling open a breaker on a perfectly healthy provider
@@ -73369,6 +73578,22 @@ function classify(error, callerSignal) {
73369
73578
  function isAborted(signal) {
73370
73579
  return signal !== undefined && signal.aborted;
73371
73580
  }
73581
+ /**
73582
+ * The usage a failed leg was billed for, when the leg reached an answer.
73583
+ *
73584
+ * A leg that failed after the provider answered — content that does not parse,
73585
+ * or prose where a tool call was mandatory — was still charged. A leg that
73586
+ * never answered (timeout, outage, skip) carries no usage, and none is invented.
73587
+ *
73588
+ * @param error The thrown value.
73589
+ * @returns The billed usage, or undefined when the leg never produced an answer.
73590
+ */
73591
+ function billedUsageOf(error) {
73592
+ if (error instanceof LlmResponseFormatError || error instanceof ToolChoiceIgnoredError) {
73593
+ return error.usage;
73594
+ }
73595
+ return undefined;
73596
+ }
73372
73597
  /**
73373
73598
  * Walk a chain until a leg answers.
73374
73599
  *
@@ -73416,10 +73641,27 @@ async function executeChain(alias, execution) {
73416
73641
  execution.onAttempt?.(record);
73417
73642
  continue;
73418
73643
  }
73644
+ const budgetMs = legBudgetMs(route.timeoutMs, execution.deadlineAtMs, now());
73645
+ if (budgetMs <= 0) {
73646
+ // The caller's deadline is spent. Dispatching now would start a call
73647
+ // that is cancelled the moment it begins, and charge nothing but noise.
73648
+ const record = {
73649
+ routeKey: route.routeKey,
73650
+ role: route.role,
73651
+ provider: route.providerName,
73652
+ modelId: route.modelId,
73653
+ outcome: "skipped",
73654
+ durationMs: 0,
73655
+ reason: "caller deadline exhausted before this leg",
73656
+ };
73657
+ attempts.push(record);
73658
+ execution.onAttempt?.(record);
73659
+ continue;
73660
+ }
73419
73661
  const startedAt = now();
73420
73662
  const holdsProbe = execution.breakers.onAttemptStart(route.routeKey);
73421
73663
  try {
73422
- const response = await runLeg(leg, leg.params, execution);
73664
+ const response = await runLeg(leg, leg.params, execution, budgetMs);
73423
73665
  execution.breakers.onSuccess(route.routeKey);
73424
73666
  totalUsage = sumUsage(totalUsage, response.usage);
73425
73667
  const record = {
@@ -73429,6 +73671,8 @@ async function executeChain(alias, execution) {
73429
73671
  modelId: route.modelId,
73430
73672
  outcome: "ok",
73431
73673
  durationMs: now() - startedAt,
73674
+ budgetMs,
73675
+ servedModel: response.servedModel ?? null,
73432
73676
  usage: response.usage,
73433
73677
  };
73434
73678
  attempts.push(record);
@@ -73445,9 +73689,11 @@ async function executeChain(alias, execution) {
73445
73689
  // took must come back, or a half-open route admits no probe ever again.
73446
73690
  execution.breakers.onAttemptAbandoned(route.routeKey);
73447
73691
  }
73448
- // A provider that answered with unparseable content still billed for the
73449
- // answer; the spend belongs in the total whether or not a later leg serves.
73450
- const billed = error instanceof LlmResponseFormatError ? error.usage : undefined;
73692
+ // A provider that answered — with unparseable content, or in prose where a
73693
+ // tool call was mandatory — still billed for the answer; the spend belongs
73694
+ // in the total whether or not a later leg serves.
73695
+ const billed = billedUsageOf(error);
73696
+ const answeredBy = error instanceof ToolChoiceIgnoredError ? error.servedModel : undefined;
73451
73697
  totalUsage = sumUsage(totalUsage, billed);
73452
73698
  const record = {
73453
73699
  routeKey: route.routeKey,
@@ -73456,7 +73702,9 @@ async function executeChain(alias, execution) {
73456
73702
  modelId: route.modelId,
73457
73703
  outcome,
73458
73704
  durationMs: now() - startedAt,
73705
+ budgetMs,
73459
73706
  reason,
73707
+ ...(answeredBy === undefined ? {} : { servedModel: answeredBy }),
73460
73708
  ...(billed === undefined ? {} : { usage: billed }),
73461
73709
  };
73462
73710
  attempts.push(record);
@@ -73648,6 +73896,7 @@ var aliases = {
73648
73896
  supports_tools: true,
73649
73897
  supports_cache_control: true,
73650
73898
  supports_vision: false,
73899
+ supports_tool_choice: false,
73651
73900
  context_window: 1048576
73652
73901
  },
73653
73902
  price_per_mtok: {
@@ -73674,6 +73923,7 @@ var aliases = {
73674
73923
  supports_tools: true,
73675
73924
  supports_cache_control: true,
73676
73925
  supports_vision: false,
73926
+ supports_tool_choice: true,
73677
73927
  context_window: 1048576
73678
73928
  },
73679
73929
  price_per_mtok: {
@@ -73700,6 +73950,7 @@ var aliases = {
73700
73950
  supports_tools: true,
73701
73951
  supports_cache_control: true,
73702
73952
  supports_vision: true,
73953
+ supports_tool_choice: true,
73703
73954
  context_window: 1000000
73704
73955
  },
73705
73956
  price_per_mtok: {
@@ -73744,6 +73995,7 @@ var aliases = {
73744
73995
  supports_tools: true,
73745
73996
  supports_cache_control: true,
73746
73997
  supports_vision: true,
73998
+ supports_tool_choice: true,
73747
73999
  context_window: 1048576
73748
74000
  },
73749
74001
  price_per_mtok: {
@@ -73770,6 +74022,7 @@ var aliases = {
73770
74022
  supports_tools: true,
73771
74023
  supports_cache_control: true,
73772
74024
  supports_vision: false,
74025
+ supports_tool_choice: false,
73773
74026
  context_window: 1048576
73774
74027
  },
73775
74028
  price_per_mtok: {
@@ -73796,6 +74049,7 @@ var aliases = {
73796
74049
  supports_tools: true,
73797
74050
  supports_cache_control: true,
73798
74051
  supports_vision: true,
74052
+ supports_tool_choice: true,
73799
74053
  context_window: 1000000
73800
74054
  },
73801
74055
  price_per_mtok: {
@@ -73840,6 +74094,7 @@ var aliases = {
73840
74094
  supports_tools: true,
73841
74095
  supports_cache_control: true,
73842
74096
  supports_vision: false,
74097
+ supports_tool_choice: false,
73843
74098
  context_window: 1048576
73844
74099
  },
73845
74100
  price_per_mtok: {
@@ -73866,6 +74121,7 @@ var aliases = {
73866
74121
  supports_tools: true,
73867
74122
  supports_cache_control: false,
73868
74123
  supports_vision: false,
74124
+ supports_tool_choice: true,
73869
74125
  context_window: 131072
73870
74126
  },
73871
74127
  price_per_mtok: {
@@ -73892,6 +74148,7 @@ var aliases = {
73892
74148
  supports_tools: true,
73893
74149
  supports_cache_control: true,
73894
74150
  supports_vision: true,
74151
+ supports_tool_choice: true,
73895
74152
  context_window: 200000
73896
74153
  },
73897
74154
  price_per_mtok: {
@@ -73935,6 +74192,7 @@ var aliases = {
73935
74192
  supports_tools: true,
73936
74193
  supports_cache_control: false,
73937
74194
  supports_vision: false,
74195
+ supports_tool_choice: true,
73938
74196
  context_window: 131072
73939
74197
  },
73940
74198
  price_per_mtok: {
@@ -73961,6 +74219,7 @@ var aliases = {
73961
74219
  supports_tools: true,
73962
74220
  supports_cache_control: true,
73963
74221
  supports_vision: false,
74222
+ supports_tool_choice: true,
73964
74223
  context_window: 262144
73965
74224
  },
73966
74225
  price_per_mtok: {
@@ -73987,6 +74246,7 @@ var aliases = {
73987
74246
  supports_tools: true,
73988
74247
  supports_cache_control: true,
73989
74248
  supports_vision: true,
74249
+ supports_tool_choice: true,
73990
74250
  context_window: 200000
73991
74251
  },
73992
74252
  price_per_mtok: {
@@ -74032,6 +74292,7 @@ var aliases = {
74032
74292
  supports_tools: true,
74033
74293
  supports_cache_control: true,
74034
74294
  supports_vision: false,
74295
+ supports_tool_choice: false,
74035
74296
  context_window: 1048576
74036
74297
  },
74037
74298
  price_per_mtok: {
@@ -74043,6 +74304,105 @@ var aliases = {
74043
74304
  notes: "Pinned with no fallback (PD-6). A judge that failed over would grade one model's output against another model's standard, corrupting every gate decided by it."
74044
74305
  }
74045
74306
  ]
74307
+ },
74308
+ "llm.decide": {
74309
+ workload: "Per-symbol trading decision: a multi-turn tool-calling read that ends in exactly one decision tool call",
74310
+ latency_class: "hot-path",
74311
+ criticality: "trading-adjacent",
74312
+ isolation_capable: false,
74313
+ eval_gate: "tool-call",
74314
+ policy_note: "The dedicated decision alias. The per-symbol decision call is a trading decision, not a latency-critical extraction, so it gets its own chain instead of borrowing llm.fast's. It is evaluated in SHADOW beside the live llm.fast path (schema-valid rate, latency, served model, action agreement) and serves live traffic only through the engine's governed routing flag after that evidence. Leg budgets are the hot-path class's today; budgets set from each model's measured p95 are a separate, evidence-gated change. Its monthly budget bounds the shadow's own spend and is added to the gateway total, so the shadow cannot consume the live aliases' budget.",
74315
+ budget: {
74316
+ basis: "provisional-pre-baseline",
74317
+ monthly_usd: 600,
74318
+ alert_pct: [
74319
+ 50,
74320
+ 80,
74321
+ 100
74322
+ ]
74323
+ },
74324
+ routes: [
74325
+ {
74326
+ role: "primary",
74327
+ provider: "deepinfra",
74328
+ model_id: "deepseek-ai/DeepSeek-V4-Pro",
74329
+ model_id_status: "confirmed",
74330
+ model_id_source: "DeepInfra /v1/openai/models catalogue queried against the live account 2026-09-11; tool-calling and latency verified by direct call on the same account",
74331
+ model_family: "DeepSeek V4 Pro",
74332
+ lumic_model: "deepseek-ai/DeepSeek-V4-Pro",
74333
+ params: {
74334
+ temperature: null,
74335
+ max_output_tokens: null,
74336
+ supports_temperature: true,
74337
+ supports_json_schema: true,
74338
+ supports_tools: true,
74339
+ supports_cache_control: true,
74340
+ supports_vision: false,
74341
+ supports_tool_choice: false,
74342
+ context_window: 1048576
74343
+ },
74344
+ price_per_mtok: {
74345
+ input: 1.3,
74346
+ output: 2.6,
74347
+ as_of: "2026-09-11",
74348
+ source: "DeepInfra /v1/openai/models catalogue queried against the live account 2026-09-11; tool-calling and latency verified by direct call on the same account"
74349
+ },
74350
+ notes: "The advanced tier the per-symbol decision prompt was written for: a long, multi-turn, tool-calling read of about 21k prompt tokens, where the hot-path alias's small models answer faster but lose the schema contract more often. It ignores a mandatory tool_choice (measured), so the decision contract's own validation stays the guard on this leg."
74351
+ },
74352
+ {
74353
+ role: "secondary",
74354
+ provider: "deepinfra",
74355
+ model_id: "deepseek-ai/DeepSeek-V4-Flash",
74356
+ model_id_status: "confirmed",
74357
+ model_id_source: "DeepInfra /v1/openai/models catalogue queried against the live account 2026-09-11; tool-calling and latency verified by direct call on the same account",
74358
+ model_family: "DeepSeek V4 Flash",
74359
+ lumic_model: "deepseek-ai/DeepSeek-V4-Flash",
74360
+ params: {
74361
+ temperature: null,
74362
+ max_output_tokens: null,
74363
+ supports_temperature: true,
74364
+ supports_json_schema: true,
74365
+ supports_tools: true,
74366
+ supports_cache_control: true,
74367
+ supports_vision: false,
74368
+ supports_tool_choice: false,
74369
+ context_window: 1048576
74370
+ },
74371
+ price_per_mtok: {
74372
+ input: 0.09,
74373
+ output: 0.18,
74374
+ as_of: "2026-09-11",
74375
+ source: "DeepInfra /v1/openai/models catalogue queried against the live account 2026-09-11; tool-calling and latency verified by direct call on the same account"
74376
+ },
74377
+ notes: "The current decision model, kept as the secondary so a slow or failing primary falls back to the model the decision path already runs on."
74378
+ },
74379
+ {
74380
+ role: "closed_incumbent",
74381
+ provider: "anthropic",
74382
+ model_id: "claude-haiku-4-5",
74383
+ model_id_status: "confirmed",
74384
+ model_id_source: "lumic-utils/src/types/openai-types.ts SUPPORTED_MODELS — already routed on in production",
74385
+ model_family: "Haiku-class",
74386
+ lumic_model: "claude-haiku-4-5",
74387
+ params: {
74388
+ temperature: null,
74389
+ max_output_tokens: 64000,
74390
+ supports_temperature: true,
74391
+ supports_json_schema: true,
74392
+ supports_tools: true,
74393
+ supports_cache_control: true,
74394
+ supports_vision: true,
74395
+ supports_tool_choice: true,
74396
+ context_window: 200000
74397
+ },
74398
+ price_per_mtok: {
74399
+ input: 1,
74400
+ output: 5,
74401
+ as_of: "2026-09-10",
74402
+ source: "docs/llm-provider-migration.md#3 Haiku-class anchor"
74403
+ }
74404
+ }
74405
+ ]
74046
74406
  }
74047
74407
  };
74048
74408
  var open_items = [
@@ -74532,6 +74892,10 @@ function createDirectTransport(config) {
74532
74892
  tool_calls: Array.isArray(result.tool_calls)
74533
74893
  ? result.tool_calls
74534
74894
  : undefined,
74895
+ // The incumbent client reports the model it resolved; unreported stays null.
74896
+ servedModel: typeof result.usage?.model === "string" && result.usage.model !== ""
74897
+ ? result.usage.model
74898
+ : null,
74535
74899
  };
74536
74900
  },
74537
74901
  };
@@ -74539,9 +74903,9 @@ function createDirectTransport(config) {
74539
74903
  /**
74540
74904
  * Normalise the incumbent client's usage shape.
74541
74905
  *
74542
- * Missing counts stay zero rather than being estimated, for the same reason
74543
- * they do on the gateway path: an invented token count flows straight into the
74544
- * budget accounting the spend controls depend on.
74906
+ * A missing count is `null` rather than zero or an estimate, for the same
74907
+ * reason as on the gateway path: an invented token count — zero included —
74908
+ * flows straight into the budget accounting the spend controls depend on.
74545
74909
  *
74546
74910
  * @param usage The incumbent client's usage, if any.
74547
74911
  * @param request The request it answers.
@@ -74549,13 +74913,13 @@ function createDirectTransport(config) {
74549
74913
  */
74550
74914
  function readUsage$1(usage, request) {
74551
74915
  return {
74552
- prompt_tokens: usage?.prompt_tokens ?? 0,
74553
- completion_tokens: usage?.completion_tokens ?? 0,
74916
+ prompt_tokens: usage?.prompt_tokens ?? null,
74917
+ completion_tokens: usage?.completion_tokens ?? null,
74554
74918
  reasoning_tokens: usage?.reasoning_tokens,
74555
74919
  cached_tokens: usage?.cached_tokens,
74556
74920
  provider: usage?.provider ?? request.route.providerName,
74557
74921
  model: usage?.model ?? request.route.modelId,
74558
- cost: usage?.cost ?? 0,
74922
+ cost: usage?.cost ?? null,
74559
74923
  };
74560
74924
  }
74561
74925
  /**
@@ -74608,10 +74972,12 @@ async function resolveDefaultDirectCaller() {
74608
74972
  * never in application code (PD-5), which is what makes a model swap a config
74609
74973
  * change rather than a deploy.
74610
74974
  *
74611
- * The client still walks its own chain on top of the gateway's, and the
74612
- * duplication is deliberate. The gateway's fallbacks cover a provider being
74613
- * down; the client's cover the gateway being down. Only one of those two can
74614
- * cover the other, so the outer chain is the one that must exist.
74975
+ * The client's chain is the single fallback owner: every leg is addressed to the
74976
+ * gateway by its own model name, so the gateway serves one deployment per leg
74977
+ * and needs no fallback of its own. A proxy-side fallback inside a leg would
74978
+ * spend the leg's budget on a model the chain did not choose and report the
74979
+ * answer as the leg's; the served model is read from the response body so such
74980
+ * a substitution stays visible while any remains configured.
74615
74981
  *
74616
74982
  * The gateway key is read from the environment by NAME at call time and never
74617
74983
  * stored, logged, or included in an error (PD-2). Reading it per call rather
@@ -74623,6 +74989,10 @@ async function resolveDefaultDirectCaller() {
74623
74989
  const RETRYABLE_STATUSES = new Set([408, 409, 425, 429, 500, 502, 503, 504]);
74624
74990
  /** Maximum characters of an error body echoed into a message. */
74625
74991
  const ERROR_BODY_EXCERPT = 400;
74992
+ /** Response header in which the LiteLLM proxy reports the call's cost in USD. */
74993
+ const RESPONSE_COST_HEADER = "x-litellm-response-cost";
74994
+ /** Response header naming the proxy deployment that served the call. */
74995
+ const DEPLOYMENT_ID_HEADER = "x-litellm-model-id";
74626
74996
  /**
74627
74997
  * Thrown when the gateway itself is unreachable, as opposed to a provider
74628
74998
  * behind it failing.
@@ -74679,31 +75049,67 @@ function readGatewayKey(envVar) {
74679
75049
  /**
74680
75050
  * Extract usage from a chat-completion response.
74681
75051
  *
74682
- * Absent counts stay zero rather than being estimated. A fabricated token count
74683
- * would flow straight into the budget accounting that the spend controls are
74684
- * built on, and a budget computed from invented numbers is worse than one that
74685
- * knows it is missing a call.
75052
+ * A count the provider did not report is `null`, never zero and never an
75053
+ * estimate. A fabricated count — zero included — would flow straight into the
75054
+ * budget accounting the spend controls are built on, where a zero reads as a
75055
+ * free call and can never trip a limit.
75056
+ *
75057
+ * Cost is read from the body's `usage.response_cost` or, failing that, from the
75058
+ * proxy's cost header, which is where the LiteLLM proxy reports it.
74686
75059
  *
74687
75060
  * @param payload The parsed response body.
74688
75061
  * @param request The request it answers.
75062
+ * @param headers The response headers.
74689
75063
  * @returns The usage record.
74690
75064
  */
74691
- function readUsage(payload, request) {
75065
+ function readUsage(payload, request, headers) {
74692
75066
  const usage = (payload.usage ?? {});
74693
75067
  const details = (usage.prompt_tokens_details ?? {});
74694
75068
  const cached = details.cached_tokens;
74695
75069
  const reasoningDetails = (usage.completion_tokens_details ?? {});
74696
75070
  const reasoning = reasoningDetails.reasoning_tokens;
74697
75071
  return {
74698
- prompt_tokens: typeof usage.prompt_tokens === "number" ? usage.prompt_tokens : 0,
74699
- completion_tokens: typeof usage.completion_tokens === "number" ? usage.completion_tokens : 0,
75072
+ prompt_tokens: finiteOrNull(usage.prompt_tokens),
75073
+ completion_tokens: finiteOrNull(usage.completion_tokens),
74700
75074
  reasoning_tokens: typeof reasoning === "number" ? reasoning : undefined,
74701
75075
  cached_tokens: typeof cached === "number" ? cached : undefined,
74702
75076
  provider: request.route.providerName,
74703
75077
  model: request.route.modelId,
74704
- cost: typeof usage.response_cost === "number" ? usage.response_cost : 0,
75078
+ cost: finiteOrNull(usage.response_cost) ?? headerNumber(headers, RESPONSE_COST_HEADER),
74705
75079
  };
74706
75080
  }
75081
+ /**
75082
+ * A reported number, or null when it was not reported as a finite number.
75083
+ *
75084
+ * @param value The raw value.
75085
+ * @returns The number, or null.
75086
+ */
75087
+ function finiteOrNull(value) {
75088
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
75089
+ }
75090
+ /**
75091
+ * A numeric response header, or null when absent or not a finite number.
75092
+ *
75093
+ * @param headers The response headers, if the transport exposes them.
75094
+ * @param name The header name.
75095
+ * @returns The number, or null.
75096
+ */
75097
+ function headerNumber(headers, name) {
75098
+ const raw = headers?.get(name);
75099
+ if (raw === undefined || raw === null || raw.trim() === "") {
75100
+ return null;
75101
+ }
75102
+ return finiteOrNull(Number(raw));
75103
+ }
75104
+ /**
75105
+ * A non-empty string, or null.
75106
+ *
75107
+ * @param value The raw value.
75108
+ * @returns The string, or null.
75109
+ */
75110
+ function nonEmptyOrNull(value) {
75111
+ return typeof value === "string" && value.trim() !== "" ? value : null;
75112
+ }
74707
75113
  /**
74708
75114
  * Build the gateway transport.
74709
75115
  *
@@ -74754,13 +75160,17 @@ function createGatewayTransport(config) {
74754
75160
  // Usage is read before the content is interpreted. The provider billed for
74755
75161
  // this answer whether or not it parses, and a parse failure that dropped
74756
75162
  // the count would report the attempt as free.
74757
- const usage = readUsage(payload, request);
75163
+ const usage = readUsage(payload, request, response.headers);
74758
75164
  return {
74759
75165
  response: interpretContent(message?.content, request.responseFormat, usage),
74760
75166
  usage,
74761
75167
  tool_calls: Array.isArray(message?.tool_calls)
74762
75168
  ? message.tool_calls
74763
75169
  : undefined,
75170
+ // The model the provider says answered, which a proxy-side fallback can
75171
+ // make differ from the leg's route model; unreported stays null.
75172
+ servedModel: nonEmptyOrNull(payload.model),
75173
+ servedDeploymentId: nonEmptyOrNull(response.headers?.get(DEPLOYMENT_ID_HEADER)),
74764
75174
  };
74765
75175
  },
74766
75176
  };
@@ -74829,7 +75239,10 @@ function interpretContent(content, responseFormat, usage) {
74829
75239
  * table, per-provider parameter normalisation, a hard per-leg timeout, a
74830
75240
  * per-route circuit breaker, an ordered fallback chain ending at the closed
74831
75241
  * incumbent, and — where the caller supplies a validator — one schema-feedback
74832
- * retry ahead of the chain. None of them is optional, because a control that a
75242
+ * retry ahead of the chain. The caller's `timeoutMs` is ONE deadline for the
75243
+ * whole call: each leg runs for its route budget or for what remains of that
75244
+ * deadline, whichever is shorter, so the chain is the single fallback owner and
75245
+ * a slow primary cannot spend the time its fallbacks need. None of them is optional, because a control that a
74833
75246
  * caller can switch off is a control that will be off on the call that needed
74834
75247
  * it.
74835
75248
  *
@@ -74965,10 +75378,14 @@ function prepareLegs(chain, options, responseFormat, transport) {
74965
75378
  * @throws {SchemaRetryExhaustedError} When a validated payload failed twice.
74966
75379
  */
74967
75380
  async function callLLMByAlias(content, responseFormat = "text", options) {
74968
- const chain = resolveChain(options.alias, {
74969
- isolated: options.isolated,
74970
- timeoutMsOverride: options.timeoutMs,
74971
- });
75381
+ // Legs keep their route budgets; the caller's timeout is the deadline they
75382
+ // share, fixed once here so a validation retry and the degraded direct path
75383
+ // spend what is left of it rather than starting a fresh one.
75384
+ const chain = resolveChain(options.alias, { isolated: options.isolated });
75385
+ const clock = config.now ?? Date.now;
75386
+ const deadlineAtMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs)
75387
+ ? clock() + options.timeoutMs
75388
+ : undefined;
74972
75389
  if (chain.routes.length === 0) {
74973
75390
  // Nothing is servable. The exclusions say why each leg was unavailable,
74974
75391
  // which is the difference between an operator reading config and an
@@ -75011,6 +75428,7 @@ async function callLLMByAlias(content, responseFormat = "text", options) {
75011
75428
  breakers,
75012
75429
  correlationId: options.correlationId,
75013
75430
  callerSignal: options.signal,
75431
+ deadlineAtMs,
75014
75432
  now: config.now,
75015
75433
  onAttempt: (record) => attemptLog.push(record),
75016
75434
  });
@@ -75045,6 +75463,7 @@ async function callLLMByAlias(content, responseFormat = "text", options) {
75045
75463
  breakers,
75046
75464
  correlationId: options.correlationId,
75047
75465
  callerSignal: options.signal,
75466
+ deadlineAtMs,
75048
75467
  now: config.now,
75049
75468
  onAttempt: (record) => attemptLog.push(record),
75050
75469
  });
@@ -75057,6 +75476,7 @@ async function callLLMByAlias(content, responseFormat = "text", options) {
75057
75476
  usage: outcome.response.usage,
75058
75477
  tool_calls: outcome.response.tool_calls,
75059
75478
  servedBy: outcome.servedBy,
75479
+ servedModel: outcome.response.servedModel ?? null,
75060
75480
  attempts: attemptLog,
75061
75481
  degraded: outcome.degraded,
75062
75482
  totalUsage: outcome.totalUsage,
@@ -75090,6 +75510,7 @@ async function callLLMByAlias(content, responseFormat = "text", options) {
75090
75510
  usage: validated.response.usage,
75091
75511
  tool_calls: validated.response.tool_calls,
75092
75512
  servedBy: routing.servedBy,
75513
+ servedModel: validated.response.servedModel ?? null,
75093
75514
  attempts: attemptLog,
75094
75515
  degraded: routing.degraded,
75095
75516
  totalUsage: validated.totalUsage,
@@ -80512,5 +80933,5 @@ const adaptic = {
80512
80933
  };
80513
80934
  const adptc = adaptic;
80514
80935
 
80515
- export { API_RETRY_CONFIGS, AVNewsArticleSchema, AVNewsResponseSchema, AdapticUtilsError, AlpacaAccountDetailsSchema, AlpacaApiError, AlpacaBarSchema, AlpacaClient, AlpacaCryptoBarsResponseSchema, AlpacaHistoricalBarsResponseSchema, AlpacaLatestBarsResponseSchema, AlpacaLatestQuotesResponseSchema, AlpacaLatestTradesResponseSchema, AlpacaMarketDataAPI, AlpacaNewsArticleSchema, AlpacaNewsResponseSchema, AlpacaOrderSchema, AlpacaOrdersArraySchema, AlpacaPortfolioHistoryResponseSchema, AlpacaPositionSchema, AlpacaPositionsArraySchema, AlpacaQuoteSchema, AlpacaTradeSchema, AlpacaTradingAPI, AlphaVantageError, AlphaVantageQuoteResponseSchema, AssetAllocationEngine, AuthenticationError, AutonomyMode, BTC_PAIRS, BarError, ChainExhaustedError, CircuitBreakerRegistry, CircuitOpenError, CryptoDataError, CryptoOrderError, DEFAULT_CACHE_OPTIONS, DEFAULT_RISK_FREE_RATE, DEFAULT_TIMEOUTS, DEFAULT_TRADING_POLICY, DataFormatError, DecisionMemoryOutcome, DecisionOutcome, DecisionRecordStatus, DirectTransportRefusedError, DuplicateClientOrderIdError, GatewayResponseError, GatewayUnreachableError, HttpClientError, HttpServerError, KEEP_ALIVE_DEFAULTS, LlmProvider, LlmResponseFormatError, MARKET_DATA_API, MassiveAggregatesResponseSchema, MassiveApiError, MassiveDailyOpenCloseSchema, MassiveErrorResponseSchema, MassiveGroupedDailyResponseSchema, MassiveLastTradeResponseSchema, MassiveTickerDetailsResponseSchema, MassiveTickerInfoSchema, MassiveTradeSchema as MassiveTradeZodSchema, MassiveTradesResponseSchema, NetworkError, NewsError, NoServableRouteError, OptionStrategyError, OptionsDataError, OverlaySeverity, OverlayStatus, OverlayType, QuoteError, RISK_FREE_RATE_TTL_MS, RateGuardTimeoutError, RateLimitError, RawMassivePriceDataSchema, SchemaRetryExhaustedError, StampedeProtectedCache, StreamProviderError, StreamTruncatedError, TRADING_API, TimeoutError, TokenBucketRateLimiter, TradeError, TrailingStopValidationError, USDC_PAIRS, USDT_PAIRS, USD_PAIRS, UnknownAliasError, UnsupportedBrokerError, UnsupportedCapabilityError, ValidationError, ValidationResponseError, WEBSOCKET_STREAMS, WebSocketError, account, adaptic, adptc, alpaca, analyzeBars, approximateImpliedVolatility, atrNs as atr, availableStatistic, bracketOrders, buildOCCSymbol, buildOptionSymbol, buildRetryPrompt, buyCryptoNotional, buyToClose, buyToOpen, buyWithStopLoss, buyWithTrailingStop, calculateMoneyness, calculateOrderValue, calculatePeriodPerformance, calculatePutCallRatio, calculateTotalFilledValue, callLLMByAlias, callWithValidation, cancelAllCryptoOrders, cancelOCOOrder, cancelOTOOrder, cancelTrailingStop, cancelTrailingStopsForSymbol, checkTradingEligibility, clearClientCache, clock, closeAllOptionPositions, closeOptionPosition, closedIncumbentLeg, collectStream, configureLlmClient, createAlpacaClient, createAlpacaMarketDataAPI, createAlpacaTradingAPI, createBracketOrder, createBrokerClient, createButterflySpread, createClientFromEnv, createCoveredCall, createCryptoLimitOrder, createCryptoMarketOrder, createCryptoOrder, createCryptoStopLimitOrder, createCryptoStopOrder, createDirectTransport, createExecutorFromTradingAPI, createGatewayTransport, createIronCondor$1 as createIronCondor, createIronCondor as createIronCondorAdvanced, createMultiLegOptionOrder, createOCOOrder, createOTOOrder, createOptionOrder, createPortfolioTrailingStops, createProtectiveBracket, createStampedeProtectedCache, createStraddle$1 as createStraddle, createStraddle as createStraddleAdvanced, createStrangle$1 as createStrangle, createStrangle as createStrangleAdvanced, createStreamManager, createTimeoutSignal, createTrailingStop, createVerticalSpread$1 as createVerticalSpread, createVerticalSpread as createVerticalSpreadAdvanced, enrichAlpacaError, entryWithPercentStopLoss, exerciseOption, extractAlpacaBrokerError, extractGreeks, filterByExpiration, filterByStrike, filterByType, filterOrdersByDateRange, findATMOptions, findATMStrikes, findNearestExpiration, findOptionsByDelta, formatOrderForLog, formatOrderSummary, gatewayModelNameFor, generateOptimalAllocation, getAccountConfiguration, getAccountDetails, getAccountSummary, getAgentPoolStatus, getAllOrders, getAlpacaBrokerErrorCode, getAlpacaBrokerErrorDetail, getAlpacaCalendar, getAlpacaClock, getAverageDailyVolume, getBars, getBuyingPower, getCachedRiskFreeRateSync, getCachedRiskFreeRateSyncWithProvenance, getCrypto24HourChange, getCryptoBars, getCryptoDailyPrices, getCryptoPairsByQuote, getCryptoPrice, getCryptoSnapshots, getCryptoSpread, getCryptoStreamUrl, getCryptoTrades, getCurrentPrice, getCurrentPrices, getDailyPrices, getDailyReturns, getDaysToExpiration, getDefaultRiskProfile, getEquityCurve, getExpirationDates, getFilledOrders, getGroupedOptionChain, getHistoricalOptionsBars, getHistoricalTrades, getIntradayPrices, getLatestBars, getLatestCryptoQuotes, getLatestCryptoTrades, getLatestNews, getLatestOptionsQuotes, getLatestOptionsTrades, getLatestQuote, getLatestQuotes, getLatestTrade, getLatestTrades, getLogger, getMarginInfo, getNews, getNewsForSymbols, getOCOOrderStatus, getOTOOrderStatus, getOpenCryptoOrders, getOpenOrders$1 as getOpenOrdersQuery, getOpenTrailingStops, getOptionChain, getOptionContract, getOptionContracts, getOptionSpread, getOptionsChain, getOptionsSnapshots, getOptionsStreamUrl, getOptionsTradingLevel, getOrderHistory, getOrdersBySymbol, getPDTStatus, getPopularCryptoPairs, getPortfolioHistory, getPreviousClose, getPriceRange, getRiskFreeRate, getRiskFreeRateWithProvenance, getSpread, getSpreads, getStockStreamUrl, getStrikePrices, getSupportedCryptoPairs, getSymbolSentiment, getTimeout, getTradeVolume, getTradingApiUrl, getTradingWebSocketUrl, getTrailingStopHWM, groupOrdersByStatus, groupOrdersBySymbol, guardSnapshots, hasActiveTrailingStop, hasGoodLiquidity as hasOptionLiquidity, hasGoodLiquidity$1 as hasStockLiquidity, hasSufficientVolume, httpAgent, httpsAgent, isAlpacaBrokerCredentials, isAvailable, isContractTradable, isCryptoPair, isExpiringWithin, isMarginAccount, isOptionOrderCancelable, isOptionOrderTerminal, isOrderFillable, isOrderFilled, isOrderOpen, isOrderTerminal$1 as isOrderTerminalStatus, isSupportedCryptoPair, isTransientNetworkError, index$1 as legacyApi, limitBuyWithTakeProfit, limitsFor, limitsInventory, listAliases, llmAliases, llmBreakers, normaliseAnthropicStream, normaliseOpenAiStream, normaliseParams, normaliseStream, ocoOrders, orderUtils, orderedRoutes, otoOrders, paginate, paginateAll, parseOCCSymbol, protectLongPosition, protectShortPosition, rateLimiters$1 as rateLimiters, resetLogger, resetProviderGuards, resetRiskFreeRateCache, resolveChain, resolveDefaultDirectCaller, riskNs as risk, rollOptionPosition, roundPriceForAlpaca$3 as roundPriceForAlpaca, roundPriceForAlpacaNumber, routeKeyFor, routeSupports, routeTable, safeValidateResponse, sampleCohort, searchNews, sellAllCrypto, sellCryptoNotional, sellToClose, sellToOpen, setLogger, setRiskFreeRate, shortWithStopLoss, sortOrdersByDate, strategyNs as strategy, sumUsage, index as tradingPolicy, trailingStops, unavailableStatistic, updateAccountConfiguration, updateTrailingStop, validateAlpacaCredentials, validateAlphaVantageApiKey, validateMassiveApiKey$1 as validateMassiveApiKey, validateMultiLegOrder, validateResponse, verifyFetchKeepAlive, volatilityNs as volatility, waitForOrderFill, withProviderGuards, withRetry, withTimeout };
80936
+ export { API_RETRY_CONFIGS, AVNewsArticleSchema, AVNewsResponseSchema, AdapticUtilsError, AlpacaAccountDetailsSchema, AlpacaApiError, AlpacaBarSchema, AlpacaClient, AlpacaCryptoBarsResponseSchema, AlpacaHistoricalBarsResponseSchema, AlpacaLatestBarsResponseSchema, AlpacaLatestQuotesResponseSchema, AlpacaLatestTradesResponseSchema, AlpacaMarketDataAPI, AlpacaNewsArticleSchema, AlpacaNewsResponseSchema, AlpacaOrderSchema, AlpacaOrdersArraySchema, AlpacaPortfolioHistoryResponseSchema, AlpacaPositionSchema, AlpacaPositionsArraySchema, AlpacaQuoteSchema, AlpacaTradeSchema, AlpacaTradingAPI, AlphaVantageError, AlphaVantageQuoteResponseSchema, AssetAllocationEngine, AuthenticationError, AutonomyMode, BTC_PAIRS, BarError, ChainExhaustedError, CircuitBreakerRegistry, CircuitOpenError, CryptoDataError, CryptoOrderError, DEFAULT_CACHE_OPTIONS, DEFAULT_RISK_FREE_RATE, DEFAULT_TIMEOUTS, DEFAULT_TRADING_POLICY, DataFormatError, DecisionMemoryOutcome, DecisionOutcome, DecisionRecordStatus, DirectTransportRefusedError, DuplicateClientOrderIdError, GatewayResponseError, GatewayUnreachableError, HttpClientError, HttpServerError, KEEP_ALIVE_DEFAULTS, LlmProvider, LlmResponseFormatError, MARKET_DATA_API, MassiveAggregatesResponseSchema, MassiveApiError, MassiveDailyOpenCloseSchema, MassiveErrorResponseSchema, MassiveGroupedDailyResponseSchema, MassiveLastTradeResponseSchema, MassiveTickerDetailsResponseSchema, MassiveTickerInfoSchema, MassiveTradeSchema as MassiveTradeZodSchema, MassiveTradesResponseSchema, NetworkError, NewsError, NoServableRouteError, OptionStrategyError, OptionsDataError, OverlaySeverity, OverlayStatus, OverlayType, PENDING_CANCEL_ERROR_CODE, PendingCancelError, QuoteError, RISK_FREE_RATE_TTL_MS, RateGuardTimeoutError, RateLimitError, RawMassivePriceDataSchema, SchemaRetryExhaustedError, StampedeProtectedCache, StreamProviderError, StreamTruncatedError, TRADING_API, TimeoutError, TokenBucketRateLimiter, ToolChoiceIgnoredError, TradeError, TrailingStopValidationError, USDC_PAIRS, USDT_PAIRS, USD_PAIRS, UnknownAliasError, UnsupportedBrokerError, UnsupportedCapabilityError, ValidationError, ValidationResponseError, WEBSOCKET_STREAMS, WebSocketError, account, adaptic, adptc, alpaca, analyzeBars, approximateImpliedVolatility, assertToolChoiceHonoured, atrNs as atr, availableStatistic, bracketOrders, buildOCCSymbol, buildOptionSymbol, buildRetryPrompt, buyCryptoNotional, buyToClose, buyToOpen, buyWithStopLoss, buyWithTrailingStop, calculateMoneyness, calculateOrderValue, calculatePeriodPerformance, calculatePutCallRatio, calculateTotalFilledValue, callLLMByAlias, callWithValidation, cancelAllCryptoOrders, cancelOCOOrder, cancelOTOOrder, cancelTrailingStop, cancelTrailingStopsForSymbol, checkTradingEligibility, clearClientCache, clock, closeAllOptionPositions, closeOptionPosition, closedIncumbentLeg, collectStream, configureLlmClient, createAlpacaClient, createAlpacaMarketDataAPI, createAlpacaTradingAPI, createBracketOrder, createBrokerClient, createButterflySpread, createClientFromEnv, createCoveredCall, createCryptoLimitOrder, createCryptoMarketOrder, createCryptoOrder, createCryptoStopLimitOrder, createCryptoStopOrder, createDirectTransport, createExecutorFromTradingAPI, createGatewayTransport, createIronCondor$1 as createIronCondor, createIronCondor as createIronCondorAdvanced, createMultiLegOptionOrder, createOCOOrder, createOTOOrder, createOptionOrder, createPortfolioTrailingStops, createProtectiveBracket, createStampedeProtectedCache, createStraddle$1 as createStraddle, createStraddle as createStraddleAdvanced, createStrangle$1 as createStrangle, createStrangle as createStrangleAdvanced, createStreamManager, createTimeoutSignal, createTrailingStop, createVerticalSpread$1 as createVerticalSpread, createVerticalSpread as createVerticalSpreadAdvanced, enrichAlpacaError, entryWithPercentStopLoss, exerciseOption, extractAlpacaBrokerError, extractGreeks, filterByExpiration, filterByStrike, filterByType, filterOrdersByDateRange, findATMOptions, findATMStrikes, findNearestExpiration, findOptionsByDelta, formatOrderForLog, formatOrderSummary, gatewayModelNameFor, generateOptimalAllocation, getAccountConfiguration, getAccountDetails, getAccountSummary, getAgentPoolStatus, getAllOrders, getAlpacaBrokerErrorCode, getAlpacaBrokerErrorDetail, getAlpacaCalendar, getAlpacaClock, getAverageDailyVolume, getBars, getBuyingPower, getCachedRiskFreeRateSync, getCachedRiskFreeRateSyncWithProvenance, getCrypto24HourChange, getCryptoBars, getCryptoDailyPrices, getCryptoPairsByQuote, getCryptoPrice, getCryptoSnapshots, getCryptoSpread, getCryptoStreamUrl, getCryptoTrades, getCurrentPrice, getCurrentPrices, getDailyPrices, getDailyReturns, getDaysToExpiration, getDefaultRiskProfile, getEquityCurve, getExpirationDates, getFilledOrders, getGroupedOptionChain, getHistoricalOptionsBars, getHistoricalTrades, getIntradayPrices, getLatestBars, getLatestCryptoQuotes, getLatestCryptoTrades, getLatestNews, getLatestOptionsQuotes, getLatestOptionsTrades, getLatestQuote, getLatestQuotes, getLatestTrade, getLatestTrades, getLogger, getMarginInfo, getNews, getNewsForSymbols, getOCOOrderStatus, getOTOOrderStatus, getOpenCryptoOrders, getOpenOrders$1 as getOpenOrdersQuery, getOpenTrailingStops, getOptionChain, getOptionContract, getOptionContracts, getOptionSpread, getOptionsChain, getOptionsSnapshots, getOptionsStreamUrl, getOptionsTradingLevel, getOrderHistory, getOrdersBySymbol, getPDTStatus, getPopularCryptoPairs, getPortfolioHistory, getPreviousClose, getPriceRange, getRiskFreeRate, getRiskFreeRateWithProvenance, getSpread, getSpreads, getStockStreamUrl, getStrikePrices, getSupportedCryptoPairs, getSymbolSentiment, getTimeout, getTradeVolume, getTradingApiUrl, getTradingWebSocketUrl, getTrailingStopHWM, groupOrdersByStatus, groupOrdersBySymbol, guardSnapshots, hasActiveTrailingStop, hasGoodLiquidity as hasOptionLiquidity, hasGoodLiquidity$1 as hasStockLiquidity, hasSufficientVolume, httpAgent, httpsAgent, isAlpacaBrokerCredentials, isAvailable, isContractTradable, isCryptoPair, isExpiringWithin, isMarginAccount, isOptionOrderCancelable, isOptionOrderTerminal, isOrderFillable, isOrderFilled, isOrderOpen, isOrderTerminal$1 as isOrderTerminalStatus, isPendingCancelRejection, isSupportedCryptoPair, isTransientNetworkError, legBudgetMs, index$1 as legacyApi, limitBuyWithTakeProfit, limitsFor, limitsInventory, listAliases, llmAliases, llmBreakers, normaliseAnthropicStream, normaliseOpenAiStream, normaliseParams, normaliseStream, ocoOrders, orderUtils, orderedRoutes, otoOrders, paginate, paginateAll, parseOCCSymbol, protectLongPosition, protectShortPosition, rateLimiters$1 as rateLimiters, resetLogger, resetProviderGuards, resetRiskFreeRateCache, resolveChain, resolveDefaultDirectCaller, riskNs as risk, rollOptionPosition, roundPriceForAlpaca$3 as roundPriceForAlpaca, roundPriceForAlpacaNumber, routeKeyFor, routeSupports, routeTable, safeValidateResponse, sampleCohort, searchNews, sellAllCrypto, sellCryptoNotional, sellToClose, sellToOpen, setLogger, setRiskFreeRate, shortWithStopLoss, sortOrdersByDate, strategyNs as strategy, sumUsage, index as tradingPolicy, trailingStops, unavailableStatistic, updateAccountConfiguration, updateTrailingStop, validateAlpacaCredentials, validateAlphaVantageApiKey, validateMassiveApiKey$1 as validateMassiveApiKey, validateMultiLegOrder, validateResponse, verifyFetchKeepAlive, volatilityNs as volatility, waitForOrderFill, withProviderGuards, withRetry, withTimeout };
80516
80937
  //# sourceMappingURL=index.mjs.map