shinjuku-shield 0.3.85 → 0.3.86

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.85. The npm packages `shinjuku-shield` and
9
+ This is version 0.3.86. 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.85 setup
15
+ npx -y --no-audit --no-fund shinjuku-shield@0.3.86 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.85
27
+ npm install -g shinjuku@0.3.86
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.85 mcp
39
+ claude mcp add --scope user shinjuku-shielded -- npx -y --no-audit --no-fund shinjuku-shield@0.3.86 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.85", "mcp"] } } }
45
+ { "mcpServers": { "shinjuku-shielded": { "command": "npx", "args": ["-y", "--no-audit", "--no-fund", "shinjuku-shield@0.3.86", "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": "654041bce4139bfb928180cc1a3c1ff05e570d9fc51ddfa718e9d0ef7fbf9095",
5
- "bytes": 12513236,
6
- "sourceCommit": "1e2a8c09784450c8bc0a5d391c428d08f699c44a",
4
+ "sha256": "b650bf0682830e05207f52bad2ba426ec7f8fa1bbbfdb7e028057865e3d83d24",
5
+ "bytes": 12522998,
6
+ "sourceCommit": "435a594c2af5c17d16a0f700bf2d9b7ff79af533",
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-23T20:33:40.235Z"
1285
+ "publishedAt": "2026-10-23T21:42:59.534Z"
1286
1286
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shield",
3
- "version": "0.3.85",
3
+ "version": "0.3.86",
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": "654041bc",
7
- "shinjukuSha256": "654041bce4139bfb928180cc1a3c1ff05e570d9fc51ddfa718e9d0ef7fbf9095",
6
+ "shinjukuRelease": "b650bf06",
7
+ "shinjukuSha256": "b650bf0682830e05207f52bad2ba426ec7f8fa1bbbfdb7e028057865e3d83d24",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
@@ -47222,6 +47222,7 @@ function reexec(args, mode, plainProxy) {
47222
47222
  const env = {
47223
47223
  ...process.env,
47224
47224
  NODE_USE_ENV_PROXY: "1",
47225
+ NODE_OPTIONS: proxiedNodeOptions(process.env.NODE_OPTIONS),
47225
47226
  ...proxyEnv(proxy),
47226
47227
  [MARK]: `${mode} ${proxy}`
47227
47228
  };
@@ -47305,7 +47306,7 @@ async function proxyEntry(argv2, run5, { err = (text3) => process.stderr.write(t
47305
47306
  }
47306
47307
  return reexec(options.args, options.mode, options.proxy);
47307
47308
  }
47308
- var TOR_HTTP_TUNNEL, TOR_CHECK, PROOF_TIMEOUT_MS, LOCAL_PROOF_TIMEOUT_MS, LOCAL_PROOF_MAX_BYTES, torProvenAt, proxyProvenAt, MARK, TOR_RUN_STARTED_ENV, ownProxyRefused, ONION_V3, RETRY_AFTER_CAP_MS, RETRY_AFTER_FLOOR_MS, RETRY_AFTER_DEFAULT_MS, RPC_NO_ANSWER_LIMIT_MS, RPC_PACING_CAP_MS, PACING_NOTICE_EVERY_MS, PACING_RATE_WINDOW_MS, TOR_MAX_ROTATIONS, runStartedAt, rpcNotBefore, pacingWaiters, pacingSince, pacedMs, rpcAnswered, lastAnswerAt, readSteps, pacingNoticeAt, pacingSamples, torMoves, torCircuitId, torMovesRefused, pendingMove, inTorRun, runNetworkMode, isOnionOrigin, ProxyUsageError, isOnionHost, TOR_EXPERT_BUNDLE, TUNNEL_LINE, TOR_INSTALL, OS_NAMES, osKey, torNotRunningMessage, NOTICE, QUIET_RUN_MARK, QUIET_NOTICE, quietRun, TOR_TROUBLE_LINE, quietTorErr, withRunTag, proxyEnv, pacedSoFar, RESENDABLE_METHOD, torCircuit, GLOBAL_DISPATCHER2, laneDispatchers, paymentWarmStop, PROXY_WARNING_FLAG, proxiedExecArgv, TOR_FROM_CONFIG_ENV, TOR_FROM_CONFIG_HINT;
47309
+ var TOR_HTTP_TUNNEL, TOR_CHECK, PROOF_TIMEOUT_MS, LOCAL_PROOF_TIMEOUT_MS, LOCAL_PROOF_MAX_BYTES, torProvenAt, proxyProvenAt, MARK, TOR_RUN_STARTED_ENV, ownProxyRefused, ONION_V3, RETRY_AFTER_CAP_MS, RETRY_AFTER_FLOOR_MS, RETRY_AFTER_DEFAULT_MS, RPC_NO_ANSWER_LIMIT_MS, RPC_PACING_CAP_MS, PACING_NOTICE_EVERY_MS, PACING_RATE_WINDOW_MS, TOR_MAX_ROTATIONS, runStartedAt, rpcNotBefore, pacingWaiters, pacingSince, pacedMs, rpcAnswered, lastAnswerAt, readSteps, pacingNoticeAt, pacingSamples, torMoves, torCircuitId, torMovesRefused, pendingMove, inTorRun, runNetworkMode, isOnionOrigin, ProxyUsageError, isOnionHost, TOR_EXPERT_BUNDLE, TUNNEL_LINE, TOR_INSTALL, OS_NAMES, osKey, torNotRunningMessage, NOTICE, QUIET_RUN_MARK, QUIET_NOTICE, quietRun, TOR_TROUBLE_LINE, quietTorErr, withRunTag, proxyEnv, pacedSoFar, RESENDABLE_METHOD, torCircuit, GLOBAL_DISPATCHER2, laneDispatchers, paymentWarmStop, PROXY_WARNING_FLAG, proxiedExecArgv, proxiedNodeOptions, TOR_FROM_CONFIG_ENV, TOR_FROM_CONFIG_HINT;
47309
47310
  var init_proxy = __esm({
47310
47311
  "wallet/src/net/proxy.mjs"() {
47311
47312
  "use strict";
@@ -47402,6 +47403,10 @@ var init_proxy = __esm({
47402
47403
  laneDispatchers = /* @__PURE__ */ new Map();
47403
47404
  PROXY_WARNING_FLAG = "--disable-warning=UNDICI-EHPA";
47404
47405
  proxiedExecArgv = (execArgv = process.execArgv) => execArgv.includes(PROXY_WARNING_FLAG) ? [...execArgv] : [...execArgv, PROXY_WARNING_FLAG];
47406
+ proxiedNodeOptions = (nodeOptions = process.env.NODE_OPTIONS) => {
47407
+ const options = String(nodeOptions ?? "").trim();
47408
+ return options.split(/\s+/).includes(PROXY_WARNING_FLAG) ? options : `${options} ${PROXY_WARNING_FLAG}`.trim();
47409
+ };
47405
47410
  TOR_FROM_CONFIG_ENV = "SHINJUKU_WALLET_TOR_FROM_CONFIG";
47406
47411
  TOR_FROM_CONFIG_HINT = '. Tor is on for every command because setup recorded it (config.json "tor": true); to run this one command without Tor, add --no-tor (the servers then see your IP address)';
47407
47412
  }
@@ -65869,6 +65874,27 @@ var init_errorCodes = __esm({
65869
65874
  "relay_reclaim_nothing_held",
65870
65875
  "relay_refused_deposit_body_invalid",
65871
65876
  "relay_refused_deposit_replay_conflict",
65877
+ // The exit relay's refusals (ShieldedSolanaP03ExitRelay.ts, createShieldedFacilitatorRoutes.ts), as
65878
+ // p03_unshield_relay_refused_<reason> (T16, 2026-10-23: an unknown one printed a bare p03_resource_cli_failed).
65879
+ "exit_amount_invalid",
65880
+ "exit_anchor_not_retained",
65881
+ "exit_body_invalid",
65882
+ "exit_buffer_unavailable",
65883
+ "exit_memo_digest_mismatch",
65884
+ "exit_operator_review_required",
65885
+ "exit_pending_retry_same_request",
65886
+ "exit_pool_busy_retry_same_request",
65887
+ "exit_pool_domain_mismatch",
65888
+ "exit_pool_invalid",
65889
+ "exit_proof_invalid",
65890
+ "exit_quote_expired",
65891
+ "exit_relay_busy",
65892
+ "exit_relay_unavailable",
65893
+ "exit_relayer_underfunded",
65894
+ "exit_replay_conflict",
65895
+ "exit_replay_consumed",
65896
+ "exit_settlement_not_landed_retry_same_request",
65897
+ "exit_vault_insufficient",
65872
65898
  "relay_relayer_underfunded_retry_later",
65873
65899
  "relay_service_required",
65874
65900
  "relay_transaction_invalid",
@@ -107736,10 +107762,23 @@ function markedAnchorRequest(requestAnchor) {
107736
107762
  }
107737
107763
  async function exitWhenAnchored(run5, { now = Date.now, sleep: sleep6 = (ms) => new Promise((resolve40) => setTimeout(resolve40, ms)), ring } = {}) {
107738
107764
  const deadline = now() + EXIT_ANCHOR_WAIT_MS;
107765
+ let reproved = false;
107766
+ let clockWaits = 0;
107739
107767
  for (; ; ) {
107740
107768
  try {
107741
107769
  return await run5();
107742
107770
  } catch (error) {
107771
+ if (!reproved && /p03_unshield_relay_refused_exit_quote_expired/.test(String(error?.message))) {
107772
+ reproved = true;
107773
+ progress("the exit proof's time window ended before the facilitator's exit transaction landed; nothing was sent. The wallet makes a new exit proof now and sends it");
107774
+ continue;
107775
+ }
107776
+ if (reproved && clockWaits < EXPIRED_PROOF_CLOCK_WAITS && /p03_note_exit_proved_still_valid_rerun_later/.test(String(error?.message))) {
107777
+ clockWaits += 1;
107778
+ progress(`the finalized chain clock has not passed the old exit proof's end yet; waiting ${EXPIRED_PROOF_CLOCK_WAIT_MS / 1e3} s, then the wallet makes the new proof (wait ${clockWaits}/${EXPIRED_PROOF_CLOCK_WAITS}; nothing was sent)`);
107779
+ await sleep6(EXPIRED_PROOF_CLOCK_WAIT_MS);
107780
+ continue;
107781
+ }
107743
107782
  if (!/p03_note_exit_anchor_not_current_wait_next_epoch/.test(String(error?.message))) throw error;
107744
107783
  const start = now();
107745
107784
  const subAnchors = subAnchorPool(start) !== void 0;
@@ -107776,7 +107815,13 @@ function facilitatorExitRelay(origin, { fetchImpl = fetch, pause: pause3 = (ms)
107776
107815
  const answer = await response.json().catch(() => ({}));
107777
107816
  if (response.status === 200 && answer.success === true && isSolanaSignature(answer.transaction)) return answer.transaction;
107778
107817
  if (response.status === 200 && answer.success === true) fail29("relay_transaction_invalid");
107779
- if (response.status === 200) fail29(`relay_refused_${String(answer.errorReason ?? "unknown").replace(/[^a-z0-9_]/g, "")}`);
107818
+ const refused = String(answer.errorReason ?? "unknown").replace(/[^a-z0-9_]/g, "");
107819
+ if (response.status === 200 && RELAY_RETRY_SAME.test(refused) && attempt < attempts) {
107820
+ progress(`waiting: the facilitator's exit relay answered ${refused} (its exit transaction has not landed yet); sending the same exit again in ${RELAY_RETRY_SAME_SECONDS} s (retry ${attempt}/${attempts - 1}). Nothing is lost: the note is still yours, and a rerun of the same command continues`);
107821
+ await pause3(RELAY_RETRY_SAME_SECONDS * 1e3);
107822
+ continue;
107823
+ }
107824
+ if (response.status === 200) fail29(`relay_refused_${refused}`);
107780
107825
  if (![429, 503].includes(response.status) || attempt >= attempts) fail29(`relay_status_${response.status}_retry_same_unshield`);
107781
107826
  const seconds2 = Number(response.headers.get("retry-after"));
107782
107827
  const waitSeconds = Number.isFinite(seconds2) && seconds2 > 0 ? Math.min(seconds2, 120) : 15;
@@ -107934,7 +107979,7 @@ async function unshieldOnce({ options, origin, historyUrl, walletPath: walletPat
107934
107979
  store.writeWal(`self-pay-${unshieldId}`, { ...store.readWal(`self-pay-${unshieldId}`), state: "exited", exitTransaction: exit.signature });
107935
107980
  return { amountAtomic: String(exit.amountAtomic), selfPayTransaction: paid.transaction, exitTransaction: exit.signature, replayed: exit.replayed };
107936
107981
  }
107937
- var import_web3121, USAGE2, REQUIRED2, OPTIONAL, fail29, progress, EPOCH_MS2, EPOCH_MARGIN_MS, EXIT_ANCHOR_WAIT_MS;
107982
+ var import_web3121, USAGE2, REQUIRED2, OPTIONAL, fail29, RELAY_RETRY_SAME, RELAY_RETRY_SAME_SECONDS, progress, EPOCH_MS2, EPOCH_MARGIN_MS, EXIT_ANCHOR_WAIT_MS, EXPIRED_PROOF_CLOCK_WAITS, EXPIRED_PROOF_CLOCK_WAIT_MS;
107938
107983
  var init_cli2 = __esm({
107939
107984
  "wallet/src/unshield/cli.mjs"() {
107940
107985
  "use strict";
@@ -107979,6 +108024,8 @@ var init_cli2 = __esm({
107979
108024
  fail29 = (reason) => {
107980
108025
  throw new Error(`p03_unshield_${reason}`);
107981
108026
  };
108027
+ RELAY_RETRY_SAME = /_retry_same_request$|^p03_e1_value_pending$|^p03_e1_proof_unavailable$|^p03_validity_proof_unavailable$/;
108028
+ RELAY_RETRY_SAME_SECONDS = 10;
107982
108029
  progress = (text3) => {
107983
108030
  try {
107984
108031
  process.stderr.write(`${text3}
@@ -107989,6 +108036,8 @@ var init_cli2 = __esm({
107989
108036
  EPOCH_MS2 = 3e5;
107990
108037
  EPOCH_MARGIN_MS = 35e3;
107991
108038
  EXIT_ANCHOR_WAIT_MS = EPOCH_MS2 + 6e4;
108039
+ EXPIRED_PROOF_CLOCK_WAITS = 8;
108040
+ EXPIRED_PROOF_CLOCK_WAIT_MS = 15e3;
107992
108041
  if (process.argv[1] && resolve19(process.argv[1]) === fileURLToPath13(new URL("__bundled_source__/wallet/src/unshield/cli.mjs", import.meta.url).href)) {
107993
108042
  main3().catch((error) => {
107994
108043
  console.error(describeCliErrorWithHint(error));
@@ -109931,6 +109980,12 @@ function refusalSay(a, tool, again, npmVersion) {
109931
109980
  if (code2 === "mcp_move_count_unreadable") {
109932
109981
  return "Nothing was moved. This wallet's 24-hour limit cannot be checked just now. Try again in a minute; if it repeats, tell your owner.";
109933
109982
  }
109983
+ if (code2 === "inference_ceiling_over_cap" && a.capKind === "call" && usdcText(a.capAtomic) && usdcText(a.ceilingAtomic) && usdcText(a.capNeededAtomic)) {
109984
+ const needed = usdcText(a.capNeededAtomic);
109985
+ const pays = usdcText(a.paysUpToAtomic);
109986
+ const allow = pays && BigInt(a.paysUpToAtomic) < BigInt(a.capNeededAtomic) ? `Allow ${needed} USDC; it still pays only up to ${pays} USDC.` : `Allow ${needed} USDC to pay it.`;
109987
+ return `Nothing was paid: this request's ${usdcText(a.capAtomic)} USDC cap is below the ${usdcText(a.ceilingAtomic)} USDC ceiling. ${allow}`;
109988
+ }
109934
109989
  if ((code2 === "inference_ceiling_over_cap" || code2 === "p03_float_over_maximum") && usdcText(a.totalAtomic) && usdcText(a.capAtomic)) {
109935
109990
  return `Nothing was paid: up to ${usdcText(a.totalAtomic)} USDC is over your ${usdcText(a.capAtomic)} USDC limit. ${raiseLine("--max-payment", npmVersion)}`;
109936
109991
  }
@@ -127775,6 +127830,15 @@ ${canonical5}`;
127775
127830
  const proxied = () => typeof ops.proxied === "function" ? ops.proxied() : proxyProvenAt() !== void 0;
127776
127831
  const usd = (atomic4) => `$${formatAtomic(atomic4, 6)}`;
127777
127832
  const INFERENCE_COST_WORDS = "A short qwen3-8-flash answer pays about 0.0011 USDC: usePod's price (often 0.00001-0.0001 USDC) plus the Shinjuku fee (0.001 USDC). The call's cap (max_payment, else --max-payment) must still cover the call's ceiling: max_tokens at the model's output price plus the prompt, and at least 0.01 USDC";
127833
+ const paysUpToAtomic = (inference, asked) => {
127834
+ try {
127835
+ const extra = inference.listModels().find((model) => model.id === asked.model).priceOutPerMAtomic * 100n;
127836
+ const lifted = BigInt(inference.ceilingAtomic({ ...asked, maxTokens: asked.maxTokens + 1e8 }));
127837
+ return lifted > BigInt(inference.USEPOD_MIN_PAYMENT_ATOMIC ?? 10000n) ? lifted - extra + 1000n : void 0;
127838
+ } catch {
127839
+ return void 0;
127840
+ }
127841
+ };
127778
127842
  const moreTokens = (maxTokens) => Math.max(1024, 2 * maxTokens);
127779
127843
  const torRefusal = () => refuse12(
127780
127844
  "inference_tor_required",
@@ -127864,7 +127928,7 @@ ${canonical5}`;
127864
127928
  const inferenceChat2 = {
127865
127929
  name: "inference_chat",
127866
127930
  title: "Ask an AI model, paid privately",
127867
- description: `Use this when the user wants an AI model's answer paid from the SHIELDED balance: it calls usePod's x402 chat endpoint (usePod picks the provider) and pays it privately (the chain does not show which wallet paid); use this tool, not x402_pay, for usePod. Only prompt (or messages) is needed: model defaults to qwen3-8-flash and max_tokens to 512. Called with neither prompt nor messages, it lists the models and their prices and pays nothing (for example qwen3-8-flash, deepseek-v4-1-flash, claude-opus-5). COST: a short qwen3-8-flash answer pays about 0.0011 USDC (usePod's price, often 0.00001-0.0001 USDC, plus the Shinjuku fee 0.001 USDC). The call's cap (max_payment, in USDC like "0.05", or max_payment_atomic; else --max-payment) must still cover the call's ceiling: max_tokens of output plus the prompt at the model's prices, and at least 0.01 USDC (usePod's floor for its standard offer). The total paid is never above the cap. The unused part is NOT refunded when the answer is shorter. max_tokens must be at least ${INFERENCE_MIN_MAX_TOKENS} (a reasoning model spends its first tokens thinking). Example: { "prompt": "Name three prime numbers" }. PRIVACY: usePod and the model's provider see the prompt and the answer; never put keys, passphrases, or secrets in it. It runs only when this MCP server was started with --tor or --proxy (else inference_tor_required, nothing paid), so they never see this machine's IP. The same caps as x402_pay apply (max_payment can only lower --max-payment). When the call can cost more than --max-payment and the MCP client can ask the user, the wallet asks the user once to allow this one call; else the answer says the cost and the limit. On an error code that ends in _retry_same_request, call inference_chat again with the SAME request_id and arguments: it never pays twice. No streaming.`,
127931
+ description: `Use this when the user wants an AI model's answer paid from the SHIELDED balance: it calls usePod's x402 chat endpoint (usePod picks the provider) and pays it privately (the chain does not show which wallet paid); use this tool, not x402_pay, for usePod. Only prompt (or messages) is needed: model defaults to qwen3-8-flash and max_tokens to 512. Called with neither prompt nor messages, it lists the models and their prices and pays nothing (for example qwen3-8-flash, deepseek-v4-1-flash, claude-opus-5). COST: a short qwen3-8-flash answer pays about 0.0011 USDC (usePod's price, often 0.00001-0.0001 USDC, plus the Shinjuku fee 0.001 USDC). The call's cap (max_payment, in USDC like "0.05", or max_payment_atomic; else --max-payment) must still cover the call's ceiling: max_tokens of output plus the prompt at the model's prices, and at least 0.01 USDC (usePod's floor for its standard offer). The total paid is never above the cap, and the cap is only a limit: a larger cap never pays more. The unused part of max_tokens is NOT refunded when the answer is shorter. max_tokens must be at least ${INFERENCE_MIN_MAX_TOKENS} (a reasoning model spends its first tokens thinking). Example: { "prompt": "Name three prime numbers" }. PRIVACY: usePod and the model's provider see the prompt and the answer; never put keys, passphrases, or secrets in it. It runs only when this MCP server was started with --tor or --proxy (else inference_tor_required, nothing paid), so they never see this machine's IP. The same caps as x402_pay apply (max_payment can only lower --max-payment). When the call can cost more than --max-payment and the MCP client can ask the user, the wallet asks the user once to allow this one call; else the answer says the cost and the limit. On an error code that ends in _retry_same_request, call inference_chat again with the SAME request_id and arguments: it never pays twice. No streaming.`,
127868
127932
  inputSchema: {
127869
127933
  type: "object",
127870
127934
  properties: {
@@ -127967,15 +128031,24 @@ ${canonical5}`;
127967
128031
  let askedLimit = false;
127968
128032
  let narrowed = cap.maxPaymentAtomic;
127969
128033
  if (ceiling > BigInt(narrowed)) {
127970
- const detail = `up to ${args.max_tokens} output tokens on ${args.model} have a ceiling of ${usdcText2(ceiling)} USDC, above this call's cap of ${usdcText2(narrowed)} USDC; the cap must be at least the ceiling; nothing was paid`;
128034
+ const detail = `up to ${args.max_tokens} output tokens on ${args.model} have a ceiling of ${usdcText2(ceiling)} USDC, above this call's cap of ${usdcText2(narrowed)} USDC; the cap must be at least the ceiling; nothing was paid; the cap is only a limit, not the price`;
127971
128035
  const atFloor = ceiling <= BigInt(inference.USEPOD_MIN_PAYMENT_ATOMIC ?? 10000n);
127972
- const pays = atFloor ? `The cap must be at least the ceiling ${usdcText2(ceiling)} USDC; the call usually pays about 0.0011 USDC (usePod's price plus the 0.001 USDC Shinjuku fee).` : `The cap must be at least the ceiling ${usdcText2(ceiling)} USDC; the call pays usePod's price for these max_tokens, at most the ceiling, plus the 0.001 USDC Shinjuku fee.`;
128036
+ const paysUpTo = paysUpToAtomic(inference, { model: args.model, messages, maxTokens: args.max_tokens });
128037
+ const pays = `A cap (max_payment, or --max-payment) is only a limit, not the price: the call pays usePod's price for its max_tokens plus the 0.001 USDC Shinjuku fee${paysUpTo === void 0 ? "" : ` (up to ${usdcText2(paysUpTo)} USDC in all at this model's listed prices)`}, and a larger cap never makes it pay more. The cap must still be at least the ceiling ${usdcText2(ceiling)} USDC.`;
127973
128038
  if (BigInt(narrowed) !== BigInt(launch)) {
127974
128039
  return refuse12(
127975
128040
  "inference_ceiling_over_cap",
127976
128041
  detail,
127977
128042
  atFloor ? `${pays} So pass max_payment "${usdcText2(ceiling + 1000n)}" (the ceiling plus the fee) or more if the spending limit allows it, or leave max_payment out` : `${pays} So lower max_tokens (at least ${INFERENCE_MIN_MAX_TOKENS}), pick a cheaper model (inference_chat with no prompt lists them), or pass max_payment "${usdcText2(ceiling + 1000n)}" (the ceiling plus the fee) if the spending limit allows it`,
127978
- { requestId, paid: false, ...facts }
128043
+ {
128044
+ requestId,
128045
+ paid: false,
128046
+ ...facts,
128047
+ capKind: "call",
128048
+ capAtomic: String(narrowed),
128049
+ capNeededAtomic: (ceiling + 1000n).toString(),
128050
+ ...paysUpTo === void 0 ? {} : { paysUpToAtomic: paysUpTo.toString() }
128051
+ }
127979
128052
  );
127980
128053
  }
127981
128054
  askedLimit = true;
@@ -155839,6 +155912,7 @@ init_job();
155839
155912
  init_rpcDefault();
155840
155913
  init_adoptOneTime();
155841
155914
  init_privateSend();
155915
+ init_selfPay();
155842
155916
  init_payMe();
155843
155917
  init_transferToken();
155844
155918
  init_recoveredPayment();
@@ -156718,7 +156792,7 @@ function inferPreflight(inference, values, prompt) {
156718
156792
  const floor = BigInt(inference.USEPOD_MIN_PAYMENT_ATOMIC ?? 0n);
156719
156793
  const worst = ceiling > floor ? ceiling : floor;
156720
156794
  if (worst > BigInt(values.get("--max-payment"))) {
156721
- throw new PublicFailure(`inference_ceiling_over_cap: usePod's price for this request may be up to ${usdcOr(worst)} (ceiling ${usdcOr(ceiling)}, minimum ${usdcOr(floor)}), and Shinjuku's fee comes on top; that is above --max-payment ${usdcFlagText(values.get("--max-payment"))} (${usdcOr(values.get("--max-payment"))}); nothing was paid. Raise --max-payment to at least ${usdcFlagText(worst)} (${usdcOr(worst)}) plus Shinjuku's fee${ceiling > floor ? ", or lower --max-tokens" : ""}`);
156795
+ throw new PublicFailure(`inference_ceiling_over_cap: usePod's price for this request may be up to ${usdcOr(worst)} (ceiling ${usdcOr(ceiling)}, minimum ${usdcOr(floor)}), and Shinjuku's fee comes on top; that is above --max-payment ${usdcFlagText(values.get("--max-payment"))} (${usdcOr(values.get("--max-payment"))}); nothing was paid. Raise --max-payment to at least ${usdcFlagText(worst)} (${usdcOr(worst)}) plus Shinjuku's fee${ceiling > floor ? ", or lower --max-tokens" : ""}. The cap is only a limit, not the price: the call pays usePod's price for its tokens plus the 0.001 USDC fee, and a larger cap never pays more`);
156722
156796
  }
156723
156797
  return { model, maxTokens, ceiling, worst };
156724
156798
  }
@@ -159701,6 +159775,23 @@ async function runUnshieldCommand(args, values, deps, io) {
159701
159775
  setArg("--recipient-owner", owner.toBase58());
159702
159776
  }
159703
159777
  let id = values.get("--unshield-id");
159778
+ let recorded;
159779
+ if (id !== void 0) {
159780
+ let opened;
159781
+ try {
159782
+ opened = openStore();
159783
+ } catch {
159784
+ opened = void 0;
159785
+ }
159786
+ recorded = opened === void 0 ? void 0 : recordedUnshield(opened, id, values);
159787
+ }
159788
+ if (recorded && values.get("--amount") === "all") {
159789
+ setArg("--amount", recorded.amountAtomic);
159790
+ io.err(`--amount all: unshield ${id} started earlier with ${usdcOr(recorded.amountAtomic)}; this run finishes it (it never sends twice)
159791
+ `);
159792
+ } else if (recorded && values.get("--amount") !== recorded.amountAtomic) {
159793
+ throw unshieldRefusal("id_reused_with_different_amount", `unshield ${id} started earlier with ${usdcOr(recorded.amountAtomic)}, not ${usdcOr(values.get("--amount"))}, and one unshield id always sends the amount it started with; nothing was sent. Finish it: ${CLI2} unshield --amount all${recorded.flags} --to ${owner.toBase58()} --unshield-id ${id} (it never sends twice). To send another amount, leave out --unshield-id`);
159794
+ }
159704
159795
  const ensureId = () => {
159705
159796
  if (id !== void 0) return;
159706
159797
  id = `unshield-${randomBytes35(6).toString("hex")}`;
@@ -159724,9 +159815,36 @@ async function runUnshieldCommand(args, values, deps, io) {
159724
159815
  if (!/^[1-9][0-9]*$/.test(String(sent ?? ""))) return;
159725
159816
  const walletAt = gated.indexOf("--wallet");
159726
159817
  const report = await refreshAfterUnshield(walletAt >= 0 ? gated[walletAt + 1] : values.get("--wallet"), values, deps, io);
159727
- if (terminal) io.out(`${privateBalanceLine(report, "Private balance now")}
159818
+ if (!terminal) return;
159819
+ let unfinished2 = [];
159820
+ try {
159821
+ unfinished2 = unfinishedUnshields(openStore());
159822
+ } catch {
159823
+ }
159824
+ io.out(`${[privateBalanceLine(report, "Private balance now"), ...unfinished2.map(unfinishedUnshieldLine)].join("\n")}
159728
159825
  `);
159729
159826
  }
159827
+ function recordedUnshield(store, id, values) {
159828
+ if (!/^[0-9a-zA-Z_-]{1,48}$/.test(id)) return void 0;
159829
+ const whole = (atomic4) => /^[1-9][0-9]*$/.test(String(atomic4 ?? ""));
159830
+ if (isSingleSend(values.get("--parts") ?? 1, values.get("--max-delay-minutes") ?? 0)) {
159831
+ const record = store.readWal(`self-pay-${id}`);
159832
+ return record?.schema === SELF_PAY_WAL_SCHEMA && whole(record.amountAtomic) ? { amountAtomic: String(record.amountAtomic), flags: "" } : void 0;
159833
+ }
159834
+ const plan = store.readWal(privateSendPlanName(id));
159835
+ return plan?.schema === PRIVATE_SEND_WAL_SCHEMA && whole(plan.amountAtomic) && Array.isArray(plan.parts) ? { amountAtomic: String(plan.amountAtomic), flags: ` --parts ${plan.parts.length} --max-delay-minutes ${plan.maxDelayMinutes}` } : void 0;
159836
+ }
159837
+ function unfinishedUnshields(store) {
159838
+ const read = (name) => {
159839
+ try {
159840
+ return store.readWal(name);
159841
+ } catch {
159842
+ return void 0;
159843
+ }
159844
+ };
159845
+ return store.listWal().filter((name) => name.startsWith("self-pay-")).map((name) => name.slice("self-pay-".length)).filter((unshieldId) => !unshieldId.startsWith("merge-") && !/^pocket-\d+-f\d+$/.test(unshieldId)).map((unshieldId) => ({ unshieldId, record: read(`self-pay-${unshieldId}`) })).filter(({ record }) => record?.schema === SELF_PAY_WAL_SCHEMA && record.ownerUnshield === true && record.state === "paid" && record.mode !== DIRECT_EXIT_MODE && /^[1-9][0-9]*$/.test(String(record.amountAtomic ?? ""))).map(({ unshieldId, record }) => ({ unshieldId, amountAtomic: String(record.amountAtomic) }));
159846
+ }
159847
+ var unfinishedUnshieldLine = ({ unshieldId, amountAtomic }) => `${usdcOr(amountAtomic)} is in an unshield that has not finished (unshield id ${unshieldId}): run ${CLI2} unshield --amount all --to <the address you sent it to> --unshield-id ${unshieldId} to finish it; it never sends twice`;
159730
159848
  function unshieldDoneText(answer, to) {
159731
159849
  const send3 = answer.privateSend;
159732
159850
  if (!send3) {
@@ -160442,17 +160560,19 @@ async function runBalance(args, values, deps, io) {
160442
160560
  }
160443
160561
  if (config?.pool === store.state.pool && config.program && config.proofTools && config.memoService) report.next = `${CLI2} balance --refresh --fresh`;
160444
160562
  }
160563
+ const unfinished2 = unfinishedUnshields(store);
160445
160564
  if (deps.stdoutIsTTY?.() === true && !values.has("--json")) {
160446
160565
  const notCurrent = report.status !== "current" && report.status !== "empty";
160447
160566
  io.out(`${[
160448
160567
  privateBalanceLine(report),
160568
+ ...unfinished2.map(unfinishedUnshieldLine),
160449
160569
  ...shown.map((entry) => entry.notice).filter((notice) => typeof notice === "string" && notice),
160450
160570
  ...notCurrent && typeof report.next === "string" ? [`next: ${report.next}`] : []
160451
160571
  ].join("\n")}
160452
160572
  `);
160453
160573
  return;
160454
160574
  }
160455
- io.out(`${JSON.stringify(shown.length ? { ...report, earlierDeposits: shown } : report, null, 2)}
160575
+ io.out(`${JSON.stringify({ ...report, ...shown.length ? { earlierDeposits: shown } : {}, ...unfinished2.length ? { unfinishedUnshields: unfinished2 } : {} }, null, 2)}
160456
160576
  `);
160457
160577
  }
160458
160578
  async function adoptOneTimeForBalance({ store, values, deps, io }) {
@@ -160769,6 +160889,8 @@ function failureHint(command2, reason, deps, values, floatError) {
160769
160889
  if (relayHint) return relayHint;
160770
160890
  const privacyCashHint = privacyCashFailureHint(reason);
160771
160891
  if (privacyCashHint) return privacyCashHint;
160892
+ const exitRelayHint = exitRelayFailureHint(reason);
160893
+ if (exitRelayHint) return exitRelayHint;
160772
160894
  if (/_retry_same_request/.test(reason) && /the quote ended/.test(reason)) {
160773
160895
  return `the quote of this request ended, so the same payment can no longer settle. Run ${CLI2} cancel with the same request id (${CLI2} help cancel): it checks on chain that nothing was paid and frees the request. Then pay again with a new --request-id`;
160774
160896
  }
@@ -160787,6 +160909,25 @@ function failureHint(command2, reason, deps, values, floatError) {
160787
160909
  }
160788
160910
  return void 0;
160789
160911
  }
160912
+ function exitRelayFailureHint(reason) {
160913
+ if (!/p03_unshield_relay_refused_/.test(reason) || /_retry_same_request|exit_pocket_/.test(reason)) return void 0;
160914
+ if (/relay_refused_exit_quote_expired/.test(reason)) {
160915
+ return "the exit proof's time window ended before the facilitator's exit transaction landed, and the new proof met the same; nothing was sent and the note is still yours. Rerun the same command (same --unshield-id): it makes a new proof, and it never sends twice";
160916
+ }
160917
+ if (/relay_refused_exit_anchor_not_retained/.test(reason)) {
160918
+ return "the pool no longer keeps the anchor this exit proof names; nothing was sent and the note is still yours. Rerun the same command after the proof's time window ends (at most 10 minutes after it was made): the wallet then makes a new proof";
160919
+ }
160920
+ if (/relay_refused_exit_replay_consumed/.test(reason)) {
160921
+ return `the chain shows this exit's note is already spent (an earlier exit of it landed, or another payment of this wallet spent it); nothing new was sent. ${CLI2} balance --refresh shows where the value is now`;
160922
+ }
160923
+ if (/relay_refused_exit_operator_review_required/.test(reason)) {
160924
+ return "the facilitator cannot tell yet whether its exit transaction for this note landed, so it holds this exit for its operator and sends nothing more. Keep the wallet folder as it is and rerun the same command later (same --unshield-id); it never sends twice";
160925
+ }
160926
+ if (/relay_refused_exit_vault_insufficient/.test(reason)) {
160927
+ return "the pool's vault holds less than this exit right now; nothing was sent and the note is still yours. Rerun the same command later (same --unshield-id)";
160928
+ }
160929
+ return "the facilitator's exit relay refused this exit (the code names why); nothing was sent and the note is still yours. Rerun the same command once (same --unshield-id); if the same code comes back, keep the wallet folder as it is and ask for help";
160930
+ }
160790
160931
  function relayDepositFailureHint(reason) {
160791
160932
  if (/p03_deposit_relay_approval_standing/.test(reason)) {
160792
160933
  return `your USDC is still at the one-time address with an approval that names this deposit alone: it can only become your own note, so it is safe to leave. Rerun the same command later; it signs nothing new while the approval stands. To take the USDC back instead: ${CLI2} help deposit-reclaim`;