shinjuku-shield 0.3.110 → 0.3.112

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.110. The npm packages `shinjuku-shield` and
9
+ This is version 0.3.112. 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.110
15
+ npm install -g shinjuku@0.3.112
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.110 setup`.
21
+ installing, type `npx shinjuku@0.3.112 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.110 mcp
39
+ claude mcp add --scope user shinjuku -- npx -y --no-audit --no-fund shinjuku-shield@0.3.112 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.110", "mcp"] } } }
45
+ { "mcpServers": { "shinjuku": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.112", "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": "f6ab1c4098bf84c9d5af4dbbcbf9aa077430dd347aa14912fbaf571741bd314a",
5
- "bytes": 8332382,
6
- "sourceCommit": "f296b3b0e23b608d4bac4428a0e5763ba8313414",
4
+ "sha256": "1406c0d44399772d43e33dc6b4cfb7428f9f24215e13389ac46f5dd6dc28d224",
5
+ "bytes": 8331849,
6
+ "sourceCommit": "c46e085c7ef73488d9a62c58b19f13891a4724b5",
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-27T14:09:40.228Z"
1040
+ "publishedAt": "2026-10-27T15:32:15.515Z"
1041
1041
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shield",
3
- "version": "0.3.110",
3
+ "version": "0.3.112",
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": "f6ab1c40",
7
- "shinjukuSha256": "f6ab1c4098bf84c9d5af4dbbcbf9aa077430dd347aa14912fbaf571741bd314a",
6
+ "shinjukuRelease": "1406c0d4",
7
+ "shinjukuSha256": "1406c0d44399772d43e33dc6b4cfb7428f9f24215e13389ac46f5dd6dc28d224",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-shield#readme",
@@ -119435,16 +119435,15 @@ function plainSentence(next, room) {
119435
119435
  }
119436
119436
  return "";
119437
119437
  }
119438
- function heldBySendSay(sends, verb) {
119439
- const send3 = Array.isArray(sends) ? sends.find((one) => typeof one?.requestId === "string") : void 0;
119440
- return send3 ? `Nothing was ${verb}. Your private send ${send3.requestId} set this money aside for its next payment; stop that send to pay now, or add money.` : `Nothing was ${verb}. A private send in progress set this money aside. Stop that send to pay now, or add money.`;
119438
+ function heldBySendSay(verb) {
119439
+ return `Nothing was ${verb}. A private send in progress set this money aside. Stop that send to pay now, or add money.`;
119441
119440
  }
119442
119441
  function refusalSay(a, tool, again, npmVersion) {
119443
119442
  const code2 = str(a.error);
119444
119443
  const detail = str(a.detail);
119445
119444
  const next = str(a.next);
119446
119445
  const moneyVerb = MOVE_CAP_CODES.has(code2) || tool === "wallet_shield" || tool === "wallet_unshield" ? "moved" : "paid";
119447
- const ask = typeof a.requestId === "string" && a.requestId ? `Ask again with request_id ${a.requestId}` : "Ask again with the same request_id";
119446
+ const ask = "Ask again";
119448
119447
  if (/_refund_pending$/.test(code2)) {
119449
119448
  const total2 = usdcText2(a.refundAtomic ?? a.paidAtomic);
119450
119449
  const when = Number.isSafeInteger(a.refundEtaSeconds) && a.refundEtaSeconds > 0 ? ` within about ${minutesText(Math.ceil(a.refundEtaSeconds / 60))}` : "";
@@ -119461,13 +119460,13 @@ function refusalSay(a, tool, again, npmVersion) {
119461
119460
  if (a.refundArrived === true) return `The seller did not take it. ${yours} is back in your private balance. ${other}`;
119462
119461
  return `The seller did not take it; ${total2 ? `${total2} USDC` : "your money"} comes back by itself${when}. ${other}`;
119463
119462
  }
119463
+ const askAgain = "Asking again pays again.";
119464
119464
  if (code2 === "p03_float_receipt_pruned" && a.conversationNotKept === true) {
119465
- return "Paid once, not again. The wallet keeps no copy of a private conversation, so the answer cannot come back. A new request_id pays again.";
119465
+ return `Paid once, not again: the wallet keeps no copy of a private conversation. ${askAgain}`;
119466
119466
  }
119467
- if (code2 === "p03_float_receipt_pruned") return "This request was paid more than 10 minutes ago. A new request_id pays again.";
119467
+ if (code2 === "p03_float_receipt_pruned") return `This request was paid more than 10 minutes ago. ${askAgain}`;
119468
119468
  if (/^p03_(float|resource_payment)_request_(id_conflict|data_mismatch)$/.test(code2)) {
119469
- const fresh = typeof a.newRequestId === "string" && a.newRequestId ? `request_id ${a.newRequestId}` : "a new request_id";
119470
- return `Nothing was paid: this request_id belongs to an earlier payment. For this request, ask again with ${fresh}.`;
119469
+ return "Nothing was paid: this request matched an earlier payment with other details. Ask again to pay for this one.";
119471
119470
  }
119472
119471
  if (code2 === "p03_resource_payment_memory_only_needs_float") {
119473
119472
  return "Nothing was paid: this seller now asks a payment whose record would keep your words, and a private call never pays that way. Ask again.";
@@ -119567,7 +119566,7 @@ function refusalSay(a, tool, again, npmVersion) {
119567
119566
  if (code2 === "mcp_setup_restart_needed") return "The wallet is set up, but it must reconnect once. Follow the steps below, then try again. Nothing was paid.";
119568
119567
  if (code2 === "mcp_restart_needed") return "Nothing was paid. Restart this app, then try again.";
119569
119568
  if (code2 === "p03_merchant_respend_value_held_by_private_send") {
119570
- return heldBySendSay(a.privateSends, tool === "wallet_unshield" || tool === "wallet_transfer_token" || tool === "wallet_transfer_token_status" ? "sent" : "paid");
119569
+ return heldBySendSay(tool === "wallet_unshield" || tool === "wallet_transfer_token" || tool === "wallet_transfer_token_status" ? "sent" : "paid");
119571
119570
  }
119572
119571
  if (/accounts_split/.test(code2) || /accounts_split/.test(detail) || typeof a.joining === "boolean") {
119573
119572
  const verb = tool === "wallet_unshield" ? "sent" : "paid";
@@ -119836,8 +119835,7 @@ function heldByPaymentSay(detail) {
119836
119835
  if (!items.length) return "";
119837
119836
  const held = usdcText2(detail.unspentAtomic);
119838
119837
  const what = positive(held) ? `${held} USDC is` : "Your USDC is";
119839
- const id = items.find((item) => typeof item.requestId === "string" && item.requestId)?.requestId;
119840
- return id ? `${what} held by an unfinished payment (request_id ${id}). Ask me to finish it, or to close it with wallet_cancel.` : `${what} held by an unfinished payment. Ask me to finish it, or to close it with wallet_cancel.`;
119838
+ return `${what} held by an unfinished payment. Ask me to finish it, or to close it with wallet_cancel.`;
119841
119839
  }
119842
119840
  function ownWorkWords(lockedBy) {
119843
119841
  return typeof lockedBy === "string" && Object.hasOwn(OWN_WORK_WORDS, lockedBy) ? OWN_WORK_WORDS[lockedBy] : void 0;
@@ -119913,25 +119911,29 @@ function balanceLine(a, room = SAY_MAX_WORDS) {
119913
119911
  const going = sends.find((send3) => Number.isSafeInteger(send3.failedTries) && send3.failedTries > 0) ?? sends[0];
119914
119912
  if (going && !moving) {
119915
119913
  const left = usdcText2(going.leftAtomic) ?? "";
119914
+ const total = usdcText2(going.amountAtomic);
119915
+ const to = typeof going.to === "string" && going.to.length > 10 ? ` to ${going.to.slice(0, 4)}...${going.to.slice(-4)}` : "";
119916
+ const named = `your private send${total ? ` of ${total} USDC` : ""}${to}`;
119917
+ const short = `your private send${to}`;
119916
119918
  if (Number.isSafeInteger(going.failedTries) && going.failedTries > 0) {
119917
119919
  const tries = `${going.failedTries} ${going.failedTries === 1 ? "try" : "tries"}`;
119918
119920
  return have(
119919
- `; your private send "${going.requestId}" has ${left} USDC not sent: its next payment did not go through (${tries}).`,
119920
- `; private send "${going.requestId}": next payment did not go through (${tries}).`,
119921
+ `; ${named} has ${left} USDC not sent: its next payment did not go through (${tries}).`,
119922
+ `; ${short}: next payment did not go through (${tries}).`,
119921
119923
  "; a private send's next payment did not go through yet."
119922
119924
  );
119923
119925
  }
119924
119926
  if (!(Number.isSafeInteger(going.nextPaymentInMinutes) && going.nextPaymentInMinutes > 0)) {
119925
119927
  return have(
119926
- `; your private send "${going.requestId}" has ${left} USDC not sent yet, and the wallet tries its next payment again by itself.`,
119927
- `; private send "${going.requestId}": ${left} USDC not sent yet.`,
119928
+ `; ${named} has ${left} USDC not sent yet, and the wallet tries its next payment again by itself.`,
119929
+ `; ${short}: ${left} USDC not sent yet.`,
119928
119930
  "; a private send is not all sent yet."
119929
119931
  );
119930
119932
  }
119931
119933
  const when = `in about ${minutesText(going.nextPaymentInMinutes)}`;
119932
119934
  return have(
119933
- `; your private send "${going.requestId}" has ${left} USDC left to go, and its next payment goes ${when}.`,
119934
- `; private send "${going.requestId}": next payment ${when}.`,
119935
+ `; ${named} has ${left} USDC left to go, and its next payment goes ${when}.`,
119936
+ `; ${short}: next payment ${when}.`,
119935
119937
  `; a private send's next payment goes ${when}.`
119936
119938
  );
119937
119939
  }
@@ -120032,6 +120034,9 @@ function transferSay(a) {
120032
120034
  const sent = usdcText2(a.send?.amountAtomic);
120033
120035
  const kept2 = a.send?.keptPrivateAtomic && a.send.keptPrivateAtomic !== "0" ? usdcText2(a.send.keptPrivateAtomic) : null;
120034
120036
  const later = "If this chat ends first, it continues at your next request.";
120037
+ if (a.replayed === true) {
120038
+ return `This request matched an earlier transfer to ${to}; this call moved nothing. Ask again to transfer again.`;
120039
+ }
120035
120040
  switch (a.stage) {
120036
120041
  case "selling":
120037
120042
  return `Selling the token for ${usdc6}; then it goes privately to ${to}. ${later}`;
@@ -120041,7 +120046,7 @@ function transferSay(a) {
120041
120046
  // At most 24 words with the kept part (SAY_MAX_WORDS 25). A send step that stopped (tools.mjs transferAnswer sendStop,
120042
120047
  // 0.3.109 fault 5) says why first: money another send set aside, or money the wallet gets ready by itself.
120043
120048
  case "sending":
120044
- if (Array.isArray(a.privateSends)) return heldBySendSay(a.privateSends, "sent");
120049
+ if (Array.isArray(a.privateSends)) return heldBySendSay("sent");
120045
120050
  if (a.joining === true) return "Not sent yet: the wallet gets the money ready by itself. Ask again in a few minutes; do not cancel.";
120046
120051
  return sent ? `Sending ${sent} USDC (not the token) privately to ${to}${kept2 ? `; ${kept2} USDC stays private` : ""}. ${later}` : `Sending ${usdc6} privately to ${to}. It gets USDC, not the token. ${later}`;
120047
120052
  case "sold_waiting_room":
@@ -120084,8 +120089,7 @@ function stoppedSay(a) {
120084
120089
  }
120085
120090
  function build(a, tool, npmVersion) {
120086
120091
  if (!a || typeof a !== "object" || Array.isArray(a)) return void 0;
120087
- const id = typeof a.requestId === "string" && a.requestId ? a.requestId : void 0;
120088
- const again = id ? `Ask again with request_id ${id}; it never pays twice.` : "Ask again with the same request_id; it never pays twice.";
120092
+ const again = "Ask again; it never pays twice.";
120089
120093
  if (a.ok === false) return refusalSay(a, tool, again, npmVersion);
120090
120094
  if (TRANSFER_TOOLS.has(tool) || tool === void 0 && a.delivers === "USDC" && (a.token || Array.isArray(a.transfers))) return transferSay(a);
120091
120095
  if (tool === "wallet_receive_shielded" || tool === void 0 && typeof a.receiveShielded === "string") {
@@ -120128,7 +120132,7 @@ function build(a, tool, npmVersion) {
120128
120132
  return `${what} The wallet first gets your whole balance ready to send${steps}, then sends it.`;
120129
120133
  }
120130
120134
  const head = `Not sent yet: the wallet is getting your balance ready to send${steps}.`;
120131
- const ask = id ? `Ask again with request_id ${id} until it sends` : "Ask again with the same request_id until it sends";
120135
+ const ask = "Ask again until it sends";
120132
120136
  return [`${head} ${ask}; it never sends twice.`, `${head} ${ask}.`].find((line) => words(line) <= SAY_MAX_WORDS) ?? head;
120133
120137
  }
120134
120138
  if (typeof a.to === "string" && a.to && Object.hasOwn(a, "stage")) {
@@ -120163,15 +120167,14 @@ function build(a, tool, npmVersion) {
120163
120167
  if (typeof a.address === "string" && a.address && typeof a.stage === "string") return linkSay(a);
120164
120168
  if (a.stage === "waiting_for_funds" && typeof a.depositAddress === "string") {
120165
120169
  const send3 = usdcText2(a.sendAtomic ?? a.amountAtomic);
120166
- return `Send exactly ${send3 ? `${send3} USDC` : "the amount"} on Solana to ${a.depositAddress} in one transfer. Then ask again with request_id ${id ?? "the same request_id"}.`;
120170
+ return `Send exactly ${send3 ? `${send3} USDC` : "the amount"} on Solana to ${a.depositAddress} in one transfer. Then ask again.`;
120167
120171
  }
120168
120172
  const shieldJob = SHIELD_TOOLS.has(tool) || tool === void 0 && Object.hasOwn(a, "amountAtomic") && Object.hasOwn(a, "stage") && !Object.hasOwn(a, "paid");
120169
120173
  if (shieldJob && typeof a.stage === "string") {
120170
120174
  const amount = usdcText2(a.amountAtomic);
120171
120175
  const what = amount ? `${amount} USDC` : "The USDC";
120172
120176
  if (a.done === true && a.replayed === true) {
120173
- const fresh = typeof a.newRequestId === "string" && a.newRequestId ? `request_id ${a.newRequestId}` : "a new request_id";
120174
- return `This request_id finished earlier (${what} made private then); this call moved nothing. To shield more, ask again with ${fresh}.`;
120177
+ return `This request finished earlier (${what} made private then); this call moved nothing. Ask again to shield more.`;
120175
120178
  }
120176
120179
  if (a.done === true) return withBalance(`${what} is now private.`, a);
120177
120180
  if (a.stage === "waiting" && a.code === "mcp_balance_unknown") {
@@ -120184,14 +120187,14 @@ function build(a, tool, npmVersion) {
120184
120187
  }
120185
120188
  if (tool === "wallet_receipts" && Number.isSafeInteger(a.count)) {
120186
120189
  const arrived = Array.isArray(a.receipts) ? a.receipts.find((row) => row?.kind === "add-funds" && /^[1-9][0-9]*$/.test(String(row.heldAtomic ?? ""))) : void 0;
120187
- if (arrived) return `${a.count} ${a.count === 1 ? "entry" : "entries"}. ${usdcText2(arrived.heldAtomic)} USDC reached add-funds address ${arrived.requestId} and becomes private by itself.`;
120190
+ if (arrived) return `${a.count} ${a.count === 1 ? "entry" : "entries"}. ${usdcText2(arrived.heldAtomic)} USDC reached an add-funds address and becomes private by itself.`;
120188
120191
  return a.count === 0 ? "No receipts or wallet moves yet." : `${a.count} ${a.count === 1 ? "entry" : "entries, newest first"}. Each payment names its seller; pay.shinjukustaition.com is Shinjuku, the private payment service.`;
120189
120192
  }
120190
120193
  if (Array.isArray(a.models) && a.models.length > 0 && a.paid === void 0) {
120191
120194
  const count2 = `${a.models.length} AI ${a.models.length === 1 ? "model is" : "models are"}`;
120192
120195
  return a.ready === false ? `Nothing was asked or paid. ${count2} listed, but asking one needs Tor on; the next step says how to turn it on.` : `Nothing was asked or paid. ${count2} ready: ask a question, and each answer is paid privately from your balance.`;
120193
120196
  }
120194
- if (a.done === false) return id ? `Still running. Ask again with request_id ${id}; it never moves money twice.` : "Still running. Ask again in about 30 seconds.";
120197
+ if (a.done === false) return "Still running. Ask again in about 30 seconds; it never moves money twice.";
120195
120198
  return "Done.";
120196
120199
  }
120197
120200
  function sayFor(answer, { tool, npmVersion } = {}) {
@@ -127078,7 +127081,7 @@ function createWalletTools({
127078
127081
  body: { type: "string", description: "Request body text (POST only)." },
127079
127082
  max_payment: { type: "string", description: 'Optional: the most this call may pay, in USDC, like "0.05"; at most the spending limit.' },
127080
127083
  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." },
127081
- request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Your stable name for this payment. Leave it out for a new payment; pass the same one to resume a failed payment without paying twice." }
127084
+ 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." }
127082
127085
  },
127083
127086
  required: ["url"],
127084
127087
  additionalProperties: false
@@ -127107,7 +127110,7 @@ ${canonical5}`;
127107
127110
  log("MCP_PAY_JOINED");
127108
127111
  return running;
127109
127112
  }
127110
- const task = payCall({ target, request, argument: argument2, requestId, given, calledAt, progress: progress3, canProgress, signal }).then((output2) => given ? output2 : withAutoId(output2, "x402_pay", requestId));
127113
+ const task = payCall({ target, request, argument: argument2, requestId, given, calledAt, progress: progress3, canProgress, signal });
127111
127114
  if (joinKey !== void 0) {
127112
127115
  paysRunning.set(joinKey, task);
127113
127116
  task.then(() => paysRunning.delete(joinKey), () => paysRunning.delete(joinKey));
@@ -127331,7 +127334,6 @@ ${canonical5}`;
127331
127334
  }
127332
127335
  const { bytes: bytes2, truncated } = await readBody(response);
127333
127336
  const payment = response.headers.get(PAYMENT_HEADER);
127334
- const autoId = given ? void 0 : requestId;
127335
127337
  if (!payment) {
127336
127338
  const moved2 = movedTo(response, target.url);
127337
127339
  return answerResult(paidAnswerContent({
@@ -127351,8 +127353,7 @@ ${canonical5}`;
127351
127353
  detail: `the URL answered HTTP ${response.status} without asking for payment; nothing was paid`,
127352
127354
  ...moved2 ? { movedTo: moved2, next: `nothing was paid: this URL answered HTTP ${response.status}, which says it moved to ${moved2}, and gave no data. Tell the user it moved. If the user wants it, call x402_pay again with url "${moved2}", the same method, headers and body, and a NEW request_id` } : response.status >= 200 && response.status < 300 ? {} : { next: `nothing was paid: this URL answered HTTP ${response.status} and did not ask for payment, so it gave no data; check the URL, or pay another seller` }
127353
127355
  },
127354
- "x402_pay",
127355
- autoId
127356
+ "x402_pay"
127356
127357
  )
127357
127358
  }));
127358
127359
  }
@@ -127425,13 +127426,9 @@ ${canonical5}`;
127425
127426
  ...balanceAnswer(one),
127426
127427
  ...takeRefundNotices(),
127427
127428
  next: 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"
127428
- }, "x402_pay", autoId)
127429
+ }, "x402_pay")
127429
127430
  }));
127430
127431
  }
127431
- const withIdSentence = (say3, id) => {
127432
- const line = `${say3} Its request_id is ${id}.`;
127433
- return line.trim().split(/\s+/).length <= SAY_MAX_WORDS ? line : say3;
127434
- };
127435
127432
  function networkFields() {
127436
127433
  const mode = runNetworkMode();
127437
127434
  let onion = false;
@@ -127442,9 +127439,8 @@ ${canonical5}`;
127442
127439
  }
127443
127440
  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 this wallet was started with; 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. To hide it, the user runs ${terminalCommand2("setup --yes --prefer-tor")} once in a terminal (it records Tor and checks the wallet's files again; nothing else changes; if Tor is not running, it says how to start it), then closes this app and opens it again` } };
127444
127441
  }
127445
- function sayFirst(fields, tool, autoId) {
127446
- let say3 = sayFor(fields, { tool, npmVersion });
127447
- if (autoId) say3 = withIdSentence(say3, autoId);
127442
+ function sayFirst(fields, tool) {
127443
+ const say3 = sayFor(fields, { tool, npmVersion });
127448
127444
  const usdc6 = usdcFields(fields);
127449
127445
  let news = [];
127450
127446
  try {
@@ -127454,21 +127450,19 @@ ${canonical5}`;
127454
127450
  }
127455
127451
  return { say: say3, ...fields, ...Object.keys(usdc6).length ? { usdc: usdc6 } : {}, ...Array.isArray(news) && news.length ? { news } : {} };
127456
127452
  }
127457
- function withAutoId(output2, tool, requestId, nextNote) {
127453
+ function withAutoId(output2, tool, nextNote) {
127458
127454
  const said = addSay(output2, tool, { news: watcher, npmVersion });
127459
127455
  const blocks = Array.isArray(said?.content) && said.content.length ? said.content : void 0;
127460
127456
  const text3 = blocks ? blocks[0]?.type === "text" ? blocks[0].text : void 0 : said?.text;
127461
127457
  if (typeof text3 !== "string" || text3.length > 262144 || !/^\s*\{\s*"say"\s*:/.test(text3.slice(0, 64))) return said;
127462
- const named = text3.slice(0, 600).includes(`Its request_id is ${requestId}.`);
127463
- if (named && !nextNote) return said;
127464
127458
  let parsed2;
127465
127459
  try {
127466
127460
  parsed2 = JSON.parse(text3);
127467
127461
  } catch {
127468
127462
  return said;
127469
127463
  }
127470
- if (!named && typeof parsed2.say === "string") parsed2.say = withIdSentence(parsed2.say, requestId);
127471
- if (nextNote && typeof parsed2.next === "string") parsed2.next = `${parsed2.next.replace(/[.\s]+$/, "")}. ${nextNote}`;
127464
+ if (typeof parsed2.next !== "string") return said;
127465
+ parsed2.next = `${parsed2.next.replace(/[.\s]+$/, "")}. ${nextNote}`;
127472
127466
  const out = JSON.stringify(parsed2, null, /^\s*\{\s*\n/.test(text3) ? 2 : void 0);
127473
127467
  return { ...said, text: out, ...blocks ? { content: [{ ...blocks[0], text: out }, ...blocks.slice(1)] } : {} };
127474
127468
  }
@@ -127543,7 +127537,9 @@ ${canonical5}`;
127543
127537
  const float = floatRefusal(error);
127544
127538
  if (float && float[0] === code2) {
127545
127539
  const [, detail, sharedNext] = float;
127546
- const floatNext = inference ? sharedNext.replace(/\bx402_pay\b/g, tool) : sharedNext;
127540
+ const prunedId = code2 === "p03_float_receipt_pruned" ? newRequestIdFor(requestId) : void 0;
127541
+ const sharedFloatNext = inference ? sharedNext.replace(/\bx402_pay\b/g, tool) : sharedNext;
127542
+ const floatNext = prunedId ? `${sharedFloatNext}: use the new request_id "${prunedId}"` : sharedFloatNext;
127547
127543
  let one;
127548
127544
  try {
127549
127545
  one = readBalance(paidStore ?? session.openStore());
@@ -127578,6 +127574,7 @@ ${canonical5}`;
127578
127574
  ...typeof error?.feeAtomic === "string" ? { feeAtomic: error.feeAtomic } : {},
127579
127575
  // conversationNotKept (floatPay.mjs finishedPruned): a private conversation, whose answer the wallet never kept.
127580
127576
  ...error?.conversationNotKept === true ? { conversationNotKept: true } : {},
127577
+ ...prunedId ? { newRequestId: prunedId } : {},
127581
127578
  // floatId: Shinjuku's id of this payment, which wallet_receipts shows with the refund's state and transaction (realSpeed3
127582
127579
  // force-live 5r, 2026-10-22: the answer gave no id, so nobody could look the refund up).
127583
127580
  // refundAtomic and refundEtaSeconds (Shinjuku's time) for a proven refund; an unproven send carries ambiguous: true.
@@ -128356,6 +128353,7 @@ ${canonical5}`;
128356
128353
  privateSends.push({
128357
128354
  requestId: transfer ? plan.transferTokenId : shownId(plan.mcpRequestId, recordId2),
128358
128355
  ...transfer ? { tool: "wallet_transfer_token" } : {},
128356
+ to: plan.mcpTo,
128359
128357
  amountAtomic: plan.amountAtomic,
128360
128358
  sentAtomic: send3.sentAtomic,
128361
128359
  leftAtomic: send3.remainingAtomic,
@@ -130403,7 +130401,6 @@ ${canonical5}`;
130403
130401
  return withAutoId(
130404
130402
  answer,
130405
130403
  spec.tool,
130406
- claimed.requestId,
130407
130404
  "The same call without request_id within 10 minutes after it ends reads this same request; to do it again later, ask after 10 minutes or give a new request_id."
130408
130405
  );
130409
130406
  }
@@ -131714,7 +131711,7 @@ ${canonical5}`;
131714
131711
  properties: {
131715
131712
  amount: { type: "string", description: 'USDC to shield, like "0.37" (up to 6 decimals). Pass this or amount_atomic.' },
131716
131713
  amount_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "USDC to shield, in atomic units (1000000 = 1 USDC, 370000 = 0.37 USDC): any amount from 10000 (0.01 USDC) while the balance stays at most 5 USDC." },
131717
- request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional. Your stable name for this deposit, like shield-1; pass it again to read the status." }
131714
+ request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional. Leave it out for a new deposit (the answer names the id it made). Pass the request_id of an earlier answer only to read or continue that deposit." }
131718
131715
  },
131719
131716
  required: [],
131720
131717
  additionalProperties: false
@@ -131732,7 +131729,7 @@ ${canonical5}`;
131732
131729
  amount: { type: "string", description: 'USDC to send, like "2" or "0.37" (up to 6 decimals). Pass this or amount_atomic.' },
131733
131730
  amount_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "USDC to send, in atomic units (1000000 = 1 USDC)." },
131734
131731
  to: { type: "string", description: "The receiving Solana address (base58), exactly as the user gave it. Leave it out to send to the owner's own wallet when the owner set exactly one (wallet_balance session.unshield.to); with none or several set, pass it." },
131735
- request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional. Your stable name for this send, like send-1; pass it again to read the status." },
131732
+ request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional. Leave it out for a new send (the answer names the id it made). Pass the request_id of an earlier answer only to read or continue that send." },
131736
131733
  parts: { type: "integer", minimum: 1, maximum: 8, description: "Optional, default 1: send the amount as this many smaller payments (a private send), of random sizes in whole 0.01 USDC: the sizes differ only when the amount allows it (0.02 USDC in 2 goes as 0.01 + 0.01; privateSend lists each amount). Each one carries at least 10000 atomic (0.01 USDC). Above 1 it needs max_delay_minutes 1-60 (smaller payments with no wait are refused: they would leave together). Each one is sent after a random wait of up to max_delay_minutes (the first one too), so the chain shows no single exit of this amount at this time; max_delay_minutes alone delays one exit. " + SEND_RUNS_ON + ` Late ones never go together. While some are unsent, x402_pay may spend only the rest of the balance. The plan is saved in the wallet before the first one, so the same request_id (with the same \`parts\` and max_delay_minutes) continues after a restart and never sends one twice; the answer's privateSend lists each one (state, amount, when it may go, transactions), sentAtomic, and remainingAtomic. The user confirms the whole send once. Each one is a full unshield, so it takes as long as one, and the facilitator pays the network fee of each. Privacy limit (tell the user): every payment goes to the SAME \`to\`, so anyone who adds up that address's incoming transfers sees the total; the smaller payments hide the time and the single amount of the exit, not the sum the address received. wallet_cancel with the same request_id stops the ones not sent yet (their value stays in the private balance); while one is being sent, that call waits for it to finish, up to about ${CALL_BUDGET_MS / 1e3} s.` },
131737
131734
  max_delay_minutes: { type: "integer", minimum: 0, maximum: 60, description: "Optional, default 0: each smaller payment, the first included, is sent after a random wait of up to this many minutes (a private send)." }
131738
131735
  },
@@ -131773,7 +131770,7 @@ ${canonical5}`;
131773
131770
  properties: {
131774
131771
  amount: { type: "string", description: 'USDC to shield, like "0.37" (up to 6 decimals). Pass this or amount_atomic, unless action is cancel.' },
131775
131772
  amount_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "USDC to shield, in atomic units (1000000 = 1 USDC, 370000 = 0.37 USDC): any amount from 10000 (0.01 USDC) while the balance stays at most 5 USDC. Required unless action is cancel." },
131776
- request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional for a deposit, needed for cancel. Your stable name for this deposit, like from-phantom-1; pass it again (with the same amount) to read the status." },
131773
+ request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Optional for a deposit, needed for cancel. Leave it out for a new deposit (the answer names the id it made). Pass the request_id of an earlier answer (with the same amount) only to read, continue, or cancel that deposit." },
131777
131774
  action: { type: "string", enum: ["deposit", "cancel"], description: "deposit (the default): start or read this deposit. cancel: close this request's one-time address while nothing at all reached it (no amount needed)." }
131778
131775
  },
131779
131776
  required: [],
@@ -132674,7 +132671,10 @@ ${canonical5}`;
132674
132671
  } else if (sendStop === "split") {
132675
132672
  next = `${sentSome ? "Nothing more was sent" : "Nothing was sent yet"}: the private balance holds the next payment of this send, but the wallet must first get that money ready to send. It does that by itself, at each wallet request. Do not cancel the send: tell the user it is not stuck. In a few minutes, ${poll} again: it continues the send where it stopped (it never sends a payment twice)`;
132676
132673
  }
132677
- const inBox = plan.approval?.by === "elicitation";
132674
+ if (extra.replayed === true) {
132675
+ next = `This request_id belongs to an earlier transfer; this call moved nothing new. About that transfer: ${next.replace(/\.$/, "")}. To transfer again, call wallet_transfer_token with the new request_id "${extra.newRequestId}"`;
132676
+ }
132677
+ const inBox = plan.approval?.by === "elicitation" && extra.replayed !== true;
132678
132678
  return json3({
132679
132679
  ok: true,
132680
132680
  ...view2,
@@ -132796,7 +132796,9 @@ ${canonical5}`;
132796
132796
  const existing = readTransferToken(store, id);
132797
132797
  if (existing) {
132798
132798
  if (!sameTransfer(existing, request)) return transferReused(id);
132799
- return TRANSFER_RUNNING_STATES.has(existing.state) && existing.state !== "sold_waiting_room" || tidyOpen(existing) ? await answerSoon(id, { reused: true }, call) : transferAnswer(existing, { reused: true });
132799
+ if (TRANSFER_RUNNING_STATES.has(existing.state) && existing.state !== "sold_waiting_room" || tidyOpen(existing)) return await answerSoon(id, { reused: true }, call);
132800
+ const ended = ["done", "cancelled"].includes(existing.state) ? { replayed: true, newRequestId: `${id.slice(0, 31)}-${Date.now().toString(36)}` } : {};
132801
+ return transferAnswer(existing, { reused: true, ...ended });
132800
132802
  }
132801
132803
  } catch (error) {
132802
132804
  return transferRefusal(error, "transfer_plan_unreadable", "tell your owner");
@@ -133042,7 +133044,7 @@ ${canonical5}`;
133042
133044
  token_mint: { type: "string", description: "The base58 mint address of the token to sell, like DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263 (BONK). Never a symbol." },
133043
133045
  amount_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: 'How much of the token to sell, in its own base units: "1250000" = 1.25 of a 6-decimal token.' },
133044
133046
  to: { type: "string", description: "The receiving Solana address (base58), exactly as the user gave it. It receives USDC and must already be able to receive USDC." },
133045
- request_id: { type: "string", pattern: "^[A-Za-z0-9_-]{1,40}$", description: "Your stable name for this transfer, like move-bonk-1; wallet_transfer_token_status takes it." },
133047
+ request_id: { type: "string", pattern: "^[A-Za-z0-9_-]{1,40}$", description: "A new name for each new transfer, like move-bonk-k7q2 (an id an earlier transfer used only reads that transfer and moves nothing); wallet_transfer_token_status takes it." },
133046
133048
  parts: { type: "integer", minimum: 2, maximum: 8, description: "Optional, default 2: the USDC leaves as this many payments of random sizes in whole 0.01 USDC (each at least 0.01 USDC; the sizes differ only when the amount allows it: 0.02 USDC in 2 goes as 0.01 + 0.01)." },
133047
133049
  max_delay_minutes: { type: "integer", minimum: 1, maximum: 60, description: "Optional, default 30: each payment waits a random time up to this many minutes before it leaves." },
133048
133050
  max_slippage_bps: { type: "integer", minimum: 1, maximum: 300, description: "Optional, default 50 (0.5 %): the sell fails rather than give less than the quote minus this." },
@@ -133258,7 +133260,7 @@ ${canonical5}`;
133258
133260
  temperature: { type: "number", minimum: 0, maximum: 2, description: "Sampling temperature; leave it out for the model's default." },
133259
133261
  max_payment: { type: "string", description: 'Optional: the most this call may pay, in USDC, like "0.05"; at most the spending limit.' },
133260
133262
  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." },
133261
- request_id: { type: "string", pattern: "^[A-Za-z0-9._:-]{1,128}$", description: "Your stable name for this call. Leave it out for a new call; pass the same one to resume a failed call without paying twice." }
133263
+ 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." }
133262
133264
  },
133263
133265
  additionalProperties: false
133264
133266
  },