shinjuku-shielded 0.3.54 → 0.3.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/manifest.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schema": "carbon-shielded-wallet/bundle-manifest/v1",
3
3
  "file": "shinjuku-wallet.mjs",
4
- "sha256": "285d2c1db3cfd9d8a5df47acc2c70e7e65816539c3f2ad216ede6a16358d762b",
5
- "bytes": 12117554,
6
- "sourceCommit": "9df252eb8f4e71413bfcb8ff3ad289a96079e147",
4
+ "sha256": "70ff655b113c19d39c8594566c77d38fe7d459ea415f66cda1c09919841ad1a9",
5
+ "bytes": 12128720,
6
+ "sourceCommit": "d13f57d40a91eecbd9b06acd4f8c587d34bc256a",
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",
@@ -1282,5 +1282,5 @@
1282
1282
  "version": "1.8.0"
1283
1283
  }
1284
1284
  ],
1285
- "publishedAt": "2026-10-21T20:41:22.351Z"
1285
+ "publishedAt": "2026-10-21T23:25:11.303Z"
1286
1286
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shielded",
3
- "version": "0.3.54",
3
+ "version": "0.3.55",
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": "285d2c1d",
7
- "shinjukuSha256": "285d2c1db3cfd9d8a5df47acc2c70e7e65816539c3f2ad216ede6a16358d762b",
6
+ "shinjukuRelease": "70ff655b",
7
+ "shinjukuSha256": "70ff655b113c19d39c8594566c77d38fe7d459ea415f66cda1c09919841ad1a9",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
@@ -42692,7 +42692,7 @@ async function routeFacilitatorsOverOnion(argv2, {
42692
42692
  if (!groups.has(key)) groups.set(key, { onions });
42693
42693
  routes.set(origin, groups.get(key));
42694
42694
  routes.set(payHostOf(origin), groups.get(key));
42695
- const hosts = onions.map((onion) => `${new URL(onion).host.slice(0, 8)}\u2026`).join(", ");
42695
+ const hosts = onions.map((onion) => `${new URL(onion).host.slice(0, 8)}...`).join(", ");
42696
42696
  const source = pinned ? "pinned in this wallet" : `published by ${origin}`;
42697
42697
  err(`tor: ${origin} is reached over its onion service ${hosts} (${onions.length > 1 ? `${source}; the first to answer serves this run` : source}), so no Tor exit and no front such as Cloudflare sees these requests
42698
42698
  `);
@@ -42702,7 +42702,7 @@ async function routeFacilitatorsOverOnion(argv2, {
42702
42702
  }
42703
42703
  }
42704
42704
  for (const [origin, onions] of Object.entries(PINNED_HISTORY_ONIONS)) {
42705
- const group = { onions, path: HISTORY_PROBE_PATH, onNone: () => err(`tor: no history-proxy onion answered its start probe (${onions.length} tried); the pool history reads go to the pinned onion ${new URL(onions[0]).host.slice(0, 8)}\u2026 and wait for it
42705
+ const group = { onions, path: HISTORY_PROBE_PATH, onNone: () => err(`tor: no history-proxy onion answered its start probe (${onions.length} tried); the pool history reads go to the pinned onion ${new URL(onions[0]).host.slice(0, 8)}... and wait for it
42706
42706
  `) };
42707
42707
  groups.set(onions.join(" "), group);
42708
42708
  routes.set(origin, group);
@@ -42757,7 +42757,7 @@ async function routeFacilitatorsOverOnion(argv2, {
42757
42757
  }
42758
42758
  };
42759
42759
  }
42760
- var SITE_ONIONS, PINNED_FACILITATOR_ONIONS, HISTORY_ONIONS, PINNED_HISTORY_ONIONS, HISTORY_PROBE_PATH, FACILITATOR_FLAGS, V3_ONION, DISCOVERY_TIMEOUT_MS, WARM_TIMEOUT_MS, RACE_TIMEOUT_MS, routedGroups, routedOnions, routedOrigins, onionsOfOrigin, GLOBAL_DISPATCHER, onionDispatchers;
42760
+ var SITE_ONIONS, PINNED_FACILITATOR_ONIONS, HISTORY_ONIONS, PINNED_HISTORY_ONIONS, HISTORY_PROBE_PATH, FACILITATOR_FLAGS, V3_ONION, DISCOVERY_TIMEOUT_MS, WARM_TIMEOUT_MS, RACE_TIMEOUT_MS, routedGroups, routedOnions, routedOrigins, onionsOfOrigin, routesOverOnion, GLOBAL_DISPATCHER, onionDispatchers;
42761
42761
  var init_facilitatorOnion = __esm({
42762
42762
  "wallet/src/net/facilitatorOnion.mjs"() {
42763
42763
  "use strict";
@@ -42788,6 +42788,7 @@ var init_facilitatorOnion = __esm({
42788
42788
  routedOnions = () => [...new Set(routedGroups.flatMap((group) => group.winner ? [group.winner] : group.onions))];
42789
42789
  routedOrigins = /* @__PURE__ */ new Map();
42790
42790
  onionsOfOrigin = (origin) => routedOrigins.get(origin) ?? PINNED_FACILITATOR_ONIONS[origin] ?? [];
42791
+ routesOverOnion = (origin) => routedOrigins.has(origin);
42791
42792
  GLOBAL_DISPATCHER = /* @__PURE__ */ Symbol.for("undici.globalDispatcher.1");
42792
42793
  onionDispatchers = /* @__PURE__ */ new Map();
42793
42794
  }
@@ -47037,7 +47038,7 @@ async function proxyEntry(argv2, run5, { err = (text3) => process.stderr.write(t
47037
47038
  }
47038
47039
  return reexec(options.args, options.mode, options.proxy);
47039
47040
  }
47040
- 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, torRotations, torCircuitId, torRotationsRefused, pendingMove, inTorRun, isOnionOrigin, ProxyUsageError, isOnionHost, TOR_EXPERT_BUNDLE, TUNNEL_LINE, TOR_INSTALL, OS_NAMES, osKey, torNotRunningMessage, NOTICE, withRunTag, proxyEnv, pacedSoFar, RESENDABLE_METHOD, torCircuit, GLOBAL_DISPATCHER2, laneDispatchers, paymentWarmStop;
47041
+ 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, torRotations, torCircuitId, torRotationsRefused, pendingMove, inTorRun, runNetworkMode, isOnionOrigin, ProxyUsageError, isOnionHost, TOR_EXPERT_BUNDLE, TUNNEL_LINE, TOR_INSTALL, OS_NAMES, osKey, torNotRunningMessage, NOTICE, withRunTag, proxyEnv, pacedSoFar, RESENDABLE_METHOD, torCircuit, GLOBAL_DISPATCHER2, laneDispatchers, paymentWarmStop;
47041
47042
  var init_proxy = __esm({
47042
47043
  "wallet/src/net/proxy.mjs"() {
47043
47044
  "use strict";
@@ -47075,6 +47076,10 @@ var init_proxy = __esm({
47075
47076
  torCircuitId = 0;
47076
47077
  torRotationsRefused = false;
47077
47078
  inTorRun = () => String(process.env[MARK] ?? "").startsWith("tor ");
47079
+ runNetworkMode = () => {
47080
+ const mode = String(process.env[MARK] ?? "").split(" ")[0];
47081
+ return mode === "tor" || mode === "proxy" ? mode : null;
47082
+ };
47078
47083
  isOnionOrigin = (url) => inTorRun() && url?.protocol === "http:" && ONION_V3.test(url.hostname);
47079
47084
  ProxyUsageError = class extends Error {
47080
47085
  };
@@ -104623,7 +104628,8 @@ async function getRangeOnce(fetchImpl, url, start, end, label, { lane, signal, s
104623
104628
  if (at !== length) throw passing(fail26("setup_download_failed", `${label} answered a range of the wrong length; rerun setup: it keeps what it already checked`));
104624
104629
  return { bytes: bytes2, start, total };
104625
104630
  }
104626
- async function downloadSet({ fetchImpl, base, entries, budget, onFile }) {
104631
+ async function downloadSet({ fetchImpl, base, entries, budget, onFile, onBytes = () => {
104632
+ } }) {
104627
104633
  const stop3 = new AbortController();
104628
104634
  const files = entries.map((entry) => ({ entry, url: new URL(entry.path, base).href, label: `the proof-tool file ${entry.path}`, buffer: void 0, left: 0 }));
104629
104635
  const queue = files.map((file) => ({ file, start: 0, end: RANGE_BYTES - 1, head: true, attempts: 0 })).sort((x, y) => Number(y.file.entry.path.endsWith(".params")) - Number(x.file.entry.path.endsWith(".params")));
@@ -104660,6 +104666,7 @@ async function downloadSet({ fetchImpl, base, entries, budget, onFile }) {
104660
104666
  }
104661
104667
  if (answer.whole) {
104662
104668
  const bytes2 = await getBytes(fetchImpl, file.url, FILE_TIMEOUT_MS, file.label, LIMITS.file, budget, { lane: task.lane, stop: stop3.signal });
104669
+ onBytes(bytes2.length);
104663
104670
  await finish(file, bytes2);
104664
104671
  return;
104665
104672
  }
@@ -104669,6 +104676,7 @@ async function downloadSet({ fetchImpl, base, entries, budget, onFile }) {
104669
104676
  throw fail26("setup_download_failed", `${file.label} changed size during the download; rerun setup: it keeps what it already checked`);
104670
104677
  }
104671
104678
  answer.bytes.copy(file.buffer, answer.start);
104679
+ onBytes(answer.bytes.length);
104672
104680
  file.left -= 1;
104673
104681
  if (file.left === 0) await finish(file, file.buffer);
104674
104682
  };
@@ -104721,6 +104729,7 @@ async function downloadSet({ fetchImpl, base, entries, budget, onFile }) {
104721
104729
  }
104722
104730
  if (got) {
104723
104731
  got.copy(task.file.buffer, error.from);
104732
+ onBytes(got.length);
104724
104733
  task.start = Math.max(task.start, error.from + got.length);
104725
104734
  }
104726
104735
  if (!got || got.length < RANGE_FLOOR_BYTES) task.attempts += 1;
@@ -104760,6 +104769,8 @@ async function fetchRelease(facilitator, { fetchImpl = fetch } = {}) {
104760
104769
  if (!HEX643.test(String(tools.sumsSha256)) || !DIR2.test(String(tools.dir)) || tools.sumsSha256.slice(0, 8) !== tools.dir) {
104761
104770
  throw fail26("setup_release_invalid", "the release document names a proof-tool set whose folder is not the first 8 hex of its list hash");
104762
104771
  }
104772
+ const setBytes = Number(tools.bytes);
104773
+ if (Number.isSafeInteger(setBytes) && setBytes > 0 && setBytes <= LIMITS.total) releasedBytes.set(`${new URL(facilitator).origin} ${tools.sumsSha256}`, setBytes);
104763
104774
  return release;
104764
104775
  }
104765
104776
  function expectedProofTools(facilitator, trust, pins = PINNED_PROOF_TOOLS) {
@@ -104810,8 +104821,16 @@ function verifyToolsFolder(folder, sumsSha256) {
104810
104821
  return false;
104811
104822
  }
104812
104823
  }
104813
- async function installProofTools({ facilitator, toolsRoot, expected, fetchImpl = fetch, onProgress = () => {
104814
- } }) {
104824
+ async function installProofTools({
104825
+ facilitator,
104826
+ toolsRoot,
104827
+ expected,
104828
+ fetchImpl = fetch,
104829
+ onProgress = () => {
104830
+ },
104831
+ progressLine = (text3) => process.stderr.write(text3),
104832
+ progressEveryMs = PROGRESS_EVERY_MS
104833
+ }) {
104815
104834
  const { dir, sumsSha256 } = expected;
104816
104835
  if (!HEX643.test(String(sumsSha256)) || dir !== sumsSha256.slice(0, 8)) throw fail26("setup_release_invalid", "the expected proof-tool set is not a 64-hex list hash");
104817
104836
  const folder = join21(toolsRoot, dir);
@@ -104870,12 +104889,45 @@ async function installProofTools({ facilitator, toolsRoot, expected, fetchImpl =
104870
104889
  budget.left -= bytes2.length;
104871
104890
  }
104872
104891
  const keptPaths = new Set(kept.map(({ entry }) => entry.path));
104873
- await downloadSet({ fetchImpl, base, entries: entries.filter((entry) => !keptPaths.has(entry.path)), budget, onFile: (entry, bytes2) => {
104874
- write(entry, bytes2);
104875
- bytesDone += bytes2.length;
104876
- downloaded += 1;
104877
- onProgress({ filesDone: kept.length + downloaded, filesTotal: entries.length, bytesDone });
104878
- } });
104892
+ const missing = entries.filter((entry) => !keptPaths.has(entry.path));
104893
+ const keptBytes = kept.reduce((sum2, { bytes: bytes2 }) => sum2 + bytes2.length, 0);
104894
+ const named = releasedBytes.get(`${new URL(facilitator).origin} ${sumsSha256}`);
104895
+ const totalBytes = named !== void 0 && named >= keptBytes ? named : void 0;
104896
+ const torNote = inTorRun() ? "over Tor this takes a few minutes" : void 0;
104897
+ let arrived = 0;
104898
+ const say6 = (text3, notes) => {
104899
+ const shown = notes.filter(Boolean);
104900
+ try {
104901
+ progressLine(`downloading the proof tools: ${text3}${shown.length ? ` (${shown.join("; ")})` : ""}
104902
+ `);
104903
+ } catch {
104904
+ }
104905
+ };
104906
+ let ticker;
104907
+ if (missing.length > 0) {
104908
+ say6(
104909
+ `${totalBytes === void 0 ? "" : `${megabytes(totalBytes)} MB in `}${entries.length} files`,
104910
+ [kept.length ? `${kept.length} files, ${megabytes(keptBytes)} MB, already here from a stopped run` : void 0, torNote]
104911
+ );
104912
+ ticker = setInterval(() => {
104913
+ const done = keptBytes + arrived;
104914
+ const files = `${kept.length + downloaded} of ${entries.length} files`;
104915
+ say6(totalBytes !== void 0 ? `${megabytes(Math.min(done, totalBytes))} of ${megabytes(totalBytes)} MB, ${files}` : `${megabytes(done)} MB so far, ${files}`, [torNote]);
104916
+ }, progressEveryMs);
104917
+ ticker.unref?.();
104918
+ }
104919
+ try {
104920
+ await downloadSet({ fetchImpl, base, entries: missing, budget, onBytes: (count2) => {
104921
+ arrived += count2;
104922
+ }, onFile: (entry, bytes2) => {
104923
+ write(entry, bytes2);
104924
+ bytesDone += bytes2.length;
104925
+ downloaded += 1;
104926
+ onProgress({ filesDone: kept.length + downloaded, filesTotal: entries.length, bytesDone });
104927
+ } });
104928
+ } finally {
104929
+ clearInterval(ticker);
104930
+ }
104879
104931
  writeFileSync(join21(partial, "SHA256SUMS"), sums, { flag: "wx", mode: 384 });
104880
104932
  if (!verifyToolsFolder(partial, sumsSha256)) throw fail26("setup_file_hash_mismatch", "the downloaded proof-tools folder did not check out on disk; rerun setup");
104881
104933
  if (existsSync(folder)) rmSync(folder, { recursive: true, force: true });
@@ -104946,7 +104998,7 @@ async function refreshTrustedProofTools({ home, facilitator, pool: pool2, fetchI
104946
104998
  });
104947
104999
  return Object.freeze({ changed: true, dir: tools.dir, sumsSha256: tools.sumsSha256, downloaded: tools.downloaded });
104948
105000
  }
104949
- var PINNED_PROOF_TOOLS, UPDATE_NEXT, HEX643, DIR2, SUMS_LINE, MAX_FILES, LIMITS, RELEASE_TIMEOUT_MS, FILE_TIMEOUT_MS, DOWNLOAD_LANES, RANGE_BYTES, RANGE_TIMEOUT_MS, RANGE_STALL_MS, RANGE_FLOOR_BYTES, RANGE_ATTEMPTS, HEDGE_AFTER_MS, CONTENT_RANGE, REQUIRED_FILES, sha2569, fail26, SMALL_HEDGE_MS, SMALL_LANES, LOST, passing, localPath, executable, toolPaths;
105001
+ var PINNED_PROOF_TOOLS, UPDATE_NEXT, HEX643, DIR2, SUMS_LINE, MAX_FILES, LIMITS, RELEASE_TIMEOUT_MS, FILE_TIMEOUT_MS, DOWNLOAD_LANES, RANGE_BYTES, RANGE_TIMEOUT_MS, RANGE_STALL_MS, RANGE_FLOOR_BYTES, RANGE_ATTEMPTS, HEDGE_AFTER_MS, CONTENT_RANGE, PROGRESS_EVERY_MS, releasedBytes, megabytes, REQUIRED_FILES, sha2569, fail26, SMALL_HEDGE_MS, SMALL_LANES, LOST, passing, localPath, executable, toolPaths;
104950
105002
  var init_proofTools = __esm({
104951
105003
  "wallet/src/setup/proofTools.mjs"() {
104952
105004
  "use strict";
@@ -104977,6 +105029,9 @@ var init_proofTools = __esm({
104977
105029
  RANGE_ATTEMPTS = 8;
104978
105030
  HEDGE_AFTER_MS = 3e3;
104979
105031
  CONTENT_RANGE = /^bytes (\d+)-(\d+)\/(\d+)$/;
105032
+ PROGRESS_EVERY_MS = 1e4;
105033
+ releasedBytes = /* @__PURE__ */ new Map();
105034
+ megabytes = (bytes2) => Math.round(bytes2 / 1e6);
104980
105035
  REQUIRED_FILES = Object.freeze(["proof-tools.json", "exit-proof-tools.json", "public-profile.json"]);
104981
105036
  sha2569 = (bytes2) => createHash26("sha256").update(bytes2).digest("hex");
104982
105037
  fail26 = (code2, detail) => new Error(`${code2}: ${detail}`);
@@ -107669,6 +107724,7 @@ function addFundsHeldSay(entries, have = "") {
107669
107724
  let startMinutes = null;
107670
107725
  for (const entry of entries) {
107671
107726
  if (!entry || typeof entry !== "object") continue;
107727
+ if (entry.landed === true) continue;
107672
107728
  const amount = ATOMIC3.test(String(entry.heldAtomic ?? "")) ? BigInt(entry.heldAtomic) : 0n;
107673
107729
  const left = Number.isSafeInteger(entry.privateInMinutes) ? entry.privateInMinutes : null;
107674
107730
  if (entry.state === "shielding" && entry.startsAtNextRequest === true) {
@@ -107705,9 +107761,10 @@ function addFundsHeldSay(entries, have = "") {
107705
107761
  const lead = (text3) => have ? `${have}; ${text3}` : `${text3[0].toUpperCase()}${text3.slice(1)}`;
107706
107762
  const now = !nowUnknown ? `${usdcText(nowHeld)} USDC is becoming private now` : nowHeld > 0n ? `${usdcText(nowHeld)} USDC and an amount still being read are becoming private now` : "an amount still being read is becoming private now";
107707
107763
  const later = startMinutes !== null ? `within about 1 minute of your next wallet request${startMinutes > 0 ? ` after about ${minutesText(startMinutes)}` : ""}` : minutesKnown && minutes !== null ? `in about ${minutesText(minutes)}` : "in a few minutes";
107708
- if (nowAny && waiting > 0n) return `${lead(`${now}, and ${usdcText(waiting)} USDC more ${later}`)}.`;
107764
+ const askLater = startMinutes !== null && startMinutes > 0 ? `ask again in about ${minutesText(startMinutes)}, and it becomes private within about 1 minute of that request` : null;
107765
+ if (nowAny && waiting > 0n) return askLater ? `${lead(`${now}; ${usdcText(waiting)} USDC more arrived: ask again in about ${minutesText(startMinutes)} for it`)}.` : `${lead(`${now}, and ${usdcText(waiting)} USDC more ${later}`)}.`;
107709
107766
  if (nowAny) return `${lead(now)}. Nothing to do.`;
107710
- const arrived = `${usdcText(waiting)} USDC arrived and becomes private ${later === "in a few minutes" ? "within a few minutes of your next wallet request" : later}`;
107767
+ const arrived = askLater ? `${usdcText(waiting)} USDC arrived: ${askLater}` : `${usdcText(waiting)} USDC arrived and becomes private ${later === "in a few minutes" ? "within a few minutes of your next wallet request" : later}`;
107711
107768
  if (startMinutes !== null) return `${lead(arrived)}.`;
107712
107769
  if (!minutesKnown || minutes === null) {
107713
107770
  return have ? `${lead(arrived)}. Nothing to do.` : `${lead(arrived)}. A random wait hides the link to your deposit. Nothing to do.`;
@@ -107757,6 +107814,11 @@ function refreshErrorWords(code2) {
107757
107814
  function balanceSay(a) {
107758
107815
  const balance = usdcText(a.balanceAtomic);
107759
107816
  const pub = usdcText(a.unshieldedAtomic);
107817
+ const landing = usdcText(a.landingAtomic);
107818
+ if (positive(landing) && !a.refreshError) {
107819
+ const line = `${landing} USDC you added landed and is being counted. Ask again in about 30 seconds.`;
107820
+ return balance !== void 0 && a.refreshing !== true ? `You have ${balance} USDC private; ${line}` : line;
107821
+ }
107760
107822
  const arriving = addFundsHeldSay(a.addFunds);
107761
107823
  if (arriving) {
107762
107824
  const both = balance !== void 0 && a.refreshing !== true ? addFundsHeldSay(a.addFunds, `You have ${balance} USDC private`) : "";
@@ -107845,13 +107907,15 @@ function transferSay(a) {
107845
107907
  const about = usdcText(a.quote?.outAtomic);
107846
107908
  const usdc8 = sold ? `${sold} USDC` : about ? `about ${about} USDC` : "USDC";
107847
107909
  const to = str(a.to) || "the address";
107910
+ const later = "If this chat ends first, it continues at your next request.";
107848
107911
  switch (a.stage) {
107849
107912
  case "selling":
107850
- return `Selling the token for ${usdc8}. Then it becomes private and goes to ${to} by itself.`;
107913
+ return `Selling the token for ${usdc8}; then it goes privately to ${to}. ${later}`;
107914
+ // J-3: a shield that already landed is not "making it private now" (the resumed run is about to plan the send).
107851
107915
  case "shielding":
107852
- return `Sold for ${usdc8}. Making it private now; then it goes to ${to} by itself.`;
107916
+ return a.shield?.landed === true ? `Sold for ${usdc8} and made private; the send to ${to} starts now. ${later}` : `Sold for ${usdc8}. Making it private now; then it goes to ${to}. ${later}`;
107853
107917
  case "sending":
107854
- return `Sending ${usdc8} privately to ${to}. The address gets USDC, not the token.`;
107918
+ return `Sending ${usdc8} privately to ${to}. It gets USDC, not the token. ${later}`;
107855
107919
  case "sold_waiting_room":
107856
107920
  return `Sold for ${usdc8}, but it does not fit the 5 USDC private balance limit yet. Read next for what to do.`;
107857
107921
  case "done":
@@ -114048,8 +114112,11 @@ function transferTokenView(plan) {
114048
114112
  parts: plan.parts,
114049
114113
  maxDelayMinutes: plan.maxDelayMinutes,
114050
114114
  delivers: "USDC",
114051
- limit: "B receives USDC, not the token. Swap it at B yourself; the automatic swap at B is not built yet.",
114052
- privacy: "the total B receives equals the deposit, and the pool's crowd is small today: a longer max_delay_minutes hides better",
114115
+ // Real-user pass J-1 (2026-10-21): "B" is an internal name; the answer names the address the user gave.
114116
+ limit: "the address in to receives USDC, not the token. Swap it there yourself; an automatic swap there is not built yet.",
114117
+ // J-4: a plan exists only after the address's USDC account was read (tools.mjs beginTransfer refuses mcp_unshield_to_invalid before the sell).
114118
+ receiving: "the address in to already has a USDC account (checked before the sell), so it can receive this USDC",
114119
+ privacy: "the total the address in to receives equals the deposit, and the pool's crowd is small today: a longer max_delay_minutes hides better",
114053
114120
  ...plan.stopCode ? { stopCode: plan.stopCode, stopReason: plan.stopReason } : {},
114054
114121
  ...plan.state === "sold_waiting_room" && plan.waitCode ? { waitCode: plan.waitCode, waitReason: plan.waitReason } : {},
114055
114122
  ...plan.cancelRequested ? { cancelRequested: true } : {}
@@ -114496,7 +114563,7 @@ function safeHttpUrl(value) {
114496
114563
  }
114497
114564
  }
114498
114565
  function clip(text3, max) {
114499
- return text3.length > max ? `${text3.slice(0, max - 1)}\u2026` : text3;
114566
+ return text3.length > max ? `${text3.slice(0, max - 3)}...` : text3;
114500
114567
  }
114501
114568
  function cachePath(key, cacheDir) {
114502
114569
  return join27(cacheDir, `x402-discover-${createHash29("sha256").update(key).digest("hex").slice(0, 24)}.json`);
@@ -115470,7 +115537,7 @@ async function waitWithProgress(ms, progress3, message) {
115470
115537
  await sleep2(Math.min(1e4, left));
115471
115538
  }
115472
115539
  }
115473
- function waitOrProgress(promises, ms, { progress: progress3, message, everyMs = PROGRESS_EVERY_MS } = {}) {
115540
+ function waitOrProgress(promises, ms, { progress: progress3, message, everyMs = PROGRESS_EVERY_MS2 } = {}) {
115474
115541
  return new Promise((resolve38, reject) => {
115475
115542
  let timer;
115476
115543
  let beat;
@@ -116438,7 +116505,7 @@ function createWalletTools({
116438
116505
  return { ok: false, why: error?.timedOut === true ? "timeout" : "error" };
116439
116506
  }
116440
116507
  }
116441
- const progressEveryMs = Number.isSafeInteger(ops.progressEveryMs) && ops.progressEveryMs > 0 ? ops.progressEveryMs : PROGRESS_EVERY_MS;
116508
+ const progressEveryMs = Number.isSafeInteger(ops.progressEveryMs) && ops.progressEveryMs > 0 ? ops.progressEveryMs : PROGRESS_EVERY_MS2;
116442
116509
  const statusWait = (promises, ms, progress3, message) => waitOrProgress(promises, ms, { progress: progress3, message, everyMs: progressEveryMs });
116443
116510
  const notAllowedNext = (why, requestId, numbers, words2) => why === "timeout" ? `No answer yet. Ask again with request_id ${requestId} to ask the user again.` : `${limitNext(numbers, words2)} The user did not allow it once${why === "not_ticked" ? " (the box was not ticked)" : ""}.`;
116444
116511
  const cancels = /* @__PURE__ */ new Map();
@@ -117303,6 +117370,7 @@ function createWalletTools({
117303
117370
  status: 402,
117304
117371
  accepts,
117305
117372
  rail,
117373
+ ...networkFields(),
117306
117374
  caps: shownCaps({ ...limits, sessionLeftAtomic: sessionLeftShown(policy, (() => {
117307
117375
  try {
117308
117376
  return session.openStore();
@@ -117745,6 +117813,16 @@ ${canonical5}`;
117745
117813
  const line = `${say6} Its request_id is ${id}.`;
117746
117814
  return line.trim().split(/\s+/).length <= SAY_MAX_WORDS ? line : say6;
117747
117815
  };
117816
+ function networkFields() {
117817
+ const mode = runNetworkMode();
117818
+ let onion = false;
117819
+ try {
117820
+ onion = mode === "tor" && Boolean(session.memoServiceUrl) && routesOverOnion(new URL(session.memoServiceUrl).origin);
117821
+ } catch {
117822
+ onion = false;
117823
+ }
117824
+ return { network: mode === "tor" ? { mode: "tor", detail: `every request of this wallet goes through Tor (checked at start)${onion ? "; the facilitator is reached over its onion service, so no Tor exit sees those requests" : ""}` } : mode === "proxy" ? { mode: "proxy", detail: "every request of this wallet goes through the proxy in its launch line (--proxy); it is not checked as Tor" } : { mode: "direct", detail: "no Tor: the facilitator, the RPC provider and each seller see this computer's IP address; start the MCP with --tor (or run setup --prefer-tor) to hide it" } };
117825
+ }
117748
117826
  function sayFirst(fields, tool, autoId) {
117749
117827
  let say6 = sayFor(fields, { tool });
117750
117828
  if (autoId) say6 = withIdSentence(say6, autoId);
@@ -117993,11 +118071,13 @@ ${canonical5}`;
117993
118071
  const startAt = record.state === "open" ? watcher?.startAtOf(linkKindOf(record.id), record.id) ?? null : null;
117994
118072
  let shieldingAtomic = null;
117995
118073
  let depositSent = false;
118074
+ let landed = false;
117996
118075
  if (record.state === "shielding") {
117997
118076
  try {
117998
118077
  const deposit = store.readWal(`deposit-${record.depositId ?? payMeRoundDepositId(record.id, record.round ?? 1)}`);
117999
118078
  if (/^[1-9][0-9]*$/.test(String(deposit?.amountAtomic ?? ""))) shieldingAtomic = String(deposit.amountAtomic);
118000
- depositSent = DEPOSIT_SENT_STATES.has(deposit?.state);
118079
+ landed = depositLanded(deposit);
118080
+ depositSent = landed || DEPOSIT_SENT_STATES.has(deposit?.state);
118001
118081
  } catch {
118002
118082
  shieldingAtomic = null;
118003
118083
  }
@@ -118009,6 +118089,7 @@ ${canonical5}`;
118009
118089
  state: record.state,
118010
118090
  heldAtomic: held,
118011
118091
  ...record.state === "shielding" ? { shieldingAtomic } : {},
118092
+ ...landed ? { landed: true } : {},
118012
118093
  // The one number say and next name (real-user pass D1: say 6, privateInMinutes 5): the random wait left, plus
118013
118094
  // about a minute for the deposit itself; 0 once the wait is over (the deposit starts at the next request).
118014
118095
  ...startAt !== null ? { privateInMinutes: startAt <= Date.now() ? 0 : Math.ceil((startAt - Date.now()) / 6e4) + 1 } : {},
@@ -118019,10 +118100,58 @@ ${canonical5}`;
118019
118100
  };
118020
118101
  });
118021
118102
  }
118103
+ function finishSentDeposits(store) {
118104
+ if (!elsewhereEnabled || paysInFlight > 0 || moneyCallsInFlight > 0) return [];
118105
+ const passes = [];
118106
+ for (const record of listPayMe(store)) {
118107
+ if (record.state !== "shielding") continue;
118108
+ let deposit;
118109
+ try {
118110
+ deposit = store.readWal(`deposit-${record.depositId ?? payMeRoundDepositId(record.id, record.round ?? 1)}`);
118111
+ } catch {
118112
+ continue;
118113
+ }
118114
+ if (deposit?.schema !== DEPOSIT_WAL_SCHEMA || !DEPOSIT_FINISH_STATES.has(deposit.state)) continue;
118115
+ passes.push(payMePass(record.id).then((outcome) => {
118116
+ const round = outcome?.record?.round ?? 1;
118117
+ if ((outcome?.stage === "done" || outcome?.stage === "partly_shielded") && outcome.record?.doneAt && !payMeRefreshed.has(`${record.id}:${round}`)) {
118118
+ payMeRefreshed.add(`${record.id}:${round}`);
118119
+ afterShield({ requestId: record.id });
118120
+ log(`MCP_PAY_ME_DONE requestId=${record.id} by=balance`);
118121
+ }
118122
+ return outcome;
118123
+ }));
118124
+ }
118125
+ return passes;
118126
+ }
118127
+ function landedNotCounted(store) {
118128
+ let snapshot;
118129
+ try {
118130
+ snapshot = readBalanceSnapshot(store)?.snapshot;
118131
+ } catch {
118132
+ snapshot = void 0;
118133
+ }
118134
+ const walkedDigests = new Set((snapshot?.outputs ?? []).map((output2) => output2.digest));
118135
+ const finishedHere = new Set(listPayMe(store).filter((record) => record.state === "shielding").map((record) => `deposit-${record.depositId ?? payMeRoundDepositId(record.id, record.round ?? 1)}`));
118136
+ let total = 0n;
118137
+ for (const name of store.listWal()) {
118138
+ if (!name.startsWith("deposit-")) continue;
118139
+ let deposit;
118140
+ try {
118141
+ deposit = store.readWal(name);
118142
+ } catch {
118143
+ continue;
118144
+ }
118145
+ if (!depositLanded(deposit) || !/^[1-9][0-9]*$/.test(String(deposit.amountAtomic ?? ""))) continue;
118146
+ const landing = deposit.state === "submitted" ? finishedHere.has(name) && !walkedDigests.has(String(deposit.memoDigest ?? "").replace(/^0x/i, "").toLowerCase()) : snapshot !== void 0 && !["finalized", "published"].includes(snapshot.walStates?.[name]);
118147
+ if (landing) total += BigInt(deposit.amountAtomic);
118148
+ }
118149
+ return total;
118150
+ }
118022
118151
  const balance = {
118023
118152
  name: "wallet_balance",
118024
118153
  title: "Balance",
118025
- description: `Use this when the user asks "what is my balance" or "how much USDC do I have": it answers ONE balance (balanceAtomic, what x402_pay can spend) and the public (unshielded) USDC. Pays nothing and signs nothing. balanceAtomic is one number; breakdown names its parts (breakdown.shieldedAtomic is what wallet_unshield can send; privateSendPendingAtomic is the part of a running private send (wallet_unshield with parts or max_delay_minutes) not sent yet, already taken off shieldedAtomic; refundsInFlightAtomic is a Shinjuku refund on its way back; setAsideAtomic (only when not 0) is value an older version of this wallet set aside to pay sellers; during a pool move, pools lists each pool's part and movingAtomic is value on its way from the old pool to the new one; the move field shows the move, poolSwitches each switch). UNSHIELDED (unshieldedAtomic) is the plain public USDC on the wallet's own Solana key (ownAddress), the USDC wallet_shield makes private; unshieldedSolLamports is that key's SOL, information only: wallet_shield needs none (the facilitator's relayer pays every fee and rent). When the local balance is stale (a new deposit, a received payment), it reads the chain by itself first; when that takes longer than about 20 s it answers the last balance with refreshing: true and finishes in the background (call wallet_balance again in 30 s). A balance with refreshing: true and refreshReason is exact (payment_settling: a payment settles and the number counts it; account_join: the wallet joins its own balance parts and the number counts them; pool_move: value from the old pool arrives and the number counts it; first_check: a new wallet's first history check, the number is its own landed deposits). status reading (balanceAtomic null): the first balance read of a wallet that may hold money is not done; call wallet_balance again in 30 s. If the public read fails, unshieldedAtomic is null with unshieldedReason; the rest still answers. earlierDeposits (only when there are any): USDC found at a one-time deposit address of an earlier pool, each with a notice line to tell the user; the wallet moves it into the current pool by itself. addFunds (only when there are any): each wallet_add_funds address still open, with its state and the USDC known to wait there (heldAtomic; null with heldNote when the address was not checked yet). session shows the spending limits (null or "none": no limit), what is left, where the limits come from, and how to change them (session.summary). verbose: true adds \`internal\` (accounts, notes, and other engineering numbers); the default answer leaves them out. To add money, use wallet_add_funds. Amounts are atomic units (USDC: 1000000 = 1 USDC); say and usdc give them in USDC.`,
118154
+ description: `Use this when the user asks "what is my balance" or "how much USDC do I have": it answers ONE balance (balanceAtomic, what x402_pay can spend) and the public (unshielded) USDC. Pays nothing and signs nothing. balanceAtomic is one number; breakdown names its parts (breakdown.shieldedAtomic is what wallet_unshield can send; privateSendPendingAtomic is the part of a running private send (wallet_unshield with parts or max_delay_minutes) not sent yet, already taken off shieldedAtomic; refundsInFlightAtomic is a Shinjuku refund on its way back; setAsideAtomic (only when not 0) is value an older version of this wallet set aside to pay sellers; during a pool move, pools lists each pool's part and movingAtomic is value on its way from the old pool to the new one; the move field shows the move, poolSwitches each switch). UNSHIELDED (unshieldedAtomic) is the plain public USDC on the wallet's own Solana key (ownAddress), the USDC wallet_shield makes private; unshieldedSolLamports is that key's SOL, information only: wallet_shield needs none (the facilitator's relayer pays every fee and rent). When the local balance is stale (a new deposit, a received payment), it reads the chain by itself first; when that takes longer than about 20 s it answers the last balance with refreshing: true and finishes in the background (call wallet_balance again in 30 s). A balance with refreshing: true and refreshReason is exact (payment_settling: a payment settles and the number counts it; account_join: the wallet joins its own balance parts and the number counts them; pool_move: value from the old pool arrives and the number counts it; first_check: a new wallet's first history check, the number is its own landed deposits). status reading (balanceAtomic null): the first balance read of a wallet that may hold money is not done; call wallet_balance again in 30 s. If the public read fails, unshieldedAtomic is null with unshieldedReason; the rest still answers. earlierDeposits (only when there are any): USDC found at a one-time deposit address of an earlier pool, each with a notice line to tell the user; the wallet moves it into the current pool by itself. addFunds (only when there are any): each wallet_add_funds address still open, with its state and the USDC known to wait there (heldAtomic; null with heldNote when the address was not checked yet). landingAtomic (only when not 0): USDC you added whose deposit landed on chain and that balanceAtomic does not count yet; this call finishes it and waits about 20 s for it, past that it is being counted: call wallet_balance again in 30 s. session shows the spending limits (null or "none": no limit), what is left, where the limits come from, and how to change them (session.summary). verbose: true adds \`internal\` (accounts, notes, and other engineering numbers); the default answer leaves them out. To add money, use wallet_add_funds. Amounts are atomic units (USDC: 1000000 = 1 USDC); say and usdc give them in USDC.`,
118026
118155
  inputSchema: { type: "object", properties: {
118027
118156
  verbose: { type: "boolean", description: "Optional. true adds `internal`: the engineering numbers (funded accounts, notes, payments without waiting, a 0 set-aside value). Default false." }
118028
118157
  }, additionalProperties: false },
@@ -118047,6 +118176,24 @@ ${canonical5}`;
118047
118176
  const balanceStartedAt = Date.now();
118048
118177
  const publicRead = withTimeout((ops.readPublicBalance ?? readOwnPublicBalance)({ session, store }), PUBLIC_READ_TIMEOUT_MS).then((read2) => ({ read: read2 }), (error2) => ({ error: error2 }));
118049
118178
  const linkReads = readOpenLinkHeld(store);
118179
+ const finishing = finishSentDeposits(store);
118180
+ const finishDeadline = finishing.length ? balanceStartedAt + balanceRefreshWaitMs : void 0;
118181
+ if (finishing.length) {
118182
+ const finished = Promise.all(finishing).then((outcomes) => outcomes.some((outcome2) => outcome2?.stage === "done" || outcome2?.stage === "partly_shielded") ? refreshShared().then(() => "done") : "passed").catch(() => "failed");
118183
+ const outcome = await Promise.race([
118184
+ finished,
118185
+ new Promise((resolve38) => {
118186
+ const timer = setTimeout(() => resolve38("waiting"), Math.max(0, finishDeadline - Date.now()));
118187
+ timer.unref?.();
118188
+ })
118189
+ ]);
118190
+ if (outcome !== "waiting") {
118191
+ try {
118192
+ store = session.openStore();
118193
+ } catch {
118194
+ }
118195
+ }
118196
+ }
118050
118197
  let report = computeBalance({ store, snapshotRead: readBalanceSnapshot(store), source: "local", firstDepositEstimate: true });
118051
118198
  let refresh = {};
118052
118199
  const recentlyFailed = refreshFailedAt && Date.now() - refreshFailedAt < REFRESH_RETRY_MS && !refreshRun;
@@ -118067,7 +118214,7 @@ ${canonical5}`;
118067
118214
  const timer = setTimeout(() => resolve38("waiting"), ms);
118068
118215
  timer.unref?.();
118069
118216
  })]);
118070
- let outcome = await waitFor(longRead ? 0 : balanceRefreshWaitMs);
118217
+ let outcome = await waitFor(longRead ? 0 : finishDeadline === void 0 ? balanceRefreshWaitMs : Math.max(0, finishDeadline - Date.now()));
118071
118218
  if (outcome === "waiting" && !longRead) outcome = await waitFor(BALANCE_REFRESH_GRACE_MS);
118072
118219
  if (outcome === "done") {
118073
118220
  try {
@@ -118156,6 +118303,14 @@ ${canonical5}`;
118156
118303
  else if (one.balanceAtomic === "0" && shieldEnabled) {
118157
118304
  next = elsewhereEnabled ? `the shielded balance is 0, so x402_pay cannot pay yet. To add funds, call wallet_add_funds: it gives a one-time address; up to ${usdcText2(payMeRoundMax())} USDC sent there on Solana becomes private a few minutes after it arrives while this MCP server runs (or call wallet_add_funds with its request_id to finish it at once)` : `the shielded balance is 0, so x402_pay cannot pay yet. To add funds: send USDC on Solana to ownAddress (this wallet's own key), then call wallet_shield with amount (any amount from ${usdcText2(10000n)} to ${usdcText2(MAX_SHIELDED_BALANCE_ATOMIC)} USDC)`;
118158
118305
  } else next = "nothing to do; x402_pay pays from balanceAtomic, wallet_unshield sends from breakdown.shieldedAtomic";
118306
+ let landingAtomic = 0n;
118307
+ try {
118308
+ landingAtomic = landedNotCounted(store);
118309
+ } catch {
118310
+ landingAtomic = 0n;
118311
+ }
118312
+ const landedNext = landingAtomic > 0n && !refresh.refreshError ? `${usdcText2(landingAtomic)} USDC you added landed on chain and is being counted (landingAtomic, not in balanceAtomic yet): call wallet_balance again in ${POLL_SECONDS3} s; that call waits up to ${waitSeconds} s for it. Tell the user it needs no step from them` : void 0;
118313
+ if (landedNext) next = landedNext;
118159
118314
  const stalled = [...resumeRefusals].map(([requestId, error2]) => `"${requestId}" (${error2})`);
118160
118315
  if (stalled.length && one.breakdown?.privateSendPendingAtomic !== "0") {
118161
118316
  next = `${next}. A private send did not continue after this server started: ${stalled.join(", ")}. Its unsent parts (breakdown.privateSendPendingAtomic) stay off balanceAtomic: call wallet_unshield with its request_id and the same arguments to see why and run it, or stop them with wallet_cancel and its request_id`;
@@ -118213,16 +118368,16 @@ ${canonical5}`;
118213
118368
  } catch {
118214
118369
  sendWaiting = void 0;
118215
118370
  }
118216
- const arrivingAll = addFundsOpen.filter((link2) => link2.state === "shielding" || /^[1-9][0-9]*$/.test(String(link2.heldAtomic ?? "")));
118371
+ const arrivingAll = addFundsOpen.filter((link2) => link2.landed !== true && (link2.state === "shielding" || /^[1-9][0-9]*$/.test(String(link2.heldAtomic ?? ""))));
118217
118372
  const arrivingLine = (arriving) => {
118218
118373
  const amount = /^[1-9][0-9]*$/.test(String(arriving.shieldingAtomic ?? "")) ? `${usdcText2(arriving.shieldingAtomic)} USDC` : /^[1-9][0-9]*$/.test(String(arriving.heldAtomic ?? "")) ? `${usdcText2(arriving.heldAtomic)} USDC` : "USDC (an amount still being read)";
118219
- const when = arriving.state === "shielding" && !arriving.startsAtNextRequest ? "is becoming private now (its deposit transaction was sent; about 1 minute)" : arriving.startsInMinutes > 0 ? `becomes private within about 1 minute of the user's next wallet request after about ${minutesText2(arriving.startsInMinutes)} (a random wait hides the link to the deposit); no step for the user` : "becomes private: its deposit starts after this answer and takes about 1 minute; if this wallet process ends first, the next wallet request continues it";
118374
+ const when = arriving.state === "shielding" && !arriving.startsAtNextRequest ? "is becoming private now (its deposit transaction was sent; about 1 minute)" : arriving.startsInMinutes > 0 ? `waits about ${minutesText2(arriving.startsInMinutes)} (a random wait hides the link to the deposit); call wallet_balance again after that, and it becomes private within about 1 minute of that request; no step for the user` : "becomes private: its deposit starts after this answer and takes about 1 minute; if this wallet process ends first, the next wallet request continues it";
118220
118375
  return `${amount} arrived at add-funds address "${arriving.requestId}" and ${when}`;
118221
118376
  };
118222
118377
  if (arrivingAll.length === 1) {
118223
- next = `${arrivingLine(arrivingAll[0])}. Tell the user it needs no step from them; do not hurry it. If balanceAtomic does not show it 10 minutes after that, call wallet_add_funds with request_id "${arrivingAll[0].requestId}": it finishes it now. Do not ask for a new address`;
118378
+ next = `${landedNext ? `${landedNext}. ` : ""}${arrivingLine(arrivingAll[0])}. Tell the user it needs no step from them; do not hurry it. If balanceAtomic does not show it 10 minutes after that, call wallet_add_funds with request_id "${arrivingAll[0].requestId}": it finishes it now. Do not ask for a new address`;
118224
118379
  } else if (arrivingAll.length > 1) {
118225
- next = `${arrivingAll.map(arrivingLine).join("; ")}. Tell the user the total and that it needs no step from them; do not hurry it. If balanceAtomic does not show one 10 minutes after its time, call wallet_add_funds with its request_id: it finishes it now. Do not ask for a new address`;
118380
+ next = `${landedNext ? `${landedNext}. ` : ""}${arrivingAll.map(arrivingLine).join("; ")}. Tell the user the total and that it needs no step from them; do not hurry it. If balanceAtomic does not show one 10 minutes after its time, call wallet_add_funds with its request_id: it finishes it now. Do not ask for a new address`;
118226
118381
  }
118227
118382
  const earlierDeposits = (ops.oneTimeAdopt === false ? [] : adopter.outcomes()).map((outcome) => ({
118228
118383
  pool: outcome.pool,
@@ -118304,8 +118459,10 @@ ${canonical5}`;
118304
118459
  ...move ? { move } : {},
118305
118460
  ...openSwitches.length ? { poolSwitches: openSwitches } : {},
118306
118461
  ...addFundsOpen.length ? { addFunds: addFundsOpen } : {},
118462
+ ...landingAtomic > 0n ? { landingAtomic: landingAtomic.toString() } : {},
118307
118463
  ...sendWaiting ? { sendWaiting } : {},
118308
118464
  ...notices,
118465
+ ...networkFields(),
118309
118466
  session: {
118310
118467
  ...shownCaps({ ...policy.limits, sessionLeftAtomic: sessionLeft }),
118311
118468
  sessionSpentAtomic: policy.sessionSpentAtomic,
@@ -120851,6 +121008,7 @@ ${canonical5}`;
120851
121008
  id,
120852
121009
  canShield: elsewhereEnabled,
120853
121010
  allowance: async ({ record }) => payMeRoom(record),
121011
+ ...ops.payMeSteps ?? {},
120854
121012
  // Counted against --max-shield once per round, when it is proved and about to be submitted (as a deposit from elsewhere is).
120855
121013
  onShield: ({ amountAtomic, round }) => {
120856
121014
  const key = `${id}:${round}`;
@@ -121502,10 +121660,23 @@ ${canonical5}`;
121502
121660
  }
121503
121661
  const code2 = transferCodes.get(plan.id);
121504
121662
  const poll = `call wallet_transfer_token_status with request_id "${plan.id}"`;
121663
+ let landed = false;
121664
+ if (view2.stage === "shielding") {
121665
+ try {
121666
+ landed = ["finalized", "published"].includes(session.openStore().readWal(`deposit-${plan.depositId}`)?.state);
121667
+ } catch {
121668
+ landed = false;
121669
+ }
121670
+ if (landed) view2.shield = { ...view2.shield, landed: true };
121671
+ }
121672
+ const kept = "; if the host ends this server first, the next start of the wallet continues it (nothing runs twice)";
121505
121673
  const next = {
121506
- selling: `${poll} in ${POLL_SECONDS3} s: the sell lands in about 15 s and then the shield starts at the next poll`,
121507
- shielding: `${poll} in ${POLL_SECONDS3} s: the deposit takes a few minutes, then the private send starts at the next poll`,
121508
- sending: `${poll} in ${POLL_SECONDS3} s; the parts leave after random waits of up to ${plan.maxDelayMinutes} min each. Tell the user the address receives USDC, not the token`,
121674
+ selling: `${poll} in ${POLL_SECONDS3} s: the sell lands in about 15 s and then the shield starts at the next poll${kept}`,
121675
+ shielding: landed ? `${poll} in 10 s: the deposit landed and the private send starts now${kept}` : `${poll} in ${POLL_SECONDS3} s: the deposit takes a few minutes, then the private send starts at the next poll${kept}`,
121676
+ // J-5 (2026-10-21): a part whose time passed while no wallet server ran draws a new wait of up to the delay from the next
121677
+ // start (privateSend.mjs runPrivateSend late redraw: its leave time never shows when the wallet came back), so the
121678
+ // "up to N min" counts from the last start, not from the call.
121679
+ sending: `${poll} in ${POLL_SECONDS3} s; the parts leave after random waits of up to ${plan.maxDelayMinutes} min each${kept}. A part whose time passed while the wallet did not run waits again, up to ${plan.maxDelayMinutes} min from the next start, so its leave time does not show when the wallet came back: tell the user the time counts while the wallet runs. Tell the user the address receives USDC, not the token`,
121509
121680
  sold_waiting_room: `the sold USDC (${/^\d+$/.test(String(plan.sell?.usdcAtomic ?? "")) ? usdcText2(plan.sell.usdcAtomic) : "an unknown amount of"} USDC) no longer fits the 5 USDC shielded balance cap, so it waits on the wallet's own key and nothing was shielded. Spend some with x402_pay or send some out with wallet_unshield, then ${poll}; or cancel with action "cancel" and keep the USDC on the own key`,
121510
121681
  done: "the transfer is done: tell the user the address received USDC, not the token (swap it there); wallet_balance shows the shielded balance",
121511
121682
  cancelled: (() => {
@@ -122912,7 +123083,7 @@ function createSetupTools({
122912
123083
  };
122913
123084
  return { tools: [setupTool, statusTool, ...MCP_TOOL_NAMES.map((name) => name === "x402_discover" ? discoverTool(discoverCacheDir) : notSetUp(name))], doneTool };
122914
123085
  }
122915
- var import_web3132, 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, 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, MONEY_FIRST_ANSWER_MS, JOIN_CALL_MAX_MS, JOIN_STEP_MS, sleep2, PROGRESS_EVERY_MS, READ_BUDGET_MS, UNPAID_TOR_TRY_MS, UNPAID_CLEAR_MS, READ_BUDGET_TOOLS, FLOAT_BUSY_CODES, RETRY_MARGIN_MS, ASK_PAY_CODES, limitNext, SAFE_TEXT, MCP_TOOL_ALIASES, POOL_SWITCH_WHY, POOL_SWITCH_NEXT, SWITCH_STATES, quietSwitch, switchKey, ATOMIC_TEXT, switchAmount, switchEta, switchRows, OWNER_CAP_CODES, JOIN_END_MCP, JOIN_END_TERMINAL_LIBRARY, JOIN_RETRY_BACKOFF_MS, WALLET_CAP_FIX, capFix, ownerOnlyNext, mcpNext, CONFIDENTIAL_RETIRED, json2, answerResult, discoverTool, refuse11, publicOffer, FLOAT_PAID_CODES, floatStillSettling, MONEY_ID2, toRecordId, linkKindOf, moneyId, REFUSED_CODES, refusedCodeOf, PLAIN_REASONS, plainReason, KEPT_SEND_TRIES, shownId, isSplitSend, sendPartId, PROMISED_NEXT, PUBLIC_READ_TIMEOUT_MS, withTimeout, ownKey, readFailure, elsewhereId, AGENT_ID_SCHEMA, agentIdName, minutesText2, DEPOSIT_SENT_STATES, usdcText2, walkWalletHistory, REFRESH_LEASE_WALK_MS, HOME_MARKS_SETTLED_MS2, AUTO_BACKUPS_KEPT, AUTO_BACKUP_NAME, AUTO_BACKUP_VERIFIED, FRONTED_SETTLE_MARGIN_SLOTS, BALANCE_REFRESH_WAIT_MS, BALANCE_REFRESH_GRACE_MS, BALANCE_REFRESH_LONG_MS, INBOUND_REFRESH_AGE_MS, 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, SWITCH_WAIT_MS, UNSHIELD_TO_NO_USDC_DETAIL, UNSHIELD_TO_INVALID_NEXT, INFERENCE_MIN_MAX_TOKENS, ELICIT_TIMEOUT_MS, ELICIT_UNSHOWN_MS, elicitUnshown, POLL_SECONDS3, TEMP_CLEAN_DELAY_MS, KEPT_ON_DISK, MAX_SHIELDED_BALANCE_ATOMIC, receiveGenerator, MCP_TOOL_NAMES, MCP_HIDDEN_TOOL_NAMES, SETUP_TERMINAL_NEXT, SETUP_POLL_NEXT, PRIVATE_INFERENCE_WHY, PRIVATE_INFERENCE_NEXT;
123086
+ var import_web3132, 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, 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, MONEY_FIRST_ANSWER_MS, JOIN_CALL_MAX_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, limitNext, SAFE_TEXT, MCP_TOOL_ALIASES, POOL_SWITCH_WHY, POOL_SWITCH_NEXT, SWITCH_STATES, quietSwitch, switchKey, ATOMIC_TEXT, switchAmount, switchEta, switchRows, OWNER_CAP_CODES, JOIN_END_MCP, JOIN_END_TERMINAL_LIBRARY, JOIN_RETRY_BACKOFF_MS, WALLET_CAP_FIX, capFix, ownerOnlyNext, mcpNext, CONFIDENTIAL_RETIRED, json2, answerResult, discoverTool, refuse11, publicOffer, FLOAT_PAID_CODES, floatStillSettling, MONEY_ID2, toRecordId, linkKindOf, moneyId, REFUSED_CODES, refusedCodeOf, PLAIN_REASONS, plainReason, KEPT_SEND_TRIES, shownId, isSplitSend, sendPartId, PROMISED_NEXT, PUBLIC_READ_TIMEOUT_MS, withTimeout, ownKey, readFailure, elsewhereId, AGENT_ID_SCHEMA, agentIdName, minutesText2, DEPOSIT_SENT_STATES, depositLanded, DEPOSIT_FINISH_STATES, usdcText2, walkWalletHistory, REFRESH_LEASE_WALK_MS, HOME_MARKS_SETTLED_MS2, AUTO_BACKUPS_KEPT, AUTO_BACKUP_NAME, AUTO_BACKUP_VERIFIED, FRONTED_SETTLE_MARGIN_SLOTS, BALANCE_REFRESH_WAIT_MS, BALANCE_REFRESH_GRACE_MS, BALANCE_REFRESH_LONG_MS, INBOUND_REFRESH_AGE_MS, 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, SWITCH_WAIT_MS, UNSHIELD_TO_NO_USDC_DETAIL, UNSHIELD_TO_INVALID_NEXT, INFERENCE_MIN_MAX_TOKENS, ELICIT_TIMEOUT_MS, ELICIT_UNSHOWN_MS, elicitUnshown, POLL_SECONDS3, TEMP_CLEAN_DELAY_MS, KEPT_ON_DISK, MAX_SHIELDED_BALANCE_ATOMIC, receiveGenerator, MCP_TOOL_NAMES, MCP_HIDDEN_TOOL_NAMES, SETUP_TERMINAL_NEXT, SETUP_POLL_NEXT, PRIVATE_INFERENCE_WHY, PRIVATE_INFERENCE_NEXT;
122916
123087
  var init_tools = __esm({
122917
123088
  "wallet/src/mcp/tools.mjs"() {
122918
123089
  "use strict";
@@ -122936,6 +123107,7 @@ var init_tools = __esm({
122936
123107
  init_processLease();
122937
123108
  init_store();
122938
123109
  init_proxy();
123110
+ init_facilitatorOnion();
122939
123111
  init_book();
122940
123112
  init_errorLine();
122941
123113
  init_spendPolicy();
@@ -123024,7 +123196,7 @@ var init_tools = __esm({
123024
123196
  const timer = setTimeout(resolve38, Math.max(0, ms));
123025
123197
  timer.unref?.();
123026
123198
  });
123027
- PROGRESS_EVERY_MS = 1e4;
123199
+ PROGRESS_EVERY_MS2 = 1e4;
123028
123200
  READ_BUDGET_MS = 3e4;
123029
123201
  UNPAID_TOR_TRY_MS = 12e3;
123030
123202
  UNPAID_CLEAR_MS = 25e3;
@@ -123218,6 +123390,8 @@ var init_tools = __esm({
123218
123390
  agentIdName = (kind, recordId2) => `mcp-agent-id-${kind}-${recordId2}`;
123219
123391
  minutesText2 = (n) => `${n} minute${Number(n) === 1 ? "" : "s"}`;
123220
123392
  DEPOSIT_SENT_STATES = /* @__PURE__ */ new Set(["signed", "finalized", "published"]);
123393
+ depositLanded = (deposit) => deposit?.schema === DEPOSIT_WAL_SCHEMA && (["finalized", "published"].includes(deposit.state) || deposit.state === "submitted" && typeof deposit.relay?.result?.signature === "string" && deposit.relay.result.signature.length > 0);
123394
+ DEPOSIT_FINISH_STATES = /* @__PURE__ */ new Set(["signed", "submitted", "finalized", "published"]);
123221
123395
  usdcText2 = (atomic4, decimals = 6) => {
123222
123396
  const text3 = formatAtomic(BigInt(atomic4), decimals);
123223
123397
  return text3.includes(".") ? text3.replace(/0+$/, "").replace(/\.$/, "") : text3;
@@ -150706,9 +150880,11 @@ async function runSetupCommand(values, deps) {
150706
150880
  }
150707
150881
  const home = setupHome(deps.env);
150708
150882
  const interactive = !values.has("--yes");
150883
+ const torRunRecords = !values.has("--prefer-tor") && !values.has("--no-tor") && childRunProxy(deps.env).args[0] === "--tor";
150709
150884
  if (interactive && !deps.stdinIsTTY()) {
150885
+ const line = ["setup", "--yes", ...setupArgvOf(values), ...childRunProxy(deps.env).args].map(shellWord).join(" ");
150710
150886
  throw new UsageError(
150711
- "setup asks its questions in a terminal; in a script or a pipe pass --yes (with any of --max-payment, --max-session, --max-cumulative, --prefer-tor, --private-inference)",
150887
+ `setup asks its questions in a terminal; in a script or a pipe pass --yes: npx -y shinjuku-shielded ${line} (add any of --max-payment, --max-session, --max-cumulative, --prefer-tor, --private-inference)`,
150712
150888
  COMMAND_BY_NAME.get("setup")
150713
150889
  );
150714
150890
  }
@@ -150736,7 +150912,7 @@ async function runSetupCommand(values, deps) {
150736
150912
  ...values.has("--max-cumulative") ? { maxTotal: values.get("--max-cumulative") } : {},
150737
150913
  ...values.has("--max-shield") ? { maxShield: values.get("--max-shield") } : {},
150738
150914
  ...values.has("--max-unshield") ? { maxUnshield: values.get("--max-unshield") } : {},
150739
- ...values.has("--prefer-tor") ? { tor: true } : {},
150915
+ ...values.has("--prefer-tor") || torRunRecords ? { tor: true } : {},
150740
150916
  ...values.has("--passphrase-file") ? { passphraseFile: values.get("--passphrase-file") } : {},
150741
150917
  ...values.has("--facilitator") ? { facilitator: values.get("--facilitator") } : {},
150742
150918
  ...values.has("--trust-proof-tools") ? { trustProofTools: values.get("--trust-proof-tools") } : {}
@@ -150747,7 +150923,7 @@ async function runSetupCommand(values, deps) {
150747
150923
  io.out(`No spending limit is set. To add one later: ${CLI2} limits --max-payment <usdc> --max-cumulative <usdc> (in USDC, like 0.05; --max-payment caps one payment, --max-cumulative all payments together). A limit for any 24 hours: --max-session <usdc> in the MCP entry of your agent app.
150748
150924
  `);
150749
150925
  }
150750
- if (!values.has("--prefer-tor")) {
150926
+ if (!values.has("--prefer-tor") && !torRunRecords) {
150751
150927
  const tor = String(await deps.ask("Send every request through Tor (needs Tor running with HTTPTunnelPort 127.0.0.1:9080)? [y/N] ")).trim();
150752
150928
  options.tor = /^y(es)?$/i.test(tor);
150753
150929
  }
@@ -150757,8 +150933,10 @@ async function runSetupCommand(values, deps) {
150757
150933
  }
150758
150934
  }
150759
150935
  const inference = inferenceRecordOf(old, privateInference);
150760
- if (privateInference === true && options.tor !== true && old?.tor !== true) {
150936
+ if (privateInference === true && (options.tor !== true || torRunRecords) && old?.tor !== true) {
150761
150937
  io.out('Private inference needs Tor, so setup records Tor for mcp (Tor must run with HTTPTunnelPort 127.0.0.1:9080), and the commands below use --tor. To turn both off later: set "tor": false and "inference": { "enabled": false } in config.json (mcp --no-tor skips Tor for one run).\n\n');
150938
+ } else if (torRunRecords && old?.tor !== true) {
150939
+ io.out('Tor is recorded for mcp: this setup runs with --tor, so config.json gets "tor": true and the MCP runs behind Tor too (Tor must run with HTTPTunnelPort 127.0.0.1:9080; mcp --no-tor skips it for one run).\n\n');
150762
150940
  }
150763
150941
  if (behindTor(options.tor === true || privateInference === true || options.tor === void 0 && old?.tor === true)) {
150764
150942
  io.out("Setup continues behind Tor: the release, the proof tools, and the RPC requests go over Tor.\n");
@@ -151535,8 +151713,8 @@ var COMMANDS = [
151535
151713
  flag2("--max-total", "<usdc>", "the older name of --max-cumulative", { check: "atomic", usdc: true, hidden: true }),
151536
151714
  flag2("--max-shield", "<usdc>", "optional: the mcp --max-shield cap, in USDC (one wallet_shield deposit, and this wallet in any 24 hours)", { check: "atomic", usdc: true }),
151537
151715
  flag2("--max-unshield", "<usdc>", "optional: the mcp --max-unshield cap, in USDC (one wallet_unshield send, and this wallet in any 24 hours)", { check: "atomic", usdc: true }),
151538
- bool2("--prefer-tor", "record Tor for mcp: it then runs behind Tor (needs Tor with HTTPTunnelPort 127.0.0.1:9080); mcp --no-tor skips it for one run. Setup itself then runs behind Tor too"),
151539
- bool2("--no-tor", "run this setup without Tor although Tor is recorded or asked for (the facilitator then sees this machine's IP address download the release and the proof tools); config.json still records Tor"),
151716
+ bool2("--prefer-tor", "record Tor for mcp: it then runs behind Tor (needs Tor with HTTPTunnelPort 127.0.0.1:9080); mcp --no-tor skips it for one run. Setup itself then runs behind Tor too. setup --tor does the same: it runs setup behind Tor and records Tor for mcp"),
151717
+ bool2("--no-tor", "run this setup without Tor although Tor is recorded or asked for (the facilitator then sees this machine's IP address download the release and the proof tools); config.json still records Tor. With --tor it only stops --tor from recording Tor (the run itself still goes over Tor)"),
151540
151718
  bool2("--private-inference", 'answer yes to "Set your LLM to private inference now?": your agent buys AI answers from usePod, paid from your shielded balance over Tor. Records inference.enabled and Tor (as --prefer-tor) in config.json, and prints the path to the first private call. Without it, --yes keeps what config.json records'),
151541
151719
  flag2("--passphrase-file", "<file>", "your own passphrase file (8 or more characters); default: setup writes a new random passphrase to <home>/passphrase.txt, readable by you alone. Needed for a wallet that exists already"),
151542
151720
  flag2("--facilitator", "<https origin>", `the facilitator whose release, pool, and proof tools setup uses; default ${GUIDE.facilitator}`, { check: "origin" }),
@@ -151551,7 +151729,7 @@ var COMMANDS = [
151551
151729
  ],
151552
151730
  notes: [
151553
151731
  "npx -y shinjuku-shielded setup runs the same command. Without --yes it asks in the terminal: the caps (Enter keeps the default), Tor, and private inference.",
151554
- "When Tor is recorded or asked for (a yes, --prefer-tor, or --private-inference), setup itself runs behind Tor: the release, the proof-tool downloads, and the RPC requests go over Tor, so the facilitator never sees this machine's IP address make the wallet. Tor must run first (torrc: HTTPTunnelPort 127.0.0.1:9080); --no-tor runs it without Tor.",
151732
+ `When Tor is recorded or asked for (a yes, --tor, --prefer-tor, or --private-inference), setup itself runs behind Tor: the release, the proof-tool downloads, and the RPC requests go over Tor, so the facilitator never sees this machine's IP address make the wallet. config.json then records Tor for mcp ("tor": true), so the MCP runs behind Tor too. Tor must run first (torrc: HTTPTunnelPort 127.0.0.1:9080); --no-tor runs it without Tor.`,
151555
151733
  `Private inference (a yes, or --private-inference): ${PRIVATE_INFERENCE_WHY} Setup records Tor, then prints five steps with real commands: add-funds (it shields the USDC), the first call (${CLI2} infer - --model qwen3-8-flash ... --tor, about 0.0011 USDC = 1100 atomic; its --max-payment must cover the 10000 = 0.01 USDC ceiling plus the fee), infer serve for an OpenAI client, and an MCP restart for inference_chat. In a terminal it then offers to run add-funds; it never pays an inference call.`,
151556
151734
  "The wallet home is SHIELDED_WALLET_HOME, default ~/.carbon-shielded-wallet (every command of this CLI uses it). setup writes there: config.json, passphrase.txt (mode 0600; on Windows an access list of your account alone), the wallet (<pool>/wallet.json, made by init with the production pool, program, network, and USDC), a recovery file recovery-<time>.txt, and proof-tools/<dir>/.",
151557
151735
  "The passphrase is never printed and no MCP tool takes or returns it. The recovery file holds the seed and the key: copy it to two offline places, then delete it from this machine.",