shinjuku-shield 0.3.91 → 0.3.92

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,13 +6,13 @@ 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.91. The npm packages `shinjuku-shield` and
9
+ This is version 0.3.92. The npm packages `shinjuku-shield` and
10
10
  `shinjuku-shielded` hold the same files; `shinjuku` is the short command.
11
11
 
12
12
  ## Set up
13
13
 
14
14
  ```sh
15
- npx -y --no-audit --no-fund shinjuku-shield@0.3.91 setup
15
+ npx -y --no-audit --no-fund shinjuku-shield@0.3.92 setup
16
16
  ```
17
17
 
18
18
  Setup makes the wallet on this machine, a private passphrase file, and the
@@ -24,7 +24,7 @@ server to that host's config.
24
24
  ## The `shinjuku` command
25
25
 
26
26
  ```sh
27
- npm install -g shinjuku@0.3.91
27
+ npm install -g shinjuku@0.3.92
28
28
  shinjuku help
29
29
  ```
30
30
 
@@ -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-shielded -- npx -y --no-audit --no-fund shinjuku-shield@0.3.91 mcp
39
+ claude mcp add --scope user shinjuku-shielded -- npx -y --no-audit --no-fund shinjuku-shield@0.3.92 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-shielded": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.91", "mcp"] } } }
45
+ { "mcpServers": { "shinjuku-shielded": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.92", "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": "1d7842469a3a56390eae2b339649cb42551a19ef63c2f9b1d5716f875ce11b52",
5
- "bytes": 12694423,
6
- "sourceCommit": "49f574ac0d496bfe5fda2fff09ed900810a54eed",
4
+ "sha256": "afa037c8868c722838c0642335b1767af4bcc0040878ae441e83cb94dd9aeea2",
5
+ "bytes": 12692822,
6
+ "sourceCommit": "3b6e926704a348088fa33d114ed7913e2d610033",
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",
@@ -1297,5 +1297,5 @@
1297
1297
  "version": "1.8.0"
1298
1298
  }
1299
1299
  ],
1300
- "publishedAt": "2026-10-24T01:44:56.272Z"
1300
+ "publishedAt": "2026-10-24T02:20:31.413Z"
1301
1301
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shield",
3
- "version": "0.3.91",
3
+ "version": "0.3.92",
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": "1d784246",
7
- "shinjukuSha256": "1d7842469a3a56390eae2b339649cb42551a19ef63c2f9b1d5716f875ce11b52",
6
+ "shinjukuRelease": "afa037c8",
7
+ "shinjukuSha256": "afa037c8868c722838c0642335b1767af4bcc0040878ae441e83cb94dd9aeea2",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
@@ -127591,7 +127591,7 @@ ${canonical5}`;
127591
127591
  const balance = {
127592
127592
  name: "wallet_balance",
127593
127593
  title: "Balance",
127594
- 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. When the user asks to see the wallet ("show my wallet"), print \`card\` as it is: at most 8 lines with the private balance, the public USDC, Tor on or off, the last 3 payments (time UTC, seller host, USDC), and example requests. balanceParts (only above 1): a send of all of it first gets the balance ready to send (next says the minutes; never say parts or join to the user). 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 gets its own balance ready to send and the number counts all of it; pool_move: value from the old pool arrives and the number counts it; refund_check: a refund counted from an old read, which the wallet checks now (never tell the user it is still on its way); 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. ownKeyTokens (only when there are any): the other tokens on ownAddress (not its USDC), each { mint, amountAtomic, decimals, amount }, at most 10 (ownKeyTokensMore counts the rest); a token is its mint, never a name or symbol; wallet_transfer_token with that mint as token_mint sells one and sends the USDC privately. 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). startsAtNextRequest true (old name): its deposit is not sent yet; once the wallet has seen USDC there, it sends it by itself in about startsInMinutes + 1 minutes, in every host, with no ask. 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 as next says (at once in the same turn when this call waited its 20 s, else 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, only when a limit is set; with none, say nothing about limits, and never tell the user the session counters (sessionSpentAtomic, sessionMovedAtomic): they are limit bookkeeping and may count earlier conversations). 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.`,
127594
+ 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. When the user asks to see the wallet ("show my wallet"), print \`card\` as it is (at most 8 lines: balances, Tor, the last 3 payments, example requests). balanceParts (only above 1): a send of all of it first gets the balance ready to send (next says the minutes; never say parts or join to the user). 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 gets its own balance ready to send and the number counts all of it; pool_move: value from the old pool arrives and the number counts it; refund_check: a refund counted from an old read, which the wallet checks now (never tell the user it is still on its way); 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. ownKeyTokens (only when there are any): the other tokens on ownAddress (not its USDC), each { mint, amountAtomic, decimals, amount }, at most 10 (ownKeyTokensMore counts the rest); a token is its mint, never a name or symbol; wallet_transfer_token with that mint as token_mint sells one and sends the USDC privately. 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). startsAtNextRequest true (old name): its deposit is not sent yet; once the wallet has seen USDC there, it sends it by itself in about startsInMinutes + 1 minutes, in every host, with no ask. 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 as next says (at once in the same turn when this call waited its 20 s, else in 30 s). session: the spending limits (null or "none": no limit), what is left, and how to change them (session.summary, only when a limit is set). With no limit set, say nothing about limits or the session counters (limit bookkeeping; they may count earlier conversations). To add money, use wallet_add_funds. Amounts are atomic units (USDC: 1000000 = 1 USDC); say and usdc give them in USDC.`,
127595
127595
  inputSchema: { type: "object", properties: {
127596
127596
  verbose: { type: "boolean", description: "Optional. true adds `internal`: the engineering numbers (funded accounts, notes, payments without waiting, a 0 set-aside value). Default false." }
127597
127597
  }, additionalProperties: false },
@@ -129812,7 +129812,7 @@ ${canonical5}`;
129812
129812
  decimals = Number(session.openStore().state.decimals);
129813
129813
  } catch {
129814
129814
  }
129815
- const amount = formatAtomic(BigInt(job.amountAtomic), decimals);
129815
+ const amount = usdcText2(job.amountAtomic, decimals);
129816
129816
  const askedAt = Date.now();
129817
129817
  try {
129818
129818
  const how = isSplitSend(job) ? job.parts === 1 ? ` after a random wait of up to ${job.maxDelayMinutes} min` : ` as ${job.parts} separate exits of unequal size, each after a random wait of up to ${job.maxDelayMinutes} min` : "";
@@ -130105,13 +130105,13 @@ ${canonical5}`;
130105
130105
  }
130106
130106
  if (checkInput) {
130107
130107
  try {
130108
- await recipientCheck(checkInput);
130108
+ await withTimeout(recipientCheck(checkInput), RECIPIENT_CHECK_TIMEOUT_MS);
130109
130109
  } catch (error) {
130110
130110
  if (errorCode2(error, "") === "mcp_unshield_to_invalid") {
130111
130111
  log(`MCP_UNSHIELD_TO_REFUSED requestId=${request.requestId}`);
130112
130112
  return refuse12("mcp_unshield_to_invalid", /has no USDC token account/.test(String(error?.message)) ? UNSHIELD_TO_NO_USDC_DETAIL : String(error.message).replace(/^mcp_unshield_to_invalid:\s*/, ""), UNSHIELD_TO_INVALID_NEXT, { requestId: request.requestId, to: request.to });
130113
130113
  }
130114
- log(`MCP_UNSHIELD_TO_CHECK_SKIPPED requestId=${request.requestId} ${errorCode2(error, "unknown")}`);
130114
+ log(`MCP_UNSHIELD_TO_CHECK_SKIPPED requestId=${request.requestId} ${error?.timedOut === true ? "timeout" : errorCode2(error, "unknown")}`);
130115
130115
  }
130116
130116
  }
130117
130117
  }
@@ -131189,7 +131189,7 @@ ${canonical5}`;
131189
131189
  const unshield = {
131190
131190
  name: "wallet_unshield",
131191
131191
  title: "Send USDC privately from the shielded balance",
131192
- description: `Use this when the user says "send 2 USDC to <address>" or "withdraw to my wallet" (leave \`to\` out for the owner's own wallet when one is set): it sends amount (USDC, like "2"; or amount_atomic) from the SHIELDED balance as plain USDC to \`to\`, a public Solana address (the USDC goes to its USDC token account). BEFORE you call it to send all of the balance when wallet_balance showed balanceParts above 1, write to the user that it takes about joinMinutes minutes (the wallet first gets the balance ready to send; never say parts or join to the user; some apps show nothing until this call ends). Privately: the chain shows the pool paying the address, not which wallet paid. The wallet's own key (ownAddress in wallet_balance) is this wallet's public address: it holds the public USDC, wallet_shield deposits from it, and wallet_transfer_token sells from it. So it is refused as \`to\`: an exit there would show on chain that the private balance belongs to this wallet. This holds whether or not that key ever deposited: wallet_add_funds deposits come from one-time addresses, not from this key, so never tell the user the deposits came from it. The facilitator pays every network fee; no SOL is needed. The owner's --unshield-to addresses send at once. Any other address is sent only after the user confirms it in the MCP client (a confirmation dialog); a client that cannot ask is refused (mcp_unshield_to_not_allowed) and a decline sends nothing (mcp_unshield_declined). \`to\` must already hold USDC (have a USDC token account): an address without one is refused before any dialog (mcp_unshield_to_invalid). It takes minutes (one wait of up to 5 minutes is normal): the call answers within about 20 s with stage (confirming, queued, refreshing, self-pay = preparing, exit = sending, done; joining = the wallet first gets the balance ready to send, then sends; the answer says how many more asks and when (joinAsks, joinMinutes), ok: true); call wallet_unshield again with the SAME request_id, amount, and to for the status (each call answers within about 20 s, at once when the send ends; a confirmation dialog the user answers may take longer). When the balance is in several parts, the FIRST call answers at once with stage joining and joinTellFirst: true; write its say to the user, then call again with the same request_id at once. One exception: for a single send (no parts, no delay) whose balance is in several parts, when this MCP app sends a progress token with the call, that next call joins the parts and then sends, taking up to ${JOIN_CALL_MAX_MS / 6e4} minutes, with a progress line every ${JOIN_STEP_MS / 1e3} s ("getting your balance ready to send (about M min)"; some apps show none of them, so the time is said before the call, as above). Without a progress token it answers within about 20 s with stage joining and when to ask again. The same request_id never sends twice, also after a restart. request_id is optional: without it the answer names the id it made, and the same call again within 10 minutes after it ends reads that same send. When it lands, the wallet writes an encrypted backup (backupPath). x402_pay calls wait while it runs. An optional owner cap (--max-unshield) may bound each call and what this wallet sends in any 24 hours, from every MCP process (mcp_unshield_cap_exceeded: tell the owner). Other refusals: mcp_argument_invalid, mcp_request_id_reused, mcp_unshield_to_invalid, wallet_locked, mcp_tool_disabled, mcp_unshield_failed, and the unshield's own p03_* codes. PRIVATE SEND (optional): when the user says "send it in parts", "spread it out", or "do not send it all at once", pass parts (2-8) WITH max_delay_minutes (1-60; parts with no wait are refused: they would leave together): the amount is split into parts exits of unequal size (each at least 0.01 USDC), each sent after a random wait of up to max_delay_minutes (part 1 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 parts never go together. While parts are unsent, x402_pay may spend only the rest of the balance. The plan is saved in the wallet before part 1, so the same request_id (with the same parts and max_delay_minutes) continues after a restart and never sends a part twice; the answer's privateSend lists each part (state, amount, when it may go, transactions), sentAtomic, and remainingAtomic. The user confirms the whole send once. Both left out: one exit, as before. Each part is a full unshield, so it takes as long as one, and the facilitator pays the network fee of each. Privacy limit: every part goes to the SAME \`to\`, so anyone who adds up that address's incoming transfers sees the total; the parts 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 parts not sent yet (their value stays in the shielded balance); while a part is being sent, that call waits for the part to finish, up to about ${CALL_BUDGET_MS / 1e3} s.`,
131192
+ description: `Use this when the user says "send 2 USDC to <address>" or "withdraw to my wallet" (leave \`to\` out for the owner's own wallet when one is set): it sends amount (USDC, like "2"; or amount_atomic) from the SHIELDED balance as plain USDC to \`to\`, a public Solana address (the USDC goes to its USDC token account). BEFORE you call it to send all of the balance when wallet_balance showed balanceParts above 1, write to the user that it takes about joinMinutes minutes (the wallet first gets the balance ready to send; never say parts or join to the user; some apps show nothing until this call ends). Privately: the chain shows the pool paying the address, not which wallet paid. The wallet's own key (ownAddress in wallet_balance) is this wallet's public address: it holds the public USDC, wallet_shield deposits from it, and wallet_transfer_token sells from it. So it is refused as \`to\`: an exit there would show on chain that the private balance belongs to this wallet. This holds whether or not that key ever deposited: wallet_add_funds deposits come from one-time addresses, not from this key, so never tell the user the deposits came from it. The facilitator pays every network fee; no SOL is needed. The owner's --unshield-to addresses send at once. Any other address is sent only after the user confirms it in the MCP client (a confirmation dialog); a client that cannot ask is refused (mcp_unshield_to_not_allowed) and a decline sends nothing (mcp_unshield_declined). \`to\` must already hold USDC (have a USDC token account): an address without one is refused before any dialog (mcp_unshield_to_invalid). It takes minutes (one wait of up to 5 minutes is normal): the call answers within about 20 s with stage (confirming, queued, refreshing, self-pay = preparing, exit = sending, done; joining = the wallet first gets the balance ready to send, then sends; the answer says how many more asks and when (joinAsks, joinMinutes), ok: true); call wallet_unshield again with the SAME request_id, amount, and to for the status (each call answers within about 20 s, at once when the send ends; a confirmation dialog the user answers may take longer). When the balance is in several parts, the FIRST call answers at once with stage joining and joinTellFirst: true; write its say to the user, then call again with the same request_id at once. One exception: for a single send (no parts, no delay) whose balance is in several parts, when this MCP app sends a progress token with the call, that next call joins the parts and then sends, taking up to ${JOIN_CALL_MAX_MS / 6e4} minutes, with a progress line every ${JOIN_STEP_MS / 1e3} s ("getting your balance ready to send (about M min)"; some apps show none of them, so the time is said before the call, as above). Without a progress token it answers within about 20 s with stage joining and when to ask again. The same request_id never sends twice, also after a restart. request_id is optional: without it the answer names the id it made, and the same call again within 10 minutes after it ends reads that same send. When it lands, the wallet writes an encrypted backup (backupPath). x402_pay calls wait while it runs. An optional owner cap (--max-unshield) may bound each call and what this wallet sends in any 24 hours, from every MCP process (mcp_unshield_cap_exceeded: tell the owner). Other refusals: mcp_argument_invalid, mcp_request_id_reused, mcp_unshield_to_invalid, wallet_locked, mcp_tool_disabled, mcp_unshield_failed, and the unshield's own p03_* codes. PRIVATE SEND (optional): when the user says "send it in parts", "spread it out", or "do not send it all at once", pass parts (2-8) WITH max_delay_minutes (1-60); the parts argument says how it works, and its privacy limit, which you tell the user.`,
131193
131193
  inputSchema: {
131194
131194
  type: "object",
131195
131195
  properties: {
@@ -131197,7 +131197,7 @@ ${canonical5}`;
131197
131197
  amount_atomic: { type: "string", pattern: "^[1-9][0-9]*$", description: "USDC to send, in atomic units (1000000 = 1 USDC)." },
131198
131198
  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." },
131199
131199
  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." },
131200
- parts: { type: "integer", minimum: 1, maximum: 8, description: "Optional, default 1: send the amount as this many separate exits of unequal size (a private send). Each part carries at least 10000 atomic (0.01 USDC). Above 1 it needs max_delay_minutes 1-60." },
131200
+ parts: { type: "integer", minimum: 1, maximum: 8, description: "Optional, default 1: send the amount as this many separate exits of unequal size (a private send). Each part carries at least 10000 atomic (0.01 USDC). Above 1 it needs max_delay_minutes 1-60 (parts with no wait are refused: they would leave together). Each part is sent after a random wait of up to max_delay_minutes (part 1 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 parts never go together. While parts are unsent, x402_pay may spend only the rest of the balance. The plan is saved in the wallet before part 1, so the same request_id (with the same parts and max_delay_minutes) continues after a restart and never sends a part twice; the answer's privateSend lists each part (state, amount, when it may go, transactions), sentAtomic, and remainingAtomic. The user confirms the whole send once. Each part 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 part goes to the SAME \`to\`, so anyone who adds up that address's incoming transfers sees the total; the parts 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 parts not sent yet (their value stays in the shielded balance); while a part is being sent, that call waits for the part to finish, up to about ${CALL_BUDGET_MS / 1e3} s.` },
131201
131201
  max_delay_minutes: { type: "integer", minimum: 0, maximum: 60, description: "Optional, default 0: each part, part 1 included, is sent after a random wait of up to this many minutes (a private send)." }
131202
131202
  },
131203
131203
  additionalProperties: false
@@ -132164,7 +132164,7 @@ ${canonical5}`;
132164
132164
  async function confirmTransfer(request, quote, decimals) {
132165
132165
  const usdcOut = usdcText2(BigInt(quote.outAtomic));
132166
132166
  const usdcMin = usdcText2(BigInt(quote.minOutAtomic));
132167
- const amount = formatAtomic(BigInt(request.amountAtomic), decimals);
132167
+ const amount = usdcText2(request.amountAtomic, decimals);
132168
132168
  const message = `Sell ${amount} of token ${request.tokenMint} for about ${usdcOut} USDC (at least ${usdcMin}; price impact ${(Number(quote.priceImpactPct) * 100).toFixed(2)} %), shield it, and send it to ${request.to} as ${request.parts} parts, each after a random wait of up to ${request.maxDelayMinutes} min? The address receives USDC, not the token.`;
132169
132169
  const askedAt = Date.now();
132170
132170
  try {
@@ -133648,7 +133648,7 @@ function createSetupTools({
133648
133648
  };
133649
133649
  return { tools: [setupTool, statusTool, ...MCP_TOOL_NAMES.map((name) => name === "x402_discover" ? discoverTool(discoverCacheDir) : notSetUp(name))], doneTool };
133650
133650
  }
133651
- var import_web3133, shownCaps, RELEASE_RETRY_MS, STAGE_WORDS, stageWords, INTERNAL_BALANCE_DETAIL, shownBreakdown, sessionLeftShown, TOKEN_UNIT_TOOLS, RESOURCE_PAYMENT_WAL_PREFIX2, POOL_LAYOUT_CODE, POOL_LAYOUT_UPDATE, USEPOD_VIA_INFERENCE_CHAT, SEND_RUNS_ON, DEPOSIT_RUNS_ON, CLI_FUNDS_PLAN_PREFIX, CLI_FUNDS_SCHEMA, SELF_PAY_ORIGIN3, REQUEST_ID4, POSITIVE_ATOMIC6, PRIVATE_HOST3, FORBIDDEN_HEADERS3, MAX_BODY_BYTES2, PREVIEW_TIMEOUT_MS, PREVIEW_SELLER_READ_MS, CALL_BUDGET_MS, budgetLeft, MONEY_FIRST_ANSWER_MS, BALANCE_AFTER_LANDING_MS, JOIN_CALL_MAX_MS, JOIN_NUDGE_QUIET_MS, JOIN_STEP_MS, sleep2, PROGRESS_EVERY_MS2, READ_BUDGET_MS, UNPAID_TOR_TRY_MS, UNPAID_CLEAR_MS, READ_BUDGET_TOOLS, FLOAT_BUSY_CODES, RETRY_MARGIN_MS, ASK_PAY_CODES, runningNpmVersion, terminalCommand, capPlaceWords, capRaiseText, cheaperSellerText, limitNext, SAFE_TEXT, MCP_TOOL_ALIASES, POOL_SWITCH_WHY, POOL_SWITCH_NEXT, SWITCH_STATES, quietSwitch, switchKey, ATOMIC_TEXT, FLOAT_ID, ANSWERED_EXIT_RETRY_MS, ANSWERED_EXIT_UNCONFIRMED_MS, ANSWERED_EXIT_RETRY_WINDOW_MS, ANSWERED_EXIT_CLOSED, switchAmount, switchEta, switchRows, OWNER_CAP_CODES, JOIN_END_MCP, JOIN_END_TERMINAL_LIBRARY, JOIN_RETRY_BACKOFF_MS, WALLET_CAP_FIX, MCP_CAP_FIX, capFix, ownerOnlyNext, mcpNext, CONFIDENTIAL_RETIRED, json3, answerResult, discoverTool, refuse12, publicOffer, FLOAT_PAID_CODES, FLOAT_PRIVATE_LEG, 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, OWN_KEY_TOKENS_MAX, OWN_TOKENS_GRACE_MS, tokenAmountText, elsewhereId, AGENT_ID_SCHEMA, agentIdName, minutesText2, ageWords2, DEPOSIT_SENT_STATES, depositLanded, 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, TRANSFER_CHANGE_READ_MS, TEMP_CLEAN_DELAY_MS, KEPT_ON_DISK, MAX_SHIELDED_BALANCE_ATOMIC, receiveGenerator, MCP_TOOL_NAMES, MCP_HIDDEN_TOOL_NAMES, setupTerminalNext, SETUP_POLL_NEXT, PRIVATE_INFERENCE_WHY, PRIVATE_INFERENCE_NEXT;
133651
+ var import_web3133, shownCaps, RELEASE_RETRY_MS, STAGE_WORDS, stageWords, INTERNAL_BALANCE_DETAIL, shownBreakdown, sessionLeftShown, TOKEN_UNIT_TOOLS, RESOURCE_PAYMENT_WAL_PREFIX2, POOL_LAYOUT_CODE, POOL_LAYOUT_UPDATE, USEPOD_VIA_INFERENCE_CHAT, SEND_RUNS_ON, DEPOSIT_RUNS_ON, CLI_FUNDS_PLAN_PREFIX, CLI_FUNDS_SCHEMA, SELF_PAY_ORIGIN3, REQUEST_ID4, POSITIVE_ATOMIC6, PRIVATE_HOST3, FORBIDDEN_HEADERS3, MAX_BODY_BYTES2, PREVIEW_TIMEOUT_MS, PREVIEW_SELLER_READ_MS, CALL_BUDGET_MS, budgetLeft, MONEY_FIRST_ANSWER_MS, BALANCE_AFTER_LANDING_MS, JOIN_CALL_MAX_MS, JOIN_NUDGE_QUIET_MS, JOIN_STEP_MS, sleep2, PROGRESS_EVERY_MS2, READ_BUDGET_MS, UNPAID_TOR_TRY_MS, UNPAID_CLEAR_MS, READ_BUDGET_TOOLS, FLOAT_BUSY_CODES, RETRY_MARGIN_MS, ASK_PAY_CODES, runningNpmVersion, terminalCommand, capPlaceWords, capRaiseText, cheaperSellerText, limitNext, SAFE_TEXT, MCP_TOOL_ALIASES, POOL_SWITCH_WHY, POOL_SWITCH_NEXT, SWITCH_STATES, quietSwitch, switchKey, ATOMIC_TEXT, FLOAT_ID, ANSWERED_EXIT_RETRY_MS, ANSWERED_EXIT_UNCONFIRMED_MS, ANSWERED_EXIT_RETRY_WINDOW_MS, ANSWERED_EXIT_CLOSED, switchAmount, switchEta, switchRows, OWNER_CAP_CODES, JOIN_END_MCP, JOIN_END_TERMINAL_LIBRARY, JOIN_RETRY_BACKOFF_MS, WALLET_CAP_FIX, MCP_CAP_FIX, capFix, ownerOnlyNext, mcpNext, CONFIDENTIAL_RETIRED, json3, answerResult, discoverTool, refuse12, publicOffer, FLOAT_PAID_CODES, FLOAT_PRIVATE_LEG, floatStillSettling, MONEY_ID2, toRecordId, linkKindOf, moneyId, REFUSED_CODES, refusedCodeOf, PLAIN_REASONS, plainReason, KEPT_SEND_TRIES, shownId, isSplitSend, sendPartId, PROMISED_NEXT, PUBLIC_READ_TIMEOUT_MS, RECIPIENT_CHECK_TIMEOUT_MS, withTimeout, ownKey, readFailure, OWN_KEY_TOKENS_MAX, OWN_TOKENS_GRACE_MS, tokenAmountText, elsewhereId, AGENT_ID_SCHEMA, agentIdName, minutesText2, ageWords2, DEPOSIT_SENT_STATES, depositLanded, 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, TRANSFER_CHANGE_READ_MS, TEMP_CLEAN_DELAY_MS, KEPT_ON_DISK, MAX_SHIELDED_BALANCE_ATOMIC, receiveGenerator, MCP_TOOL_NAMES, MCP_HIDDEN_TOOL_NAMES, setupTerminalNext, SETUP_POLL_NEXT, PRIVATE_INFERENCE_WHY, PRIVATE_INFERENCE_NEXT;
133652
133652
  var init_tools = __esm({
133653
133653
  "wallet/src/mcp/tools.mjs"() {
133654
133654
  "use strict";
@@ -133976,6 +133976,7 @@ var init_tools = __esm({
133976
133976
  sendPartId = (requestId) => (index) => `mp${index}-${toRecordId(requestId)}`;
133977
133977
  PROMISED_NEXT = "Nothing was sent. Part of the shielded balance is promised to the unsent parts of a private send. Wait until that send is done (wallet_unshield with its request_id shows its parts), or stop its unsent parts with wallet_cancel and its request_id, then call wallet_unshield again with the same arguments; or send at most freeAtomic now with a NEW request_id";
133978
133978
  PUBLIC_READ_TIMEOUT_MS = 3e4;
133979
+ RECIPIENT_CHECK_TIMEOUT_MS = 8e3;
133979
133980
  withTimeout = (promise, ms) => Promise.race([
133980
133981
  promise,
133981
133982
  new Promise((_, reject) => {
@@ -135192,7 +135193,7 @@ var init_stdioServer = __esm({
135192
135193
  SUPPORTED_PROTOCOL_VERSIONS = Object.freeze(["2025-06-18", "2025-03-26", "2024-11-05"]);
135193
135194
  MAX_LINE_BYTES = 4 * 1024 * 1024;
135194
135195
  POSITIVE_ATOMIC7 = /^[1-9][0-9]{0,30}$/;
135195
- INSTRUCTIONS = `A self-custody Shinjuku Shield wallet on this machine; the keys and the passphrase stay in this process and no tool takes or returns them. Every tool answer starts with say: one plain sentence to tell the user; the other fields hold the exact numbers. Map the user's plain request to one tool: "what is my balance" -> wallet_balance; "add money" / "deposit" / "fund the wallet" / "move USDC in from Phantom, an exchange, or another wallet" -> wallet_add_funds (it answers a one-time Solana address; the user sends USDC to it from any wallet or exchange, and the money becomes private a few minutes after it arrives, at the next wallet call: no other step after the send); "shield the USDC on this wallet's own address" -> wallet_shield (only for public USDC already on that address, wallet_balance unshielded; to add money, use wallet_add_funds); "stop" / "cancel" -> wallet_cancel with the request_id of what to stop; "pay this URL" -> x402_pay (call it directly, no preview is needed: it checks the price against any limit the owner set when it pays and pays nothing above it); "what does this URL cost" -> x402_preview (only when the user asks for the price; it pays nothing); "find a paid API for X" -> x402_discover (the wallet starts downloading the public listings when it starts and keeps a copy for 10 minutes; a search with no fresh copy waits for the download: a few seconds on a fast link, up to a few minutes on a slow link or over Tor; your search words never leave this machine), then x402_pay on the url it found; "send 2 USDC to <address>" -> wallet_unshield. wallet_balance answers ONE balance (balanceAtomic; balance is the same amount in USDC); breakdown names its parts. x402_pay pays any x402 seller from that one balance: a seller that cannot take a private payment directly (Coinbase CDP, PayAI, ...) is paid through Shinjuku (the wallet pays Shinjuku privately, the seller's price plus a small fee, and Shinjuku pays the seller); x402_pay's answer shows priceAtomic, feeAtomic, and amountAtomic (the total), and max_payment (USDC text, like "0.05") or max_payment_atomic, when you pass one, must cover the total. If such a seller does not take the payment, the refund comes back to the balance by itself within about 7 minutes; nothing more to do. Tool arguments and fields ending in Atomic are atomic units (USDC: 1000000 = 1 USDC); tell the user amounts in USDC. The wallet does the upkeep by itself: it reads the chain when the local balance is stale and writes an encrypted backup after money moves; the user never runs a CLI command for this. wallet_shield, wallet_add_funds, and wallet_unshield can run for minutes in the background: call again with the SAME request_id (each answer shows it as requestId) as its next says; the same request_id never moves money twice. Pass your own request_id to x402_pay: a retry after a timeout with that same request_id never pays twice. wallet_unshield never sends to the wallet's own deposit address (that would link the send to the user's deposits); a send to an address the owner did not approve ahead asks the user to confirm it in this client first. When the owner set limits, x402_pay never pays more than the limit for one payment or, in any 24 hours, the limit for this wallet (all MCP processes together); with no limit set, the balance is the only bound. Never claim a payment happened unless the answer has a transaction. On an error code that ends in _retry_same_request, call x402_pay again with the SAME request_id: it never pays twice. Privacy: the chain does not show which wallet paid, but this MCP host and its model provider see every URL, body, price, and paid answer the agent handles. Pool switches: when a tool answer has a poolSwitch field with tellUser true, tell the user each poolSwitch.items[].message where tellUser is true, in plain words, once. The wallet moves the balance by itself; the user never needs to act. During a switch the balance stays one number (balanceAtomic). wallet_balance shows the details.`;
135196
+ INSTRUCTIONS = `A self-custody Shinjuku Shield wallet on this machine; the keys and the passphrase stay in this process and no tool takes or returns them. Every tool answer starts with say: one plain sentence to tell the user; the other fields hold the exact numbers. Map the user's plain request to one tool: "what is my balance" -> wallet_balance; "add money" / "deposit" / "fund the wallet" / "move USDC in from Phantom, an exchange, or another wallet" -> wallet_add_funds (it answers a one-time address; the money becomes private a few minutes after it arrives, at the next wallet call); "shield the USDC on this wallet's own address" -> wallet_shield (only public USDC already there); "stop" / "cancel" -> wallet_cancel with the request_id of what to stop; "pay this URL" -> x402_pay (call it directly, no preview is needed); "what does this URL cost" -> x402_preview (only when the user asks for the price); "find a paid API for X" -> x402_discover, then x402_pay on the url it found; "send 2 USDC to <address>" -> wallet_unshield. wallet_balance answers ONE balance (balanceAtomic), and x402_pay pays any x402 seller from it (through Shinjuku for a seller that cannot take a private payment: max_payment must cover the total amountAtomic, fee included; a refund comes back by itself within about 7 minutes). Fields ending in Atomic are atomic units (USDC: 1000000 = 1 USDC); tell the user amounts in USDC. The wallet does its upkeep by itself (chain reads, encrypted backups); the user never runs a CLI command for it. wallet_shield, wallet_add_funds, and wallet_unshield can run for minutes: call again with the SAME request_id as next says. Pass your own request_id to x402_pay: the same request_id never pays or moves money twice (on a code that ends in _retry_same_request, call again with it). wallet_unshield never sends to the wallet's own deposit address; a send to an address the owner did not approve ahead asks the user to confirm it in this client first. When the owner set limits, x402_pay never pays above them (one payment, and any 24 hours for this wallet across all MCP processes); with none, the balance is the only bound. Never claim a payment happened unless the answer has a transaction. Privacy: the chain does not show which wallet paid, but this MCP host and its model provider see every URL, body, price, and paid answer the agent handles. When an item of an answer's poolSwitch.items[] has tellUser true, tell the user its message once, in plain words; the wallet moves the balance by itself.`;
135196
135197
  MCP_LOG_LEVELS = Object.freeze(["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"]);
135197
135198
  POOL_SWITCH_LOG_LEVEL = "info";
135198
135199
  validId = (id) => id === null || typeof id === "string" || typeof id === "number" && Number.isFinite(id);
@@ -135220,7 +135221,7 @@ var init_stdioServer = __esm({
135220
135221
  MCP_CLOSE_UNREADABLE_BOUND_MS = 6e4;
135221
135222
  MCP_CLOSE_POLL_MS = 500;
135222
135223
  closeHistoryBusy = (server) => () => compactHistoryReadsRunning() > 0 || memoReadsAheadRunning() > 0 || server.refreshOpen?.() === true;
135223
- SETUP_INSTRUCTIONS = `A self-custody Shinjuku Shield wallet server that is NOT SET UP on this machine yet. Every money tool answers wallet_not_set_up. When the user wants to pay, check a balance, or shield USDC, call shinjuku_setup: it asks the user to confirm in this client, then creates the wallet on this machine (the keys and a random passphrase stay in local files; no tool takes or returns them) and downloads and checks the proof tools. It takes minutes: call shinjuku_setup again as its answer's next says. When it answers stage done, the wallet tools are live in this same server (tools/list changes). ${INSTRUCTIONS}`;
135224
+ SETUP_INSTRUCTIONS = `This Shinjuku Shield wallet server is NOT SET UP on this machine yet: every money tool answers wallet_not_set_up. When the user wants to pay, check a balance, or add money, call shinjuku_setup: it asks the user to confirm in this client, creates the wallet here, and downloads the proof tools (minutes: call it again as its next says). At stage done the wallet tools are live in this same server. After setup: ${INSTRUCTIONS}`;
135224
135225
  }
135225
135226
  });
135226
135227
 
@@ -159643,7 +159644,8 @@ var PublicFundsFailure = class extends Error {
159643
159644
  };
159644
159645
  var failure = (code2, text3) => new PublicFundsFailure(`p03_funds_${code2}: ${text3}`);
159645
159646
  var iso2 = (ms) => new Date(ms).toISOString();
159646
- var usdc5 = (atomic4, decimals = 6) => formatAtomic(BigInt(atomic4), decimals);
159647
+ var plainUsdc = (text3) => text3.includes(".") ? text3.replace(/0+$/, "").replace(/\.$/, "") : text3;
159648
+ var usdc5 = (atomic4, decimals = 6) => plainUsdc(formatAtomic(BigInt(atomic4), decimals));
159647
159649
  var usdcFlag = usdcFlagText;
159648
159650
  var planName = (fundsId) => `${FUNDS_PLAN_PREFIX2}${fundsId}`;
159649
159651
  var lockPath = (walletFile) => `${walletFile}.add-funds.lock`;
@@ -159862,7 +159864,7 @@ function fundsView(store, plan, { now = Date.now(), running } = {}) {
159862
159864
  function fundsSummary(view2) {
159863
159865
  const privacy = `. Privacy: ${view2.privacy}`;
159864
159866
  const status = view2.status.endsWith(privacy) ? view2.status.slice(0, -privacy.length) : view2.status;
159865
- const total = view2.balance?.total ? ` (your balance now: ${view2.balance.total} USDC)` : "";
159867
+ const total = view2.balance?.total ? ` (your balance now: ${plainUsdc(String(view2.balance.total))} USDC)` : "";
159866
159868
  return [
159867
159869
  view2.ready ? `add-funds ${view2.fundsId} is ready: ${view2.amount} was added to your shielded balance${total}.` : `add-funds ${view2.fundsId} is not ready yet: ${status}.`,
159868
159870
  `Next: ${view2.next}.`
@@ -159894,6 +159896,7 @@ function fundsStatusSummary(view2) {
159894
159896
  }
159895
159897
  var HELD = /^(shinjuku(?:-wallet)?\b.*\bfailed: |shinjuku(?:-wallet)?\b.*: waiting for funds, nothing failed \(|hint: |Help: |next: |Examples?:|Every flag: | shinjuku(?:-wallet)? )/;
159896
159898
  var STEP_TOR_LINE = /^(?:tor|proxy): /;
159899
+ var DENOMINATION_WARNING = /^warning: no pool denomination is configured\b/;
159897
159900
  async function runStep(ctx, argv2) {
159898
159901
  if (ctx.signal.aborted) return { exit: 1, held: [], codes: [], details: {}, aborted: true };
159899
159902
  const file = join37(ctx.tempDir, `step-${randomBytes28(8).toString("hex")}.json`);
@@ -159919,7 +159922,7 @@ async function runStep(ctx, argv2) {
159919
159922
  `);
159920
159923
  return;
159921
159924
  }
159922
- if (ctx.quiet && isProgressLine(text3)) return;
159925
+ if (ctx.quiet && (isProgressLine(text3) || DENOMINATION_WARNING.test(text3))) return;
159923
159926
  if (!text3.trim() || ctx.shown.has(text3)) return;
159924
159927
  ctx.shown.add(text3);
159925
159928
  ctx.io.err(` ${text3}