shinjuku-shielded 0.3.23 → 0.3.25

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/manifest.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schema": "carbon-shielded-wallet/bundle-manifest/v1",
3
3
  "file": "shinjuku-wallet.mjs",
4
- "sha256": "10c8bdfe8bd677ce6f50d143c1af4b28d5b3f653c5c65f6356f1034e6f5840df",
5
- "bytes": 11132902,
6
- "sourceCommit": "f718d9f247df010a8e2b5045666be78877dd3933",
4
+ "sha256": "1505ebf0afdb67fbbe6050d5065335f3b13e94cefd356fb422ce946f4fba3253",
5
+ "bytes": 11138284,
6
+ "sourceCommit": "d34c7e8fbb9d384d53557aeeac05386f35ce2fb9",
7
7
  "sourceDirty": false,
8
8
  "installRecordMatchesLockfile": true,
9
9
  "releaseRule": "valid only when two fresh npm ci --ignore-scripts builds on different OSes give the same sha256; installRecordMatchesLockfile true means every entry npm installed equals its package-lock.json entry and every entry not marked optional, devOptional included, is installed: an optional entry (a package for another platform) may be absent",
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shielded",
3
- "version": "0.3.23",
3
+ "version": "0.3.25",
4
4
  "mcpName": "io.github.ShinjukuStaition/shinjuku-mcp",
5
5
  "description": "Shinjuku Shielded, the private x402 facilitator on Solana: the self-custody wallet CLI and its MCP server. Shield, pay, and unshield USDC from any MCP agent, under caps you set.",
6
- "shinjukuRelease": "10c8bdfe",
7
- "shinjukuSha256": "10c8bdfe8bd677ce6f50d143c1af4b28d5b3f653c5c65f6356f1034e6f5840df",
6
+ "shinjukuRelease": "1505ebf0",
7
+ "shinjukuSha256": "1505ebf0afdb67fbbe6050d5065335f3b13e94cefd356fb422ce946f4fba3253",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
@@ -59758,6 +59758,16 @@ var init_recoveryCache = __esm({
59758
59758
  });
59759
59759
 
59760
59760
  // wallet/src/succession/successorHistories.mjs
59761
+ function withoutProgramHistory(identity) {
59762
+ if (!identity || typeof identity !== "object" || identity.programHistory === void 0) return void 0;
59763
+ const { programHistory: _history, ...rest } = identity;
59764
+ return rest;
59765
+ }
59766
+ function errorChainText(error) {
59767
+ const parts = [];
59768
+ for (let current = error, depth = 0; current && depth < 8; current = current.cause, depth += 1) parts.push(String(current?.message ?? current));
59769
+ return parts.join(" ");
59770
+ }
59761
59771
  async function loadSuccessorHistories({
59762
59772
  history,
59763
59773
  store,
@@ -59775,24 +59785,34 @@ async function loadSuccessorHistories({
59775
59785
  if (typeof target !== "string") return walks;
59776
59786
  if (seen.has(target) || walks.length >= MAX_SUCCESSOR_HOPS) throw new Error("wallet_succession_chain_invalid");
59777
59787
  seen.add(target);
59778
- const walk = await loadHistory({
59788
+ const identity = deploymentIdentityFor?.(target) ?? deploymentIdentity;
59789
+ const read = (given) => loadHistory({
59779
59790
  store,
59780
59791
  connection,
59781
59792
  programId,
59782
59793
  pool: target,
59783
- deploymentIdentity: deploymentIdentityFor?.(target) ?? deploymentIdentity,
59794
+ deploymentIdentity: given,
59784
59795
  cache: walletSuccessorHistoryCache(store, target)
59785
59796
  });
59797
+ let walk;
59798
+ try {
59799
+ walk = await read(identity);
59800
+ } catch (error) {
59801
+ const historyFree = withoutProgramHistory(identity);
59802
+ if (!historyFree || !HISTORY_FREE_NEEDED.test(errorChainText(error))) throw error;
59803
+ walk = await read(historyFree);
59804
+ }
59786
59805
  if (walk?.pool !== target) throw new Error("wallet_succession_chain_invalid");
59787
59806
  walks.push(walk);
59788
59807
  current = walk;
59789
59808
  }
59790
59809
  }
59791
- var MAX_SUCCESSOR_HOPS;
59810
+ var HISTORY_FREE_NEEDED, MAX_SUCCESSOR_HOPS;
59792
59811
  var init_successorHistories = __esm({
59793
59812
  "wallet/src/succession/successorHistories.mjs"() {
59794
59813
  "use strict";
59795
59814
  init_walletHistory();
59815
+ HISTORY_FREE_NEEDED = /p03_program_history_coverage_interval_invalid_pool_initialized_after_last_upgrade_use_history_free_identity/;
59796
59816
  MAX_SUCCESSOR_HOPS = 8;
59797
59817
  }
59798
59818
  });
@@ -90683,14 +90703,27 @@ function floatRecordOf(store, requestId) {
90683
90703
  function floatInnerRequestId(requestId) {
90684
90704
  return requestId.length <= 122 ? `${requestId}:float` : `float:${hash8(requestId)}`;
90685
90705
  }
90686
- function floatRefundsInFlightAtomic(store, refreshedAtMs) {
90706
+ function floatRefundsInFlightAtomic(store, snapshot) {
90707
+ const refreshedAtMs = Date.parse(snapshot?.refreshedAt ?? "");
90708
+ const outputs = Array.isArray(snapshot?.outputs) ? snapshot.outputs : [];
90709
+ const knowsRecipients = outputs.some((output2) => output2?.kind === "note" && output2.diversifier !== void 0);
90710
+ const arrived = (refundTo) => {
90711
+ let address5;
90712
+ try {
90713
+ address5 = decodeShieldedSolanaP03Address(String(refundTo));
90714
+ } catch {
90715
+ return false;
90716
+ }
90717
+ return outputs.some((output2) => output2?.kind === "note" && output2.diversifier === address5.diversifier.toString() && output2.recipientX === address5.recipientX.toString());
90718
+ };
90687
90719
  let total = 0n;
90688
90720
  try {
90689
90721
  for (const name of store.listWal()) {
90690
90722
  if (!name.startsWith(FLOAT_PAYMENT_WAL_PREFIX)) continue;
90691
90723
  const record = store.readWal(name);
90692
90724
  if (record?.schema !== FLOAT_PAYMENT_SCHEMA || record.state !== "refund_pending" || !POSITIVE.test(String(record.paidAtomic))) continue;
90693
- if (Number.isFinite(refreshedAtMs) && refreshedAtMs >= record.refundPendingAtMs + REFUND_SETTLED_AFTER_MS) continue;
90725
+ if (arrived(record.refundTo)) continue;
90726
+ if (!knowsRecipients && Number.isFinite(refreshedAtMs) && refreshedAtMs >= record.refundPendingAtMs + REFUND_SETTLED_AFTER_MS) continue;
90694
90727
  total += BigInt(record.paidAtomic);
90695
90728
  }
90696
90729
  } catch {
@@ -118895,7 +118928,27 @@ function snapshotFile(store) {
118895
118928
  return file;
118896
118929
  }
118897
118930
  function captureWalStates(store) {
118898
- return Object.fromEntries(store.listWal().sort().map((name) => [name, String(store.readWal(name)?.state)]));
118931
+ return Object.fromEntries(store.listWal().sort().map((name) => [name, walStateOf(name, store.readWal(name))]));
118932
+ }
118933
+ var SUCCESSION_JOB_SCHEMA = "carbon-shielded-wallet/pool-succession/v1";
118934
+ function walStateOf(name, record) {
118935
+ if (name.startsWith("succ-") && record?.schema === SUCCESSION_JOB_SCHEMA && canonicalAtomic(String(record.movedAtomic))) {
118936
+ return `${record.state}|${record.movedAtomic}`;
118937
+ }
118938
+ return String(record?.state);
118939
+ }
118940
+ function movedInAfterSnapshot(store, state, snapshot) {
118941
+ let total = 0n;
118942
+ for (const name of store.listWal()) {
118943
+ if (!name.startsWith("succ-")) continue;
118944
+ const record = store.readWal(name);
118945
+ if (record?.schema !== SUCCESSION_JOB_SCHEMA || record.to !== state.pool || !canonicalAtomic(String(record.movedAtomic))) continue;
118946
+ const before = Object.hasOwn(snapshot.walStates, name) ? String(snapshot.walStates[name]) : void 0;
118947
+ const kept = before?.includes("|") ? before.split("|")[1] : before === "moved" ? String(record.movedAtomic) : "0";
118948
+ const moved = BigInt(record.movedAtomic) - (canonicalAtomic(kept) ? BigInt(kept) : 0n);
118949
+ if (moved > 0n) total += moved;
118950
+ }
118951
+ return total;
118899
118952
  }
118900
118953
  function snapshotFromRecovery({ state, programId, recovery, history, walStates, fresh, now = Date.now() }) {
118901
118954
  if (!Array.isArray(recovery?.recovered)) throw new Error("balance_snapshot_recovery_invalid");
@@ -118908,7 +118961,10 @@ function snapshotFromRecovery({ state, programId, recovery, history, walStates,
118908
118961
  leafIndex: String(entry?.leafIndex),
118909
118962
  valueAtomic: value === void 0 ? void 0 : String(value),
118910
118963
  spendStatus: entry?.spendStatus,
118911
- digest: digestKey(entry?.digest)
118964
+ digest: digestKey(entry?.digest),
118965
+ // A note's one-time recipient (diversifier d and recipient x): a float refund is matched to its
118966
+ // own refund address by these (floatRefundsInFlightAtomic). Public to this wallet only (sealed cache).
118967
+ ...entry?.kind === "note" && /^[0-9]+$/.test(String(entry.note?.d)) && /^[0-9]+$/.test(String(entry.note?.pDX)) ? { diversifier: String(entry.note.d), recipientX: String(entry.note.pDX) } : {}
118912
118968
  };
118913
118969
  if (!["account", "note"].includes(output2.kind) || !canonicalAtomic(output2.valueAtomic) || !canonicalAtomic(output2.commitment) || !canonicalAtomic(output2.leafIndex) || !SPEND_STATUSES.has(output2.spendStatus) || !/^[0-9a-f]{64}$/.test(output2.digest)) {
118914
118970
  throw new Error("balance_snapshot_output_invalid");
@@ -119030,7 +119086,8 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
119030
119086
  ...entry.record.relay?.approvedOnChain ? { approvalStanding: true } : {}
119031
119087
  }));
119032
119088
  const receipts = records.filter((entry) => entry.type === "payment" && entry.phase === "done");
119033
- const paid = receipts.filter((entry) => !String(entry.record.url ?? "").startsWith(SELF_PAY_ORIGIN2) && !String(entry.record.url ?? "").includes(POOL_MOVE_PATH2));
119089
+ const refunded = new Set(records.filter((entry) => entry.type === "float-payment" && entry.state === "refund_pending").map((entry) => entry.record.innerRequestId).filter((id) => typeof id === "string"));
119090
+ const paid = receipts.filter((entry) => !String(entry.record.url ?? "").startsWith(SELF_PAY_ORIGIN2) && !String(entry.record.url ?? "").includes(POOL_MOVE_PATH2) && !refunded.has(entry.record.requestId));
119034
119091
  const sum2 = (values) => values.reduce((total, value) => total + BigInt(value ?? "0"), 0n);
119035
119092
  const config = state.config ?? {};
119036
119093
  const capOrNone = (value) => String(value) === NO_LIMIT_ATOMIC2 ? "none" : value;
@@ -119110,7 +119167,8 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
119110
119167
  detail.unspentAtomic = unspentAtomic.toString();
119111
119168
  const settling = [];
119112
119169
  const movable = snapshot.outputs.filter((output2) => output2.spendStatus === "succession-movable");
119113
- const successionMovableAtomic = sum2(movable.map((output2) => output2.valueAtomic));
119170
+ const movedInAtomic = movedInAfterSnapshot(store, state, snapshot);
119171
+ const successionMovableAtomic = sum2(movable.map((output2) => output2.valueAtomic)) + movedInAtomic;
119114
119172
  detail.successionMovableAtomic = successionMovableAtomic.toString();
119115
119173
  detail.unspentAccounts = [...unspent.values()].filter((output2) => output2.kind === "account").length;
119116
119174
  detail.unspentNotes = [...unspent.values()].filter((output2) => output2.kind === "note").length;
@@ -119240,7 +119298,10 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
119240
119298
  const anchorIndex = canonicalAtomic(snapshot.anchorIndex) ? BigInt(snapshot.anchorIndex) : void 0;
119241
119299
  detail.fundedAccounts = unknownInputs ? 0 : [...unspent.values()].filter((output2) => output2.kind === "account" && BigInt(output2.valueAtomic) > 0n && !reserved.has(output2.commitment) && !settlingInputs.has(output2.commitment) && (anchorIndex === void 0 || BigInt(output2.leafIndex) < anchorIndex)).length;
119242
119300
  const next = detail.inFlight.length ? "finish or cancel the work in detail.inFlight; the wallet runs one payment at a time" : pendingDeposits.length ? `deposit ${pendingDeposits[0].depositId} is not finished: rerun the same deposit command with --submit --service <facilitator URL>` : pocketsUnscanned ? scanPockets2 : unspentAtomic === 0n && successionMovableAtomic > 0n ? "this pool was succeeded by a new pool: its value moves there (a move per account, the facilitator pays the fee) and counts in this balance until then" : unspentAtomic === 0n ? "nothing to spend: deposit (shinjuku-wallet help deposit), or run balance --refresh if a payment to this wallet settled" : void 0;
119243
- if (settling.length) {
119301
+ if (movedInAtomic > 0n) {
119302
+ settlingReasons.push(`${formatAtomic(movedInAtomic, decimals)} moved into this pool from the old pool after the last refresh: the balance counts it; refresh again in a minute`);
119303
+ }
119304
+ if (settling.length || movedInAtomic > 0n) {
119244
119305
  reasons.push(...settlingReasons);
119245
119306
  return report("stale", spendable + successionMovableAtomic, next ?? refreshCommand(state, snapshot.programId));
119246
119307
  }
@@ -122048,6 +122109,7 @@ init_store();
122048
122109
  var import_web3127 = __toESM(require_index_cjs(), 1);
122049
122110
  init_chain2();
122050
122111
  init_walk();
122112
+ init_receiptTransaction();
122051
122113
  init_serviceUrl();
122052
122114
  import { randomInt as randomInt4 } from "node:crypto";
122053
122115
 
@@ -122417,6 +122479,18 @@ async function chainNowSeconds(connection) {
122417
122479
  return info.data.readBigInt64LE(32);
122418
122480
  }
122419
122481
  var sameInputs = (context, inputs) => Array.isArray(context.publicInputs) && context.publicInputs.length === inputs.length && context.publicInputs.every((value, index) => fieldHex4(value) === inputs[index]);
122482
+ var landedIsLeg = (context, body) => {
122483
+ if (Array.isArray(context.publicInputs)) return sameInputs(context, body.publicInputBytesHex);
122484
+ try {
122485
+ const payload2 = Buffer.concat([
122486
+ Buffer.from(String(body.proofBytesHex).replace(/^0x/, ""), "hex"),
122487
+ ...body.publicInputBytesHex.map((value) => Buffer.from(String(value).replace(/^0x/, ""), "hex"))
122488
+ ]);
122489
+ return contextPayloadMatches(context, payload2);
122490
+ } catch {
122491
+ return false;
122492
+ }
122493
+ };
122420
122494
  async function destinationMoves(deps, sourceDomain) {
122421
122495
  if (typeof deps.loadDestinationReport !== "function") {
122422
122496
  throw refuse7("wallet_succession_history_unavailable", "no successor-pool history to confirm the move against");
@@ -122488,6 +122562,9 @@ async function runSuccessionMove({ store, verified, source, facilitator, connect
122488
122562
  let rebuilds = 0;
122489
122563
  for (; ; ) {
122490
122564
  if (leg.state === "confirmed" || leg.state === "dropped") return void 0;
122565
+ if (leg.state === "failed" && leg.lastCode === "wallet_succession_leg_receipt_invalid" && typeof leg.transaction === "string" && leg.body) {
122566
+ leg = save2(leg, { state: "relayed" });
122567
+ }
122491
122568
  if (leg.state === "failed") leg = save2(leg, { state: "planned" });
122492
122569
  if (leg.state === "planned") {
122493
122570
  const inputs = [...leg.inputs.account ? [leg.inputs.account] : [], ...leg.inputs.notes];
@@ -122585,7 +122662,7 @@ async function runSuccessionMove({ store, verified, source, facilitator, connect
122585
122662
  const moves = await destinationMoves(deps, sourceDomain);
122586
122663
  const landed = moves.find((context) => context.provenance?.signature === leg.transaction);
122587
122664
  if (landed) {
122588
- if (!sameInputs(landed, leg.body.publicInputBytesHex)) {
122665
+ if (!landedIsLeg(landed, leg.body)) {
122589
122666
  save2(leg, { state: "failed", lastCode: "wallet_succession_leg_receipt_invalid" });
122590
122667
  throw refuse7("wallet_succession_leg_receipt_invalid", "the relay's transaction is not this leg's move; tell the operator");
122591
122668
  }
@@ -122624,7 +122701,7 @@ async function runSuccessionMove({ store, verified, source, facilitator, connect
122624
122701
 
122625
122702
  // wallet/src/succession/scheduler.mjs
122626
122703
  init_successorHistories();
122627
- var SUCCESSION_JOB_SCHEMA = "carbon-shielded-wallet/pool-succession/v1";
122704
+ var SUCCESSION_JOB_SCHEMA2 = "carbon-shielded-wallet/pool-succession/v1";
122628
122705
  var SUCCESSION_DELAY_MIN_MS = 5 * 6e4;
122629
122706
  var SUCCESSION_DELAY_MAX_MS = 30 * 6e4;
122630
122707
  var SUCCESSION_LATE_START_MAX_MS = 6e4;
@@ -122724,10 +122801,10 @@ function successionMessage(job) {
122724
122801
  var SUCCESSION_WHY = "Your facilitator upgraded to a new pool. Your balance moves to the new pool so you can keep paying; the money stays yours.";
122725
122802
  function readSuccessionJob(store, fromPool) {
122726
122803
  const record = store.readWal(successionJobName(fromPool));
122727
- return record?.schema === SUCCESSION_JOB_SCHEMA && record.from === fromPool ? record : void 0;
122804
+ return record?.schema === SUCCESSION_JOB_SCHEMA2 && record.from === fromPool ? record : void 0;
122728
122805
  }
122729
122806
  function successionJobs(store) {
122730
- return store.listWal().filter((name) => /^succ-[0-9A-Za-z]{1,8}$/.test(name)).map((name) => store.readWal(name)).filter((record) => record?.schema === SUCCESSION_JOB_SCHEMA);
122807
+ return store.listWal().filter((name) => /^succ-[0-9A-Za-z]{1,8}$/.test(name)).map((name) => store.readWal(name)).filter((record) => record?.schema === SUCCESSION_JOB_SCHEMA2);
122731
122808
  }
122732
122809
  var noticeKey = (job) => job.state === "failed-retry" ? `failed:${job.lastCode ?? ""}` : PUBLIC_STATE[job.state];
122733
122810
  var SUCCESSION_NOTICE_SCHEMA = "carbon-shielded-wallet/pool-succession-notice/v1";
@@ -122877,6 +122954,7 @@ function createSuccessionScheduler(options) {
122877
122954
  const startedAtMs = now();
122878
122955
  const lateStart = /* @__PURE__ */ new Map();
122879
122956
  const recordCheckedAt = /* @__PURE__ */ new Map();
122957
+ const receiptRecheck = /* @__PURE__ */ new Set();
122880
122958
  let active;
122881
122959
  let closed = false;
122882
122960
  const listEntries = () => {
@@ -122960,7 +123038,7 @@ function createSuccessionScheduler(options) {
122960
123038
  return job;
122961
123039
  }
122962
123040
  const base = (entry, value, verified) => ({
122963
- schema: SUCCESSION_JOB_SCHEMA,
123041
+ schema: SUCCESSION_JOB_SCHEMA2,
122964
123042
  from: entry.from,
122965
123043
  to: verified?.successor ?? entry.to,
122966
123044
  record: entry.record,
@@ -123078,8 +123156,13 @@ function createSuccessionScheduler(options) {
123078
123156
  }
123079
123157
  case "move-scheduled":
123080
123158
  return now() >= startAt(job) ? startRun(entry) : job;
123081
- case "moving":
123082
123159
  case "failed-retry":
123160
+ if (job.lastCode === "wallet_succession_leg_receipt_invalid" && job.updatedAtMs < startedAtMs && !receiptRecheck.has(entry.from)) {
123161
+ receiptRecheck.add(entry.from);
123162
+ job = save2(store, job, { permanent: false, nextTryAtMs: now() });
123163
+ }
123164
+ return now() >= (job.nextTryAtMs ?? 0) ? startRun(entry) : job;
123165
+ case "moving":
123083
123166
  return now() >= (job.nextTryAtMs ?? 0) ? startRun(entry) : job;
123084
123167
  default:
123085
123168
  return job;
@@ -124920,13 +125003,13 @@ function oneBalance(report, store) {
124920
125003
  const shieldedAtomic = report?.split?.shieldedAtomic ?? null;
124921
125004
  const pocketsAtomic = report?.split?.pocketsAtomic ?? null;
124922
125005
  const moving = movingAtomic(store);
124923
- let refreshedAtMs;
125006
+ let snapshot;
124924
125007
  try {
124925
- refreshedAtMs = Date.parse(readBalanceSnapshot(store).snapshot?.refreshedAt ?? "");
125008
+ snapshot = readBalanceSnapshot(store).snapshot;
124926
125009
  } catch {
124927
- refreshedAtMs = void 0;
125010
+ snapshot = void 0;
124928
125011
  }
124929
- const refunds = floatRefundsInFlightAtomic(store, refreshedAtMs);
125012
+ const refunds = floatRefundsInFlightAtomic(store, snapshot);
124930
125013
  const known = shieldedAtomic !== null && pocketsAtomic !== null;
124931
125014
  const setAside = known ? BigInt(pocketsAtomic) + moving : null;
124932
125015
  const total = known ? BigInt(shieldedAtomic) + setAside + refunds : null;
@@ -125528,6 +125611,13 @@ function createWalletTools({
125528
125611
  next = typeof blocking === "string" && /^[A-Za-z0-9._:-]{1,128}$/.test(blocking) ? `Nothing was paid: an earlier payment (request_id "${blocking}") is not finished, and the wallet pays one at a time. Call x402_pay again with request_id "${blocking}" and that payment's url, method, headers, and body (it finishes it and never pays twice), or close it with wallet_cancel request_id "${blocking}"; then call x402_pay again with request_id "${requestId}"` : `Nothing was paid: an earlier payment is not finished, and the wallet pays one at a time. wallet_balance lists it in detail.inFlight; finish it with x402_pay and its request_id, or close it with wallet_cancel; then call x402_pay again with request_id "${requestId}"`;
125529
125612
  } else if (/accounts_split_merge_first/.test(line)) {
125530
125613
  next = `Nothing was paid: the shielded balance covers this price in total, but it sits in several accounts and one payment draws only one account and at most 3 notes. Tell your owner to join them: shinjuku-wallet merge --wallet <wallet.json> --passphrase-file passphrase.txt --program <program> --proof-tools proof-tools.json --profile public-profile.json --facilitator <facilitator origin> (--dry-run shows the plan; shinjuku-wallet help merge). After it finished and the next pool anchor (up to 5 minutes; about 30 s on a pool with sub-anchors) passed, call x402_pay again with request_id "${requestId}" and the same url, method, headers, and body`;
125614
+ } else if (/^p03_merchant_respend_(insufficient_recovered_value|no_spendable_account)$|^(p03_payer_)?insufficient(_recovered)?_value$/.test(code)) {
125615
+ return refuse9(
125616
+ code,
125617
+ "the shielded balance is less than this payment needs (price + fee); nothing was paid",
125618
+ `Nothing was paid. Tell the user the balance is too low for this price: wallet_balance shows breakdown.shieldedAtomic (a refund still on its way shows as refundsInFlightAtomic and becomes spendable when it arrives). To pay, add USDC with wallet_shield (or wallet_shield_from_elsewhere), wait until wallet_balance counts it, then call x402_pay again with request_id "${requestId}" and the same url, method, headers, and body (it never pays twice)`,
125619
+ { requestId }
125620
+ );
125531
125621
  } else if (code.startsWith("wallet_spend_refused_")) {
125532
125622
  next = "the wallet file's own budget refused it (init --max-payment / --max-cumulative); nothing was paid. Read wallet_balance detail.budget";
125533
125623
  } else if (code.startsWith("wallet_quote_refused_") || code.startsWith("fetch_")) {