@blockrun/llm 3.23.0 → 3.23.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/README.md CHANGED
@@ -1600,9 +1600,9 @@ await status();
1600
1600
 
1601
1601
  `getOrCreateWallet()` resolves the Base wallet in a fixed order:
1602
1602
  `BLOCKRUN_WALLET_KEY` or `BASE_CHAIN_WALLET_KEY`, then `~/.blockrun/.session`,
1603
- then the legacy `~/.blockrun/wallet.key`. `LLMClient` itself reads only
1604
- `BASE_CHAIN_WALLET_KEY` (or an explicit `privateKey`). For a single run of the
1605
- helpers without changing anything, use `export BLOCKRUN_WALLET_KEY=<private-key>`.
1603
+ then the legacy `~/.blockrun/wallet.key`. `LLMClient` reads an explicit
1604
+ `privateKey`, then `BASE_CHAIN_WALLET_KEY`, then `BLOCKRUN_WALLET_KEY`. For a
1605
+ single run without changing anything, use `export BLOCKRUN_WALLET_KEY=<private-key>`.
1606
1606
 
1607
1607
  ### Wallet discovery and migration
1608
1608
 
@@ -1686,7 +1686,7 @@ const configured = new LLMClient({
1686
1686
  | `BLOCKRUN_API_KEY` | Account key (`brk_...`): account billing, no wallet needed. Create one at [user.blockrun.ai/dashboard/keys](https://user.blockrun.ai/dashboard/keys) |
1687
1687
  | `BLOCKRUN_API_BASE_URL` | Account API endpoint (default `https://api.blockrun.ai`) |
1688
1688
  | `BASE_CHAIN_WALLET_KEY` | EVM wallet private key for `LLMClient` (Base / Arc) and every EVM client |
1689
- | `BLOCKRUN_WALLET_KEY` | EVM wallet key honoured by the service clients, `BlockrunClient`, `setupAgentWallet()` and `AnthropicClient` (checked before `BASE_CHAIN_WALLET_KEY`); **not** read by `LLMClient` |
1689
+ | `BLOCKRUN_WALLET_KEY` | EVM wallet key read by every EVM client. The service clients, `BlockrunClient`, `setupAgentWallet()` and `AnthropicClient` check it before `BASE_CHAIN_WALLET_KEY`; `LLMClient` checks it after, so a process holding both keeps paying from the same wallet |
1690
1690
  | `SOLANA_WALLET_KEY` | Solana secret key, bs58-encoded, for `SolanaLLMClient` |
1691
1691
  | `SOLANA_RPC_URL` | Solana RPC for payments and balance (also `rpcUrl`) |
1692
1692
  | `SOLANA_RPC_API_KEY` / `SOLANA_RPC_HEADERS` | Header auth for that RPC (also `rpcHeaders`) |
package/dist/index.cjs CHANGED
@@ -4833,7 +4833,7 @@ function getCostSummary() {
4833
4833
  }
4834
4834
 
4835
4835
  // src/version.ts
4836
- var SDK_VERSION = "3.23.0";
4836
+ var SDK_VERSION = "3.23.2";
4837
4837
  var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
4838
4838
 
4839
4839
  // src/client.ts
@@ -4908,16 +4908,16 @@ var LLMClient = class _LLMClient {
4908
4908
  /**
4909
4909
  * Initialize the BlockRun LLM client.
4910
4910
  *
4911
- * @param options - Client configuration options (optional if BASE_CHAIN_WALLET_KEY env var is set)
4911
+ * @param options - Client configuration options (optional if BLOCKRUN_API_KEY, BASE_CHAIN_WALLET_KEY or BLOCKRUN_WALLET_KEY is set)
4912
4912
  */
4913
4913
  constructor(options = {}) {
4914
4914
  this.apiAuth = resolveApiKeyAuth(options);
4915
4915
  if (!this.apiAuth) {
4916
- const envKey = typeof process !== "undefined" && process.env ? process.env.BASE_CHAIN_WALLET_KEY : void 0;
4916
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BASE_CHAIN_WALLET_KEY || process.env.BLOCKRUN_WALLET_KEY : void 0;
4917
4917
  const privateKey = options.privateKey || envKey;
4918
4918
  if (!privateKey) {
4919
4919
  throw new Error(
4920
- "Private key required. Pass privateKey in options or set BASE_CHAIN_WALLET_KEY environment variable."
4920
+ "Private key required. Pass privateKey in options or set BASE_CHAIN_WALLET_KEY (or BLOCKRUN_WALLET_KEY) environment variable."
4921
4921
  );
4922
4922
  }
4923
4923
  validatePrivateKey(privateKey);
@@ -5606,7 +5606,7 @@ var LLMClient = class _LLMClient {
5606
5606
  }
5607
5607
  /**
5608
5608
  * Make a request with automatic x402 payment handling, returning raw JSON.
5609
- * Used for non-ChatResponse endpoints (X/Twitter, search, image edit, etc.).
5609
+ * Used for non-ChatResponse endpoints (search, image edit, etc.).
5610
5610
  */
5611
5611
  async requestWithPaymentRaw(endpoint, body) {
5612
5612
  const url = `${this.apiUrl}${endpoint}`;
@@ -6014,7 +6014,8 @@ var LLMClient = class _LLMClient {
6014
6014
  return data;
6015
6015
  }
6016
6016
  /**
6017
- * Standalone search (web, X/Twitter, news).
6017
+ * Standalone search over web and news. For X/Twitter, use chat on an
6018
+ * `xai/*` model with an `x_search` tool.
6018
6019
  *
6019
6020
  * @param query - Search query
6020
6021
  * @param options - Optional search parameters
@@ -8508,6 +8509,7 @@ var import_accounts12 = require("viem/accounts");
8508
8509
  var DEFAULT_API_URL11 = "https://blockrun.ai/api";
8509
8510
  var DEFAULT_TIMEOUT11 = 6e4;
8510
8511
  var SURF_PREFIX = "/v1/surf";
8512
+ var warnedRetired = false;
8511
8513
  var SurfClient = class {
8512
8514
  _account;
8513
8515
  get account() {
@@ -8525,6 +8527,12 @@ var SurfClient = class {
8525
8527
  apiUrl;
8526
8528
  timeout;
8527
8529
  constructor(options = {}) {
8530
+ if (!warnedRetired) {
8531
+ warnedRetired = true;
8532
+ console.warn(
8533
+ "[blockrun] SurfClient is deprecated: the gateway retired /v1/surf/* (410 endpoint_retired). Use PriceClient, LLMClient.defi*, LLMClient.pm() or LLMClient.dex* instead."
8534
+ );
8535
+ }
8528
8536
  this.apiAuth = resolveApiKeyAuth(options);
8529
8537
  if (!this.apiAuth) {
8530
8538
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
@@ -10861,6 +10869,38 @@ function capOutstandingEscrow(scheme, wallet) {
10861
10869
  return original.call(this, requirements, requestAmount, needed, context, widened, existingDeposit);
10862
10870
  };
10863
10871
  }
10872
+ var payScope = new import_async_hooks.AsyncLocalStorage();
10873
+ function registerHookDispatchers(client) {
10874
+ client.onBeforePaymentCreation(async (context) => {
10875
+ for (const hook of payScope.getStore()?.hooks.before ?? []) {
10876
+ const result = await hook(context);
10877
+ if (result && typeof result === "object" && result.abort) return result;
10878
+ }
10879
+ return void 0;
10880
+ });
10881
+ client.onAfterPaymentCreation(async (context) => {
10882
+ const scope = payScope.getStore();
10883
+ for (const hook of scope?.hooks.after ?? []) {
10884
+ try {
10885
+ await hook(context);
10886
+ } catch (error) {
10887
+ if (scope && !scope.afterError) scope.afterError = { error };
10888
+ }
10889
+ }
10890
+ });
10891
+ client.onPaymentCreationFailure(async (context) => {
10892
+ await runFailureHooks(payScope.getStore(), context);
10893
+ return void 0;
10894
+ });
10895
+ }
10896
+ async function runFailureHooks(scope, context) {
10897
+ for (const hook of scope?.hooks.failure ?? []) {
10898
+ try {
10899
+ await hook(context);
10900
+ } catch {
10901
+ }
10902
+ }
10903
+ }
10864
10904
  var RPC_SCOPE_STORE = /* @__PURE__ */ Symbol.for("@blockrun/llm/batch-rpc-scope/v1");
10865
10905
  var RPC_HEADER_HOOK = /* @__PURE__ */ Symbol.for("@blockrun/llm/batch-rpc-headers/v1");
10866
10906
  var sharedGlobal = globalThis;
@@ -10912,6 +10952,11 @@ function intentTarget(intent) {
10912
10952
  var SolanaBatchPayer = class {
10913
10953
  constructor(init) {
10914
10954
  this.init = init;
10955
+ init.configureClient?.({
10956
+ onBeforePaymentCreation: (hook) => void this.hooks.before.push(hook),
10957
+ onAfterPaymentCreation: (hook) => void this.hooks.after.push(hook),
10958
+ onPaymentCreationFailure: (hook) => void this.hooks.failure.push(hook)
10959
+ });
10915
10960
  const { operators, rateLimit, inDoubt } = init.options;
10916
10961
  if (!Array.isArray(operators) || operators.length === 0 || operators.some((o) => typeof o !== "string" || !o)) {
10917
10962
  throw new Error("batch.operators must list at least one base58 operator public key");
@@ -10944,6 +10989,7 @@ var SolanaBatchPayer = class {
10944
10989
  unresolved: 0,
10945
10990
  unresolvedByReason: {}
10946
10991
  };
10992
+ hooks = { before: [], after: [], failure: [] };
10947
10993
  /** A copy of this client's batch counters. */
10948
10994
  stats() {
10949
10995
  return {
@@ -11046,7 +11092,7 @@ var SolanaBatchPayer = class {
11046
11092
  capOutstandingEscrow(scheme, wallet);
11047
11093
  const client = new core.x402Client().register("solana:*", scheme).registerPolicy(scheme.paymentPolicy);
11048
11094
  client.setSpendControls({ maxAmountPerPayment: false });
11049
- this.init.configureClient?.(client);
11095
+ registerHookDispatchers(client);
11050
11096
  const raw = new core.x402HTTPClient(client);
11051
11097
  const http = {
11052
11098
  getPaymentSettleResponse: (getHeader) => raw.getPaymentSettleResponse(getHeader),
@@ -11340,7 +11386,10 @@ var SolanaBatchPayer = class {
11340
11386
  }
11341
11387
  wallet.active += 1;
11342
11388
  try {
11343
- return await this.payWith(wallet, paymentRequired, send, rechallenge, closesAtStart ?? wallet.closes);
11389
+ return await payScope.run(
11390
+ { hooks: this.hooks },
11391
+ () => this.payWith(wallet, paymentRequired, send, rechallenge, closesAtStart ?? wallet.closes)
11392
+ );
11344
11393
  } finally {
11345
11394
  wallet.active -= 1;
11346
11395
  }
@@ -11752,6 +11801,18 @@ var SolanaBatchPayer = class {
11752
11801
  /** Build a new batch payment for a 402: the payload, its headers and its kind. */
11753
11802
  async buildPayment(wallet, paymentRequired) {
11754
11803
  const { http, payload } = await this.createPayload(wallet, paymentRequired);
11804
+ const scope = payScope.getStore();
11805
+ if (scope?.afterError) {
11806
+ const { error } = scope.afterError;
11807
+ scope.afterError = void 0;
11808
+ await this.settle(http, payload, () => null, 0);
11809
+ await runFailureHooks(scope, {
11810
+ paymentRequired,
11811
+ selectedRequirements: payload.accepted,
11812
+ error
11813
+ });
11814
+ throw error;
11815
+ }
11755
11816
  const headers = http.encodePaymentSignatureHeader(payload);
11756
11817
  const channelId = payloadChannelId(payload);
11757
11818
  const known = channelId ? wallet.book.find(channelId) : void 0;
package/dist/index.d.cts CHANGED
@@ -499,7 +499,7 @@ interface PaymentRequired {
499
499
  extensions?: Record<string, unknown>;
500
500
  }
501
501
  interface LLMClientOptions extends ApiKeyOptions {
502
- /** EVM wallet private key (hex string starting with 0x). Optional if BASE_CHAIN_WALLET_KEY env var is set. */
502
+ /** EVM wallet private key (hex string starting with 0x). Optional if BASE_CHAIN_WALLET_KEY (or BLOCKRUN_WALLET_KEY) is set. */
503
503
  privateKey?: `0x${string}` | string;
504
504
  /** API endpoint URL (default: https://blockrun.ai/api) */
505
505
  apiUrl?: string;
@@ -975,7 +975,10 @@ interface VideoGenerateOptions {
975
975
  returnLastFrame?: boolean;
976
976
  }
977
977
  interface SearchOptions {
978
- /** Source types to search (e.g. ["web", "x", "news"]) */
978
+ /**
979
+ * Source types to search: `"web"` and/or `"news"` (default `["web"]`). `"x"` is
980
+ * rejected by the gateway; X search runs in chat on `xai/*` models (`x_search`).
981
+ */
979
982
  sources?: string[];
980
983
  /** Maximum number of results (default: 10) */
981
984
  maxResults?: number;
@@ -1119,6 +1122,7 @@ interface PriceClientOptions extends ApiKeyOptions {
1119
1122
  /** If false, construction succeeds without a wallet (free endpoints only). */
1120
1123
  requireWallet?: boolean;
1121
1124
  }
1125
+ /** @deprecated Surf is retired; see {@link SurfClient}. */
1122
1126
  interface SurfClientOptions extends ApiKeyOptions {
1123
1127
  privateKey?: `0x${string}` | string;
1124
1128
  apiUrl?: string;
@@ -1351,7 +1355,7 @@ declare class LLMClient {
1351
1355
  /**
1352
1356
  * Initialize the BlockRun LLM client.
1353
1357
  *
1354
- * @param options - Client configuration options (optional if BASE_CHAIN_WALLET_KEY env var is set)
1358
+ * @param options - Client configuration options (optional if BLOCKRUN_API_KEY, BASE_CHAIN_WALLET_KEY or BLOCKRUN_WALLET_KEY is set)
1355
1359
  */
1356
1360
  constructor(options?: LLMClientOptions);
1357
1361
  /**
@@ -1510,7 +1514,7 @@ declare class LLMClient {
1510
1514
  chatCompletionStream(model: string, messages: ChatMessage[], options?: ChatCompletionOptions): Promise<Response>;
1511
1515
  /**
1512
1516
  * Make a request with automatic x402 payment handling, returning raw JSON.
1513
- * Used for non-ChatResponse endpoints (X/Twitter, search, image edit, etc.).
1517
+ * Used for non-ChatResponse endpoints (search, image edit, etc.).
1514
1518
  */
1515
1519
  private requestWithPaymentRaw;
1516
1520
  /**
@@ -1574,7 +1578,8 @@ declare class LLMClient {
1574
1578
  */
1575
1579
  imageEdit(prompt: string, image: string | string[], options?: ImageEditOptions): Promise<ImageResponse>;
1576
1580
  /**
1577
- * Standalone search (web, X/Twitter, news).
1581
+ * Standalone search over web and news. For X/Twitter, use chat on an
1582
+ * `xai/*` model with an `x_search` tool.
1578
1583
  *
1579
1584
  * @param query - Search query
1580
1585
  * @param options - Optional search parameters
@@ -2328,6 +2333,7 @@ declare class PriceClient {
2328
2333
 
2329
2334
  type QueryValue$1 = string | number | boolean | null | undefined;
2330
2335
  type QueryParams$1 = Record<string, QueryValue$1 | QueryValue$1[]>;
2336
+ /** @deprecated Surf is retired (`410 endpoint_retired`); see the module docs for replacements. */
2331
2337
  declare class SurfClient {
2332
2338
  private _account?;
2333
2339
  private get account();
@@ -3194,14 +3200,20 @@ interface SolanaBatchPayerInit {
3194
3200
  /** Headers the exact path sends to `rpcUrl` (`rpcHeaders`, `SOLANA_RPC_HEADERS`, `SOLANA_RPC_API_KEY`). */
3195
3201
  rpcHeaders?: Record<string, string>;
3196
3202
  /**
3197
- * Called once with the batch scheme's `x402Client`, after the scheme and its
3198
- * policy are registered and before anything is signed. An embedder that
3199
- * enforces its own spend rules registers its payment-creation hooks here, so
3200
- * they see every batch payment this payer signs (each one's
3201
- * `selectedRequirements.amount` is the per-call ceiling, not the channel
3202
- * deposit) exactly as they see the embedder's own `exact` payments. A
3203
- * before-hook that throws refuses the payment: nothing is signed or sent,
3204
- * and `pay()` reports a fallback.
3203
+ * Called once, when the payer is constructed, with a registrar for x402
3204
+ * payment-creation hooks. An embedder that enforces its own spend rules
3205
+ * registers its hooks here, so they see every batch payment THIS payer signs
3206
+ * (each one's `selectedRequirements.amount` is the per-call ceiling, not the
3207
+ * channel deposit) exactly as they see the embedder's own `exact` payments.
3208
+ *
3209
+ * The hooks are this payer's own: payers that share a wallet (and so its
3210
+ * channel and scheme client) never run each other's hooks. A before-hook
3211
+ * that throws refuses the payment: nothing is signed or sent, and `pay()`
3212
+ * reports a fallback. An after-hook that throws does the same: the payment
3213
+ * was signed but is rolled back unsent, so the channel is not left pending,
3214
+ * and the failure hooks are told. Failure hooks are notifications only: a
3215
+ * payload one returns as `recovered` is ignored, since a batch payment is
3216
+ * never replaced by a payment built outside the batch scheme.
3205
3217
  */
3206
3218
  configureClient?: (client: X402ClientLike) => void;
3207
3219
  }
@@ -3242,6 +3254,7 @@ declare class SolanaBatchPayer {
3242
3254
  private readonly maxWaitMs;
3243
3255
  private readonly inDoubtMaxWaitMs;
3244
3256
  private readonly counters;
3257
+ private readonly hooks;
3245
3258
  constructor(init: SolanaBatchPayerInit);
3246
3259
  /** A copy of this client's batch counters. */
3247
3260
  stats(): SolanaBatchStats;
package/dist/index.d.ts CHANGED
@@ -499,7 +499,7 @@ interface PaymentRequired {
499
499
  extensions?: Record<string, unknown>;
500
500
  }
501
501
  interface LLMClientOptions extends ApiKeyOptions {
502
- /** EVM wallet private key (hex string starting with 0x). Optional if BASE_CHAIN_WALLET_KEY env var is set. */
502
+ /** EVM wallet private key (hex string starting with 0x). Optional if BASE_CHAIN_WALLET_KEY (or BLOCKRUN_WALLET_KEY) is set. */
503
503
  privateKey?: `0x${string}` | string;
504
504
  /** API endpoint URL (default: https://blockrun.ai/api) */
505
505
  apiUrl?: string;
@@ -975,7 +975,10 @@ interface VideoGenerateOptions {
975
975
  returnLastFrame?: boolean;
976
976
  }
977
977
  interface SearchOptions {
978
- /** Source types to search (e.g. ["web", "x", "news"]) */
978
+ /**
979
+ * Source types to search: `"web"` and/or `"news"` (default `["web"]`). `"x"` is
980
+ * rejected by the gateway; X search runs in chat on `xai/*` models (`x_search`).
981
+ */
979
982
  sources?: string[];
980
983
  /** Maximum number of results (default: 10) */
981
984
  maxResults?: number;
@@ -1119,6 +1122,7 @@ interface PriceClientOptions extends ApiKeyOptions {
1119
1122
  /** If false, construction succeeds without a wallet (free endpoints only). */
1120
1123
  requireWallet?: boolean;
1121
1124
  }
1125
+ /** @deprecated Surf is retired; see {@link SurfClient}. */
1122
1126
  interface SurfClientOptions extends ApiKeyOptions {
1123
1127
  privateKey?: `0x${string}` | string;
1124
1128
  apiUrl?: string;
@@ -1351,7 +1355,7 @@ declare class LLMClient {
1351
1355
  /**
1352
1356
  * Initialize the BlockRun LLM client.
1353
1357
  *
1354
- * @param options - Client configuration options (optional if BASE_CHAIN_WALLET_KEY env var is set)
1358
+ * @param options - Client configuration options (optional if BLOCKRUN_API_KEY, BASE_CHAIN_WALLET_KEY or BLOCKRUN_WALLET_KEY is set)
1355
1359
  */
1356
1360
  constructor(options?: LLMClientOptions);
1357
1361
  /**
@@ -1510,7 +1514,7 @@ declare class LLMClient {
1510
1514
  chatCompletionStream(model: string, messages: ChatMessage[], options?: ChatCompletionOptions): Promise<Response>;
1511
1515
  /**
1512
1516
  * Make a request with automatic x402 payment handling, returning raw JSON.
1513
- * Used for non-ChatResponse endpoints (X/Twitter, search, image edit, etc.).
1517
+ * Used for non-ChatResponse endpoints (search, image edit, etc.).
1514
1518
  */
1515
1519
  private requestWithPaymentRaw;
1516
1520
  /**
@@ -1574,7 +1578,8 @@ declare class LLMClient {
1574
1578
  */
1575
1579
  imageEdit(prompt: string, image: string | string[], options?: ImageEditOptions): Promise<ImageResponse>;
1576
1580
  /**
1577
- * Standalone search (web, X/Twitter, news).
1581
+ * Standalone search over web and news. For X/Twitter, use chat on an
1582
+ * `xai/*` model with an `x_search` tool.
1578
1583
  *
1579
1584
  * @param query - Search query
1580
1585
  * @param options - Optional search parameters
@@ -2328,6 +2333,7 @@ declare class PriceClient {
2328
2333
 
2329
2334
  type QueryValue$1 = string | number | boolean | null | undefined;
2330
2335
  type QueryParams$1 = Record<string, QueryValue$1 | QueryValue$1[]>;
2336
+ /** @deprecated Surf is retired (`410 endpoint_retired`); see the module docs for replacements. */
2331
2337
  declare class SurfClient {
2332
2338
  private _account?;
2333
2339
  private get account();
@@ -3194,14 +3200,20 @@ interface SolanaBatchPayerInit {
3194
3200
  /** Headers the exact path sends to `rpcUrl` (`rpcHeaders`, `SOLANA_RPC_HEADERS`, `SOLANA_RPC_API_KEY`). */
3195
3201
  rpcHeaders?: Record<string, string>;
3196
3202
  /**
3197
- * Called once with the batch scheme's `x402Client`, after the scheme and its
3198
- * policy are registered and before anything is signed. An embedder that
3199
- * enforces its own spend rules registers its payment-creation hooks here, so
3200
- * they see every batch payment this payer signs (each one's
3201
- * `selectedRequirements.amount` is the per-call ceiling, not the channel
3202
- * deposit) exactly as they see the embedder's own `exact` payments. A
3203
- * before-hook that throws refuses the payment: nothing is signed or sent,
3204
- * and `pay()` reports a fallback.
3203
+ * Called once, when the payer is constructed, with a registrar for x402
3204
+ * payment-creation hooks. An embedder that enforces its own spend rules
3205
+ * registers its hooks here, so they see every batch payment THIS payer signs
3206
+ * (each one's `selectedRequirements.amount` is the per-call ceiling, not the
3207
+ * channel deposit) exactly as they see the embedder's own `exact` payments.
3208
+ *
3209
+ * The hooks are this payer's own: payers that share a wallet (and so its
3210
+ * channel and scheme client) never run each other's hooks. A before-hook
3211
+ * that throws refuses the payment: nothing is signed or sent, and `pay()`
3212
+ * reports a fallback. An after-hook that throws does the same: the payment
3213
+ * was signed but is rolled back unsent, so the channel is not left pending,
3214
+ * and the failure hooks are told. Failure hooks are notifications only: a
3215
+ * payload one returns as `recovered` is ignored, since a batch payment is
3216
+ * never replaced by a payment built outside the batch scheme.
3205
3217
  */
3206
3218
  configureClient?: (client: X402ClientLike) => void;
3207
3219
  }
@@ -3242,6 +3254,7 @@ declare class SolanaBatchPayer {
3242
3254
  private readonly maxWaitMs;
3243
3255
  private readonly inDoubtMaxWaitMs;
3244
3256
  private readonly counters;
3257
+ private readonly hooks;
3245
3258
  constructor(init: SolanaBatchPayerInit);
3246
3259
  /** A copy of this client's batch counters. */
3247
3260
  stats(): SolanaBatchStats;
package/dist/index.js CHANGED
@@ -4702,7 +4702,7 @@ function getCostSummary() {
4702
4702
  }
4703
4703
 
4704
4704
  // src/version.ts
4705
- var SDK_VERSION = "3.23.0";
4705
+ var SDK_VERSION = "3.23.2";
4706
4706
  var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
4707
4707
 
4708
4708
  // src/client.ts
@@ -4777,16 +4777,16 @@ var LLMClient = class _LLMClient {
4777
4777
  /**
4778
4778
  * Initialize the BlockRun LLM client.
4779
4779
  *
4780
- * @param options - Client configuration options (optional if BASE_CHAIN_WALLET_KEY env var is set)
4780
+ * @param options - Client configuration options (optional if BLOCKRUN_API_KEY, BASE_CHAIN_WALLET_KEY or BLOCKRUN_WALLET_KEY is set)
4781
4781
  */
4782
4782
  constructor(options = {}) {
4783
4783
  this.apiAuth = resolveApiKeyAuth(options);
4784
4784
  if (!this.apiAuth) {
4785
- const envKey = typeof process !== "undefined" && process.env ? process.env.BASE_CHAIN_WALLET_KEY : void 0;
4785
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BASE_CHAIN_WALLET_KEY || process.env.BLOCKRUN_WALLET_KEY : void 0;
4786
4786
  const privateKey = options.privateKey || envKey;
4787
4787
  if (!privateKey) {
4788
4788
  throw new Error(
4789
- "Private key required. Pass privateKey in options or set BASE_CHAIN_WALLET_KEY environment variable."
4789
+ "Private key required. Pass privateKey in options or set BASE_CHAIN_WALLET_KEY (or BLOCKRUN_WALLET_KEY) environment variable."
4790
4790
  );
4791
4791
  }
4792
4792
  validatePrivateKey(privateKey);
@@ -5475,7 +5475,7 @@ var LLMClient = class _LLMClient {
5475
5475
  }
5476
5476
  /**
5477
5477
  * Make a request with automatic x402 payment handling, returning raw JSON.
5478
- * Used for non-ChatResponse endpoints (X/Twitter, search, image edit, etc.).
5478
+ * Used for non-ChatResponse endpoints (search, image edit, etc.).
5479
5479
  */
5480
5480
  async requestWithPaymentRaw(endpoint, body) {
5481
5481
  const url = `${this.apiUrl}${endpoint}`;
@@ -5883,7 +5883,8 @@ var LLMClient = class _LLMClient {
5883
5883
  return data;
5884
5884
  }
5885
5885
  /**
5886
- * Standalone search (web, X/Twitter, news).
5886
+ * Standalone search over web and news. For X/Twitter, use chat on an
5887
+ * `xai/*` model with an `x_search` tool.
5887
5888
  *
5888
5889
  * @param query - Search query
5889
5890
  * @param options - Optional search parameters
@@ -8377,6 +8378,7 @@ import { privateKeyToAccount as privateKeyToAccount11 } from "viem/accounts";
8377
8378
  var DEFAULT_API_URL11 = "https://blockrun.ai/api";
8378
8379
  var DEFAULT_TIMEOUT11 = 6e4;
8379
8380
  var SURF_PREFIX = "/v1/surf";
8381
+ var warnedRetired = false;
8380
8382
  var SurfClient = class {
8381
8383
  _account;
8382
8384
  get account() {
@@ -8394,6 +8396,12 @@ var SurfClient = class {
8394
8396
  apiUrl;
8395
8397
  timeout;
8396
8398
  constructor(options = {}) {
8399
+ if (!warnedRetired) {
8400
+ warnedRetired = true;
8401
+ console.warn(
8402
+ "[blockrun] SurfClient is deprecated: the gateway retired /v1/surf/* (410 endpoint_retired). Use PriceClient, LLMClient.defi*, LLMClient.pm() or LLMClient.dex* instead."
8403
+ );
8404
+ }
8397
8405
  this.apiAuth = resolveApiKeyAuth(options);
8398
8406
  if (!this.apiAuth) {
8399
8407
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
@@ -10730,6 +10738,38 @@ function capOutstandingEscrow(scheme, wallet) {
10730
10738
  return original.call(this, requirements, requestAmount, needed, context, widened, existingDeposit);
10731
10739
  };
10732
10740
  }
10741
+ var payScope = new AsyncLocalStorage();
10742
+ function registerHookDispatchers(client) {
10743
+ client.onBeforePaymentCreation(async (context) => {
10744
+ for (const hook of payScope.getStore()?.hooks.before ?? []) {
10745
+ const result = await hook(context);
10746
+ if (result && typeof result === "object" && result.abort) return result;
10747
+ }
10748
+ return void 0;
10749
+ });
10750
+ client.onAfterPaymentCreation(async (context) => {
10751
+ const scope = payScope.getStore();
10752
+ for (const hook of scope?.hooks.after ?? []) {
10753
+ try {
10754
+ await hook(context);
10755
+ } catch (error) {
10756
+ if (scope && !scope.afterError) scope.afterError = { error };
10757
+ }
10758
+ }
10759
+ });
10760
+ client.onPaymentCreationFailure(async (context) => {
10761
+ await runFailureHooks(payScope.getStore(), context);
10762
+ return void 0;
10763
+ });
10764
+ }
10765
+ async function runFailureHooks(scope, context) {
10766
+ for (const hook of scope?.hooks.failure ?? []) {
10767
+ try {
10768
+ await hook(context);
10769
+ } catch {
10770
+ }
10771
+ }
10772
+ }
10733
10773
  var RPC_SCOPE_STORE = /* @__PURE__ */ Symbol.for("@blockrun/llm/batch-rpc-scope/v1");
10734
10774
  var RPC_HEADER_HOOK = /* @__PURE__ */ Symbol.for("@blockrun/llm/batch-rpc-headers/v1");
10735
10775
  var sharedGlobal = globalThis;
@@ -10781,6 +10821,11 @@ function intentTarget(intent) {
10781
10821
  var SolanaBatchPayer = class {
10782
10822
  constructor(init) {
10783
10823
  this.init = init;
10824
+ init.configureClient?.({
10825
+ onBeforePaymentCreation: (hook) => void this.hooks.before.push(hook),
10826
+ onAfterPaymentCreation: (hook) => void this.hooks.after.push(hook),
10827
+ onPaymentCreationFailure: (hook) => void this.hooks.failure.push(hook)
10828
+ });
10784
10829
  const { operators, rateLimit, inDoubt } = init.options;
10785
10830
  if (!Array.isArray(operators) || operators.length === 0 || operators.some((o) => typeof o !== "string" || !o)) {
10786
10831
  throw new Error("batch.operators must list at least one base58 operator public key");
@@ -10813,6 +10858,7 @@ var SolanaBatchPayer = class {
10813
10858
  unresolved: 0,
10814
10859
  unresolvedByReason: {}
10815
10860
  };
10861
+ hooks = { before: [], after: [], failure: [] };
10816
10862
  /** A copy of this client's batch counters. */
10817
10863
  stats() {
10818
10864
  return {
@@ -10915,7 +10961,7 @@ var SolanaBatchPayer = class {
10915
10961
  capOutstandingEscrow(scheme, wallet);
10916
10962
  const client = new core.x402Client().register("solana:*", scheme).registerPolicy(scheme.paymentPolicy);
10917
10963
  client.setSpendControls({ maxAmountPerPayment: false });
10918
- this.init.configureClient?.(client);
10964
+ registerHookDispatchers(client);
10919
10965
  const raw = new core.x402HTTPClient(client);
10920
10966
  const http = {
10921
10967
  getPaymentSettleResponse: (getHeader) => raw.getPaymentSettleResponse(getHeader),
@@ -11209,7 +11255,10 @@ var SolanaBatchPayer = class {
11209
11255
  }
11210
11256
  wallet.active += 1;
11211
11257
  try {
11212
- return await this.payWith(wallet, paymentRequired, send, rechallenge, closesAtStart ?? wallet.closes);
11258
+ return await payScope.run(
11259
+ { hooks: this.hooks },
11260
+ () => this.payWith(wallet, paymentRequired, send, rechallenge, closesAtStart ?? wallet.closes)
11261
+ );
11213
11262
  } finally {
11214
11263
  wallet.active -= 1;
11215
11264
  }
@@ -11621,6 +11670,18 @@ var SolanaBatchPayer = class {
11621
11670
  /** Build a new batch payment for a 402: the payload, its headers and its kind. */
11622
11671
  async buildPayment(wallet, paymentRequired) {
11623
11672
  const { http, payload } = await this.createPayload(wallet, paymentRequired);
11673
+ const scope = payScope.getStore();
11674
+ if (scope?.afterError) {
11675
+ const { error } = scope.afterError;
11676
+ scope.afterError = void 0;
11677
+ await this.settle(http, payload, () => null, 0);
11678
+ await runFailureHooks(scope, {
11679
+ paymentRequired,
11680
+ selectedRequirements: payload.accepted,
11681
+ error
11682
+ });
11683
+ throw error;
11684
+ }
11624
11685
  const headers = http.encodePaymentSignatureHeader(payload);
11625
11686
  const channelId = payloadChannelId(payload);
11626
11687
  const known = channelId ? wallet.book.find(channelId) : void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/llm",
3
- "version": "3.23.0",
3
+ "version": "3.23.2",
4
4
  "type": "module",
5
5
  "description": "TypeScript SDK for BlockRun - every frontier model behind one OpenAI-compatible client. Pay with a BlockRun API key or per-call in USDC. Smart routing picks the cheapest capable model. No rate limits.",
6
6
  "main": "dist/index.cjs",