@blockrun/clawrouter 0.12.276 → 0.12.277

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
@@ -767,9 +767,24 @@ For basic usage, no configuration needed. For advanced options:
767
767
  | `CLAWROUTER_DISABLED` | `false` | Disable smart routing |
768
768
  | `CLAWROUTER_DEBUG_HEADERS` | `on` | Set to `off` to suppress `x-clawrouter-*` debug response headers |
769
769
  | `CLAWROUTER_SOLANA_RPC_URL` | `https://api.mainnet-beta.solana.com` | Solana RPC endpoint — balance checks and payment signing |
770
+ | `TWZRD_AUTO_GATE` | unset (off) | Set to `1` to compose TWZRD AutoGate after SpendControl on the x402 pre-sign hook |
770
771
 
771
772
  **Full reference:** [docs/configuration.md](docs/configuration.md)
772
773
 
774
+ ### Pre-spend trust (TWZRD AutoGate, opt-in)
775
+
776
+ Default **off**. Spend limits and counterparty lists (`clawrouter policy`) stay the vendor-neutral path. This is not a default-on vendor lock.
777
+
778
+ To also run TWZRD wash/preflight on the same `onBeforePaymentCreation` chain — after SpendControl, before the wallet signs:
779
+
780
+ ```bash
781
+ export TWZRD_AUTO_GATE=1
782
+ ```
783
+
784
+ Equivalent flag: `TWZRD_GATE_ENABLED=true`. Requires optional dependency `twzrd-x402-gate@0.9.3` (forks that omit it still install). A refuse stamps `X-Twzrd-Caller: clawrouter/<version>` so it is attributable. Unset the flag to go back to SpendControl only.
785
+
786
+ **It gates Solana, not Base.** The gate scores Solana recipients; Base/EVM classifies as `network_not_scored` and is waved through without a lookup. On Solana it POSTs the resource URL, `payTo`, price and chain to `intel.twzrd.xyz` before each signature — payment metadata leaves your machine when this is on. We bound that call at `TWZRD_GATE_TIMEOUT_MS` (default 2000) and proceed if it does not answer, because SpendControl is the guarantee and a third party must not be able to stop your payments; set `TWZRD_FAIL_OPEN=false` to refuse instead.
787
+
773
788
  ### Model Exclusion
774
789
 
775
790
  Block specific models from being routed to. Useful if a model doesn't follow your agent instructions or you want to control costs.
package/dist/cli.js CHANGED
@@ -39085,6 +39085,14 @@ var init_models = __esm({
39085
39085
  id: "google/gemini-3.8-flash",
39086
39086
  name: "Gemini 3.8 Flash",
39087
39087
  version: "3.8",
39088
+ // ⏰ PROMOTIONAL RATE, expires 2027-01-01. Google prices the whole 3.6/3.7/3.8
39089
+ // Flash band at 0.75/3.75 only through 2026-12-31, reverting to 1.50/7.50
39090
+ // (ai.google.dev/gemini-api/docs/pricing; blockrun's src/lib/models.ts carries
39091
+ // the same dated note). These numbers are not decoration here: calculateModelCost
39092
+ // feeds the maxCostPerRun projection and every `cost` in the usage journal, so
39093
+ // leaving them at the promo rate past the reversion under-reports spend 2x and
39094
+ // lets the cap run to twice its stated limit. Re-price this AND gemini-3.6-flash
39095
+ // together — they revert on the same day.
39088
39096
  inputPrice: 0.75,
39089
39097
  outputPrice: 3.75,
39090
39098
  contextWindow: 1048576,
@@ -39096,6 +39104,8 @@ var init_models = __esm({
39096
39104
  {
39097
39105
  // Newest-generation Flash with built-in thinking mode (blockrun #329,
39098
39106
  // 2026-08-03). 17% cheaper output than 3.5 Flash.
39107
+ // ⏰ Same promotional 0.75/3.75 as gemini-3.8-flash above, and the same
39108
+ // 2027-01-01 reversion to 1.50/7.50 — re-price both together.
39099
39109
  id: "google/gemini-3.6-flash",
39100
39110
  name: "Gemini 3.6 Flash",
39101
39111
  version: "3.6",
@@ -60450,6 +60460,149 @@ var init_api_key = __esm({
60450
60460
  }
60451
60461
  });
60452
60462
 
60463
+ // src/twzrd-autogate.ts
60464
+ import { randomUUID } from "crypto";
60465
+ function normalizeFlag(value) {
60466
+ const trimmed = value?.trim().toLowerCase();
60467
+ return trimmed === "" ? void 0 : trimmed;
60468
+ }
60469
+ function isTwzrdAutoGateEnabled(env = process.env) {
60470
+ const auto = normalizeFlag(env.TWZRD_AUTO_GATE);
60471
+ const gate = normalizeFlag(env.TWZRD_GATE_ENABLED);
60472
+ if (auto !== void 0 && FALSY.has(auto) || gate !== void 0 && FALSY.has(gate)) {
60473
+ return false;
60474
+ }
60475
+ return auto !== void 0 && TRUTHY.has(auto) || gate !== void 0 && TRUTHY.has(gate);
60476
+ }
60477
+ function isMissingTwzrdGateModule(err) {
60478
+ const code = err?.code;
60479
+ const msg = err instanceof Error ? err.message : String(err);
60480
+ const missingModule = code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND" || /Cannot find module/i.test(msg) || /Cannot find package/i.test(msg);
60481
+ return missingModule && new RegExp(String.raw`['"]${TWZRD_GATE_PACKAGE}['"]`).test(msg);
60482
+ }
60483
+ function twzrdGateTimeoutMs(env = process.env) {
60484
+ const raw = normalizeFlag(env.TWZRD_GATE_TIMEOUT_MS);
60485
+ if (raw === void 0) return TWZRD_GATE_TIMEOUT_MS;
60486
+ const parsed = Number(raw);
60487
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : TWZRD_GATE_TIMEOUT_MS;
60488
+ }
60489
+ function twzrdGateFailOpen(env = process.env) {
60490
+ const raw = normalizeFlag(env.TWZRD_FAIL_OPEN);
60491
+ return !(raw !== void 0 && FALSY.has(raw));
60492
+ }
60493
+ function twzrdAutoGateInstallOptions(env = process.env, runId = randomUUID) {
60494
+ return {
60495
+ refuseWashFlagged: true,
60496
+ gateOnCanSpend: false,
60497
+ unsupportedNetworkMode: "observe",
60498
+ failOpen: twzrdGateFailOpen(env),
60499
+ attribution: {
60500
+ integration: `clawrouter/${VERSION}`,
60501
+ runId: runId()
60502
+ }
60503
+ };
60504
+ }
60505
+ async function runTwzrdGateWithTimeout(run, opts) {
60506
+ const log = opts.log ?? console;
60507
+ let timer2;
60508
+ try {
60509
+ const raced = await Promise.race([
60510
+ // Swallow here, not at the race, so a late rejection never goes unhandled.
60511
+ Promise.resolve().then(run).catch((err) => ({ __twzrdError: err })),
60512
+ new Promise((resolve) => {
60513
+ timer2 = setTimeout(() => resolve(TIMED_OUT), opts.timeoutMs);
60514
+ })
60515
+ ]);
60516
+ if (raced === TIMED_OUT) {
60517
+ return decideOnGateFailure(`did not answer within ${opts.timeoutMs}ms`, opts.failOpen, log);
60518
+ }
60519
+ if (raced && typeof raced === "object" && "__twzrdError" in raced) {
60520
+ const err = raced.__twzrdError;
60521
+ const msg = err instanceof Error ? err.message : String(err);
60522
+ return decideOnGateFailure(`threw: ${msg.slice(0, 120)}`, opts.failOpen, log);
60523
+ }
60524
+ return raced;
60525
+ } finally {
60526
+ if (timer2) clearTimeout(timer2);
60527
+ }
60528
+ }
60529
+ function decideOnGateFailure(what, failOpen, log) {
60530
+ const reason = `twzrd gate ${what}`;
60531
+ if (failOpen) {
60532
+ log.warn(
60533
+ `[ClawRouter] ${reason} \u2014 proceeding. SpendControl still applied. Set TWZRD_FAIL_OPEN=false to refuse instead.`
60534
+ );
60535
+ return void 0;
60536
+ }
60537
+ log.warn(`[ClawRouter] ${reason} \u2014 refusing the payment (TWZRD_FAIL_OPEN=false).`);
60538
+ return { abort: true, reason };
60539
+ }
60540
+ async function defaultLoadTwzrdGate() {
60541
+ const specifier = TWZRD_GATE_PACKAGE;
60542
+ return import(specifier);
60543
+ }
60544
+ async function maybeComposeTwzrdAutoGate(x402, options) {
60545
+ const env = options?.env ?? process.env;
60546
+ const log = options?.log ?? console;
60547
+ if (!isTwzrdAutoGateEnabled(env)) {
60548
+ return { status: "skipped" };
60549
+ }
60550
+ const load = options?.loadGate ?? defaultLoadTwzrdGate;
60551
+ let gate;
60552
+ try {
60553
+ gate = await load();
60554
+ } catch (err) {
60555
+ if (isMissingTwzrdGateModule(err)) {
60556
+ const reason = `${TWZRD_GATE_PACKAGE} is not installed`;
60557
+ log.warn(
60558
+ `[ClawRouter] TWZRD AutoGate opted in but ${reason} \u2014 skipping. npm i ${TWZRD_GATE_PACKAGE}@0.9.3`
60559
+ );
60560
+ return { status: "unavailable", reason };
60561
+ }
60562
+ throw err;
60563
+ }
60564
+ const installOpts = twzrdAutoGateInstallOptions(env);
60565
+ const timeoutMs = twzrdGateTimeoutMs(env);
60566
+ const ready = (via) => log.log(
60567
+ `[ClawRouter] TWZRD AutoGate ON (opt-in, after SpendControl, ${via}; ${timeoutMs}ms budget, failOpen=${installOpts.failOpen}). Unset TWZRD_AUTO_GATE to disable.`
60568
+ );
60569
+ if (typeof gate.createTwzrdBeforePaymentHook === "function") {
60570
+ const hook = gate.createTwzrdBeforePaymentHook(installOpts);
60571
+ x402.onBeforePaymentCreation(
60572
+ async (ctx) => runTwzrdGateWithTimeout(() => hook(ctx.selectedRequirements, ctx), {
60573
+ timeoutMs,
60574
+ failOpen: installOpts.failOpen,
60575
+ log
60576
+ })
60577
+ );
60578
+ ready("createTwzrdBeforePaymentHook");
60579
+ return { status: "composed", via: "createTwzrdBeforePaymentHook" };
60580
+ }
60581
+ if (typeof gate.installTwzrdAutoGate === "function") {
60582
+ gate.installTwzrdAutoGate(x402, installOpts);
60583
+ log.warn(
60584
+ "[ClawRouter] TWZRD AutoGate installed via installTwzrdAutoGate \u2014 that entry point registers its own hook, so the timeout budget does not apply and it replaces onBeforePaymentCreation on this client. Prefer a gate build exporting createTwzrdBeforePaymentHook."
60585
+ );
60586
+ ready("installTwzrdAutoGate");
60587
+ return { status: "composed", via: "installTwzrdAutoGate" };
60588
+ }
60589
+ throw new Error(
60590
+ `[ClawRouter] ${TWZRD_GATE_PACKAGE} is installed but exports neither installTwzrdAutoGate nor createTwzrdBeforePaymentHook`
60591
+ );
60592
+ }
60593
+ var TWZRD_GATE_PACKAGE, TRUTHY, FALSY, TWZRD_GATE_TIMEOUT_MS, TIMED_OUT;
60594
+ var init_twzrd_autogate = __esm({
60595
+ "src/twzrd-autogate.ts"() {
60596
+ "use strict";
60597
+ init_version4();
60598
+ TWZRD_GATE_PACKAGE = "twzrd-x402-gate";
60599
+ TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
60600
+ FALSY = /* @__PURE__ */ new Set(["0", "false", "no", "off"]);
60601
+ TWZRD_GATE_TIMEOUT_MS = 2e3;
60602
+ TIMED_OUT = { __twzrdTimedOut: true };
60603
+ }
60604
+ });
60605
+
60453
60606
  // src/compression/types.ts
60454
60607
  var DEFAULT_COMPRESSION_CONFIG;
60455
60608
  var init_types2 = __esm({
@@ -92034,6 +92187,7 @@ async function startProxy(options) {
92034
92187
  const evmSigner = toClientEvmSigner(account, evmPublicClient);
92035
92188
  const spendControl = options.spendControl ?? getSharedSpendControl();
92036
92189
  registerSpendPolicyHook(x402, spendControl);
92190
+ await maybeComposeTwzrdAutoGate(x402);
92037
92191
  registerExactEvmScheme(x402, { signer: evmSigner });
92038
92192
  }
92039
92193
  let solanaAddress;
@@ -95619,6 +95773,7 @@ var init_proxy = __esm({
95619
95773
  init_auth();
95620
95774
  init_api_key();
95621
95775
  init_spend_control();
95776
+ init_twzrd_autogate();
95622
95777
  init_compression();
95623
95778
  init_errors5();
95624
95779
  init_version4();
@@ -228676,7 +228831,7 @@ import {
228676
228831
  } from "fs";
228677
228832
  import { readFile as readFileAsync } from "fs/promises";
228678
228833
  import { homedir as homedir15 } from "os";
228679
- import { randomUUID } from "crypto";
228834
+ import { randomUUID as randomUUID2 } from "crypto";
228680
228835
  import { join as join18 } from "path";
228681
228836
  async function waitForProxyHealth(port, timeoutMs = 3e3) {
228682
228837
  const start = Date.now();
@@ -228691,7 +228846,7 @@ async function waitForProxyHealth(port, timeoutMs = 3e3) {
228691
228846
  return false;
228692
228847
  }
228693
228848
  function writePrivateJsonSync(path5, value) {
228694
- const temporary = `${path5}.tmp.${randomUUID()}`;
228849
+ const temporary = `${path5}.tmp.${randomUUID2()}`;
228695
228850
  try {
228696
228851
  writeFileSync3(temporary, JSON.stringify(value, null, 2), { mode: 384 });
228697
228852
  renameSync2(temporary, path5);
@@ -231182,6 +231337,7 @@ Environment Variables:
231182
231337
  BLOCKRUN_API_KEY BlockRun API key (brk_...) \u2014 wins over the wallet when set
231183
231338
  BLOCKRUN_WALLET_KEY Private key for x402 payments (auto-generated if not set)
231184
231339
  BLOCKRUN_PROXY_PORT Default proxy port (default: 8402)
231340
+ TWZRD_AUTO_GATE Set to 1 to compose TWZRD AutoGate after SpendControl (default off)
231185
231341
 
231186
231342
  For more info: https://blockrun.ai/clawrouter.md
231187
231343
  `);
package/dist/index.js CHANGED
@@ -1270,6 +1270,14 @@ var init_models = __esm({
1270
1270
  id: "google/gemini-3.8-flash",
1271
1271
  name: "Gemini 3.8 Flash",
1272
1272
  version: "3.8",
1273
+ // ⏰ PROMOTIONAL RATE, expires 2027-01-01. Google prices the whole 3.6/3.7/3.8
1274
+ // Flash band at 0.75/3.75 only through 2026-12-31, reverting to 1.50/7.50
1275
+ // (ai.google.dev/gemini-api/docs/pricing; blockrun's src/lib/models.ts carries
1276
+ // the same dated note). These numbers are not decoration here: calculateModelCost
1277
+ // feeds the maxCostPerRun projection and every `cost` in the usage journal, so
1278
+ // leaving them at the promo rate past the reversion under-reports spend 2x and
1279
+ // lets the cap run to twice its stated limit. Re-price this AND gemini-3.6-flash
1280
+ // together — they revert on the same day.
1273
1281
  inputPrice: 0.75,
1274
1282
  outputPrice: 3.75,
1275
1283
  contextWindow: 1048576,
@@ -1281,6 +1289,8 @@ var init_models = __esm({
1281
1289
  {
1282
1290
  // Newest-generation Flash with built-in thinking mode (blockrun #329,
1283
1291
  // 2026-08-03). 17% cheaper output than 3.5 Flash.
1292
+ // ⏰ Same promotional 0.75/3.75 as gemini-3.8-flash above, and the same
1293
+ // 2027-01-01 reversion to 1.50/7.50 — re-price both together.
1284
1294
  id: "google/gemini-3.6-flash",
1285
1295
  name: "Gemini 3.6 Flash",
1286
1296
  version: "3.6",
@@ -60203,6 +60213,149 @@ var init_api_key = __esm({
60203
60213
  }
60204
60214
  });
60205
60215
 
60216
+ // src/twzrd-autogate.ts
60217
+ import { randomUUID } from "crypto";
60218
+ function normalizeFlag(value) {
60219
+ const trimmed = value?.trim().toLowerCase();
60220
+ return trimmed === "" ? void 0 : trimmed;
60221
+ }
60222
+ function isTwzrdAutoGateEnabled(env = process.env) {
60223
+ const auto = normalizeFlag(env.TWZRD_AUTO_GATE);
60224
+ const gate = normalizeFlag(env.TWZRD_GATE_ENABLED);
60225
+ if (auto !== void 0 && FALSY.has(auto) || gate !== void 0 && FALSY.has(gate)) {
60226
+ return false;
60227
+ }
60228
+ return auto !== void 0 && TRUTHY.has(auto) || gate !== void 0 && TRUTHY.has(gate);
60229
+ }
60230
+ function isMissingTwzrdGateModule(err) {
60231
+ const code = err?.code;
60232
+ const msg = err instanceof Error ? err.message : String(err);
60233
+ const missingModule = code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND" || /Cannot find module/i.test(msg) || /Cannot find package/i.test(msg);
60234
+ return missingModule && new RegExp(String.raw`['"]${TWZRD_GATE_PACKAGE}['"]`).test(msg);
60235
+ }
60236
+ function twzrdGateTimeoutMs(env = process.env) {
60237
+ const raw = normalizeFlag(env.TWZRD_GATE_TIMEOUT_MS);
60238
+ if (raw === void 0) return TWZRD_GATE_TIMEOUT_MS;
60239
+ const parsed = Number(raw);
60240
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : TWZRD_GATE_TIMEOUT_MS;
60241
+ }
60242
+ function twzrdGateFailOpen(env = process.env) {
60243
+ const raw = normalizeFlag(env.TWZRD_FAIL_OPEN);
60244
+ return !(raw !== void 0 && FALSY.has(raw));
60245
+ }
60246
+ function twzrdAutoGateInstallOptions(env = process.env, runId = randomUUID) {
60247
+ return {
60248
+ refuseWashFlagged: true,
60249
+ gateOnCanSpend: false,
60250
+ unsupportedNetworkMode: "observe",
60251
+ failOpen: twzrdGateFailOpen(env),
60252
+ attribution: {
60253
+ integration: `clawrouter/${VERSION}`,
60254
+ runId: runId()
60255
+ }
60256
+ };
60257
+ }
60258
+ async function runTwzrdGateWithTimeout(run, opts) {
60259
+ const log = opts.log ?? console;
60260
+ let timer2;
60261
+ try {
60262
+ const raced = await Promise.race([
60263
+ // Swallow here, not at the race, so a late rejection never goes unhandled.
60264
+ Promise.resolve().then(run).catch((err) => ({ __twzrdError: err })),
60265
+ new Promise((resolve) => {
60266
+ timer2 = setTimeout(() => resolve(TIMED_OUT), opts.timeoutMs);
60267
+ })
60268
+ ]);
60269
+ if (raced === TIMED_OUT) {
60270
+ return decideOnGateFailure(`did not answer within ${opts.timeoutMs}ms`, opts.failOpen, log);
60271
+ }
60272
+ if (raced && typeof raced === "object" && "__twzrdError" in raced) {
60273
+ const err = raced.__twzrdError;
60274
+ const msg = err instanceof Error ? err.message : String(err);
60275
+ return decideOnGateFailure(`threw: ${msg.slice(0, 120)}`, opts.failOpen, log);
60276
+ }
60277
+ return raced;
60278
+ } finally {
60279
+ if (timer2) clearTimeout(timer2);
60280
+ }
60281
+ }
60282
+ function decideOnGateFailure(what, failOpen, log) {
60283
+ const reason = `twzrd gate ${what}`;
60284
+ if (failOpen) {
60285
+ log.warn(
60286
+ `[ClawRouter] ${reason} \u2014 proceeding. SpendControl still applied. Set TWZRD_FAIL_OPEN=false to refuse instead.`
60287
+ );
60288
+ return void 0;
60289
+ }
60290
+ log.warn(`[ClawRouter] ${reason} \u2014 refusing the payment (TWZRD_FAIL_OPEN=false).`);
60291
+ return { abort: true, reason };
60292
+ }
60293
+ async function defaultLoadTwzrdGate() {
60294
+ const specifier = TWZRD_GATE_PACKAGE;
60295
+ return import(specifier);
60296
+ }
60297
+ async function maybeComposeTwzrdAutoGate(x402, options) {
60298
+ const env = options?.env ?? process.env;
60299
+ const log = options?.log ?? console;
60300
+ if (!isTwzrdAutoGateEnabled(env)) {
60301
+ return { status: "skipped" };
60302
+ }
60303
+ const load = options?.loadGate ?? defaultLoadTwzrdGate;
60304
+ let gate;
60305
+ try {
60306
+ gate = await load();
60307
+ } catch (err) {
60308
+ if (isMissingTwzrdGateModule(err)) {
60309
+ const reason = `${TWZRD_GATE_PACKAGE} is not installed`;
60310
+ log.warn(
60311
+ `[ClawRouter] TWZRD AutoGate opted in but ${reason} \u2014 skipping. npm i ${TWZRD_GATE_PACKAGE}@0.9.3`
60312
+ );
60313
+ return { status: "unavailable", reason };
60314
+ }
60315
+ throw err;
60316
+ }
60317
+ const installOpts = twzrdAutoGateInstallOptions(env);
60318
+ const timeoutMs = twzrdGateTimeoutMs(env);
60319
+ const ready = (via) => log.log(
60320
+ `[ClawRouter] TWZRD AutoGate ON (opt-in, after SpendControl, ${via}; ${timeoutMs}ms budget, failOpen=${installOpts.failOpen}). Unset TWZRD_AUTO_GATE to disable.`
60321
+ );
60322
+ if (typeof gate.createTwzrdBeforePaymentHook === "function") {
60323
+ const hook = gate.createTwzrdBeforePaymentHook(installOpts);
60324
+ x402.onBeforePaymentCreation(
60325
+ async (ctx) => runTwzrdGateWithTimeout(() => hook(ctx.selectedRequirements, ctx), {
60326
+ timeoutMs,
60327
+ failOpen: installOpts.failOpen,
60328
+ log
60329
+ })
60330
+ );
60331
+ ready("createTwzrdBeforePaymentHook");
60332
+ return { status: "composed", via: "createTwzrdBeforePaymentHook" };
60333
+ }
60334
+ if (typeof gate.installTwzrdAutoGate === "function") {
60335
+ gate.installTwzrdAutoGate(x402, installOpts);
60336
+ log.warn(
60337
+ "[ClawRouter] TWZRD AutoGate installed via installTwzrdAutoGate \u2014 that entry point registers its own hook, so the timeout budget does not apply and it replaces onBeforePaymentCreation on this client. Prefer a gate build exporting createTwzrdBeforePaymentHook."
60338
+ );
60339
+ ready("installTwzrdAutoGate");
60340
+ return { status: "composed", via: "installTwzrdAutoGate" };
60341
+ }
60342
+ throw new Error(
60343
+ `[ClawRouter] ${TWZRD_GATE_PACKAGE} is installed but exports neither installTwzrdAutoGate nor createTwzrdBeforePaymentHook`
60344
+ );
60345
+ }
60346
+ var TWZRD_GATE_PACKAGE, TRUTHY, FALSY, TWZRD_GATE_TIMEOUT_MS, TIMED_OUT;
60347
+ var init_twzrd_autogate = __esm({
60348
+ "src/twzrd-autogate.ts"() {
60349
+ "use strict";
60350
+ init_version4();
60351
+ TWZRD_GATE_PACKAGE = "twzrd-x402-gate";
60352
+ TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
60353
+ FALSY = /* @__PURE__ */ new Set(["0", "false", "no", "off"]);
60354
+ TWZRD_GATE_TIMEOUT_MS = 2e3;
60355
+ TIMED_OUT = { __twzrdTimedOut: true };
60356
+ }
60357
+ });
60358
+
60206
60359
  // src/compression/types.ts
60207
60360
  var DEFAULT_COMPRESSION_CONFIG;
60208
60361
  var init_types2 = __esm({
@@ -91787,6 +91940,7 @@ async function startProxy(options) {
91787
91940
  const evmSigner = toClientEvmSigner(account, evmPublicClient);
91788
91941
  const spendControl = options.spendControl ?? getSharedSpendControl();
91789
91942
  registerSpendPolicyHook(x402, spendControl);
91943
+ await maybeComposeTwzrdAutoGate(x402);
91790
91944
  registerExactEvmScheme(x402, { signer: evmSigner });
91791
91945
  }
91792
91946
  let solanaAddress;
@@ -95372,6 +95526,7 @@ var init_proxy = __esm({
95372
95526
  init_auth();
95373
95527
  init_api_key();
95374
95528
  init_spend_control();
95529
+ init_twzrd_autogate();
95375
95530
  init_compression();
95376
95531
  init_errors5();
95377
95532
  init_version4();
@@ -228160,7 +228315,7 @@ import {
228160
228315
  } from "fs";
228161
228316
  import { readFile as readFileAsync } from "fs/promises";
228162
228317
  import { homedir as homedir14 } from "os";
228163
- import { randomUUID } from "crypto";
228318
+ import { randomUUID as randomUUID2 } from "crypto";
228164
228319
  import { join as join17 } from "path";
228165
228320
  async function waitForProxyHealth(port, timeoutMs = 3e3) {
228166
228321
  const start = Date.now();
@@ -228175,7 +228330,7 @@ async function waitForProxyHealth(port, timeoutMs = 3e3) {
228175
228330
  return false;
228176
228331
  }
228177
228332
  function writePrivateJsonSync(path5, value) {
228178
- const temporary = `${path5}.tmp.${randomUUID()}`;
228333
+ const temporary = `${path5}.tmp.${randomUUID2()}`;
228179
228334
  try {
228180
228335
  writeFileSync3(temporary, JSON.stringify(value, null, 2), { mode: 384 });
228181
228336
  renameSync2(temporary, path5);
@@ -15,6 +15,7 @@ Complete reference for ClawRouter configuration options.
15
15
  - [Tier Overrides](#tier-overrides)
16
16
  - [Scoring Weights](#scoring-weights)
17
17
  - [Spend Control & Counterparty Policy](#spend-control--counterparty-policy)
18
+ - [TWZRD AutoGate (opt-in)](#twzrd-autogate-opt-in)
18
19
  - [Testing Configuration](#testing-configuration)
19
20
 
20
21
  ---
@@ -32,6 +33,9 @@ Complete reference for ClawRouter configuration options.
32
33
  | `CLAWROUTER_WORKER` | - | Set to `1` to enable Worker Mode (earn USDC by running health checks). |
33
34
  | `CLAWROUTER_DEBUG_HEADERS` | (on) | Set to `off`/`false`/`0` to suppress the `x-clawrouter-*` debug response headers. |
34
35
  | `BLOCKRUN_WEB_SEARCH` | (auto-enabled) | Set to `off` to disable BlockRun's Exa web search provider registration. |
36
+ | `TWZRD_AUTO_GATE` | unset (off) | Set to `1` to compose TWZRD AutoGate after SpendControl on the x402 pre-sign hook. Also `TWZRD_GATE_ENABLED=true`. |
37
+ | `TWZRD_GATE_TIMEOUT_MS` | `2000` | How long the gate may take to answer before the payment proceeds on its own. Only meaningful with the gate on. |
38
+ | `TWZRD_FAIL_OPEN` | `true` | Set to `false` to refuse payments when the gate is unreachable instead of proceeding. Only meaningful with the gate on. |
35
39
 
36
40
  ---
37
41
 
@@ -732,6 +736,66 @@ placement, `clawrouter doctor`'s probe) are not covered.
732
736
 
733
737
  ---
734
738
 
739
+ ## TWZRD AutoGate (opt-in)
740
+
741
+ Default **off**. This is not a re-open of default-on vendor lock. SpendControl
742
+ above remains the vendor-neutral path; TWZRD is composed only when you ask.
743
+
744
+ ```bash
745
+ export TWZRD_AUTO_GATE=1
746
+ # equivalent:
747
+ export TWZRD_GATE_ENABLED=true
748
+ ```
749
+
750
+ When set, `startProxy` registers the gate's `createTwzrdBeforePaymentHook` on the
751
+ **same** x402 client, **after** `registerSpendPolicyHook`. SpendControl lists
752
+ still run first. A wash `payTo` is refused before `signTransaction`.
753
+
754
+ ### What it actually covers
755
+
756
+ **Solana, not Base.** The gate's reputation corpus is Solana. `classifyNetwork`
757
+ returns `network_not_scored` for `base` / `eip155:*`, and under our
758
+ `unsupportedNetworkMode: "observe"` those are waved through with verdict
759
+ `unknown` and no network call. The x402 client is shared between both chains, so
760
+ the hook is registered once and applies to Solana payments; `refuseWashFlagged`
761
+ can only ever fire there.
762
+
763
+ ### What leaves your machine
764
+
765
+ On a Solana payment the gate POSTs the resource URL, `payTo`, price and chain to
766
+ `https://intel.twzrd.xyz/v1/intel/preflight`, stamped with
767
+ `X-TWZRD-Integration: clawrouter/<version>` and a per-process run id. Nothing is
768
+ sent when the flag is unset, and nothing is sent for Base.
769
+
770
+ ### Availability
771
+
772
+ The gate's own preflight sets no timeout, and the package defaults to
773
+ `failOpen: false` — an outage there would refuse every paid Solana call. That is
774
+ the shape of the v0.12.271 outage, where an unreachable third party made every
775
+ Solana payment fail with a bare `fetch failed`. So:
776
+
777
+ - We bound the answer at `TWZRD_GATE_TIMEOUT_MS` (default `2000`).
778
+ - We pass `failOpen: true`. On timeout, rejection, or outage the payment
779
+ proceeds and a warning names the reason. SpendControl still applies — it is
780
+ the vendor-neutral guarantee and is untouched by any of this.
781
+ - `TWZRD_FAIL_OPEN=false` restores refuse-on-outage if you would rather stop
782
+ paying than pay unscored.
783
+
784
+ ### Other notes
785
+
786
+ - Optional dependency: `twzrd-x402-gate@0.9.3`. Forks that omit it still install.
787
+ - Missing gate package (`MODULE_NOT_FOUND` for `twzrd-x402-gate` itself): fail
788
+ open — proxy boots, payments unguarded by TWZRD. Any other load/install error
789
+ fails closed.
790
+ - We register the hook ourselves rather than calling `installTwzrdAutoGate`,
791
+ which replaces `onBeforePaymentCreation` on the client — that would make every
792
+ hook registered afterwards skippable via `TWZRD_AUTO_GATE=0`, including ours.
793
+ - Identity: preflight stamps `X-Twzrd-Caller: clawrouter/<version>` so a refuse
794
+ is attributable.
795
+ - Unset the flag to return to SpendControl only.
796
+
797
+ ---
798
+
735
799
  ## Testing Configuration
736
800
 
737
801
  ### Dry Run (No Payments)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/clawrouter",
3
- "version": "0.12.276",
3
+ "version": "0.12.277",
4
4
  "description": "Smart LLM router — save 84% on inference costs. 76 models (7 free). Pay by credit card (API key) or USDC (x402 wallet).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -106,6 +106,9 @@
106
106
  "undici": "^8.10.0",
107
107
  "viem": "^2.39.3"
108
108
  },
109
+ "optionalDependencies": {
110
+ "twzrd-x402-gate": "0.9.3"
111
+ },
109
112
  "peerDependencies": {
110
113
  "openclaw": ">=2025.1.0"
111
114
  },