shinjuku-shield 0.3.121 → 0.3.122

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
@@ -6,19 +6,19 @@ answers privately, in a terminal chat or for your agent app. Its `mcp`
6
6
  command runs a local MCP server (stdio) for your agent. Your keys never
7
7
  leave your machine.
8
8
 
9
- This is version 0.3.121. The npm packages `shinjuku-shield` and
9
+ This is version 0.3.122. The npm packages `shinjuku-shield` and
10
10
  `shinjuku-shielded` hold the same files. The command is `shinjuku`.
11
11
 
12
12
  ## Set up
13
13
 
14
14
  ```sh
15
- npm install -g shinjuku@0.3.121
15
+ npm install -g shinjuku@0.3.122
16
16
  shinjuku setup
17
17
  ```
18
18
 
19
19
  Type the two lines as they are. `npx install shinjuku` does not work: npx
20
20
  reads `install` as the name of another npm package. To set up without
21
- installing, type `npx shinjuku@0.3.121 setup`.
21
+ installing, type `npx shinjuku@0.3.122 setup`.
22
22
 
23
23
  Setup makes the wallet on this machine, a private passphrase file, and the
24
24
  checked proof tools (about 191 MB), and writes `config.json`. It also writes
@@ -36,13 +36,13 @@ Every wallet command works after `shinjuku`, for example `shinjuku balance`,
36
36
  Claude Code:
37
37
 
38
38
  ```sh
39
- claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.121 mcp
39
+ claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.122 mcp
40
40
  ```
41
41
 
42
42
  Claude Desktop (`claude_desktop_config.json`) or Cursor (`~/.cursor/mcp.json`):
43
43
 
44
44
  ```json
45
- { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.121", "mcp"] } } }
45
+ { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.122", "mcp"] } } }
46
46
  ```
47
47
 
48
48
  On native Windows, if the host cannot find `npx`, use `"command": "cmd"` and
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": "b2d6f4507136f85fea6e6305a0a958a98b95c3ba2b82a060cc182b4a6d6f26bd",
5
- "bytes": 8471165,
6
- "sourceCommit": "9337bf3eaa4a9ac34e6d850cc800b8951dc657cd",
4
+ "sha256": "67cedf24617c4cd78d268b549ad4b346101b6de7338a5a3889df8f81a29c78a3",
5
+ "bytes": 8472739,
6
+ "sourceCommit": "31eb1a0d3a0f1a9d380e2b1bf046d8abcc5420cc",
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",
@@ -1037,5 +1037,5 @@
1037
1037
  "version": "1.8.0"
1038
1038
  }
1039
1039
  ],
1040
- "publishedAt": "2026-10-28T18:56:33.630Z"
1040
+ "publishedAt": "2026-10-28T19:36:37.026Z"
1041
1041
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shield",
3
- "version": "0.3.121",
3
+ "version": "0.3.122",
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": "b2d6f450",
7
- "shinjukuSha256": "b2d6f4507136f85fea6e6305a0a958a98b95c3ba2b82a060cc182b4a6d6f26bd",
6
+ "shinjukuRelease": "67cedf24",
7
+ "shinjukuSha256": "67cedf24617c4cd78d268b549ad4b346101b6de7338a5a3889df8f81a29c78a3",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-shield#readme",
@@ -127422,7 +127422,15 @@ ${canonical5}`;
127422
127422
  { requestId, paid: false, balanceEmpty: true, ...publicAtomic ? { publicAtomic } : {} }
127423
127423
  );
127424
127424
  }
127425
- const zeroBeforeAsk = (tool, requestId) => knownZeroBalance({ maxAgeMs: INBOUND_REFRESH_AGE_MS }).empty ? zeroBalanceStop({ tool, requestId, same: tool === "x402_pay" ? "the same url, method, headers, and body" : "the same arguments" }) : void 0;
127425
+ async function knownZeroFresh() {
127426
+ if (knownZeroBalance({ maxAgeMs: INBOUND_REFRESH_AGE_MS }).empty) return true;
127427
+ if (!knownZeroBalance().empty || refreshFailedAt && Date.now() - refreshFailedAt < REFRESH_RETRY_MS) return false;
127428
+ const outcome = await Promise.race([refreshShared().then(() => "done", () => "failed"), sleep2(balanceRefreshWaitMs).then(() => "waiting")]);
127429
+ return outcome === "done" && knownZeroBalance({ maxAgeMs: INBOUND_REFRESH_AGE_MS }).empty;
127430
+ }
127431
+ const zeroBeforeAsk = async (tool, requestId) => await knownZeroFresh() ? zeroBalanceStop({ tool, requestId, same: tool === "x402_pay" ? "the same url, method, headers, and body" : "the same arguments" }) : void 0;
127432
+ const floatSplitOf = (error) => ATOMIC_TEXT.test(String(error?.priceAtomic ?? "")) && ATOMIC_TEXT.test(String(error?.feeAtomic ?? "")) ? { priceAtomic: String(error.priceAtomic), feeAtomic: String(error.feeAtomic) } : void 0;
127433
+ const splitWords = (split4, price) => split4 ? ` (${price} ${usdcText3(split4.priceAtomic)} USDC plus Shinjuku's fee ${usdcText3(split4.feeAtomic)} USDC)` : "";
127426
127434
  async function askLimit2({ requestId, url, numbers, calledAt, message }) {
127427
127435
  const answer = await confirmOnce({ message, waitMs: budgetLeft(calledAt) });
127428
127436
  if (!answer.ok) {
@@ -127485,7 +127493,7 @@ ${canonical5}`;
127485
127493
  async function payCall({ target, request, argument: argument2, requestId, given, calledAt, progress: progress3, canProgress, signal }) {
127486
127494
  let cap = policy.resolvePaymentCap(argument2, { requestId });
127487
127495
  if (!cap.ok && cap.error === "mcp_cap_argument_above_launch_cap") {
127488
- const zero = zeroBeforeAsk("x402_pay", requestId);
127496
+ const zero = await zeroBeforeAsk("x402_pay", requestId);
127489
127497
  if (zero) return zero;
127490
127498
  const numbers = { totalAtomic: String(argument2), capAtomic: policy.limits.maxPaymentAtomic };
127491
127499
  const asked = await askLimit2({
@@ -127595,21 +127603,22 @@ ${canonical5}`;
127595
127603
  const launch = policy.limits.maxPaymentAtomic;
127596
127604
  const total = code2 === "p03_float_over_maximum" ? ATOMIC_TEXT.test(String(error?.paidAtomic ?? "")) ? String(error.paidAtomic) : void 0 : capNumbers(error?.refusal).totalAtomic;
127597
127605
  if (total !== void 0 && BigInt(total) > BigInt(launch) && BigInt(maxNow) === BigInt(launch)) {
127598
- const zero = zeroBeforeAsk("x402_pay", requestId);
127606
+ const zero = await zeroBeforeAsk("x402_pay", requestId);
127599
127607
  if (zero) return zero;
127600
127608
  const numbers = { totalAtomic: total, capAtomic: launch };
127609
+ const split4 = code2 === "p03_float_over_maximum" ? floatSplitOf(error) : void 0;
127601
127610
  const asked = await askLimit2({
127602
127611
  requestId,
127603
127612
  url: target.url,
127604
127613
  numbers,
127605
127614
  calledAt,
127606
- message: `This costs ${usdcText3(total)} USDC. Your ${payCapName(launch)} is ${usdcText3(launch)} USDC. Allow it once?`
127615
+ message: `This costs ${usdcText3(total)} USDC${splitWords(split4, "the price")}. Your ${payCapName(launch)} is ${usdcText3(launch)} USDC. Allow it once?`
127607
127616
  });
127608
127617
  if (asked.ok) {
127609
127618
  maxNow = total;
127610
127619
  continue;
127611
127620
  }
127612
- if (asked.why !== "unsupported") return notAllowed(asked.why, { tool: "x402_pay", requestId, numbers });
127621
+ if (asked.why !== "unsupported") return notAllowed(asked.why, { tool: "x402_pay", requestId, numbers }, split4 ?? {});
127613
127622
  askUnshown = true;
127614
127623
  }
127615
127624
  }
@@ -128426,6 +128435,7 @@ ${canonical5}`;
128426
128435
  };
128427
128436
  const positive2 = (atomic4) => /^[1-9][0-9]*$/.test(String(atomic4 ?? ""));
128428
128437
  let refresh = {};
128438
+ let backgroundWalked = false;
128429
128439
  const recentlyFailed = refreshFailedAt && Date.now() - refreshFailedAt < REFRESH_RETRY_MS && !refreshRun;
128430
128440
  if (report.split?.shieldedAtomic != null && report.status === "stale") {
128431
128441
  if (!recentlyFailed && (!finalityWait || refreshDeferred)) refreshShared().catch(() => {
@@ -128443,7 +128453,9 @@ ${canonical5}`;
128443
128453
  refresh = { refreshed: true };
128444
128454
  } else refresh = { refreshing: true, refreshReason: "refund_check" };
128445
128455
  } else if (report.split?.shieldedAtomic != null && !recentlyFailed && !finalityWait && !refreshRun && (report.detail?.lastRefresh?.ageSeconds ?? 0) * 1e3 >= INBOUND_REFRESH_AGE_MS) {
128446
- refreshShared({ background: true }).catch(() => {
128456
+ refreshShared({ background: true }).then(() => {
128457
+ backgroundWalked = true;
128458
+ }, () => {
128447
128459
  });
128448
128460
  } else if (report.split?.shieldedAtomic == null && recentlyFailed) {
128449
128461
  refresh = { refreshed: false, refreshError: refreshFailedCode ?? "mcp_refresh_failed" };
@@ -128505,6 +128517,13 @@ ${canonical5}`;
128505
128517
  });
128506
128518
  const answered2 = await Promise.race([publicRead, answerWindow]);
128507
128519
  if (tokensAnswered.reading) await Promise.race([tokensRead, answerWindow]);
128520
+ if (backgroundWalked) {
128521
+ try {
128522
+ store = session.openStore();
128523
+ } catch {
128524
+ }
128525
+ reread("refresh");
128526
+ }
128508
128527
  mark("balance-public-read", answered2.reading ? "missed" : answered2.read ? "answered" : "failed");
128509
128528
  mark("balance-tokens-read", tokensAnswered.reading ? "missed" : tokensAnswered.rows ? "answered" : "failed");
128510
128529
  let ownTokens;
@@ -131143,7 +131162,7 @@ ${canonical5}`;
131143
131162
  const capNext = (detail) => `Nothing was moved. Tell your owner: ${detail}. ${capRaiseText(spec.capFlag)} Then call ${spec.tool} again.`;
131144
131163
  if (max !== void 0 && amount > max) {
131145
131164
  const detail = `${record ? `the recorded ${spec.noun} of request_id "${request.requestId}" is not finished; its ` : ""}amount_atomic ${amount} (${usdcText3(amount)} USDC) is above the ${capName(spec.capFlag)} of ${usdcFlagText(max)} USDC`;
131146
- const empty = kind === "unshield" && !record && knownZeroBalance({ maxAgeMs: INBOUND_REFRESH_AGE_MS }).empty ? zeroBalanceFacts() : void 0;
131165
+ const empty = kind === "unshield" && !record && await knownZeroFresh() ? zeroBalanceFacts() : void 0;
131147
131166
  if (empty && !empty.depositInFlight && !empty.heldUnknown) {
131148
131167
  return refuse12(
131149
131168
  "mcp_unshield_balance_empty",
@@ -133876,7 +133895,7 @@ ${canonical5}`;
133876
133895
  const facts = { model: args.model, maxTokens: args.max_tokens, ceilingAtomic: ceiling.toString(), ceiling: usd(ceiling) };
133877
133896
  let cap = policy.resolvePaymentCap(argument2, { requestId });
133878
133897
  if (!cap.ok && cap.error === "mcp_cap_argument_above_launch_cap") {
133879
- const zero = zeroBeforeAsk("inference_chat", requestId);
133898
+ const zero = await zeroBeforeAsk("inference_chat", requestId);
133880
133899
  if (zero) return zero;
133881
133900
  const numbers = { totalAtomic: String(argument2), capAtomic: launch };
133882
133901
  const asked2 = await askLimit2({
@@ -133926,7 +133945,7 @@ ${canonical5}`;
133926
133945
  }
133927
133946
  );
133928
133947
  }
133929
- const zero = zeroBeforeAsk("inference_chat", requestId);
133948
+ const zero = await zeroBeforeAsk("inference_chat", requestId);
133930
133949
  if (zero) return zero;
133931
133950
  askedLimit = true;
133932
133951
  const numbers = { totalAtomic: ceiling.toString(), capAtomic: launch };
@@ -134008,22 +134027,23 @@ ${canonical5}`;
134008
134027
  const total = ATOMIC_TEXT.test(String(error?.paidAtomic ?? "")) ? String(error.paidAtomic) : void 0;
134009
134028
  const lifted = BigInt(narrowed) > BigInt(launch);
134010
134029
  if (total !== void 0 && BigInt(total) > BigInt(launch) && BigInt(narrowed) === BigInt(launch) && !askedLimit) {
134011
- const zero = zeroBeforeAsk("inference_chat", requestId);
134030
+ const zero = await zeroBeforeAsk("inference_chat", requestId);
134012
134031
  if (zero) return zero;
134013
134032
  askedLimit = true;
134014
134033
  const numbers = { totalAtomic: total, capAtomic: launch };
134034
+ const split4 = floatSplitOf(error);
134015
134035
  const asked2 = await askLimit2({
134016
134036
  requestId,
134017
134037
  url: target.url,
134018
134038
  numbers,
134019
134039
  calledAt,
134020
- message: `This costs ${usdcText3(total)} USDC with the fee. Your ${payCapName(launch)} is ${usdcText3(launch)} USDC. Allow it once?`
134040
+ message: `This costs ${usdcText3(total)} USDC${split4 ? splitWords(split4, "the AI provider's price") : " with the fee"}. Your ${payCapName(launch)} is ${usdcText3(launch)} USDC. Allow it once?`
134021
134041
  });
134022
134042
  if (asked2.ok) {
134023
134043
  narrowed = total;
134024
134044
  continue;
134025
134045
  }
134026
- if (asked2.why !== "unsupported") return notAllowed(asked2.why, { tool: "inference_chat", requestId, numbers }, facts);
134046
+ if (asked2.why !== "unsupported") return notAllowed(asked2.why, { tool: "inference_chat", requestId, numbers }, { ...facts, ...split4 });
134027
134047
  return refuse12("p03_float_over_maximum", floatRefusal(error)[1], limitNext(numbers), { requestId, paid: false, ...numbers, ...facts });
134028
134048
  }
134029
134049
  if (total !== void 0 && lifted) {
@@ -135299,8 +135319,8 @@ var init_tools = __esm({
135299
135319
  "p03_float_refund_pending"
135300
135320
  ]);
135301
135321
  FLOAT_PRIVATE_LEG = "a private payment, so this answer has no transaction ID; wallet_receipts lists it";
135302
- SELLER_ONLY_HERE = "Never reach a seller URL outside this wallet (no curl, browser, fetch or DNS lookup of your own): over Tor that would show the user's IP address to the site.";
135303
- outsideWalletRule = (what) => `Never reach ${what} outside this wallet (no curl, browser, fetch or DNS lookup of your own)` + (inTorRun() ? ": this wallet reaches it over Tor, and a request of your own would show the user's IP address" : "");
135322
+ SELLER_ONLY_HERE = "Reach a seller URL or its domain only with this wallet's tools, never yourself (no curl, browser, fetch, ping, DNS or DNS-over-HTTPS lookup), also when the user asks for no Tor: that would show the user's IP address to the site or to a DNS company.";
135323
+ outsideWalletRule = (what) => `Never reach ${what} or its domain outside this wallet (no curl, browser, fetch, ping, DNS or DNS-over-HTTPS lookup of your own), also when the user asks for no Tor` + (inTorRun() ? ": this wallet reaches it over Tor, and a lookup of your own would show the user's IP address" : "");
135304
135324
  proxyStartStep = (tor) => tor ? `Make sure Tor is running (start it, or run ${terminalCommand2("setup")} again)` : "Tell the user to make sure the private connection this wallet runs through (its proxy) is running";
135305
135325
  MONEY_ID2 = /^[A-Za-z0-9_-]{1,44}$/;
135306
135326
  toRecordId = (requestId) => requestId === "" ? "" : recordId(String(requestId));
@@ -136911,7 +136931,7 @@ var init_stdioServer = __esm({
136911
136931
  SUPPORTED_PROTOCOL_VERSIONS = Object.freeze(["2025-06-18", "2025-03-26", "2024-11-05"]);
136912
136932
  MAX_LINE_BYTES = 4 * 1024 * 1024;
136913
136933
  POSITIVE_ATOMIC7 = /^[1-9][0-9]{0,30}$/;
136914
- INSTRUCTIONS = `A self-custody Shinjuku Shield wallet on this machine; no tool takes or returns its keys or passphrase. Each answer's say is one plain sentence for the user; other fields hold the numbers. Never tell the user a tool, field, code or protocol name (x402, MCP) unless told to. Map the user's plain request to one tool: "what is my balance" -> wallet_balance (safe at any time); "add money" / "deposit" / "move USDC in from Phantom or an exchange" -> wallet_add_funds (a one-time address; the next wallet_balance after the USDC arrives makes it private, up to 5 USDC private); "shield the USDC on this wallet's own address" -> wallet_shield; "stop" / "cancel" -> wallet_cancel with the request_id of what to stop; "pay this URL" -> x402_pay (no preview needed); "what does this URL cost" -> x402_preview (only when asked); "find a paid API for X" -> x402_discover, then x402_pay on the url it found; "send 2 USDC to <address>" -> wallet_unshield; "show my payments" -> wallet_receipts; "send another token" -> wallet_transfer_token; "ask an AI model" / "which AI models" -> inference_chat (each answer paid privately; with no prompt it lists the models and prices); "give my other AI app a private AI" -> inference_server (a local OpenAI-compatible URL and key for that app, each answer paid privately). wallet_balance answers ONE balance (balanceAtomic), and x402_pay pays any paid URL from it (a seller that cannot take a private payment is paid through Shinjuku: max_payment covers amountAtomic, fee included). Fields ending in Atomic are atomic units (1000000 = 1 USDC); tell the user USDC. The wallet does its own upkeep (no user command). A shield, add-funds or unshield call can run for minutes: call again with the SAME request_id as next says. Pass your own request_id to x402_pay: the same request_id never pays or moves money twice (a code ending in _retry_same_request: call again with it). A wallet_unshield send to an address not approved ahead asks the user to confirm first. x402_pay never pays above the owner's limits (one payment; any 24 hours); with none, the balance is the only bound. Never claim a payment happened unless the answer has a transaction. Privacy: the chain does not show which wallet paid, but this app and its model provider see every URL, body, price and paid answer you handle; reach a seller only through these tools, never by your own curl, browser or DNS lookup (it shows the user's IP address). When an item of an answer's poolSwitch.items[] has tellUser true, tell the user its message once, in plain words; the wallet moves the balance by itself.`;
136934
+ INSTRUCTIONS = `A self-custody Shinjuku Shield wallet on this machine; no tool takes or returns its keys or passphrase. Each answer's say is one plain sentence for the user; other fields hold the numbers. Never tell the user a tool, field, code or protocol name (x402, MCP) unless told to. Map the user's plain request to one tool: "what is my balance" -> wallet_balance (safe at any time); "add money" / "deposit" / "move USDC in from Phantom or an exchange" -> wallet_add_funds (a one-time address; the next wallet_balance after the USDC arrives makes it private, up to 5 USDC private); "shield the USDC on this wallet's own address" -> wallet_shield; "stop" / "cancel" -> wallet_cancel with the request_id of what to stop; "pay this URL" -> x402_pay (no preview needed); "what does this URL cost" -> x402_preview (only when asked); "find a paid API for X" -> x402_discover, then x402_pay on the url it found; "send 2 USDC to <address>" -> wallet_unshield; "show my payments" -> wallet_receipts; "send another token" -> wallet_transfer_token; "ask an AI model" / "which AI models" -> inference_chat (with no prompt it lists the models and prices); "give my other AI app a private AI" -> inference_server (a local OpenAI-compatible URL and key for that app). wallet_balance answers ONE balance (balanceAtomic), and x402_pay pays any paid URL from it (a seller that cannot take a private payment is paid through Shinjuku: max_payment covers amountAtomic, fee included). Fields ending in Atomic are atomic units (1000000 = 1 USDC); tell the user USDC. The wallet does its own upkeep (no user command). A shield, add-funds or unshield call can run for minutes: call again with the SAME request_id as next says. Pass your own request_id to x402_pay: the same request_id never pays or moves money twice (a code ending in _retry_same_request: call again with it). A wallet_unshield send to an address not approved ahead asks the user to confirm first. x402_pay never pays above the owner's limits (one payment; any 24 hours); with none, the balance is the only bound. Never claim a payment happened unless the answer has a transaction. Privacy: the chain does not show which wallet paid, but this app and its model provider see every URL, body, price and paid answer you handle; reach a seller or its domain only through these tools, never yourself (no curl, browser or DNS lookup, DNS-over-HTTPS too), also when the user asks for no Tor: it shows the user's IP address to the site or a DNS company. When an item of an answer's poolSwitch.items[] has tellUser true, tell the user its message once, in plain words; the wallet moves the balance by itself.`;
136915
136935
  MCP_LOG_LEVELS = Object.freeze(["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"]);
136916
136936
  POOL_SWITCH_LOG_LEVEL = "info";
136917
136937
  validId = (id) => id === null || typeof id === "string" || typeof id === "number" && Number.isFinite(id);
@@ -136947,7 +136967,7 @@ var init_stdioServer = __esm({
136947
136967
  MCP_CLOSE_UNREADABLE_BOUND_MS = 6e4;
136948
136968
  MCP_CLOSE_POLL_MS = 500;
136949
136969
  closeHistoryBusy = (server) => () => compactHistoryReadsRunning() > 0 || memoReadsAheadRunning() > 0 || server.refreshOpen?.() === true;
136950
- SETUP_INSTRUCTIONS = `This Shinjuku Shield wallet is NOT SET UP on this machine yet: every money tool answers wallet_not_set_up. When the user wants to pay, check a balance, or add money, call shinjuku_setup: it asks the user to confirm in this app, creates the wallet, and downloads the proof tools (minutes: call again as next says). At stage done the wallet tools are live in this server. After setup: ${INSTRUCTIONS}`;
136970
+ SETUP_INSTRUCTIONS = `This Shinjuku Shield wallet is NOT SET UP yet: every money tool answers wallet_not_set_up. When the user wants to pay, check a balance, or add money, call shinjuku_setup: it asks the user to confirm, creates the wallet and downloads the proof tools (minutes: call again as next says). At stage done the wallet tools work. After setup: ${INSTRUCTIONS}`;
136951
136971
  TOR_RELAY_READY_MS = 18e4;
136952
136972
  RELAY_INIT_ID = "shinjuku-wallet-relay-initialize";
136953
136973
  runMcp = runMcpServer;
@@ -137019,11 +137039,12 @@ function errorBody(error, restartStep, leastAtomic = 0n) {
137019
137039
  } } };
137020
137040
  }
137021
137041
  if (code2 === "p03_merchant_respend_no_spendable_account") {
137042
+ const why = error?.knownZero === true ? "your private balance is 0 USDC, so there is nothing to pay with yet" : "this wallet has nothing it can pay with yet (its private balance is 0, or it holds only payments it received, which can pay only after you add money once)";
137022
137043
  return { status: 402, body: { error: {
137023
137044
  type: OPENAI_TYPE.quota,
137024
137045
  param: null,
137025
137046
  code: OPENAI_TYPE.quota,
137026
- message: `nothing was paid: this wallet has nothing it can pay with yet (its private balance is 0, or it holds only payments it received, which can pay only after you add money once). Add money first: run ${commandLine("add-funds")} in a terminal (it asks the amount, from 0.01 USDC), then send the request again. This server pays from the new balance once add-funds says ready; nothing to restart`
137047
+ message: `nothing was paid: ${why}. Add money first: run ${commandLine("add-funds")} in a terminal (it asks the amount, from 0.01 USDC), then send the request again. This server pays from the new balance once add-funds says ready; nothing to restart`
137027
137048
  } } };
137028
137049
  }
137029
137050
  if (INSUFFICIENT_CODE.test(code2 ?? "")) {
@@ -137263,7 +137284,7 @@ function createInferenceServer({
137263
137284
  } catch {
137264
137285
  empty = false;
137265
137286
  }
137266
- if (empty) throw new Error("p03_merchant_respend_no_spendable_account");
137287
+ if (empty) throw Object.assign(new Error("p03_merchant_respend_no_spendable_account"), { knownZero: true });
137267
137288
  let result;
137268
137289
  try {
137269
137290
  result = await inference.inferenceChat({
@@ -144948,7 +144969,7 @@ function inferPreflight(inference, values, prompt, deps) {
144948
144969
  const least = floor + SHINJUKU_FEE_ATOMIC;
144949
144970
  const fits = (bound) => bound === void 0 || least <= BigInt(bound);
144950
144971
  const lowerTokens = ceiling !== void 0 && ceiling > floor && fits(values.get("--max-payment")) && fits(limits?.maxPaymentAtomic) && fits(limits?.maxSessionAtomic) && fits(room?.day?.leftAtomic) && fits(room?.total?.leftAtomic);
144951
- throw new PublicFailure(`${CEILING_OVER_CAP_CODE}: the AI provider's price for this request may be up to ${usdcOr(worst)} (${ceiling === void 0 ? "its minimum for one request" : `ceiling ${usdcOr(ceiling)}, minimum ${usdcOr(floor)}`}), and with Shinjuku's ${usdcOr(SHINJUKU_FEE_ATOMIC)} fee up to ${usdcOr(needed)} in all; that is above --max-payment ${usdcFlagText(values.get("--max-payment"))} (${usdcOr(values.get("--max-payment"))}); nothing was paid. ${overLimit ?? `Raise --max-payment to at least ${usdcFlagText(needed)} (${usdcOr(needed)})`}${lowerTokens ? ", or lower --max-tokens" : ""}. The cap is only a limit, not the price: the call pays the AI provider's price for its tokens plus the 0.001 USDC fee, and a larger cap never pays more`);
144972
+ throw new PublicFailure(`${CEILING_OVER_CAP_CODE}: the AI provider's price for this request may be up to ${usdcOr(worst)} (${ceiling === void 0 ? "its minimum for one request" : `ceiling ${usdcOr(ceiling)}, minimum ${usdcOr(floor)}`}), and with Shinjuku's ${usdcOr(SHINJUKU_FEE_ATOMIC)} fee up to ${usdcOr(needed)} in all; that is above --max-payment ${usdcFlagText(values.get("--max-payment"))} USDC; nothing was paid. ${overLimit ?? `Raise --max-payment to at least ${usdcFlagText(needed)} USDC`}${lowerTokens ? ", or lower --max-tokens" : ""}. The cap is only a limit, not the price: the call pays the AI provider's price for its tokens plus the 0.001 USDC fee, and a larger cap never pays more`);
144952
144973
  }
144953
144974
  return { model, maxTokens, ceiling, worst };
144954
144975
  }
@@ -144997,7 +145018,7 @@ async function runInfer(values, deps) {
144997
145018
  try {
144998
145019
  assertSessionPool(values, session.pool);
144999
145020
  if (!quiet) {
145000
- io.err(`model ${model}: this request may pay the AI provider up to ${usdcOr(worst)}, plus Shinjuku's fee, and never more than --max-payment ${usdcFlagText(values.get("--max-payment"))} (${usdcOr(values.get("--max-payment"))}) in all. ${inference.INFERENCE_NOTICE ?? `${INFER_COST_LINE}.`}
145021
+ io.err(`model ${model}: this request may pay the AI provider up to ${usdcOr(worst)}, plus Shinjuku's fee, and never more than --max-payment ${usdcFlagText(values.get("--max-payment"))} USDC in all. ${inference.INFERENCE_NOTICE ?? `${INFER_COST_LINE}.`}
145001
145022
  `);
145002
145023
  io.err(`request-id ${requestId}${given ? "" : ` (if this run stops, rerun with --request-id ${requestId}: it never pays twice)`}
145003
145024
  `);