shinjuku-shielded 0.3.128 → 0.3.129

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.128. The npm packages `shinjuku-shield` and
9
+ This is version 0.3.129. 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.128
15
+ npm install -g shinjuku@0.3.129
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.128 setup`.
21
+ installing, type `npx shinjuku@0.3.129 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.128 mcp
39
+ claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.129 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.128", "mcp"] } } }
45
+ { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.129", "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": "821b09a3d1295b30eee38eafb6aad2665a60808a3163baddeb14c02366109e56",
5
- "bytes": 8815826,
6
- "sourceCommit": "ec580ba0a4153e45f70cf7eabc6901665f19d88c",
4
+ "sha256": "1de148b9fd19cd1bac5b690267cb77c227a3e74300873fa9eda02085833ccb2f",
5
+ "bytes": 8826955,
6
+ "sourceCommit": "2c963caa0410c71651cd64a18c704efa8b0ce95c",
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-30T02:43:43.393Z"
1040
+ "publishedAt": "2026-10-30T06:56:34.660Z"
1041
1041
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shielded",
3
- "version": "0.3.128",
3
+ "version": "0.3.129",
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": "821b09a3",
7
- "shinjukuSha256": "821b09a3d1295b30eee38eafb6aad2665a60808a3163baddeb14c02366109e56",
6
+ "shinjukuRelease": "1de148b9",
7
+ "shinjukuSha256": "1de148b9fd19cd1bac5b690267cb77c227a3e74300873fa9eda02085833ccb2f",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-shield#readme",
@@ -47684,7 +47684,7 @@ function proxiedChild(args, mode, plainProxy, stdio) {
47684
47684
  delete env[QUIET_RUN_MARK];
47685
47685
  const quiet = quietRun(args);
47686
47686
  if (quiet) env[QUIET_RUN_MARK] = quiet;
47687
- return spawn(process.execPath, [...proxiedExecArgv(process.execArgv), process.argv[1], ...args], { env, stdio });
47687
+ return spawn(process.execPath, [...proxiedExecArgv(process.execArgv), process.argv[1], ...args], { env, stdio, windowsHide: true });
47688
47688
  }
47689
47689
  function reexec(args, mode, plainProxy) {
47690
47690
  return new Promise((resolvePromise) => {
@@ -47721,7 +47721,8 @@ async function proxyEntry(argv2, run4, { err = (text3) => process.stderr.write(t
47721
47721
  try {
47722
47722
  torVersion = await proveProxy(mode, proxy);
47723
47723
  } catch (error) {
47724
- err(`shinjuku: ${error.message}${mode === "tor" && process.env[TOR_FROM_CONFIG_ENV] === "1" && !error.torHintInside ? TOR_FROM_CONFIG_HINT : ""}
47724
+ const refusal3 = `${error.message}${mode === "tor" && process.env[TOR_FROM_CONFIG_ENV] === "1" && !error.torHintInside ? TOR_FROM_CONFIG_HINT : ""}`;
47725
+ err(`shinjuku: ${process.stderr.isTTY ? refusal3.replace(/^[a-z][a-z0-9_]*: /, "") : refusal3}
47725
47726
  `);
47726
47727
  if (mcpRefused2 && argv2[0] === "mcp" && !process.stdin.isTTY && !process.env[TOR_RUN_STARTED_ENV]) {
47727
47728
  return mcpRefused2({
@@ -47776,7 +47777,7 @@ function torChild(argv2, stdio) {
47776
47777
  if (!options.mode) throw new ProxyUsageError("torChild needs --tor or --proxy");
47777
47778
  return proxiedChild(options.args, options.mode, options.proxy, stdio);
47778
47779
  }
47779
- var TOR_HTTP_TUNNEL, TOR_CHECK, PROOF_TIMEOUT_MS, LOCAL_PROOF_TIMEOUT_MS, LOCAL_PROOF_MAX_BYTES, torProvenAt, proxyProvenAt, MARK, TOR_RUN_STARTED_ENV, ownProxyRefused, ONION_V3, RETRY_AFTER_CAP_MS, RETRY_AFTER_FLOOR_MS, RETRY_AFTER_DEFAULT_MS, RPC_NO_ANSWER_LIMIT_MS, RPC_PACING_CAP_MS, PACING_NOTICE_EVERY_MS, PACING_RATE_WINDOW_MS, TOR_MAX_ROTATIONS, runStartedAt, rpcNotBefore, pacingWaiters, pacingSince, pacedMs, rpcAnswered, lastAnswerAt, readSteps, pacingNoticeAt, pacingSamples, torMoves, torCircuitId, torMovesRefused, pendingMove, inTorRun, runNetworkMode, isOnionOrigin, ProxyUsageError, isOnionHost, TOR_MIN_VERSION, localTorProof, TOR_EXPERT_BUNDLE, TOR_EXPERT_BUNDLE_SHA256, TUNNEL_LINE, TOR_DEB_KEY, TOR_DEB_KEYRING, TOR_INSTALL, torStart, LOCAL_TUNNEL, torStartText, TOR_WINDOW, torNotRunningMessage, torUpdateHow, torTooOldWhy, torTooOldMessage, NOTICE, TOR_NOTICE_LINE, ONION_DETAIL_LINE, withoutOnionDetail, QUIET_RUN_MARK, QUIET_NOTICE, QUIET_TERMINAL_COMMANDS, INFER_SUBCOMMANDS, quietRun, TOR_TROUBLE_LINE, TOR_WAIT_PLAIN, TOR_TROUBLE_PLAIN, TOR_TROUBLE_SAID, quietTorErr, withRunTag, proxyEnv, pacedSoFar, RESENDABLE_METHOD, torCircuit, GLOBAL_DISPATCHER3, laneDispatchers, laneOfDispatcher, PROXY_WARNING_FLAG, proxiedExecArgv, proxiedNodeOptions, TOR_FROM_CONFIG_ENV, TOR_FROM_CONFIG_HINT, TOR_FROM_CONFIG_SENTENCE;
47780
+ var TOR_HTTP_TUNNEL, TOR_CHECK, PROOF_TIMEOUT_MS, LOCAL_PROOF_TIMEOUT_MS, LOCAL_PROOF_MAX_BYTES, torProvenAt, proxyProvenAt, MARK, TOR_RUN_STARTED_ENV, ownProxyRefused, ONION_V3, RETRY_AFTER_CAP_MS, RETRY_AFTER_FLOOR_MS, RETRY_AFTER_DEFAULT_MS, RPC_NO_ANSWER_LIMIT_MS, RPC_PACING_CAP_MS, PACING_NOTICE_EVERY_MS, PACING_RATE_WINDOW_MS, TOR_MAX_ROTATIONS, runStartedAt, rpcNotBefore, pacingWaiters, pacingSince, pacedMs, rpcAnswered, lastAnswerAt, readSteps, pacingNoticeAt, pacingSamples, torMoves, torCircuitId, torMovesRefused, pendingMove, inTorRun, runNetworkMode, isOnionOrigin, ProxyUsageError, isOnionHost, TOR_MIN_VERSION, localTorProof, TOR_EXPERT_BUNDLE, TOR_EXPERT_BUNDLE_SHA256, TUNNEL_LINE, TOR_DEB_KEY, TOR_DEB_KEYRING, TOR_INSTALL, torStart, LOCAL_TUNNEL, torStartText, TOR_WINDOW, torNotRunningMessage, torUpdateHow, TOR_STOP_OLD_WINDOWS, torTooOldWhy, torTooOldMessage, NOTICE, TOR_NOTICE_LINE, ONION_DETAIL_LINE, withoutOnionDetail, QUIET_RUN_MARK, QUIET_NOTICE, QUIET_TERMINAL_COMMANDS, INFER_SUBCOMMANDS, quietRun, TOR_TROUBLE_LINE, TOR_WAIT_PLAIN, TOR_TROUBLE_PLAIN, TOR_TROUBLE_SAID, quietTorErr, withRunTag, proxyEnv, pacedSoFar, RESENDABLE_METHOD, torCircuit, GLOBAL_DISPATCHER3, laneDispatchers, laneOfDispatcher, PROXY_WARNING_FLAG, proxiedExecArgv, proxiedNodeOptions, TOR_FROM_CONFIG_ENV, TOR_FROM_CONFIG_HINT, TOR_FROM_CONFIG_SENTENCE;
47780
47781
  var init_proxy = __esm({
47781
47782
  "wallet/src/net/proxy.mjs"() {
47782
47783
  "use strict";
@@ -47833,7 +47834,7 @@ var init_proxy = __esm({
47833
47834
  TOR_INSTALL = Object.freeze({
47834
47835
  windows: `iwr https://archive.torproject.org/tor-package-archive/torbrowser/${TOR_EXPERT_BUNDLE}/tor-expert-bundle-windows-x86_64-${TOR_EXPERT_BUNDLE}.tar.gz -OutFile $env:TEMP\\tor.tar.gz; if ((Get-FileHash $env:TEMP\\tor.tar.gz -Algorithm SHA256).Hash -ne '${TOR_EXPERT_BUNDLE_SHA256}') { Remove-Item $env:TEMP\\tor.tar.gz; throw 'The Tor download does not match its pinned sha256, so it was deleted and not run' }; mkdir -Force $env:LOCALAPPDATA\\tor | Out-Null; & $env:SystemRoot\\System32\\tar.exe -xzf $env:TEMP\\tor.tar.gz -C $env:LOCALAPPDATA\\tor; & $env:LOCALAPPDATA\\tor\\tor\\tor.exe --HTTPTunnelPort '127.0.0.1:9080 IsolateDestAddr' --SocksPort 0`,
47835
47836
  macos: `brew install tor && (grep -qxF '${TUNNEL_LINE}' "$(brew --prefix)/etc/tor/torrc" || echo '${TUNNEL_LINE}' >> "$(brew --prefix)/etc/tor/torrc") && brew services restart tor`,
47836
- linux: `sudo apt install -y apt-transport-https gnupg wget && wget -qO- ${TOR_DEB_KEY} | gpg --dearmor | sudo tee ${TOR_DEB_KEYRING} >/dev/null && printf 'Types: deb\\nURIs: https://deb.torproject.org/torproject.org/\\nSuites: %s\\nComponents: main\\nSigned-By: ${TOR_DEB_KEYRING}\\n' "$(. /etc/os-release && echo "$VERSION_CODENAME")" | sudo tee /etc/apt/sources.list.d/tor.sources >/dev/null && sudo apt update && sudo apt install -y tor deb.torproject.org-keyring && (grep -qxF '${TUNNEL_LINE}' /etc/tor/torrc || echo '${TUNNEL_LINE}' | sudo tee -a /etc/tor/torrc) && sudo systemctl restart tor`
47837
+ linux: `sudo apt install -y apt-transport-https gnupg wget && wget -qO- ${TOR_DEB_KEY} | gpg --dearmor | sudo tee ${TOR_DEB_KEYRING} >/dev/null && printf 'Types: deb\\nURIs: https://deb.torproject.org/torproject.org/\\nSuites: %s\\nComponents: main\\nSigned-By: ${TOR_DEB_KEYRING}\\n' "$(. /etc/os-release && echo "$VERSION_CODENAME")" | sudo tee /etc/apt/sources.list.d/tor.sources >/dev/null && sudo apt update && sudo apt install -y tor deb.torproject.org-keyring && (grep -qxF '${TUNNEL_LINE}' /etc/tor/torrc || echo '${TUNNEL_LINE}' | sudo tee -a /etc/tor/torrc) && sudo service tor restart`
47837
47838
  });
47838
47839
  torStart = (platform = process.platform, tunnelHost) => {
47839
47840
  const base = platform === "win32" ? { where: "PowerShell (keep its window open while the wallet runs)", command: TOR_INSTALL.windows } : platform === "darwin" ? { where: "Terminal", command: TOR_INSTALL.macos } : { where: "a terminal", command: TOR_INSTALL.linux };
@@ -47860,7 +47861,8 @@ ${command}`;
47860
47861
  return `tor_not_running: Tor is not running on this machine, so nothing was sent.${fromConfig ? TOR_FROM_CONFIG_SENTENCE : ""} Tor hides your IP address. Start it with the command below in ${TOR_WINDOW[platform] ?? "a terminal"}${keep}:
47861
47862
  ${command}`;
47862
47863
  };
47863
- torUpdateHow = (platform = process.platform) => platform === "win32" ? "Stop your old Tor first. Start a newer Tor with the command below in PowerShell and keep that window open" : `Install a newer Tor with the command below in ${TOR_WINDOW[platform] ?? "a terminal"}`;
47864
+ torUpdateHow = (platform = process.platform) => platform === "win32" ? `${TOR_STOP_OLD_WINDOWS} Then start a newer Tor with the command below in PowerShell and keep that window open` : `Install a newer Tor with the command below in ${TOR_WINDOW[platform] ?? "a terminal"}`;
47865
+ TOR_STOP_OLD_WINDOWS = "Stop your old Tor first: close the window it runs in, or for a Tor in WSL run wsl -u root service tor stop .";
47864
47866
  torTooOldWhy = (version3) => `Your Tor program is too old (${version3}). It has a known fault that can link two of your payments, so the wallet sent nothing.`;
47865
47867
  torTooOldMessage = (version3, platform = process.platform, { fromConfig = false, tunnelHost } = {}) => {
47866
47868
  const { command } = torStart(platform, tunnelHost);
@@ -51152,7 +51154,7 @@ var init_anchorClock = __esm({
51152
51154
  init_phaseTiming();
51153
51155
  EPOCH_MS = 3e5;
51154
51156
  SUB_ANCHOR_MS = 3e4;
51155
- SUB_ANCHOR_FINALITY_MS = 2e4;
51157
+ SUB_ANCHOR_FINALITY_MS = 6e3;
51156
51158
  OBSERVATION_MAX_AGE_MS = 6e5;
51157
51159
  ANCHOR_REQUEST_TIMEOUT_MS = 9e4;
51158
51160
  MAX_FOLLOW_UPS = 3;
@@ -60661,6 +60663,16 @@ function spendHeads(spendableAtomic, largestAtomic, change = {}, options = {}) {
60661
60663
  if (change?.changeSettling === true) {
60662
60664
  const seconds2 = change.changeReadyInSeconds;
60663
60665
  const upTo = String(change.changeUpToAtomic ?? "");
60666
+ if (Number.isSafeInteger(change.changeCount) && change.changeCount >= 2 && ATOMIC.test(upTo) && BigInt(upTo) > 0n && BigInt(upTo) < BigInt(spendable)) {
60667
+ const part = usdcWords(upTo);
60668
+ return Number.isSafeInteger(seconds2) && seconds2 > 0 ? [
60669
+ `You have ${total}; after your recent payments settle (about ${seconds2} s), one payment can use up to ${part} and later payments the rest`,
60670
+ `You have ${total}; one payment can use ${part} in about ${seconds2} s`
60671
+ ] : [
60672
+ `You have ${total}; after your recent payments settle, one payment can use up to ${part} and later payments the rest`,
60673
+ `You have ${total}, ${part} for one payment after your recent ones settle`
60674
+ ];
60675
+ }
60664
60676
  if (ATOMIC.test(upTo) && BigInt(upTo) > 0n && BigInt(upTo) < BigInt(spendable)) {
60665
60677
  const part = usdcWords(upTo);
60666
60678
  return Number.isSafeInteger(seconds2) && seconds2 > 0 ? [
@@ -67229,6 +67241,9 @@ var init_errorCodes = __esm({
67229
67241
  "fetch_gate_stopped",
67230
67242
  "mcp_cap_required",
67231
67243
  "mcp_cap_invalid",
67244
+ // mcp/tools.mjs maxPaymentNotUsdc: max_payment written in atomic units (a whole number of 1000 or more, above the whole
67245
+ // balance); nothing was requested or paid (UX129-12, 2026-10-30).
67246
+ "mcp_max_payment_not_usdc",
67232
67247
  "fetch_proof_tools_unavailable",
67233
67248
  "fetch_offer_confidential",
67234
67249
  "wallet_spend_refused_per_payment_cap_exceeded",
@@ -101461,7 +101476,7 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
101461
101476
  let heldAtomic;
101462
101477
  let heldWholeAtomic;
101463
101478
  let spareMost = 0n;
101464
- const report = (status, gross, next2, cause, largest2, change) => {
101479
+ const report = (status, gross, next2, cause, largest2, change, changes) => {
101465
101480
  const atomic4 = gross === null || privateSendPending === null ? null : gross > privateSendPending ? gross - privateSendPending : 0n;
101466
101481
  const built = {
101467
101482
  // The one number: shielded + pockets. Null when either part is unknown.
@@ -101500,7 +101515,10 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
101500
101515
  const cap = atomic4 - BigInt(detail.successionMovableAtomic ?? "0");
101501
101516
  const one = cap <= 0n ? 0n : largest2 < cap ? largest2 : cap;
101502
101517
  LARGEST_PAYMENTS.set(built, one.toString());
101503
- if (change !== void 0 && one === 0n && atomic4 > 0n) CHANGE_SETTLING.set(built, (change < atomic4 ? change : atomic4).toString());
101518
+ if (change !== void 0 && one === 0n && atomic4 > 0n) {
101519
+ CHANGE_SETTLING.set(built, (change < atomic4 ? change : atomic4).toString());
101520
+ if (Number.isSafeInteger(changes) && changes >= 2) CHANGE_COUNT.set(built, changes);
101521
+ }
101504
101522
  }
101505
101523
  return built;
101506
101524
  };
@@ -101789,12 +101807,16 @@ If a payment to this wallet settled, read the chain again with:
101789
101807
  }
101790
101808
  if (settling.length || movedInAtomic > 0n) {
101791
101809
  reasons.push(...settlingReasons);
101792
- const [only] = settling;
101793
101810
  const sendHeldAtomic = sendHold === null || sendHold.notes.size > 0 ? null : sum2(free.filter(isHeld).map((output2) => output2.valueAtomic));
101794
- const inputsAtomic = settling.length === 1 ? sum2(only.inputs.map((commitment) => unspent.get(commitment).valueAtomic)) : 0n;
101795
- const changeAtomic = inputsAtomic - (settling.length === 1 ? only.amountAtomic : 0n);
101811
+ const inputsOf = (payment) => sum2(payment.inputs.map((commitment) => unspent.get(commitment).valueAtomic));
101812
+ const distinctInputs = new Set(settling.flatMap((payment) => payment.inputs)).size === settling.reduce((count2, payment) => count2 + payment.inputs.length, 0);
101813
+ const inputsAtomic = settling.length > 0 && distinctInputs ? sum2(settling.map(inputsOf)) : 0n;
101814
+ const changeAtomic = settling.reduce((most, payment) => {
101815
+ const change = inputsOf(payment) - payment.amountAtomic;
101816
+ return change > most ? change : most;
101817
+ }, 0n);
101796
101818
  const changeFreeAtomic = sendHold === null || sendHold.unpacked.length === 0 ? changeAtomic : changeAtomic > privateSendPending ? changeAtomic - privateSendPending : 0n;
101797
- const changeOnly = settling.length === 1 && only.join !== true && only.exit !== true && reservedAtomic === 0n && detail.joiningAtomic === "0" && successionMovableAtomic === 0n && sendHeldAtomic !== null && unspentAtomic - sendHeldAtomic === inputsAtomic && inputsAtomic > only.amountAtomic && changeFreeAtomic > 0n;
101819
+ const changeOnly = settling.length > 0 && distinctInputs && (settling.length === 1 || sendHeldAtomic === 0n && privateSendPending === 0n) && settling.every((payment) => payment.join !== true && payment.exit !== true && inputsOf(payment) > payment.amountAtomic) && reservedAtomic === 0n && detail.joiningAtomic === "0" && successionMovableAtomic === 0n && sendHeldAtomic !== null && unspentAtomic - sendHeldAtomic === inputsAtomic && changeFreeAtomic > 0n;
101798
101820
  return report(
101799
101821
  "stale",
101800
101822
  spendable + successionMovableAtomic,
@@ -101803,7 +101825,8 @@ If a payment to this wallet settled, read the chain again with:
101803
101825
  // still settling" while the only answered item was a join).
101804
101826
  settlingRecent ? movedInAtomic === 0n && settling.length > 0 && settling.every((payment) => payment.join) ? "account_join" : "payment_settling" : movedInAtomic > 0n ? "pool_move" : void 0,
101805
101827
  largest,
101806
- changeOnly ? changeFreeAtomic : void 0
101828
+ changeOnly ? changeFreeAtomic : void 0,
101829
+ settling.length
101807
101830
  );
101808
101831
  }
101809
101832
  return report("current", spendable + successionMovableAtomic, next, void 0, largest);
@@ -101822,7 +101845,7 @@ function combinePoolBalances(partsAtomic, movingAtomic = "0") {
101822
101845
  if (!canonicalAtomic(String(movingAtomic))) return null;
101823
101846
  return (partsAtomic.reduce((total, part) => total + BigInt(part), 0n) + BigInt(movingAtomic)).toString();
101824
101847
  }
101825
- var PAY_ME_WAL_SCHEMA, BALANCE_SNAPSHOT_SCHEMA, BALANCE_SNAPSHOT_FILE, STALE_CAUSES, staleCauseOf, LARGEST_PAYMENTS, largestPaymentOf, CHANGE_SETTLING, changeSettlingOf, changeUpToOf, SPEND_INPUTS, SEND_HOLDS, sendHoldOf, PAYMENT_SETTLING_MS, NO_VALUE_TYPES, PLAN_ONLY_TYPES, UNSENT_SELF_PAY_FIELDS, isUnsentSelfPay, planOnly, SNAPSHOT_KEY_LABEL, SPEND_STATUSES, MAINNET_USDC, SELF_PAY_ORIGIN, POCKET_FILL_RECORD, MERGE_RECORD2, POOL_MOVE_SCHEMA, FLOAT_PAYMENT_SCHEMA, POOL_MOVE_PATH, POOL_SUCCESSION_SCHEMAS, digestKey, canonicalAtomic, SUCCESSION_JOB_SCHEMA, MERGE_PAYMENT_PATH, spendEntry, PROOF_RECORD;
101848
+ var PAY_ME_WAL_SCHEMA, BALANCE_SNAPSHOT_SCHEMA, BALANCE_SNAPSHOT_FILE, STALE_CAUSES, staleCauseOf, LARGEST_PAYMENTS, largestPaymentOf, CHANGE_SETTLING, changeSettlingOf, changeUpToOf, CHANGE_COUNT, changeCountOf, SPEND_INPUTS, SEND_HOLDS, sendHoldOf, PAYMENT_SETTLING_MS, NO_VALUE_TYPES, PLAN_ONLY_TYPES, UNSENT_SELF_PAY_FIELDS, isUnsentSelfPay, planOnly, SNAPSHOT_KEY_LABEL, SPEND_STATUSES, MAINNET_USDC, SELF_PAY_ORIGIN, POCKET_FILL_RECORD, MERGE_RECORD2, POOL_MOVE_SCHEMA, FLOAT_PAYMENT_SCHEMA, POOL_MOVE_PATH, POOL_SUCCESSION_SCHEMAS, digestKey, canonicalAtomic, SUCCESSION_JOB_SCHEMA, MERGE_PAYMENT_PATH, spendEntry, PROOF_RECORD;
101826
101849
  var init_balance = __esm({
101827
101850
  "wallet/src/balance/balance.mjs"() {
101828
101851
  "use strict";
@@ -101847,6 +101870,8 @@ var init_balance = __esm({
101847
101870
  CHANGE_SETTLING = /* @__PURE__ */ new WeakMap();
101848
101871
  changeSettlingOf = (report) => report !== null && typeof report === "object" && CHANGE_SETTLING.has(report);
101849
101872
  changeUpToOf = (report) => report !== null && typeof report === "object" ? CHANGE_SETTLING.get(report) : void 0;
101873
+ CHANGE_COUNT = /* @__PURE__ */ new WeakMap();
101874
+ changeCountOf = (report) => report !== null && typeof report === "object" ? CHANGE_COUNT.get(report) : void 0;
101850
101875
  SPEND_INPUTS = /* @__PURE__ */ new WeakMap();
101851
101876
  SEND_HOLDS = /* @__PURE__ */ new WeakMap();
101852
101877
  sendHoldOf = (report) => report !== null && typeof report === "object" ? SEND_HOLDS.get(report) : void 0;
@@ -105596,15 +105621,7 @@ async function waitForNextAnchor(requirement, state, deps, ring) {
105596
105621
  const start = now();
105597
105622
  state.deadline ??= start + ANCHOR_WAIT_CAP_MS;
105598
105623
  const subAnchors = subAnchorPool(start) !== void 0;
105599
- let anchorAt;
105600
- let waitMs;
105601
- if (subAnchors) {
105602
- waitMs = nextAnchorWaitMs(start, ANCHOR_MARGIN_MS);
105603
- anchorAt = start + waitMs - SUB_ANCHOR_FINALITY_MS;
105604
- } else {
105605
- anchorAt = (epoch + 1) * ANCHOR_EPOCH_MS;
105606
- waitMs = Math.max(anchorAt - start, 0) + ANCHOR_MARGIN_MS;
105607
- }
105624
+ const waitMs = subAnchors ? nextAnchorWaitMs(start, ANCHOR_MARGIN_MS) : Math.max((epoch + 1) * ANCHOR_EPOCH_MS - start, 0) + ANCHOR_MARGIN_MS;
105608
105625
  if (start + waitMs > state.deadline) return false;
105609
105626
  const before = subAnchors ? await readRingBefore(ring, start) : void 0;
105610
105627
  if (ringCoversTree(before) && !state.coveredSkip) {
@@ -105613,7 +105630,7 @@ async function waitForNextAnchor(requirement, state, deps, ring) {
105613
105630
  return true;
105614
105631
  }
105615
105632
  try {
105616
- process.stderr.write(`your change from the last payment (or your newest deposit) is spendable after the next pool anchor at ${new Date(anchorAt).toISOString().slice(11, 19)}Z; waiting ${before ? "at most " : ""}${Math.ceil(waitMs / 1e3)} s (nothing was signed). ` + (subAnchors ? `This pool takes a sub-anchor about every ${subAnchorSeconds(start) ?? 30} s, so the wait is short${before ? " (it ends as soon as that anchor is final)" : ""}. ` : "A payment spends one funded deposit, and its change pays only after the next pool anchor. ") + `To pay several times in a row without this wait, add funds as several deposits: ${commandLine("add-funds --route exchange --amount <usdc> --split <k>")} (${commandLine("help add-funds")})
105633
+ process.stderr.write(`Waiting ${before ? "up to " : "about "}${Math.ceil(waitMs / 1e3)} s for your last payment or deposit to be ready to spend (nothing was signed yet); then it goes on by itself.
105617
105634
  `);
105618
105635
  } catch {
105619
105636
  }
@@ -106928,7 +106945,6 @@ var init_payResource = __esm({
106928
106945
  init_anchorClock();
106929
106946
  init_requestId();
106930
106947
  init_keepAlive();
106931
- init_commandName();
106932
106948
  fail24 = (reason) => {
106933
106949
  throw new Error(`p03_resource_payment_${reason}`);
106934
106950
  };
@@ -107545,6 +107561,10 @@ function refusalSay(a, tool, again, npmVersion) {
107545
107561
  const seller = tool === "inference_chat" ? "the AI provider" : "the seller";
107546
107562
  const Seller = `${seller[0].toUpperCase()}${seller.slice(1)}`;
107547
107563
  const data = tool === "inference_chat" ? "answer" : "data";
107564
+ if (code2 === "mcp_max_payment_not_usdc") {
107565
+ const cap = usdcText2(a.capAtomic);
107566
+ return `Nothing was paid: ${cap ? `a cap of ${cap} USDC` : "the cap"} is more than your balance. A cap is in USDC, like 0.01 for one cent. Ask again.`;
107567
+ }
107548
107568
  if (a.walletUpdateNeeded === true) {
107549
107569
  const cannot = "this wallet version cannot read the private balance record";
107550
107570
  const update3 = "Restart the wallet on the latest version, then ask again";
@@ -107624,7 +107644,10 @@ function refusalSay(a, tool, again, npmVersion) {
107624
107644
  const more = Number.isSafeInteger(a.retryMaxTokens) ? ` Ask again with a length limit of ${a.retryMaxTokens} tokens.` : " Ask again with a longer length limit.";
107625
107645
  if (a.replayed === true && a.paid === false) return `Nothing was paid, and the stored answer is empty.${more}`;
107626
107646
  if (a.replayed === true) return `Already paid; the stored answer is empty and nothing new was paid.${more}`;
107627
- return `${paid ? `Paid ${paid} USDC` : "Paid"}, but the model reached the answer's length limit before it answered.${more}`;
107647
+ const price = usdcText2(a.priceAtomic);
107648
+ const fee = usdcText2(a.feeAtomic);
107649
+ const head = `${paid ? `Paid ${paid} USDC${price && positive(fee) ? ` (${price} price + ${fee} Shinjuku fee)` : ""}` : "Paid"}, but`;
107650
+ return firstFit(`${head} the model reached the answer's length limit before it answered.${more}`, `${head} the answer hit its length limit.${more}`);
107628
107651
  }
107629
107652
  if (code2 === "inference_no_provider") {
107630
107653
  return "Nothing was paid: no AI provider can take this wallet's payment for this model right now. Ask for another model, or ask again later.";
@@ -108582,16 +108605,20 @@ function build(a, tool, npmVersion) {
108582
108605
  if (a.replayed === true) return "This request was already paid. Here is the same answer; nothing new was paid.";
108583
108606
  if (tool === "inference_chat" || typeof a.model === "string" && Object.hasOwn(a, "content")) {
108584
108607
  const paid = usdcText2(a.paidAtomic);
108585
- return `Answered by ${str(a.model).trim() || "the model"}.${paid ? ` Paid ${paid} USDC.` : ""}`;
108608
+ const price2 = usdcText2(a.priceAtomic);
108609
+ const fee2 = usdcText2(a.feeAtomic);
108610
+ const split4 = paid && price2 && positive(fee2) ? ` (${price2} price + ${fee2} Shinjuku fee)` : "";
108611
+ return `Answered by ${str(a.model).trim() || "the model"}.${paid ? ` Paid ${paid} USDC${split4}.` : ""}`;
108586
108612
  }
108587
108613
  const total = usdcText2(a.amountAtomic);
108588
108614
  const price = usdcText2(a.priceAtomic);
108589
108615
  const fee = usdcText2(a.feeAtomic);
108590
108616
  if (!total) return withBalance("Paid. Here is the answer.", a);
108591
- return withBalance([
108592
- price && positive(fee) ? `Paid ${total} USDC (${price} price + ${fee} fee). Here is the answer.` : `Paid ${total} USDC. Here is the answer.`,
108593
- `Paid ${total} USDC. Here is the answer.`
108594
- ], a);
108617
+ if (price && positive(fee)) {
108618
+ const paid = `Paid ${total} USDC (${price} price + ${fee} Shinjuku fee).`;
108619
+ return withBalance([`${paid} Here is the answer.`, paid], a);
108620
+ }
108621
+ return withBalance(`Paid ${total} USDC. Here is the answer.`, a);
108595
108622
  }
108596
108623
  if (a.paid === false && !Object.hasOwn(a, "stage") && (tool === void 0 || tool === "x402_pay")) {
108597
108624
  const status = Number(a.status);
@@ -111304,6 +111331,7 @@ function partsOf(report, store, snapshot) {
111304
111331
  changeSettling,
111305
111332
  changeReadyInSeconds: changeSettling ? changeReadyInSeconds() : null,
111306
111333
  changeUpToAtomic: changeSettling ? changeUpToOf(report) ?? null : null,
111334
+ ...changeSettling && Number.isSafeInteger(changeCountOf(report)) ? { changeCount: changeCountOf(report) } : {},
111307
111335
  refundsInFlightAtomic: refunds.proven.toString(),
111308
111336
  refundsNotCertainAtomic: refunds.notCertain.toString(),
111309
111337
  refundsInFlight: refunds.provenFor,
@@ -111558,10 +111586,9 @@ async function exitWhenAnchored(run4, { now = Date.now, sleep: sleep4 = (ms) =>
111558
111586
  const start = now();
111559
111587
  const subAnchors = subAnchorPool(start) !== void 0;
111560
111588
  const waitMs = nextAnchorWaitMs(start, EPOCH_MARGIN_MS);
111561
- const anchorAt = subAnchors ? start + waitMs - SUB_ANCHOR_FINALITY_MS : start - start % EPOCH_MS2 + EPOCH_MS2;
111562
111589
  if (start + waitMs > deadline) throw error;
111563
111590
  const before = subAnchors ? await readRingBefore(ring, start) : void 0;
111564
- progress(`your self-payment's note is spendable after the next pool anchor at ${new Date(anchorAt).toISOString().slice(11, 19)}Z; waiting ${Math.ceil(waitMs / 1e3)} s (nothing was signed for the exit): ${anchorWaitText(start)}; the exit then runs by itself`);
111591
+ progress(`Waiting about ${Math.ceil(waitMs / 1e3)} s for your balance to be ready to send (nothing was signed yet); then the send goes on by itself`);
111565
111592
  markPhase("anchor-wait-start");
111566
111593
  await sleepForAnchor(waitMs, { ring, before, now, sleep: sleep4 });
111567
111594
  markPhase("anchor-wait-done");
@@ -124349,6 +124376,7 @@ async function tidyMove({ plan, steps, now }) {
124349
124376
  state: "signed",
124350
124377
  account: account.address,
124351
124378
  signature: built.signature,
124379
+ ...typeof account.lamports === "bigint" ? { rentLamports: account.lamports.toString() } : {},
124352
124380
  txBase64: Buffer.from(built.raw).toString("base64"),
124353
124381
  lastValidBlockHeight: Number.isSafeInteger(built.lastValidBlockHeight) ? built.lastValidBlockHeight : null,
124354
124382
  signedAt: at()
@@ -124577,9 +124605,10 @@ function transferLeftoverView(plan) {
124577
124605
  const tidy = !plan.tidy || plan.tidy.state === "pending" ? { ...plan.tidy ?? {}, state: "kept", reason: TIDY_LATE_REASON } : plan.tidy;
124578
124606
  const state = tidy.state === "signed" ? "closing" : tidy.state;
124579
124607
  const lamports = /^\d+$/.test(String(tidy.solLamports ?? "")) ? BigInt(tidy.solLamports) : null;
124608
+ const rent = /^\d+$/.test(String(tidy.rentLamports ?? "")) ? ` (${solText(BigInt(tidy.rentLamports))} SOL)` : "";
124580
124609
  const tokenAccount2 = {
124581
- closing: "the close of the sold token's empty holder on the own key, sent before the send started, is not final yet; its rent (about 0.00204 SOL) comes back to the key when it lands",
124582
- closed: "the wallet closed the sold token's empty holder on its own key, so its rent (about 0.00204 SOL) came back to the key",
124610
+ closing: `the close of the sold token's empty holder on the own key, sent before the send started, is not final yet; its rent${rent} comes back to the key when it lands`,
124611
+ closed: `the wallet closed the sold token's empty holder on its own key, so its rent${rent} came back to the key`,
124583
124612
  gone: "the own key holds none of the sold token and has no holder for it left",
124584
124613
  kept: `the sold token's holder on the own key stays open: ${tidy.reason ?? "it was not empty"}`,
124585
124614
  failed: `the sold token's empty holder on the own key stays open: ${tidy.reason ?? "its close did not land"}`
@@ -124866,7 +124895,7 @@ async function readSoldAccount({ connection, owner, mint, commitment = "finalize
124866
124895
  const { programId, address: address4 } = candidates[index];
124867
124896
  if (!info.owner.equals(programId)) continue;
124868
124897
  const account = unpackAccount(address4, info, programId);
124869
- const view2 = { address: address4.toBase58(), programId: programId.toBase58(), amountAtomic: account.amount };
124898
+ const view2 = { address: address4.toBase58(), programId: programId.toBase58(), amountAtomic: account.amount, lamports: BigInt(info.lamports) };
124870
124899
  if (!account.mint.equals(mintKey) || !account.owner.equals(ownerKey)) return { ...view2, closeable: false, why: "the account is not the own key's account of this token" };
124871
124900
  if (account.isNative) return { ...view2, closeable: false, why: "the account is a wrapped SOL account" };
124872
124901
  if (account.closeAuthority && !account.closeAuthority.equals(ownerKey)) return { ...view2, closeable: false, why: "another key may close this account, not the own key" };
@@ -128333,6 +128362,29 @@ function createWalletTools({
128333
128362
  return null;
128334
128363
  }
128335
128364
  };
128365
+ const maxPaymentNotUsdc = (args, atomic4, tool) => {
128366
+ const raw = args?.max_payment;
128367
+ const text3 = typeof raw === "number" && Number.isSafeInteger(raw) ? String(raw) : typeof raw === "string" ? raw.trim() : "";
128368
+ if (atomic4 === void 0 || !/^[0-9]{1,31}$/.test(text3) || BigInt(text3) < MAX_PAYMENT_ATOMIC_LEAST) return void 0;
128369
+ let whole;
128370
+ try {
128371
+ const parts = balanceNow(session.openStore(), { programId: session.programId });
128372
+ if (!ATOMIC_TEXT.test(String(parts?.totalAtomic ?? ""))) return void 0;
128373
+ const atomicOf = (value2) => ATOMIC_TEXT.test(String(value2 ?? "")) ? BigInt(value2) : 0n;
128374
+ whole = BigInt(parts.totalAtomic) + atomicOf(parts.refundsNotCertainAtomic) + (parts.report?.detail?.pendingDeposits ?? []).reduce((sum2, deposit) => sum2 + atomicOf(deposit?.amountAtomic), 0n);
128375
+ } catch {
128376
+ return void 0;
128377
+ }
128378
+ if (BigInt(atomic4) <= whole) return void 0;
128379
+ const cap = usdcText3(atomic4);
128380
+ const meant = usdcText3(BigInt(text3));
128381
+ return [
128382
+ "mcp_max_payment_not_usdc",
128383
+ `max_payment "${text3}" is ${cap} USDC (max_payment is in USDC, not atomic units), more than the wallet's whole balance of ${usdcText3(whole)} USDC; nothing was requested or paid`,
128384
+ `max_payment is in USDC, like "0.01" for one cent; "${text3}" in atomic units is max_payment "${meant}". Call ${tool} again now with max_payment in USDC (the amount the user named), or leave max_payment out`,
128385
+ { paid: false, capAtomic: String(atomic4), wholeBalanceAtomic: whole.toString() }
128386
+ ];
128387
+ };
128336
128388
  const savedBalanceShort = (store, neededAtomic) => {
128337
128389
  if (!store) return void 0;
128338
128390
  try {
@@ -128470,9 +128522,9 @@ function createWalletTools({
128470
128522
  const doneInMinutesFields = (joins, startAtMs) => {
128471
128523
  if (!Number.isSafeInteger(joins) || joins < 1) return {};
128472
128524
  const left = Number.isSafeInteger(startAtMs) ? Math.max(0, startAtMs - Date.now()) : void 0;
128473
- const likely = (left ?? joinSoonMaxMs / 2) + joins * JOIN_TIME_MS.joinLikely + JOIN_TIME_MS.sendLikely;
128525
+ const least = (left ?? 0) + joins * JOIN_TIME_MS.joinLikely + JOIN_TIME_MS.sendLikely;
128474
128526
  const most = (left ?? joinSoonMaxMs) + joins * JOIN_TIME_MS.joinMost + JOIN_TIME_MS.sendMost;
128475
- const low = Math.max(1, Math.round(likely / 6e4));
128527
+ const low = Math.max(1, Math.floor(least / 6e4));
128476
128528
  return { doneInMinutesLow: low, doneInMinutesHigh: Math.max(low + 1, Math.ceil(most / 6e4)) };
128477
128529
  };
128478
128530
  const doneInWords = ({ doneInMinutesLow: low, doneInMinutesHigh: high } = {}) => Number.isSafeInteger(low) && Number.isSafeInteger(high) ? `about ${low} to ${high} minutes` : "";
@@ -129074,7 +129126,7 @@ function createWalletTools({
129074
129126
  method: { type: "string", enum: ["GET", "POST"], description: "HTTP method; default GET." },
129075
129127
  headers: { type: "object", additionalProperties: { type: "string" }, description: "Extra request headers (never payment or transport headers)." },
129076
129128
  body: { type: "string", description: "Request body text (POST only)." },
129077
- max_payment: { type: "string", description: 'Optional: the most this call may pay, in USDC, like "0.05"; at most the spending limit.' },
129129
+ max_payment: { type: "string", description: 'Optional: the most this call may pay, in USDC, like "0.01" (not atomic units); at most the spending limit.' },
129078
129130
  max_payment_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "The same in atomic units (1000000 = 1 USDC). Give max_payment or max_payment_atomic, not both with different values." },
129079
129131
  request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional. Leave it out for a new payment (the answer names the id it made). Pass the request_id of an earlier answer only to resume that payment; it never pays twice." }
129080
129132
  },
@@ -129091,6 +129143,8 @@ function createWalletTools({
129091
129143
  if (request.refusal) return refuse12(...request.refusal);
129092
129144
  const capArg = payCapArgument(args);
129093
129145
  if (capArg.refusal) return refuse12(...capArg.refusal);
129146
+ const notUsdc = maxPaymentNotUsdc(args, capArg.atomic, "x402_pay");
129147
+ if (notUsdc) return refuse12(...notUsdc);
129094
129148
  const argument2 = capArg.atomic;
129095
129149
  const given = args.request_id !== void 0 && args.request_id !== null && args._autoRequestId !== true;
129096
129150
  const requestId = args.request_id !== void 0 && args.request_id !== null ? args.request_id : `mcp-${randomBytes23(8).toString("hex")}`;
@@ -129638,7 +129692,7 @@ ${canonical5}`;
129638
129692
  ...floatPaid ? { via: "shinjuku", shinjukuTransaction } : {},
129639
129693
  ...balanceAnswer(one),
129640
129694
  ...notices,
129641
- next: (unpaid ? "nothing was paid: this request id's payment never landed (the pool's public history proves it), and the answer it got came back again; nothing new was paid. Tell the user nothing was paid for it" : receipt.replayed === true ? "this request id was already paid: the stored receipt came back and nothing new was paid" : floatPaid ? "paid through Shinjuku (privately: the chain shows Shinjuku paying the seller, not this wallet); tell the user the price (priceAtomic), the fee (feeAtomic), the total (amountAtomic), and the transaction (the seller's settle transaction; null when the seller returned none)" + (shinjukuTransaction ? ". shinjukuTransaction is this wallet's private payment to Shinjuku" : ". shinjukuTransaction (this wallet's private payment to Shinjuku) is not recorded yet; wallet_receipts shows it once it settles") + ". wallet_balance shows the new balance" : "paid; tell the user what was paid (amountAtomic, transaction). wallet_receipts lists this payment, wallet_balance shows the new balance") + (notices.refundNotices ? ". Tell the user each refundNotices line" : "")
129695
+ next: (unpaid ? "nothing was paid: this request id's payment never landed (the pool's public history proves it), and the answer it got came back again; nothing new was paid. Tell the user nothing was paid for it" : receipt.replayed === true ? "this request id was already paid: the stored receipt came back and nothing new was paid" : floatPaid ? "paid through Shinjuku (privately: the chain shows Shinjuku paying the seller, not this wallet); tell the user the price (priceAtomic), the Shinjuku fee (feeAtomic; never a network fee), the total (amountAtomic), and the transaction (the seller's settle transaction; null when the seller returned none)" + (shinjukuTransaction ? ". shinjukuTransaction is this wallet's private payment to Shinjuku" : ". shinjukuTransaction (this wallet's private payment to Shinjuku) is not recorded yet; wallet_receipts shows it once it settles") + ". wallet_balance shows the new balance" : "paid; tell the user what was paid (amountAtomic, transaction). wallet_receipts lists this payment, wallet_balance shows the new balance") + (notices.refundNotices ? ". Tell the user each refundNotices line" : "")
129642
129696
  }, "x402_pay")
129643
129697
  }));
129644
129698
  }
@@ -131755,7 +131809,7 @@ ${canonical5}`;
131755
131809
  }
131756
131810
  }
131757
131811
  let payments = kind === "moves" ? [] : paymentRows(store, names, refunds.states);
131758
- const missing = (row) => row.state === void 0 && row.privateTransaction === null && (row.scheme === "shielded-exact" || Boolean(row.facilitator));
131812
+ const missing = (row) => row.state !== "never_landed" && row.state !== "cancelled" && row.privateTransaction === null && (row.scheme === "shielded-exact" || Boolean(row.facilitator));
131759
131813
  if (payments.some(missing) && [...payments].sort((left, right) => right.at - left.at).slice(0, limit).some(missing)) {
131760
131814
  try {
131761
131815
  if (latestPrewarmedWalletHistory() === void 0 && paysInFlight === 0 && !historyPrewarmRunning()) warmPaymentHistory(session);
@@ -134679,7 +134733,7 @@ ${canonical5}`;
134679
134733
  const unshield = {
134680
134734
  name: "wallet_unshield",
134681
134735
  title: "Send USDC privately",
134682
- description: `Use this when the user says "send 2 USDC to <address>" or "withdraw to my wallet" (leave \`to\` out for the user's own wallet when one is set): it sends amount (USDC, like "2"; or amount_atomic) from the PRIVATE balance as plain USDC to \`to\`, a public Solana address (it receives the USDC at that address). BEFORE you call it to send all of the balance when wallet_balance showed balanceParts above 1, write to the user that the wallet first gets the balance ready to send (do not tell the user how the wallet stores the balance; some apps show nothing until this call ends). Privately: the chain shows the pool paying the address, not which wallet paid. The wallet's own key (ownAddress in wallet_balance) is this wallet's public address: it holds the public USDC, wallet_shield deposits from it, and wallet_transfer_token sells from it. So it is refused as \`to\`: an exit there would show on chain that the private balance belongs to this wallet. This holds whether or not that key ever deposited: wallet_add_funds deposits come from one-time addresses, not from this key, so never tell the user the deposits came from it. Shinjuku pays every network fee; no SOL is needed. The user's saved own addresses (setup or this app's wallet entry) send at once. Any other address is sent only after the user confirms it in this app (a confirmation dialog); a client that cannot ask is refused (mcp_unshield_to_not_allowed) and a decline sends nothing (mcp_unshield_declined). \`to\` must already be able to receive USDC: an address that cannot is refused before any dialog (mcp_unshield_to_invalid). It runs in steps and may wait for the pool's next anchor: the call answers within about 20 s with stage (confirming, queued, refreshing, self-pay = preparing, exit = sending, done; joining = the wallet first gets the balance ready to send, then sends; each call goes on with it (joinSteps: its steps, one after another; doneInMinutesLow to doneInMinutesHigh: the time to say), ok: true); call wallet_unshield again with the SAME request_id, amount, and to for the status (each call answers within about 20 s, at once when the send ends; a confirmation dialog the user answers may take longer). When the wallet must first get the balance ready to send, the FIRST call answers at once with stage joining and joinTellFirst: true; write its say to the user, then call again with the same request_id at once. One exception: for a send whose balance must first be made ready, when this app sends a progress token with the call, that next call gets the balance ready and then sends, with a progress line every ${JOIN_STEP_MS / 1e3} s ("getting your balance ready to send"; some apps show none of them, so the line is said before the call, as above). It answers when the send is done, or after about ${joinAnswerSeconds(JOIN_CALL_MAX_MS)} s with how far it got (joinsDone; sendState sending: the USDC is leaving, never say nothing moved); then call again with the same request_id. Without a progress token it answers within about 20 s with stage joining: call again until it sends. The same request_id never sends twice, also after a restart. request_id is optional: without it the answer names the id it made, and the same call again within 10 minutes after it ends reads that same send. When it lands, the wallet writes an encrypted backup (backupPath). x402_pay calls wait while it runs. An optional limit the user sets (the unshield limit) may bound each call and what this wallet sends in any 24 hours, from every app that runs it (mcp_unshield_cap_exceeded: tell the user). Other refusals: mcp_argument_invalid, mcp_request_id_reused, mcp_unshield_to_invalid, wallet_locked, mcp_tool_disabled, mcp_unshield_failed, and the unshield's own p03_* codes. A send is one payment to \`to\`.`,
134736
+ description: `Use this when the user says "send 2 USDC to <address>" or "withdraw to my wallet" (for "my wallet" leave \`to\` out: it uses the address saved at setup, or says how to set one; never ask the user first): it sends amount (USDC, like "2"; or amount_atomic) from the PRIVATE balance as plain USDC to \`to\`, a public Solana address (it receives the USDC at that address). BEFORE you call it to send all of the balance when wallet_balance showed balanceParts above 1, write to the user that the wallet first gets the balance ready to send (do not tell the user how the wallet stores the balance; some apps show nothing until this call ends). Privately: the chain shows the pool paying the address, not which wallet paid. The wallet's own key (ownAddress in wallet_balance) is this wallet's public address: it holds the public USDC, wallet_shield deposits from it, and wallet_transfer_token sells from it. So it is refused as \`to\`: an exit there would show on chain that the private balance belongs to this wallet. This holds whether or not that key ever deposited: wallet_add_funds deposits come from one-time addresses, not from this key, so never tell the user the deposits came from it. Shinjuku pays every network fee; no SOL is needed. The user's saved own addresses (setup or this app's wallet entry) send at once. Any other address is sent only after the user confirms it in this app (a confirmation dialog); a client that cannot ask is refused (mcp_unshield_to_not_allowed) and a decline sends nothing (mcp_unshield_declined). \`to\` must already be able to receive USDC: an address that cannot is refused before any dialog (mcp_unshield_to_invalid). It runs in steps and may wait for the pool's next anchor: the call answers within about 20 s with stage (confirming, queued, refreshing, self-pay = preparing, exit = sending, done; joining = the wallet first gets the balance ready to send, then sends; each call goes on with it (joinSteps: its steps, one after another; doneInMinutesLow to doneInMinutesHigh: the time to say), ok: true); call wallet_unshield again with the SAME request_id, amount, and to for the status (each call answers within about 20 s, at once when the send ends; a confirmation dialog the user answers may take longer). When the wallet must first get the balance ready to send, the FIRST call answers at once with stage joining and joinTellFirst: true; write its say to the user, then call again with the same request_id at once. One exception: for a send whose balance must first be made ready, when this app sends a progress token with the call, that next call gets the balance ready and then sends, with a progress line every ${JOIN_STEP_MS / 1e3} s ("getting your balance ready to send"; some apps show none of them, so the line is said before the call, as above). It answers when the send is done, or after about ${joinAnswerSeconds(JOIN_CALL_MAX_MS)} s with how far it got (joinsDone; sendState sending: the USDC is leaving, never say nothing moved); then call again with the same request_id. Without a progress token it answers within about 20 s with stage joining: call again until it sends. The same request_id never sends twice, also after a restart. request_id is optional: without it the answer names the id it made, and the same call again within 10 minutes after it ends reads that same send. When it lands, the wallet writes an encrypted backup (backupPath). x402_pay calls wait while it runs. An optional limit the user sets (the unshield limit) may bound each call and what this wallet sends in any 24 hours, from every app that runs it (mcp_unshield_cap_exceeded: tell the user). Other refusals: mcp_argument_invalid, mcp_request_id_reused, mcp_unshield_to_invalid, wallet_locked, mcp_tool_disabled, mcp_unshield_failed, and the unshield's own p03_* codes. A send is one payment to \`to\`. ${HOST_BLOCKED_LINE}`,
134683
134737
  inputSchema: {
134684
134738
  type: "object",
134685
134739
  properties: {
@@ -136389,7 +136443,7 @@ ${canonical5}`;
136389
136443
  prompt: { type: "string", description: "One user message (instead of messages)." },
136390
136444
  max_tokens: { type: "integer", minimum: 1, description: `Optional, default 4096 (512 for claude-opus-5), at least ${INFERENCE_MIN_MAX_TOKENS}. The answer's length limit, in tokens; the call pays for all of them up front. A reasoning model spends its first tokens thinking, so fewer can end with no answer.` },
136391
136445
  temperature: { type: "number", minimum: 0, maximum: 2, description: "Sampling temperature; leave it out for the model's default." },
136392
- max_payment: { type: "string", description: 'Optional: the most this call may pay, in USDC, like "0.05"; at most the spending limit.' },
136446
+ max_payment: { type: "string", description: 'Optional: the most this call may pay, in USDC, like "0.01" (not atomic units); at most the spending limit.' },
136393
136447
  max_payment_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "The most this call may pay, in atomic units; at most the spending limit." },
136394
136448
  request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional. Leave it out for a new question (the answer names the id it made). Pass the request_id of an earlier answer only to resume that call; it never pays twice." }
136395
136449
  },
@@ -136433,6 +136487,8 @@ ${canonical5}`;
136433
136487
  }
136434
136488
  const maxPayment = usdcAmountArg(args, { usdc: "max_payment", atomic: "max_payment_atomic", required: false });
136435
136489
  if (!maxPayment.ok) return refuse12("inference_max_payment_invalid", maxPayment.detail.replace(/nothing was moved/, "nothing was paid"), maxPayment.next);
136490
+ const notUsdc = maxPaymentNotUsdc(args, maxPayment.atomic, "inference_chat");
136491
+ if (notUsdc) return refuse12(...notUsdc);
136436
136492
  const messages = typeof args.prompt === "string" ? [{ role: "user", content: args.prompt }] : args.messages;
136437
136493
  const asked = { model: args.model, messages, maxTokens: args.max_tokens };
136438
136494
  let ceiling;
@@ -137633,7 +137689,7 @@ function createSetupTools({
137633
137689
  doneTool: sayOnRefusal(doneTool)
137634
137690
  };
137635
137691
  }
137636
- var import_web3133, shownCaps, RELEASE_RETRY_MS, STAGE_WORDS, stageWords, INTERNAL_BALANCE_DETAIL, shownBreakdown, sessionLeftShown, TOKEN_UNIT_TOOLS, RESOURCE_PAYMENT_WAL_PREFIX2, POOL_LAYOUT_CODE, POOL_LAYOUT_UPDATE, USEPOD_VIA_INFERENCE_CHAT, SEND_RUNS_ON, DEPOSIT_RUNS_ON, CLI_FUNDS_PLAN_PREFIX, CLI_FUNDS_SCHEMA, SELF_PAY_ORIGIN3, REQUEST_ID4, POSITIVE_ATOMIC6, PRIVATE_HOST3, FORBIDDEN_HEADERS3, MAX_BODY_BYTES2, PREVIEW_TIMEOUT_MS, PREVIEW_SELLER_READ_MS, CALL_BUDGET_MS, budgetLeft, PAY_ANSWER_MARGIN_MS, MONEY_FIRST_ANSWER_MS, BALANCE_AFTER_LANDING_MS, JOIN_CALL_MAX_MS, JOIN_CALL_ANSWER_MARGIN_S, PAY_AFTER_JOIN_MS, joinAnswerSeconds, JOIN_TIME_MS, JOIN_NUDGE_QUIET_MS, JOIN_STEP_MS, sleep2, PROGRESS_EVERY_MS2, READ_BUDGET_MS, UNPAID_TOR_TRY_MS, UNPAID_CLEAR_MS, READ_BUDGET_TOOLS, FLOAT_BUSY_CODES, RETRY_MARGIN_MS, ASK_PAY_CODES, runningNpmVersion, terminalCommand2, capPlaceWords, noLimitSetLine, CAP_NAMES, capName, entryPlaces, capRaiseText, runningLimits, payCapFlag, noPriceFits, cheaperText, limitNext, overLimitStep, SAFE_TEXT, causesLog, MCP_TOOL_ALIASES, POOL_SWITCH_WHY, POOL_SWITCH_NEXT, SWITCH_STATES, quietSwitch, switchKey, ATOMIC_TEXT, movedTo, FLOAT_ID, ANSWERED_EXIT_RETRY_MS, ANSWERED_EXIT_UNCONFIRMED_MS, ANSWERED_EXIT_RETRY_WINDOW_MS, ANSWERED_EXIT_CLOSED, switchAmount, switchEta, switchRows, OWNER_CAP_CODES, JOIN_END_MCP, JOIN_END_TERMINAL_LIBRARY, JOIN_RETRY_BACKOFF_MS, WALLET_CAP_FIX, ENTRY_CAP_CODES, MCP_CAP_FIX, capFix, ownerOnlyNext, mcpNext, CONFIDENTIAL_RETIRED, json3, answerResult, discoverTool, LAUNCH_CAP_CODES, dayCapKind, refuse12, newRequestIdFor, payCapName, publicOffer, FLOAT_PAID_CODES, FLOAT_NOT_CERTAIN_CODES, FLOAT_PRIVATE_LEG, SELLER_ONLY_HERE, outsideWalletRule, proxyStartStep, MONEY_ID2, toRecordId, linkKindOf, moneyId, REFUSED_CODES, refusedCodeOf, PLAIN_REASONS, insufficientCause, INSUFFICIENT_REASONS, reasonOf, plainReason, KEPT_SEND_TRIES, shownId, isSplitSend, PROMISED_NEXT, partFailReason, sendAnswerOf, PUBLIC_READ_TIMEOUT_MS, RECIPIENT_CHECK_TIMEOUT_MS, withTimeout, ownKey, readFailure, OWN_KEY_TOKENS_MAX, tokenAmountText, elsewhereId, AGENT_ID_SCHEMA, agentIdName, minutesText2, ageWords2, DEPOSIT_SENT_STATES, depositLanded, usdcText3, walkWalletHistory, REFRESH_LEASE_WALK_MS, HOME_MARKS_SETTLED_MS2, AUTO_BACKUPS_KEPT, AUTO_BACKUP_NAME, AUTO_BACKUP_VERIFIED, FRONTED_SETTLE_MARGIN_SLOTS, UNSHIELD_WINDOW_YIELD_MS, NOT_FINAL_YET, FINISH_FINALITY_WAIT_MS, FINISH_FINALITY_WAITS, FINISH_FINALITY_RECENT_MS, walkStillCurrent, DIRECT_EXIT_GONE, BALANCE_REFRESH_WAIT_MS, BALANCE_REFRESH_GRACE_MS, BALANCE_REFRESH_LONG_MS, INBOUND_REFRESH_AGE_MS, changeFields, balanceAnswer, CAP_BALANCE_WAIT_MS, CAP_BALANCE_RETRY_MS, REFRESH_RETRY_MS, FINALITY_POLL_MS, FINALITY_WAIT_MS, FINALITY_REFRESH_LIMIT_MS, POOL_WATCH_MS, POOL_WATCH_POLL_MS, EARLY_BALANCE_DELAY_MS, FINALITY_SETTLE_WINDOW_SLOTS, PUBLIC_READ_ANSWER_MS, LINK_READ_ANSWER_MS, OWN_READS_AHEAD_MS, OWN_PUBLIC_KNOWN_MS, FLOAT_CORRECT_WAIT_MS, SWITCH_WAIT_MS, UNSHIELD_TO_NO_USDC_DETAIL, UNSHIELD_TO_INVALID_NEXT, INFERENCE_MIN_MAX_TOKENS, ELICIT_TIMEOUT_MS, ELICIT_UNSHOWN_MS, elicitUnshown, POLL_SECONDS2, TRANSFER_CHANGE_READ_MS, TEMP_CLEAN_DELAY_MS, KEPT_ON_DISK, MAX_SHIELDED_BALANCE_ATOMIC, receiveGenerator, MCP_TOOL_NAMES, MCP_HIDDEN_TOOL_NAMES, setupTerminalNext, SETUP_RUNNING_SAY, ACCEPTED_NEXT, SETUP_RUNNING_NEXT, SETUP_ANSWER_WAIT_MS, SETUP_CONFIRMING_SAY, SETUP_CONFIRMING_NEXT, OWN_ADDRESS_QUESTION, OWN_ADDRESS_WHERE, OWN_ADDRESS_INVALID, PRIVATE_INFERENCE_WHY, RESTART_STEPS, TOR_BROWSER_PORT, SETUP_TOR_TITLE, SETUP_TOR_WHY;
137692
+ var import_web3133, shownCaps, RELEASE_RETRY_MS, STAGE_WORDS, stageWords, INTERNAL_BALANCE_DETAIL, shownBreakdown, sessionLeftShown, TOKEN_UNIT_TOOLS, RESOURCE_PAYMENT_WAL_PREFIX2, POOL_LAYOUT_CODE, POOL_LAYOUT_UPDATE, USEPOD_VIA_INFERENCE_CHAT, SEND_RUNS_ON, DEPOSIT_RUNS_ON, CLI_FUNDS_PLAN_PREFIX, CLI_FUNDS_SCHEMA, SELF_PAY_ORIGIN3, REQUEST_ID4, POSITIVE_ATOMIC6, PRIVATE_HOST3, FORBIDDEN_HEADERS3, MAX_BODY_BYTES2, PREVIEW_TIMEOUT_MS, PREVIEW_SELLER_READ_MS, CALL_BUDGET_MS, budgetLeft, PAY_ANSWER_MARGIN_MS, MONEY_FIRST_ANSWER_MS, BALANCE_AFTER_LANDING_MS, JOIN_CALL_MAX_MS, JOIN_CALL_ANSWER_MARGIN_S, PAY_AFTER_JOIN_MS, joinAnswerSeconds, JOIN_TIME_MS, JOIN_NUDGE_QUIET_MS, JOIN_STEP_MS, sleep2, PROGRESS_EVERY_MS2, READ_BUDGET_MS, UNPAID_TOR_TRY_MS, UNPAID_CLEAR_MS, READ_BUDGET_TOOLS, FLOAT_BUSY_CODES, RETRY_MARGIN_MS, ASK_PAY_CODES, runningNpmVersion, terminalCommand2, capPlaceWords, noLimitSetLine, CAP_NAMES, capName, entryPlaces, capRaiseText, runningLimits, payCapFlag, noPriceFits, cheaperText, limitNext, overLimitStep, SAFE_TEXT, causesLog, MCP_TOOL_ALIASES, POOL_SWITCH_WHY, POOL_SWITCH_NEXT, SWITCH_STATES, quietSwitch, switchKey, ATOMIC_TEXT, MAX_PAYMENT_ATOMIC_LEAST, movedTo, FLOAT_ID, ANSWERED_EXIT_RETRY_MS, ANSWERED_EXIT_UNCONFIRMED_MS, ANSWERED_EXIT_RETRY_WINDOW_MS, ANSWERED_EXIT_CLOSED, switchAmount, switchEta, switchRows, OWNER_CAP_CODES, JOIN_END_MCP, JOIN_END_TERMINAL_LIBRARY, JOIN_RETRY_BACKOFF_MS, WALLET_CAP_FIX, ENTRY_CAP_CODES, MCP_CAP_FIX, capFix, ownerOnlyNext, mcpNext, CONFIDENTIAL_RETIRED, json3, answerResult, discoverTool, LAUNCH_CAP_CODES, dayCapKind, refuse12, newRequestIdFor, payCapName, publicOffer, FLOAT_PAID_CODES, FLOAT_NOT_CERTAIN_CODES, FLOAT_PRIVATE_LEG, SELLER_ONLY_HERE, outsideWalletRule, proxyStartStep, MONEY_ID2, toRecordId, linkKindOf, moneyId, REFUSED_CODES, refusedCodeOf, PLAIN_REASONS, insufficientCause, INSUFFICIENT_REASONS, reasonOf, plainReason, KEPT_SEND_TRIES, shownId, isSplitSend, PROMISED_NEXT, partFailReason, sendAnswerOf, PUBLIC_READ_TIMEOUT_MS, RECIPIENT_CHECK_TIMEOUT_MS, withTimeout, ownKey, readFailure, OWN_KEY_TOKENS_MAX, tokenAmountText, elsewhereId, AGENT_ID_SCHEMA, agentIdName, minutesText2, ageWords2, DEPOSIT_SENT_STATES, depositLanded, usdcText3, walkWalletHistory, REFRESH_LEASE_WALK_MS, HOME_MARKS_SETTLED_MS2, AUTO_BACKUPS_KEPT, AUTO_BACKUP_NAME, AUTO_BACKUP_VERIFIED, FRONTED_SETTLE_MARGIN_SLOTS, UNSHIELD_WINDOW_YIELD_MS, NOT_FINAL_YET, FINISH_FINALITY_WAIT_MS, FINISH_FINALITY_WAITS, FINISH_FINALITY_RECENT_MS, walkStillCurrent, DIRECT_EXIT_GONE, BALANCE_REFRESH_WAIT_MS, BALANCE_REFRESH_GRACE_MS, BALANCE_REFRESH_LONG_MS, INBOUND_REFRESH_AGE_MS, changeFields, balanceAnswer, CAP_BALANCE_WAIT_MS, CAP_BALANCE_RETRY_MS, REFRESH_RETRY_MS, FINALITY_POLL_MS, FINALITY_WAIT_MS, FINALITY_REFRESH_LIMIT_MS, POOL_WATCH_MS, POOL_WATCH_POLL_MS, EARLY_BALANCE_DELAY_MS, FINALITY_SETTLE_WINDOW_SLOTS, PUBLIC_READ_ANSWER_MS, LINK_READ_ANSWER_MS, OWN_READS_AHEAD_MS, OWN_PUBLIC_KNOWN_MS, FLOAT_CORRECT_WAIT_MS, SWITCH_WAIT_MS, UNSHIELD_TO_NO_USDC_DETAIL, UNSHIELD_TO_INVALID_NEXT, INFERENCE_MIN_MAX_TOKENS, ELICIT_TIMEOUT_MS, ELICIT_UNSHOWN_MS, elicitUnshown, POLL_SECONDS2, TRANSFER_CHANGE_READ_MS, TEMP_CLEAN_DELAY_MS, KEPT_ON_DISK, MAX_SHIELDED_BALANCE_ATOMIC, receiveGenerator, MCP_TOOL_NAMES, MCP_HIDDEN_TOOL_NAMES, setupTerminalNext, SETUP_RUNNING_SAY, ACCEPTED_NEXT, SETUP_RUNNING_NEXT, SETUP_ANSWER_WAIT_MS, SETUP_CONFIRMING_SAY, SETUP_CONFIRMING_NEXT, OWN_ADDRESS_QUESTION, OWN_ADDRESS_WHERE, OWN_ADDRESS_INVALID, PRIVATE_INFERENCE_WHY, RESTART_STEPS, HOST_BLOCKED_LINE, TOR_BROWSER_PORT, SETUP_TOR_TITLE, SETUP_TOR_WHY;
137637
137693
  var init_tools = __esm({
137638
137694
  "wallet/src/mcp/tools.mjs"() {
137639
137695
  "use strict";
@@ -137826,6 +137882,7 @@ var init_tools = __esm({
137826
137882
  quietSwitch = (item) => item.state === "done" && item.tellUser !== true;
137827
137883
  switchKey = (item) => `${item.from}:${item.state}`;
137828
137884
  ATOMIC_TEXT = /^[0-9]{1,31}$/;
137885
+ MAX_PAYMENT_ATOMIC_LEAST = 1000n;
137829
137886
  movedTo = (response, requested) => {
137830
137887
  if (![301, 302, 303, 307, 308].includes(response.status)) return void 0;
137831
137888
  const location = response.headers.get("location");
@@ -138112,7 +138169,9 @@ var init_tools = __esm({
138112
138169
  return {
138113
138170
  changeSettling: true,
138114
138171
  ...Number.isSafeInteger(one.changeReadyInSeconds) ? { changeReadyInSeconds: one.changeReadyInSeconds } : {},
138115
- ...ATOMIC_TEXT.test(shown) && ATOMIC_TEXT.test(upTo) && BigInt(upTo) < BigInt(shown) ? { changeUpToAtomic: upTo } : {}
138172
+ ...ATOMIC_TEXT.test(shown) && ATOMIC_TEXT.test(upTo) && BigInt(upTo) < BigInt(shown) ? { changeUpToAtomic: upTo } : {},
138173
+ // UX129-9: the value is the changes of this many recent payments (2 or more); changeUpToAtomic is then the largest.
138174
+ ...Number.isSafeInteger(one.changeCount) && one.changeCount >= 2 ? { changeCount: one.changeCount } : {}
138116
138175
  };
138117
138176
  };
138118
138177
  balanceAnswer = (one, { shown = true } = {}) => one ? {
@@ -138193,6 +138252,7 @@ var init_tools = __esm({
138193
138252
  OWN_ADDRESS_INVALID = "That is not a Solana address (32 to 44 letters and digits, as your wallet app shows it)";
138194
138253
  PRIVATE_INFERENCE_WHY = "Your agent then buys AI answers from an AI provider, paid from your private balance over Tor; the AI provider and the company that runs the model see the prompt, not who paid.";
138195
138254
  RESTART_STEPS = `In Claude Code: type /mcp, pick ${MCP_SERVER_NAME}, and choose Reconnect. In Claude Desktop: quit it and open it again. In another app: restart it`;
138255
+ HOST_BLOCKED_LINE = "If this app blocks a wallet tool (Claude Code's auto mode can), say nothing moved; in Claude Code, shift+tab turns auto mode off, then ask again. Never suggest a rule that lets a payment or send run with no question, or another way around the block.";
138196
138256
  TOR_BROWSER_PORT = "http://127.0.0.1:9150";
138197
138257
  SETUP_TOR_TITLE = "Use Tor?";
138198
138258
  SETUP_TOR_WHY = "It hides your IP address from the payment service and sellers.";
@@ -139592,7 +139652,7 @@ async function runSetupModeServer({
139592
139652
  }
139593
139653
  return { closed: ended };
139594
139654
  }
139595
- var import_web3134, SUPPORTED_PROTOCOL_VERSIONS, MAX_LINE_BYTES, POSITIVE_ATOMIC7, INSTRUCTIONS, MCP_LOG_LEVELS, POOL_SWITCH_LOG_LEVEL, validId, PRODUCT_NAME, listedTitle, BUNDLED_SOURCE_DIR, SHA256_HEX, UPDATE_NOTICE_TOOLS, UPDATE_NOTICE_MAX_TEXT, NPM_VERSION2, GIT_COMMIT, ownWalletVersion, mcpServerVersion, UPDATE_NOTICE_TEXT, updateNoticeLine, commandUpdateField, LAUNCH_NOTICE_TOOLS, withLaunchLine, capRequired, setupInvalid, TOOL_ROOTS_WAIT_MS, ROOTS_FREE_TOOLS, MCP_CLOSE_BOUND_MS, MCP_CLOSE_HISTORY_BOUND_MS, MCP_CLOSE_UNREADABLE_BOUND_MS, MCP_CLOSE_POLL_MS, closeHistoryBusy, SETUP_INSTRUCTIONS, TOR_RELAY_READY_MS, RELAY_INIT_ID, runMcp;
139655
+ var import_web3134, SUPPORTED_PROTOCOL_VERSIONS, MAX_LINE_BYTES, POSITIVE_ATOMIC7, INSTRUCTIONS_BASE, INSTRUCTIONS, MCP_LOG_LEVELS, POOL_SWITCH_LOG_LEVEL, validId, PRODUCT_NAME, listedTitle, BUNDLED_SOURCE_DIR, SHA256_HEX, UPDATE_NOTICE_TOOLS, UPDATE_NOTICE_MAX_TEXT, NPM_VERSION2, GIT_COMMIT, ownWalletVersion, mcpServerVersion, UPDATE_NOTICE_TEXT, updateNoticeLine, commandUpdateField, LAUNCH_NOTICE_TOOLS, withLaunchLine, capRequired, setupInvalid, TOOL_ROOTS_WAIT_MS, ROOTS_FREE_TOOLS, MCP_CLOSE_BOUND_MS, MCP_CLOSE_HISTORY_BOUND_MS, MCP_CLOSE_UNREADABLE_BOUND_MS, MCP_CLOSE_POLL_MS, closeHistoryBusy, SETUP_INSTRUCTIONS, TOR_RELAY_READY_MS, RELAY_INIT_ID, runMcp;
139596
139656
  var init_stdioServer = __esm({
139597
139657
  "wallet/src/mcp/stdioServer.mjs"() {
139598
139658
  "use strict";
@@ -139625,7 +139685,8 @@ var init_stdioServer = __esm({
139625
139685
  SUPPORTED_PROTOCOL_VERSIONS = Object.freeze(["2025-06-18", "2025-03-26", "2024-11-05"]);
139626
139686
  MAX_LINE_BYTES = 4 * 1024 * 1024;
139627
139687
  POSITIVE_ATOMIC7 = /^[1-9][0-9]{0,30}$/;
139628
- 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 user's spending 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.`;
139688
+ INSTRUCTIONS_BASE = `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 user's spending 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.`;
139689
+ INSTRUCTIONS = `${INSTRUCTIONS_BASE} ${HOST_BLOCKED_LINE}`;
139629
139690
  MCP_LOG_LEVELS = Object.freeze(["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"]);
139630
139691
  POOL_SWITCH_LOG_LEVEL = "info";
139631
139692
  validId = (id) => id === null || typeof id === "string" || typeof id === "number" && Number.isFinite(id);
@@ -139663,7 +139724,7 @@ var init_stdioServer = __esm({
139663
139724
  MCP_CLOSE_UNREADABLE_BOUND_MS = 6e4;
139664
139725
  MCP_CLOSE_POLL_MS = 500;
139665
139726
  closeHistoryBusy = (server) => () => compactHistoryReadsRunning() > 0 || memoReadsAheadRunning() > 0 || server.refreshOpen?.() === true;
139666
- 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 setup files (minutes: call again as next says). At stage done the wallet tools work. After setup: ${INSTRUCTIONS}`;
139727
+ 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 setup files (minutes: call again as next says). At stage done the wallet tools work. After setup: ${INSTRUCTIONS_BASE}`;
139667
139728
  TOR_RELAY_READY_MS = 18e4;
139668
139729
  RELAY_INIT_ID = "shinjuku-wallet-relay-initialize";
139669
139730
  runMcp = runMcpServer;
@@ -142007,7 +142068,7 @@ function torTooOldAnswer(version3, { tunnelHost, platform = process.platform } =
142007
142068
  ok: false,
142008
142069
  error: "tor_too_old",
142009
142070
  detail: `${torTooOldWhy(version3)} Tor 0.4.9.13 and newer fix that fault. Nothing was paid.`,
142010
- next: `Tell the user why: their Tor program is too old and has a known fault that can link two of their payments, and the wallet never falls back to a direct connection. Show torUpdateCommand in one code block, to copy into ${where}: it installs a newer Tor and starts it for the wallet.${platform === "win32" ? " The user stops the old Tor first." : ""} If you can run commands here, offer to run it for the user. When the newer Tor runs: ${RESTART}.`,
142071
+ next: `Tell the user why: their Tor program is too old and has a known fault that can link two of their payments, and the wallet never falls back to a direct connection. Show torUpdateCommand in one code block, to copy into ${where}: it installs a newer Tor and starts it for the wallet.${platform === "win32" ? ` Before it, the user stops the old Tor: ${TOR_STOP_OLD_WINDOWS.replace(/^Stop your old Tor first: /, "")}` : ""} If you can run commands here, offer to run it for the user. When the newer Tor runs: ${RESTART}.`,
142011
142072
  torUpdateCommand: command
142012
142073
  };
142013
142074
  }
@@ -142056,7 +142117,7 @@ async function serveTorMissingMcp({ message, recheck = async () => ({ answer: fa
142056
142117
  const dispatch = createMcpDispatcher({
142057
142118
  tools,
142058
142119
  aliases: MCP_TOOL_ALIASES,
142059
- instructions: tooOldAtStart ? `The Shinjuku Shield wallet is set to run behind Tor, and the Tor program on this machine is too old (it has a known fault that can link two payments). Call wallet_balance: its answer has the command that installs a newer Tor. ${RESTART} after the newer Tor starts.` : `The Shinjuku Shield wallet is set to run behind Tor, and Tor is not running on this machine. Call wallet_balance: its answer has the install command for the user's OS. ${RESTART} after Tor starts.`
142120
+ instructions: tooOldAtStart ? `The Shinjuku Shield wallet is set to run behind Tor, and the Tor program on this machine is too old (it has a known fault that can link two payments). Call any wallet tool you may use (x402_pay too): its answer has the command that installs a newer Tor, and it moves no money. ${RESTART} after the newer Tor starts.` : `The Shinjuku Shield wallet is set to run behind Tor, and Tor is not running on this machine. Call any wallet tool you may use (x402_pay too): its answer has the install command for the user's OS, and it moves no money. ${RESTART} after Tor starts.`
142060
142121
  });
142061
142122
  await serveStdio({ input, output: output2, dispatch, log });
142062
142123
  return 1;
@@ -147802,12 +147863,22 @@ function noticeLines(notice, width, compact = false) {
147802
147863
  }
147803
147864
  return lines;
147804
147865
  }
147866
+ function balanceShown(data) {
147867
+ const balance = clean4(data.balance);
147868
+ if (balance) return { bar: balance, row: balance, cls: "verm", step: null };
147869
+ const command = `${clean4(data.command) || "shinjuku"} balance`;
147870
+ if (data.balanceRead === "reading") return { bar: "reading your balance\u2026", row: "reading\u2026", cls: "mute", step: null };
147871
+ if (data.balanceRead === "slow") return { bar: "balance not known yet", row: "not known yet: the read is slow", cls: "mute", step: "it goes on, and shows here when it ends." };
147872
+ if (data.balanceRead === "failed") return { bar: "balance not known yet", row: "not known yet: the read failed", cls: "mute", step: `run ${command} to see why.` };
147873
+ if (data.balanceRead === "done") return { bar: "balance not known yet", row: "not known yet", cls: "mute", step: `run ${command} to read it again.` };
147874
+ return { bar: "balance not known yet", row: "not known yet", cls: "mute", step: null };
147875
+ }
147805
147876
  function homeStatus(data, wide, width) {
147806
147877
  const notices = walletNotices(data);
147807
147878
  const [notice] = notices;
147808
147879
  const head = notice ? [[" \u25CB ", "mute"], [notice.state, "bone"]] : [[" \u25CF ", "brass"], ["ready", "bone"]];
147809
- const balance = clean4(data.balance);
147810
- const middle = [[balance || "balance not known yet", balance ? "verm" : "mute"]];
147880
+ const shown = balanceShown(data);
147881
+ const middle = [[shown.bar, shown.cls]];
147811
147882
  if (data.tor === true || data.tor === false) middle.push([data.tor ? "tor on" : "tor off", "bone"]);
147812
147883
  const bar = (items) => [...head, ...items.flatMap((item) => [[" \u2502 ", "ash"], item])];
147813
147884
  const command = notices.find((item) => item.command)?.command;
@@ -147928,12 +147999,13 @@ function publicRows(data, w) {
147928
147999
  return { row: ["public", value2, "verm"], step: Object.hasOwn(PUBLIC_STEP, pub.step) ? [PUBLIC_STEP[pub.step]] : [] };
147929
148000
  }
147930
148001
  function walletBlock(data, wide, w, compact = false) {
147931
- const balance = clean4(data.balance);
148002
+ const shown = balanceShown(data);
147932
148003
  const notices = walletNotices(data);
147933
148004
  const state = notices.length ? `\u25CB ${notices[0].state}` : "\u25CF ready";
147934
148005
  const extra = notices.flatMap((notice) => noticeLines(notice, w - 4, compact));
147935
148006
  if (compact) return rowsBlock(PANELS.wallet, state, "mute", [], extra, "dim");
147936
- const rows2 = [["balance", balance || "not known yet", balance ? "verm" : "mute"]];
148007
+ const rows2 = [["balance", shown.row, shown.cls]];
148008
+ if (shown.step) extra.unshift(...wrap(shown.step, w - 4));
147937
148009
  const adding = clean4(data.addingFunds);
147938
148010
  const moving = clean4(data.movingOut);
147939
148011
  const live = (value2) => value2 === "none" ? "bone" : "verm";
@@ -148165,7 +148237,7 @@ async function stopReading(stdin) {
148165
148237
  handle.readStop();
148166
148238
  }
148167
148239
  }
148168
- async function openHomeScreen({ stdout = process.stdout, stdin = process.stdin, env = process.env, platform = process.platform, data } = {}) {
148240
+ async function openHomeScreen({ stdout = process.stdout, stdin = process.stdin, env = process.env, platform = process.platform, data, watch } = {}) {
148169
148241
  if (!homeScreenAllowed(stdout, env) || !stdin?.isTTY || typeof stdin.setRawMode !== "function") return { outcome: "off" };
148170
148242
  if (!compose(data, stdout.columns - 1, stdout.rows)) return { outcome: "off" };
148171
148243
  const codes = paletteFor(screenColor(stdout, env));
@@ -148228,6 +148300,7 @@ async function openHomeScreen({ stdout = process.stdout, stdin = process.stdin,
148228
148300
  const rawBefore = Boolean(stdin.isRaw);
148229
148301
  let reading = false;
148230
148302
  let timer;
148303
+ let stopWatch;
148231
148304
  process.once("exit", restoreScreen);
148232
148305
  for (const signal of signals) process.on(signal, onSignal);
148233
148306
  stdout.on?.("resize", onResize);
@@ -148240,11 +148313,21 @@ async function openHomeScreen({ stdout = process.stdout, stdin = process.stdin,
148240
148313
  write("\x1B[?1049h\x1B[?25l");
148241
148314
  draw();
148242
148315
  timer = setInterval(spin, BAND_MS2);
148316
+ stopWatch = watch?.((next) => {
148317
+ if (result === void 0 && next) {
148318
+ data = next;
148319
+ draw();
148320
+ }
148321
+ });
148243
148322
  await settled;
148244
148323
  } catch {
148245
148324
  finish({ outcome: "off" });
148246
148325
  } finally {
148247
148326
  clearInterval(timer);
148327
+ try {
148328
+ stopWatch?.();
148329
+ } catch {
148330
+ }
148248
148331
  stdout.removeListener?.("resize", onResize);
148249
148332
  for (const signal of signals) process.removeListener(signal, onSignal);
148250
148333
  process.removeListener("exit", restoreScreen);
@@ -148357,12 +148440,16 @@ function sellerWords(seller) {
148357
148440
  return text3.length > SELLER_CHARS ? `${text3.slice(0, SELLER_CHARS - 3)}...` : text3;
148358
148441
  }
148359
148442
  var REFUND_WORDS = Object.freeze({ refunded: "refunded", refund_sent: "coming back", refund_pending: "coming back", refund_not_seen: "refund not seen" });
148443
+ function aiPaymentWords(item) {
148444
+ const parts = /^private AI: (.+) \(([^()]+)\)$/.exec(item);
148445
+ return parts ? `private AI, ${parts[2]}: ${parts[1]}` : item;
148446
+ }
148360
148447
  function paymentRow(name, record, store, walk) {
148361
148448
  let ok;
148362
148449
  let at;
148363
148450
  let words2;
148364
148451
  const view2 = receiptView(record);
148365
- const seller = view2.kind === "inference" && typeof view2.item === "string" ? view2.item : sellerWords(view2.seller);
148452
+ const seller = view2.kind === "inference" && typeof view2.item === "string" ? aiPaymentWords(view2.item) : sellerWords(view2.seller);
148366
148453
  if (name.startsWith(FLOAT_PAYMENT_WAL_PREFIX2)) {
148367
148454
  if (record?.schema !== FLOAT_PAYMENT_SCHEMA2) return void 0;
148368
148455
  const state = floatRecordState(record);
@@ -150894,16 +150981,26 @@ function allowWalletChecks(deps) {
150894
150981
  const file = claudeCodeSettingsFile({ env: deps.env, home: deps.homedir() });
150895
150982
  try {
150896
150983
  const { changed, backup } = allowInClaudeCode(file);
150897
- return { same: !changed, say: `Claude Code runs the wallet's checks that move no money (balance, receipts) without a question${backup ? ` (its old settings file was copied to ${backup}, readable by you alone)` : ""}. Payments and sends still ask first. To undo that, remove the shinjuku rules in Claude Code's /permissions.` };
150984
+ return { same: !changed, say: `Claude Code runs the wallet's checks that move no money (balance, receipts) without a question${backup ? ` (its old settings file was copied to ${backup}, readable by you alone)` : ""}. In Claude Code's default mode, payments and sends still ask first; its auto mode may pay with no question or block a send (press shift+tab until auto mode is off). To undo the wallet's rules, remove the shinjuku rules in Claude Code's /permissions.` };
150898
150985
  } catch (error) {
150899
150986
  const unreadable = String(error.message).startsWith("setup_host_config_unreadable:");
150900
150987
  return { same: false, say: `Claude Code still asks before each wallet check: ${unreadable ? `the wallet cannot read the settings in ${file}, so it did not change that file` : `the wallet could not write ${file}`}.` };
150901
150988
  }
150902
150989
  }
150903
- function addToHosts(targets, home, deps, { force = false, npmVersion } = {}) {
150990
+ function otherHomeOfClaudeCodeEntry(existing, options) {
150991
+ const named = existing?.env?.SHIELDED_WALLET_HOME;
150992
+ const env = { ...options.env };
150993
+ delete env.SHIELDED_WALLET_HOME;
150994
+ if (typeof named === "string" && named) env.SHIELDED_WALLET_HOME = named;
150995
+ const home = setupHome(env);
150996
+ if (!isOlderClaudeCodeEntry(existing, { ...options, env, home })) return void 0;
150997
+ return resolve33(home) === resolve33(options.home) ? void 0 : home;
150998
+ }
150999
+ async function addToHosts(targets, home, deps, { force = false, npmVersion, ask } = {}) {
150904
151000
  const io = deps.io;
150905
151001
  const options = { home, env: deps.env, platform: deps.platform ?? process.platform, npmVersion };
150906
151002
  const done = /* @__PURE__ */ new Set();
151003
+ const kept2 = /* @__PURE__ */ new Set();
150907
151004
  for (const target of targets) {
150908
151005
  if (target === "claude-code") {
150909
151006
  const why = claudeCodeShellWhy(options);
@@ -150926,11 +151023,21 @@ function addToHosts(targets, home, deps, { force = false, npmVersion } = {}) {
150926
151023
  continue;
150927
151024
  }
150928
151025
  const olderOwn = !force && existing !== void 0 && isOlderClaudeCodeEntry(existing, options) && quiet(["mcp", "remove", "--scope", "user", MCP_SERVER_NAME]) === 0;
151026
+ const otherHome = !force && !olderOwn && existing !== void 0 ? otherHomeOfClaudeCodeEntry(existing, options) : void 0;
151027
+ const replace = otherHome !== void 0 && ask !== void 0 ? yes(await ask(`Claude Code already has a Shinjuku Shield entry for another wallet home (${otherHome}). Replace it with this one? [y/N] `)) : void 0;
151028
+ if (replace === false) {
151029
+ kept2.add(target);
151030
+ io.out(`Claude Code keeps the wallet in ${otherHome}; nothing was changed. To use this wallet there instead, do this step again and answer y.
151031
+ `);
151032
+ continue;
151033
+ }
151034
+ const replacedOther = replace === true && quiet(["mcp", "remove", "--scope", "user", MCP_SERVER_NAME]) === 0;
150929
151035
  let status = deps.runClaudeCli(claudeCodeArgs(options));
150930
- const removed = olderOwn || status !== 0 && force && quiet(["mcp", "remove", "--scope", "user", MCP_SERVER_NAME]) === 0;
151036
+ const removed = olderOwn || replacedOther || status !== 0 && force && quiet(["mcp", "remove", "--scope", "user", MCP_SERVER_NAME]) === 0;
150931
151037
  if (removed && status !== 0) status = deps.runClaudeCli(claudeCodeArgs(options));
150932
151038
  if (status === 0) done.add(target);
150933
- io.out(status === 0 ? `${removed ? "Replaced the wallet in" : "Added the wallet to"} Claude Code, for all your folders. ${allowWalletChecks(deps).say} ${RESTART_APP("Claude Code")}
151039
+ const homeNow = replacedOther ? ` Claude Code now uses the wallet in ${home}; the wallet in ${otherHome} is unchanged.` : "";
151040
+ io.out(status === 0 ? `${removed ? "Replaced the wallet in" : "Added the wallet to"} Claude Code, for all your folders.${homeNow} ${allowWalletChecks(deps).say} ${RESTART_APP("Claude Code")}
150934
151041
  ` : `Claude Code did not take the server; its message is above. Run this yourself:
150935
151042
  ${claudeCodeCommand(options)}
150936
151043
  ` + (removed ? `Setup removed the old ${MCP_SERVER_NAME} entry first, so Claude Code has none until that line runs.
@@ -150956,7 +151063,7 @@ function addToHosts(targets, home, deps, { force = false, npmVersion } = {}) {
150956
151063
  `);
150957
151064
  }
150958
151065
  }
150959
- return done;
151066
+ return { added: done, kept: kept2 };
150960
151067
  }
150961
151068
  function setupArgvOf(values) {
150962
151069
  const usdcNames = new Set(COMMAND_BY_NAME.get("setup").flags.filter((spec) => spec.usdc).map((spec) => spec.name));
@@ -151278,7 +151385,13 @@ Copy it to two offline places now; it is the only way to restore this wallet.
151278
151385
  `);
151279
151386
  const rerun = values.get(SETUP_RERUN) === "settings";
151280
151387
  const targets = values.get("--add-to") ?? [];
151281
- const added = targets.length ? addToHosts(targets, home, deps, { force: values.has("--force"), npmVersion }) : /* @__PURE__ */ new Set();
151388
+ const hosts = targets.length ? await addToHosts(
151389
+ targets,
151390
+ home,
151391
+ deps,
151392
+ { force: values.has("--force"), npmVersion, ask: interactive && deps.stdinIsTTY() ? deps.ask : void 0 }
151393
+ ) : void 0;
151394
+ const added = /* @__PURE__ */ new Set([...hosts?.added ?? [], ...hosts?.kept ?? []]);
151282
151395
  const config = setupConfigOf(home);
151283
151396
  if (readsBack) {
151284
151397
  await readBackHere(config, deps, values.get(RESTORED) ?? `Run setup again when the connection works; it continues there:
@@ -154449,7 +154562,7 @@ async function runBalance(args, values, deps, io) {
154449
154562
  await refreshed.prune();
154450
154563
  publicSince = Date.now();
154451
154564
  store = refreshed.store;
154452
- earlierDeposits = await (deps.adoptOneTime ?? adoptOneTimeForBalance)({ store, values, deps, io });
154565
+ if (deps.env[HOME_READ_ENV] !== "1") earlierDeposits = await (deps.adoptOneTime ?? adoptOneTimeForBalance)({ store, values, deps, io });
154453
154566
  } else {
154454
154567
  store = openPoolWallet(values, deps);
154455
154568
  }
@@ -154992,7 +155105,7 @@ function failureHint(command, reason, deps, values, floatError) {
154992
155105
  return "this payment settled, but in another transaction than the seller's receipt names; the wallet counts the settled one. Do not pay this request again";
154993
155106
  }
154994
155107
  if (/p03_quote_anchor_older_than_note_wait_next_epoch/.test(reason)) {
154995
- return `your newest deposit or change is newer than the newest pool anchor, so it cannot be spent yet. Nothing was signed, and the quote was dropped. pay, buy-item, pocket fill, and unshield wait for the next anchor by themselves (up to 5 minutes and 60 s); when that wait ran out, Shinjuku wrote no new anchor yet. Rerun the same command after the next pool anchor. To pay several times in a row without this wait, add funds as several deposits: ${cli2()} add-funds --route exchange --amount <usdc> --split <k> (${cli2()} help add-funds)`;
155108
+ return "your last payment or deposit is not ready to spend yet, and the wallet's wait for it ran out. Nothing was signed or paid. Run the same command again in a minute";
154996
155109
  }
154997
155110
  if (/p03_merchant_respend_(insufficient_recovered_value|accounts_split_merge_first)/.test(reason)) {
154998
155111
  const parts = runBalanceParts(values, deps);
@@ -155426,21 +155539,104 @@ Every command: ${cli2()} help
155426
155539
  `;
155427
155540
  }
155428
155541
  var bareAnimationPlayed = false;
155542
+ var HOME_READ_CAP_MS = 6e4;
155543
+ var HOME_READ_ENV = "SHINJUKU_WALLET_HOME_READ";
155544
+ function homeBalanceReadRun(argv2, env) {
155545
+ const words2 = ["balance", "--refresh", "--fresh"];
155546
+ if (runNetworkMode() !== null) {
155547
+ const proxy = childRunProxy(env);
155548
+ return { args: [...words2, ...proxy.args], env: { ...proxy.env, [HOME_READ_ENV]: "1" } };
155549
+ }
155550
+ const own = { ...env, [HOME_READ_ENV]: "1" };
155551
+ return { args: torArgv([...words2, ...argv2], own), env: own };
155552
+ }
155553
+ function startHomeBalanceRead(argv2, env) {
155554
+ if (cliEntry2 === void 0) return void 0;
155555
+ const run4 = homeBalanceReadRun(argv2, env);
155556
+ let child;
155557
+ try {
155558
+ child = spawn9(process.execPath, [...process.execArgv, cliEntry2, ...run4.args], { env: run4.env, stdio: "ignore", detached: true, windowsHide: true });
155559
+ } catch {
155560
+ return void 0;
155561
+ }
155562
+ child.unref();
155563
+ return new Promise((done) => {
155564
+ child.once("error", () => done(1));
155565
+ child.once("exit", (code2) => done(code2 ?? 1));
155566
+ });
155567
+ }
155568
+ function homeBalanceRead(data, argv2, deps, readData) {
155569
+ if (data?.setUp !== true || data.balance !== null || data.walletIssue || data.restoreUnread || data.setupFilesMissing) return void 0;
155570
+ const ended = deps.homeBalanceRead(argv2, deps.env);
155571
+ if (!ended) return void 0;
155572
+ const startedAt = deps.now();
155573
+ let current = { ...data, balanceRead: "reading" };
155574
+ let endCode;
155575
+ const watchers = /* @__PURE__ */ new Set();
155576
+ const show = (next) => {
155577
+ current = next;
155578
+ for (const watcher of watchers) watcher(next);
155579
+ };
155580
+ const reread = () => {
155581
+ let next;
155582
+ try {
155583
+ next = readData();
155584
+ } catch {
155585
+ next = { ...current, balance: null };
155586
+ }
155587
+ show(next?.balance !== null && next?.balance !== void 0 ? next : { ...next ?? current, balanceRead: endCode === 0 ? "done" : "failed" });
155588
+ };
155589
+ const cap = setTimeout(() => {
155590
+ if (endCode === void 0) show({ ...current, balanceRead: "slow" });
155591
+ }, HOME_READ_CAP_MS);
155592
+ cap.unref?.();
155593
+ const settled = ended.then((code2) => {
155594
+ endCode = code2;
155595
+ clearTimeout(cap);
155596
+ if (watchers.size) reread();
155597
+ });
155598
+ return {
155599
+ data: () => current,
155600
+ watch(watcher) {
155601
+ watchers.add(watcher);
155602
+ if (endCode !== void 0 && current.balanceRead === "reading") reread();
155603
+ return () => {
155604
+ watchers.delete(watcher);
155605
+ };
155606
+ },
155607
+ async finish(io) {
155608
+ const left = HOME_READ_CAP_MS - (deps.now() - startedAt);
155609
+ if (endCode !== void 0 || left <= 0) return;
155610
+ io.err(`Finishing the balance read first (at most ${Math.ceil(left / 1e3)} s)\u2026
155611
+ `);
155612
+ let timer;
155613
+ await Promise.race([settled, new Promise((done) => {
155614
+ timer = setTimeout(done, left);
155615
+ })]);
155616
+ clearTimeout(timer);
155617
+ }
155618
+ };
155619
+ }
155429
155620
  function defaultDependencies() {
155430
155621
  return {
155431
155622
  env: process.env,
155432
155623
  now: () => Date.now(),
155433
155624
  io: { out: (text3) => process.stdout.write(text3), err: (text3) => process.stderr.write(text3) },
155434
155625
  openAnimation: () => playOpenAnimation(),
155435
- /** The facts of the bare command's home screen (cli/homeData.mjs): local files only, never a network read. */
155626
+ /**
155627
+ * The facts of the bare command's home screen (cli/homeData.mjs), from local files only. The screen's one network
155628
+ * read is homeBalanceRead, a balance child run behind the run's Tor, only when these facts give no balance.
155629
+ */
155436
155630
  homeData: (options) => readHomeData(options),
155631
+ /** The home screen's balance read (startHomeBalanceRead): a promise of its exit code, or undefined when none starts. */
155632
+ homeBalanceRead: (argv2, env) => startHomeBalanceRead(argv2, env),
155437
155633
  /**
155438
155634
  * True when the bare command's home screen can draw: stdout and stdin a terminal, CI unset, TERM not dumb, a window it
155439
155635
  * fits. Only then are its facts read, so a pipe never opens the wallet.
155440
155636
  */
155441
155637
  homeScreenOn: (env) => homeScreenAllowed(process.stdout, env) && Boolean(process.stdin.isTTY),
155442
- /** The bare command's home screen (cli/homeScreen.mjs) for `data` (homeData): { outcome, key }. */
155443
- homeScreen: (data, env) => openHomeScreen({ env, data }),
155638
+ /** The bare command's home screen (cli/homeScreen.mjs) for `data` (homeData), redrawn on each `watch` change: { outcome, key }. */
155639
+ homeScreen: (data, env, watch) => openHomeScreen({ env, data, watch }),
155444
155640
  /**
155445
155641
  * The one job a home screen key runs, as the bin runs that typed command (bin/shinjuku-wallet.mjs): help text in this
155446
155642
  * process; any other command through torArgv and proxyEntry, so a home whose setup recorded Tor runs it behind Tor
@@ -155661,11 +155857,14 @@ async function runShinjukuWallet(argv2, overrides = {}) {
155661
155857
  if (name === void 0 || name === "help" || name === "--help" || name === "-h") {
155662
155858
  if (name === void 0) {
155663
155859
  const screenOn = deps.homeScreenOn(deps.env);
155664
- const data = screenOn ? deps.homeData({ env: deps.env, walletVersion: deps.walletVersion, torRun: homeJobTor(argv2, deps.env) }) : void 0;
155860
+ const torRun = homeJobTor(argv2, deps.env);
155861
+ const readData = () => deps.homeData({ env: deps.env, walletVersion: deps.walletVersion, torRun });
155862
+ const data = screenOn ? readData() : void 0;
155863
+ const balanceRead = screenOn ? homeBalanceRead(data, argv2, deps, readData) : void 0;
155665
155864
  const played = await deps.openAnimation();
155666
155865
  bareAnimationPlayed = true;
155667
155866
  if (played === "interrupted") return 130;
155668
- const screen = screenOn ? await deps.homeScreen(data, deps.env) : { outcome: "off" };
155867
+ const screen = screenOn ? await deps.homeScreen(balanceRead ? balanceRead.data() : data, deps.env, balanceRead?.watch) : { outcome: "off" };
155669
155868
  if (screen.outcome === "interrupted") return 130;
155670
155869
  if (screen.outcome === "quit") return 0;
155671
155870
  if (screen.outcome !== "key") {
@@ -155676,7 +155875,10 @@ async function runShinjukuWallet(argv2, overrides = {}) {
155676
155875
  io.out(overviewText());
155677
155876
  return 0;
155678
155877
  }
155679
- const job = (words3) => deps.runHomeJob([...words3, ...argv2], overrides);
155878
+ const job = async (words3) => {
155879
+ await balanceRead?.finish(io);
155880
+ return deps.runHomeJob([...words3, ...argv2], overrides);
155881
+ };
155680
155882
  if (screen.key === "s") return job(["setup"]);
155681
155883
  if (screen.key === "a") return job(["add-funds"]);
155682
155884
  if (screen.key === "u") return job(["unshield"]);
@@ -155711,7 +155913,9 @@ ${HOST_TARGETS.map((host, index) => ` ${index + 1} ${hostNames[host] ?? host}`
155711
155913
  }
155712
155914
  const home = setupHome(deps.env);
155713
155915
  const npmVersion = deps.walletVersion().npm;
155714
- if (addToHosts([host], home, deps, { npmVersion }).has(host)) {
155916
+ const hosts = await addToHosts([host], home, deps, { npmVersion, ask: deps.stdinIsTTY() ? deps.ask : void 0 });
155917
+ if (hosts.kept.has(host)) return 0;
155918
+ if (hosts.added.has(host)) {
155715
155919
  io.out(`Next: ask ${hostNames[host]} "what is my balance".
155716
155920
  `);
155717
155921
  return 0;