shinjuku-shielded 0.3.72 → 0.3.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/manifest.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schema": "carbon-shielded-wallet/bundle-manifest/v1",
3
3
  "file": "shinjuku-wallet.mjs",
4
- "sha256": "07990722e8c34815b7af576fe293f456c602895b74cbe6f75d5ea3d8b5a1d578",
5
- "bytes": 12201104,
6
- "sourceCommit": "1fbac7a5425ed727041cc29b69fd7b31e8fba117",
4
+ "sha256": "bf5ad0c16095110ed0258b16525e55dc743dbaa36b6ba0e0738d4ab9c185ce90",
5
+ "bytes": 12226842,
6
+ "sourceCommit": "48cab6600f17f6d97cd27fcb657eaf344c1257ef",
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-22T17:29:13.245Z"
1285
+ "publishedAt": "2026-10-22T19:31:45.493Z"
1286
1286
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "shinjuku-shielded",
3
- "version": "0.3.72",
3
+ "version": "0.3.74",
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": "07990722",
7
- "shinjukuSha256": "07990722e8c34815b7af576fe293f456c602895b74cbe6f75d5ea3d8b5a1d578",
6
+ "shinjukuRelease": "bf5ad0c1",
7
+ "shinjukuSha256": "bf5ad0c16095110ed0258b16525e55dc743dbaa36b6ba0e0738d4ab9c185ce90",
8
8
  "license": "SEE LICENSE IN LICENSE.txt",
9
9
  "author": "Shinjuku Shielded",
10
10
  "homepage": "https://github.com/ShinjukuStaition/shinjuku-mcp#readme",
@@ -42600,6 +42600,11 @@ var init_rpcDefault = __esm({
42600
42600
  });
42601
42601
 
42602
42602
  // wallet/src/net/facilitatorOnion.mjs
42603
+ function takeSiteOnionRaces() {
42604
+ if (siteRacesTaken) return Promise.resolve([]);
42605
+ siteRacesTaken = true;
42606
+ return Promise.all(siteRaces);
42607
+ }
42603
42608
  function onionInit(init = {}) {
42604
42609
  if (init.dispatcher) return init;
42605
42610
  try {
@@ -42637,7 +42642,8 @@ function raceOnions(onions, {
42637
42642
  onNone,
42638
42643
  spares = [],
42639
42644
  spareAfterMs = SPARE_AFTER_MS,
42640
- onSpares
42645
+ onSpares,
42646
+ onProbe
42641
42647
  } = {}) {
42642
42648
  const controller = new AbortController();
42643
42649
  const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(timeoutMs)]);
@@ -42667,9 +42673,22 @@ function raceOnions(onions, {
42667
42673
  if (!controller.signal.aborted) onNone?.();
42668
42674
  settle(onions[0]);
42669
42675
  };
42676
+ const told = (onion, event) => {
42677
+ try {
42678
+ onProbe?.(onion, event);
42679
+ } catch {
42680
+ }
42681
+ };
42670
42682
  const start = (onion) => {
42671
42683
  open3 += 1;
42672
- probe(onion).then(settle, failed);
42684
+ told(onion, "start");
42685
+ probe(onion).then((won) => {
42686
+ told(onion, "answered");
42687
+ settle(won);
42688
+ }, () => {
42689
+ told(onion, controller.signal.aborted ? "stopped" : signal.aborted ? "timeout" : "failed");
42690
+ failed();
42691
+ });
42673
42692
  };
42674
42693
  startSpares = (cause) => {
42675
42694
  if (sparesStarted || done || signal.aborted) return;
@@ -42790,6 +42809,33 @@ async function routeFacilitatorsOverOnion(argv2, {
42790
42809
  const race = (group, onions, options) => {
42791
42810
  let unanswered2 = false;
42792
42811
  const onNone = options.onNone;
42812
+ const first = firstRaces.has(group.label) ? void 0 : { startedAt: Date.now() };
42813
+ if (first) firstRaces.set(group.label, first);
42814
+ let site;
42815
+ let ended;
42816
+ if (first && !group.path) {
42817
+ site = { startedAt: first.startedAt, probes: [] };
42818
+ siteRaces.push(new Promise((resolve38) => {
42819
+ ended = resolve38;
42820
+ }));
42821
+ const pinned = group.onions.length - (group.spares?.length ?? 0);
42822
+ const role = (onion) => {
42823
+ const index = group.onions.indexOf(onion);
42824
+ return index < pinned ? `pinned${index + 1}` : `spare${index - pinned + 1}`;
42825
+ };
42826
+ const note = (entry) => {
42827
+ if (site.endedAt === void 0) site.probes.push({ ...entry, at: Date.now() });
42828
+ };
42829
+ const onSpares = options.onSpares;
42830
+ options = {
42831
+ ...options,
42832
+ onProbe: (onion, event) => note({ role: role(onion), event }),
42833
+ ...onSpares ? { onSpares: (info) => {
42834
+ note({ role: "spares", event: "join" });
42835
+ onSpares(info);
42836
+ } } : {}
42837
+ };
42838
+ }
42793
42839
  const raced = raceOnions(onions, {
42794
42840
  fetchImpl,
42795
42841
  timeoutMs: raceTimeoutMs,
@@ -42800,10 +42846,18 @@ async function routeFacilitatorsOverOnion(argv2, {
42800
42846
  onNone?.();
42801
42847
  }
42802
42848
  });
42803
- const first = firstRaces.has(group.label) ? void 0 : { startedAt: Date.now() };
42804
- if (first) firstRaces.set(group.label, first);
42805
42849
  group.pending = raced.winner.then((onion) => {
42806
42850
  if (first) Object.assign(first, { endedAt: Date.now(), answered: !unanswered2 });
42851
+ if (site) {
42852
+ const open3 = /* @__PURE__ */ new Set();
42853
+ for (const probe of site.probes) {
42854
+ if (probe.event === "start") open3.add(probe.role);
42855
+ else open3.delete(probe.role);
42856
+ }
42857
+ for (const role of open3) site.probes.push({ role, event: "stopped", at: first.endedAt });
42858
+ Object.assign(site, { endedAt: first.endedAt, answered: !unanswered2 });
42859
+ ended(site);
42860
+ }
42807
42861
  group.winner = onion;
42808
42862
  group.pending = void 0;
42809
42863
  if (!unanswered2 && !raced.stopped) err(`tor: the onion ${hostOf(onion)} of ${group.label} answered first; it serves this run's requests to it
@@ -42904,18 +42958,18 @@ async function routeFacilitatorsOverOnion(argv2, {
42904
42958
  }
42905
42959
  };
42906
42960
  }
42907
- var SITE_ONIONS, SITE_SPARE_ONIONS, PINNED_FACILITATOR_ONIONS, PINNED_SPARE_ONIONS, HISTORY_ONIONS, PINNED_HISTORY_ONIONS, HISTORY_PROBE_PATH, FACILITATOR_FLAGS, V3_ONION, DISCOVERY_TIMEOUT_MS, WARM_TIMEOUT_MS, RACE_TIMEOUT_MS, ROUTE_WAIT_MS, SPARE_AFTER_MS, SPARE_CAUSE, routedGroups, liveOnions, hostOf, noAnswer, routedOnions, routedOrigins, onionsOfOrigin, routesOverOnion, firstRaces, firstOnionRace, GLOBAL_DISPATCHER, onionDispatchers;
42961
+ var SITE_ONIONS, SITE_SPARE_ONIONS, PINNED_FACILITATOR_ONIONS, PINNED_SPARE_ONIONS, HISTORY_ONIONS, PINNED_HISTORY_ONIONS, HISTORY_PROBE_PATH, FACILITATOR_FLAGS, V3_ONION, DISCOVERY_TIMEOUT_MS, WARM_TIMEOUT_MS, RACE_TIMEOUT_MS, ROUTE_WAIT_MS, SPARE_AFTER_MS, SPARE_CAUSE, routedGroups, liveOnions, hostOf, noAnswer, routedOnions, routedOrigins, onionsOfOrigin, routesOverOnion, firstRaces, siteRaces, siteRacesTaken, firstOnionRace, GLOBAL_DISPATCHER, onionDispatchers;
42908
42962
  var init_facilitatorOnion = __esm({
42909
42963
  "wallet/src/net/facilitatorOnion.mjs"() {
42910
42964
  "use strict";
42911
42965
  init_rpcDefault();
42912
42966
  SITE_ONIONS = Object.freeze([
42913
42967
  "http://2kfhlfuyuwvhmibjrpxqsg4nrbcxasjgjq7kmnfzgfwzptkhhznz3dad.onion",
42914
- "http://chhgw7nfggqhhjawz54bpsiihdow66s2yvkh6ccnugpcdmhhww6xkkqd.onion"
42968
+ "http://vofhhl336r4bnwn7hid2uvqaa72wx66xkdu2yt4n54xljsxwn2yds6qd.onion"
42915
42969
  ]);
42916
42970
  SITE_SPARE_ONIONS = Object.freeze([
42917
42971
  "http://ntr5qkdfgff4gspwderjnqtkzekyhb5a4j76ov4dqncxmcb53gdsmmid.onion",
42918
- "http://vofhhl336r4bnwn7hid2uvqaa72wx66xkdu2yt4n54xljsxwn2yds6qd.onion"
42972
+ "http://chhgw7nfggqhhjawz54bpsiihdow66s2yvkh6ccnugpcdmhhww6xkkqd.onion"
42919
42973
  ]);
42920
42974
  PINNED_FACILITATOR_ONIONS = Object.freeze({
42921
42975
  "https://shinjukustaition.com": SITE_ONIONS,
@@ -42955,6 +43009,8 @@ var init_facilitatorOnion = __esm({
42955
43009
  onionsOfOrigin = (origin) => routedOrigins.get(origin) ?? PINNED_FACILITATOR_ONIONS[origin] ?? [];
42956
43010
  routesOverOnion = (origin) => routedOrigins.has(origin);
42957
43011
  firstRaces = /* @__PURE__ */ new Map();
43012
+ siteRaces = [];
43013
+ siteRacesTaken = false;
42958
43014
  firstOnionRace = (origin) => {
42959
43015
  const race = firstRaces.get(origin);
42960
43016
  return race === void 0 ? void 0 : { ...race };
@@ -46739,6 +46795,16 @@ function markPhase(phase, outcome) {
46739
46795
  } catch {
46740
46796
  }
46741
46797
  }
46798
+ function poolReadOutcome(slot3, moved) {
46799
+ let text3;
46800
+ try {
46801
+ text3 = BigInt(slot3).toString();
46802
+ } catch {
46803
+ return void 0;
46804
+ }
46805
+ if (!/^\d{1,20}$/.test(text3)) return void 0;
46806
+ return `slot${text3}${Number.isSafeInteger(moved) && moved >= 0 ? `_moved${moved}` : ""}`;
46807
+ }
46742
46808
  var scope, phaseScopeActive;
46743
46809
  var init_phaseTiming = __esm({
46744
46810
  "wallet/src/pay/phaseTiming.mjs"() {
@@ -53881,7 +53947,7 @@ async function refreshedFromSnapshot({ connection, program, poolKey, authenticat
53881
53947
  if (snapshot.terminal !== authenticated.finalizedPoolSnapshot.terminal || snapshot.currentIndex !== authenticated.finalizedPoolSnapshot.currentIndex || snapshot.currentRoot !== authenticated.finalizedPoolSnapshot.currentRoot || !sameP03PoolPolicy(snapshot, authenticated.finalizedPoolSnapshot.policy) || !sameE1TreeHeader(snapshot, authenticated.finalizedPoolSnapshot.e1Tree, program, poolKey) || paymentSettler !== void 0 && snapshot.paymentSettler?.toBase58() !== paymentSettler) {
53882
53948
  throw Object.assign(
53883
53949
  new Error("p03_history_refresh_transition_detected"),
53884
- { currentIndex: snapshot.currentIndex, snapshotBinding: { programId: b58(program), pool: poolKey.toBase58() } }
53950
+ { currentIndex: snapshot.currentIndex, snapshotSlot: snapshot.slot, snapshotBinding: { programId: b58(program), pool: poolKey.toBase58() } }
53885
53951
  );
53886
53952
  }
53887
53953
  const priorCoverage = authenticated.deploymentCoverage;
@@ -54131,7 +54197,8 @@ async function readFinalizedPoolSliced(connection, program, pool2) {
54131
54197
  } catch {
54132
54198
  snapshot = void 0;
54133
54199
  }
54134
- markPhase(snapshot === void 0 ? "pool-sliced-fallback-answer" : "pool-sliced");
54200
+ if (snapshot === void 0) markPhase("pool-sliced-fallback-answer");
54201
+ else markPhase("pool-sliced", poolReadOutcome(snapshot.slot));
54135
54202
  return snapshot;
54136
54203
  }
54137
54204
  async function readFinalizedPoolForRefresh(connection, program, pool2) {
@@ -57150,6 +57217,16 @@ function rewriteFoldRoots(pool2, cache, next) {
57150
57217
  })().catch(() => {
57151
57218
  });
57152
57219
  }
57220
+ function dropCompactCacheRecords(pool2, cache) {
57221
+ if (cache === void 0 || cache === null) return Promise.resolve();
57222
+ return (async () => {
57223
+ const current = await cache.load();
57224
+ if (!current || current.pool !== pool2) return;
57225
+ const { foldRoots, savedAt, served, ...rest } = current;
57226
+ await cache.save({ ...rest, served: servedWithoutRecords(served) }, { savedAt });
57227
+ })().catch(() => {
57228
+ });
57229
+ }
57153
57230
  function markFoldRootsVerified(pool2, cache) {
57154
57231
  rewriteFoldRoots(pool2, cache, (foldRoots) => {
57155
57232
  const hashed = lastFolds.get(pool2);
@@ -57224,12 +57301,12 @@ function canonicalAddress(value, label) {
57224
57301
  }
57225
57302
  throw refuse3("p03_compact_history_record_invalid");
57226
57303
  }
57227
- function validateImport(raw, from) {
57304
+ function validateImport(raw, from, size = P03_COMPACT_PAGE) {
57228
57305
  const invalid5 = () => refuse3("p03_compact_history_source_answer_invalid");
57229
57306
  if (raw === null || typeof raw !== "object" || Array.isArray(raw) || Object.keys(raw).sort().join(",") !== IMPORT_KEYS.join(",")) throw invalid5();
57230
57307
  const { afterOrdinal, record, values, signature: signature2, slot: slot3, transactionIndex, instructionIndex } = raw;
57231
57308
  if (!isIndex(afterOrdinal) || !isSignature(signature2) || !isIndex(slot3) || !isIndex(transactionIndex) || !isIndex(instructionIndex)) throw invalid5();
57232
- if (from !== void 0 && !(afterOrdinal > from && afterOrdinal <= from + P03_COMPACT_PAGE || from === 0 && afterOrdinal === 0)) {
57309
+ if (from !== void 0 && !(afterOrdinal > from && afterOrdinal <= from + size || from === 0 && afterOrdinal === 0)) {
57233
57310
  throw invalid5();
57234
57311
  }
57235
57312
  if (!Array.isArray(values) || values.length > IMPORT_MAX_VALUES) throw invalid5();
@@ -57509,8 +57586,70 @@ function requestCompactFirstPage({ history, pool: pool2, signal }) {
57509
57586
  return Promise.resolve({ error });
57510
57587
  }
57511
57588
  }
57512
- function pageShapeValid(result, pool2, from) {
57513
- return Boolean(result) && typeof result === "object" && result.pool === pool2 && result.fromOrdinal === from && isIndex(result.total) && isIndex(result.syncedSlot) && Array.isArray(result.records) && result.records.length === Math.max(0, Math.min(result.total, from + P03_COMPACT_PAGE) - from) && Boolean(result.initialization) && isSignature(result.initialization.signature) && isIndex(result.initialization.slot);
57589
+ function pageShapeValid(result, pool2, from, size = P03_COMPACT_PAGE) {
57590
+ return Boolean(result) && typeof result === "object" && result.pool === pool2 && result.fromOrdinal === from && isIndex(result.total) && isIndex(result.syncedSlot) && Array.isArray(result.records) && result.records.length === Math.max(0, Math.min(result.total, from + size) - from) && Boolean(result.initialization) && isSignature(result.initialization.signature) && isIndex(result.initialization.slot);
57591
+ }
57592
+ function requestTailPage(history, pool2, from, grid) {
57593
+ return Promise.resolve().then(() => history._rpcRequest("getP03Transitions", [pool2, { commitment: "finalized", fromOrdinal: from, toOrdinal: from + grid }])).then((response) => ({ response }), (error) => ({ error }));
57594
+ }
57595
+ function requestCompactTailPage({ history, pool: pool2, currentIndex }) {
57596
+ try {
57597
+ const count2 = Number(BigInt(currentIndex) / SUBTREE_LEAVES3);
57598
+ if (!Number.isSafeInteger(count2) || count2 === 0) return Promise.resolve({ error: new Error("no records") });
57599
+ const source = typeof history === "string" ? connect2(history, "finalized", { timeoutMs: HISTORY_RPC_REQUEST_TIMEOUT_MS }) : history;
57600
+ if (!source || typeof source._rpcRequest !== "function") return Promise.resolve({ error: refuse3("p03_compact_history_connection_invalid") });
57601
+ return requestTailPage(source, new import_web397.PublicKey(pool2).toBase58(), tailFrom(count2, P03_COMPACT_TAIL_GRID), P03_COMPACT_TAIL_GRID);
57602
+ } catch (error) {
57603
+ return Promise.resolve({ error });
57604
+ }
57605
+ }
57606
+ function combineCompactTail(prefix, answer, { pool: pool2, from, count: count2, grid }) {
57607
+ if (answer.error !== void 0) return { miss: "fetch-failed" };
57608
+ const { response } = answer;
57609
+ if (response === null || typeof response !== "object" || "error" in response) return { miss: "fetch-failed" };
57610
+ const result = response.result;
57611
+ if (!pageShapeValid(result, pool2, from, grid)) return { miss: "shape-invalid" };
57612
+ if (result.total < count2) return { miss: "behind" };
57613
+ if (result.initialization.signature !== prefix.initialization.signature || result.initialization.slot !== prefix.initialization.slot) {
57614
+ return { miss: "init-mismatch" };
57615
+ }
57616
+ if (isIndex(prefix.syncedSlot) && result.syncedSlot < prefix.syncedSlot) return { miss: "synced-older" };
57617
+ let tailImports = [];
57618
+ if (result.imports !== void 0) {
57619
+ if (!Array.isArray(result.imports)) return { miss: "import-invalid" };
57620
+ try {
57621
+ for (const entry of result.imports) validateImport(entry, from, grid);
57622
+ } catch {
57623
+ return { miss: "import-invalid" };
57624
+ }
57625
+ tailImports = result.imports;
57626
+ }
57627
+ const cachedImports = Array.isArray(prefix.imports) ? prefix.imports : [];
57628
+ const held = Math.min(prefix.records.length, count2);
57629
+ for (let ordinal = from; ordinal < held; ordinal += 1) {
57630
+ if (!sameJson(prefix.records[ordinal], result.records[ordinal - from])) return { miss: "overlap-mismatch" };
57631
+ }
57632
+ const between = (entry) => entry?.afterOrdinal > from && entry.afterOrdinal < held;
57633
+ if (!sameJson(cachedImports.filter(between), tailImports.filter(between))) return { miss: "overlap-mismatch" };
57634
+ return { page: {
57635
+ records: [...prefix.records.slice(0, from), ...result.records.slice(0, count2 - from)],
57636
+ imports: [...from === 0 ? [] : cachedImports.filter((entry) => entry?.afterOrdinal <= from), ...tailImports],
57637
+ initialization: result.initialization,
57638
+ syncedSlot: result.syncedSlot
57639
+ } };
57640
+ }
57641
+ function comparePageZero(first, prefix, { pool: pool2, from }) {
57642
+ return first.then((answer) => {
57643
+ const result = answer?.response?.result;
57644
+ if (answer?.error !== void 0 || !pageShapeValid(result, pool2, 0)) return "unavailable";
57645
+ const upTo = Math.min(from, result.records.length);
57646
+ for (let ordinal = 0; ordinal < upTo; ordinal += 1) {
57647
+ if (!sameJson(prefix.records[ordinal], result.records[ordinal])) return "mismatch";
57648
+ }
57649
+ const reach = result.total > upTo ? upTo : upTo - 1;
57650
+ const imports = (list) => (Array.isArray(list) ? list : []).filter((entry) => entry?.afterOrdinal <= reach);
57651
+ return sameJson(imports(prefix.imports), imports(result.imports)) ? "match" : "mismatch";
57652
+ }, () => "unavailable");
57514
57653
  }
57515
57654
  function isCompactPageZeroAnswer(response, pool2) {
57516
57655
  if (response === null || typeof response !== "object" || "error" in response) return false;
@@ -57882,7 +58021,19 @@ async function readCompactP03HistoryNow({
57882
58021
  // check: they are NOT verified yet. A payment asks the memo service for their memos here
57883
58022
  // (payAndFetch.mjs), and uses an answer only for a context the verified report names. Its
57884
58023
  // result and any throw are ignored.
57885
- onList
58024
+ onList,
58025
+ // compactP03HistoryReport: the records of a compact cache that is not warm (cached.served). When the
58026
+ // list is read (no knownSnapshot), these records (or `served`'s, when the pool outgrew them) that pass
58027
+ // the start of the pool's last grid page take that page instead of every page (3b, combineCompactTail);
58028
+ // page 0 still goes out and is read to its end.
58029
+ prefix,
58030
+ // The tail page's size; P03_COMPACT_TAIL_GRID outside tests.
58031
+ tailGrid = P03_COMPACT_TAIL_GRID,
58032
+ // false: no tail request (compactP03HistoryReport's read again after a refused tail hit; it sent its tail already).
58033
+ tail = true,
58034
+ // compactP03HistoryReport: called on a tail hit with { first (page 0's answer promise), snapshot (the pool
58035
+ // account read) }, so a refused list can be read again from that same page 0 and pool read.
58036
+ onTailHit
57886
58037
  }) {
57887
58038
  const program = new import_web397.PublicKey(programId);
57888
58039
  const poolKey = new import_web397.PublicKey(pool2);
@@ -57909,24 +58060,63 @@ async function readCompactP03HistoryNow({
57909
58060
  }
57910
58061
  }
57911
58062
  snapshot ??= await readFinalizedPool(own, program, poolKey, { treeSlices: true });
57912
- markPhase(knownSnapshot !== void 0 ? "compact-pool-reused" : "compact-pool");
58063
+ markPhase(knownSnapshot !== void 0 ? "compact-pool-reused" : "compact-pool", poolReadOutcome(snapshot.slot));
57913
58064
  if (snapshot.terminal) throw refuse3("p03_compact_history_pool_terminal");
57914
58065
  if (snapshot.currentIndex % SUBTREE_LEAVES3 !== 0n) throw refuse3("p03_compact_history_snapshot_invalid");
57915
58066
  const count2 = Number(snapshot.currentIndex / SUBTREE_LEAVES3);
57916
58067
  if (!Number.isSafeInteger(count2)) throw refuse3("p03_compact_history_snapshot_invalid");
57917
58068
  const anchors = freezeFinalizedAnchors(snapshot);
57918
- const readServedPages = () => {
58069
+ const readServedPages = (zero = first) => {
57919
58070
  if (reusedFirst !== void 0) markPhase("compact-behind-page0-reused");
57920
58071
  if (first === void 0 && preFirst !== void 0) markPhase("compact-page0-handed-over");
57921
- return readPages(source, poolString, count2, first ?? preFirst, { wakeSlot: snapshot.slot });
58072
+ return readPages(source, poolString, count2, zero ?? preFirst, { wakeSlot: snapshot.slot });
58073
+ };
58074
+ let prefixCheck;
58075
+ const readTailOrPages = async () => {
58076
+ if (knownSnapshot !== void 0 || !tail) return readServedPages();
58077
+ const zero = first ?? preFirst ?? requestPage(source, poolString, 0);
58078
+ if (first === void 0 && preFirst === void 0) markPhase("compact-page0-sent");
58079
+ const from = tailFrom(count2, tailGrid);
58080
+ const tailRead = requestTailPage(source, poolString, from, tailGrid);
58081
+ markPhase("compact-tail-sent");
58082
+ const cachedRecords = served ?? prefix;
58083
+ if (!Array.isArray(cachedRecords?.records) || !cachedRecords.initialization) {
58084
+ markPhase("compact-tail-miss-no-cache");
58085
+ return readServedPages(zero);
58086
+ }
58087
+ if (cachedRecords.records.length <= from) {
58088
+ markPhase("compact-tail-miss-k-not-above-from");
58089
+ return readServedPages(zero);
58090
+ }
58091
+ if (await Promise.race([zero.then((answer) => answer.error === void 0), tailRead.then(() => false)])) {
58092
+ markPhase("compact-tail-miss-page0-first");
58093
+ return readServedPages(zero);
58094
+ }
58095
+ const combined = combineCompactTail(cachedRecords, await tailRead, { pool: poolString, from, count: count2, grid: tailGrid });
58096
+ if (combined.miss !== void 0) {
58097
+ markPhase(`compact-tail-miss-${combined.miss}`);
58098
+ return readServedPages(zero);
58099
+ }
58100
+ markPhase("compact-tail-hit");
58101
+ prefixCheck = comparePageZero(zero, cachedRecords, { pool: poolString, from });
58102
+ try {
58103
+ onTailHit?.({ first: zero, snapshot, check: prefixCheck });
58104
+ } catch {
58105
+ }
58106
+ return combined.page;
57922
58107
  };
57923
- const page = count2 === 0 ? { records: [], imports: [], initialization: void 0, syncedSlot: void 0 } : served !== void 0 && Array.isArray(served.records) && served.records.length >= count2 && served.initialization ? {
58108
+ const servedReaches = count2 > 0 && served !== void 0 && Array.isArray(served.records) && served.records.length >= count2 && Boolean(served.initialization);
58109
+ if (servedReaches && knownSnapshot === void 0 && tail && (first !== void 0 || preFirst !== void 0)) {
58110
+ requestTailPage(source, poolString, tailFrom(count2, tailGrid), tailGrid);
58111
+ markPhase("compact-tail-sent-unused");
58112
+ }
58113
+ const page = count2 === 0 ? { records: [], imports: [], initialization: void 0, syncedSlot: void 0 } : servedReaches ? {
57924
58114
  records: served.records.slice(0, count2),
57925
58115
  imports: Array.isArray(served.imports) ? served.imports : [],
57926
58116
  initialization: served.initialization,
57927
58117
  syncedSlot: served.syncedSlot,
57928
58118
  cached: true
57929
- } : await readServedPages().catch((error) => {
58119
+ } : await readTailOrPages().catch((error) => {
57930
58120
  if (error?.compactHistoryRefusal === "p03_compact_history_source_behind") error.behindSnapshot = snapshot;
57931
58121
  throw error;
57932
58122
  });
@@ -58085,7 +58275,9 @@ async function readCompactP03HistoryNow({
58085
58275
  ...page.imports.length > 0 ? { imports: page.imports } : {},
58086
58276
  initialization: page.initialization,
58087
58277
  syncedSlot: page.syncedSlot
58088
- })
58278
+ }),
58279
+ // 3b, a tail hit only: resolves "match", "mismatch" or "unavailable" when page 0 has come in full (comparePageZero).
58280
+ ...prefixCheck !== void 0 ? { prefixCheck } : {}
58089
58281
  });
58090
58282
  }
58091
58283
  function refusalReason(error) {
@@ -58116,7 +58308,8 @@ async function compactP03HistoryReportRun({
58116
58308
  knownFirst,
58117
58309
  poolPreRead,
58118
58310
  firstPreRead,
58119
- onList
58311
+ onList,
58312
+ tailGrid
58120
58313
  }) {
58121
58314
  if (deploymentIdentity === void 0) throw new Error("p03_history_deployment_identity_required");
58122
58315
  const program = new import_web397.PublicKey(programId);
@@ -58189,7 +58382,7 @@ async function compactP03HistoryReportRun({
58189
58382
  };
58190
58383
  const preFold = cached ? startCompactPreFold(poolKey.toBase58(), cached) : void 0;
58191
58384
  const e1Ahead = cached ? startCompactE1Candidate(program.toBase58(), poolKey.toBase58(), cached, preFold) : void 0;
58192
- const compact = await readCompactP03History({
58385
+ const readOnce = (extra) => readCompactP03History({
58193
58386
  history,
58194
58387
  own: connection,
58195
58388
  programId,
@@ -58203,8 +58396,48 @@ async function compactP03HistoryReportRun({
58203
58396
  ...firstPreRead !== void 0 ? { firstPreRead } : {},
58204
58397
  ...onList !== void 0 ? { onList } : {},
58205
58398
  ...warm ? { served: cached.served } : {},
58206
- ...cached ? { initialization: cached.initialization } : {}
58399
+ ...cached ? { initialization: cached.initialization } : {},
58400
+ ...tailGrid !== void 0 ? { tailGrid } : {},
58401
+ ...extra
58207
58402
  });
58403
+ let pageZeroMismatch = false;
58404
+ let readAgain = false;
58405
+ let replaced = false;
58406
+ let saving;
58407
+ let dropping;
58408
+ const watchPageZero = (check) => check.then((result) => {
58409
+ markPhase(`compact-page0-compare-${result}`);
58410
+ if (result !== "mismatch") return;
58411
+ try {
58412
+ process.stderr.write("history: WARNING the history source's page 0 differs from this wallet's cached records before the pool's last page; the cache's records are replaced or dropped so the next read takes every page\n");
58413
+ } catch {
58414
+ }
58415
+ pageZeroMismatch = true;
58416
+ if (replaced) return;
58417
+ if (!readAgain) lastFolds.delete(poolKey.toBase58());
58418
+ const drop = () => replaced ? void 0 : dropCompactCacheRecords(poolKey.toBase58(), cache);
58419
+ dropping = (saving ?? Promise.resolve()).then(drop, drop);
58420
+ }).catch(() => {
58421
+ });
58422
+ let tailHit;
58423
+ let compact;
58424
+ try {
58425
+ compact = await readOnce(cached ? { ...warm ? {} : { prefix: cached.served }, onTailHit: (hit) => {
58426
+ tailHit = hit;
58427
+ watchPageZero(hit.check);
58428
+ } } : {});
58429
+ } catch (error) {
58430
+ if (tailHit === void 0 || !TAIL_RETRY_REFUSALS.has(error?.compactHistoryRefusal)) throw error;
58431
+ markPhase("compact-tail-refused-page0");
58432
+ readAgain = true;
58433
+ lastFolds.delete(poolKey.toBase58());
58434
+ try {
58435
+ process.stderr.write(`history: WARNING this wallet's cached records with the pool's last page failed a check (${error.compactHistoryRefusal}); reading every page again
58436
+ `);
58437
+ } catch {
58438
+ }
58439
+ compact = await readOnce({ firstPreRead: tailHit.first, poolPreRead: Promise.resolve(tailHit.snapshot), tail: false });
58440
+ }
58208
58441
  const initialization = compact.verifiedInitialization;
58209
58442
  if (initialization === void 0) throw refuse3("p03_compact_history_pool_empty");
58210
58443
  const snapshot = compact.poolSnapshot;
@@ -58267,8 +58500,9 @@ async function compactP03HistoryReportRun({
58267
58500
  });
58268
58501
  }
58269
58502
  if (cache) {
58503
+ if (dropping !== void 0) await dropping;
58270
58504
  try {
58271
- await cache.save({
58505
+ await (saving = Promise.resolve().then(() => cache.save({
58272
58506
  programId: program.toBase58(),
58273
58507
  pool: poolKey.toBase58(),
58274
58508
  snapshot: {
@@ -58280,10 +58514,12 @@ async function compactP03HistoryReportRun({
58280
58514
  // pre-fold hashes their roots ahead. Counts only: every root is checked live again.
58281
58515
  anchorCounts: [...new Set(compact.finalizedPoolSnapshot.anchors.map((anchor) => Number(anchor.index / SUBTREE_LEAVES3)))]
58282
58516
  },
58283
- served: compact.served,
58517
+ served: pageZeroMismatch && !readAgain ? servedWithoutRecords(compact.served) : compact.served,
58284
58518
  initialization,
58285
58519
  attestation: attested.attestation
58286
- });
58520
+ })).then(() => {
58521
+ if (readAgain) replaced = true;
58522
+ }));
58287
58523
  } catch {
58288
58524
  }
58289
58525
  }
@@ -58304,7 +58540,7 @@ async function tryCompactP03HistoryReport(options) {
58304
58540
  return { ok: false, reason, ...detail ? { detail } : {}, error };
58305
58541
  }
58306
58542
  }
58307
- var import_web397, P03_COMPACT_HISTORY_SOURCE_MODEL, P03_COMPACT_PAGE, P03_COMPACT_SPOT_CHECKS, FIELD_MODULUS5, SUBTREE_LEAVES3, LOCAL_DEPTH2, OUTER_DEPTH2, PRIMARY_PROOF_BYTES2, FIELD_BYTES3, ED25519_SPKI_PREFIX2, VERSION_1_PREFIX2, HEX64, BASE58, REPLAY_STORE_INDEXED2, PROFILES2, MOVE_HEAD_BYTES2, MOVE_INSERTED_VALUES2, refuse3, hash4, bitAt, zeroRootsMemo, lastFolds, PRE_FOLD_SLICE_MS, preFolds, hex64, droppedPools, verifyQuietMs, VERIFY_SLICE_MS, VERIFY_POLL_MS, verifying, unrefTurn, isSignature, isIndex, IMPORT_KEYS, IMPORT_MAX_VALUES, LIST_ATTEMPTS, pageZeroInFlight, OWN_READ_TIMEOUT_MS, BOUNDED_OWN_METHODS, SPOT_HEDGE_AFTER_MS, SPOT_HEDGE_LANE, spotReadId, POOL_HEDGE_AFTER_MS, POOL_HEDGE_LANE, poolReadId, compactReportsRunning, compactHistoryReadsRunning;
58543
+ var import_web397, P03_COMPACT_HISTORY_SOURCE_MODEL, P03_COMPACT_PAGE, P03_COMPACT_SPOT_CHECKS, FIELD_MODULUS5, SUBTREE_LEAVES3, LOCAL_DEPTH2, OUTER_DEPTH2, PRIMARY_PROOF_BYTES2, FIELD_BYTES3, ED25519_SPKI_PREFIX2, VERSION_1_PREFIX2, HEX64, BASE58, REPLAY_STORE_INDEXED2, PROFILES2, MOVE_HEAD_BYTES2, MOVE_INSERTED_VALUES2, refuse3, hash4, bitAt, zeroRootsMemo, lastFolds, PRE_FOLD_SLICE_MS, preFolds, hex64, droppedPools, servedWithoutRecords, verifyQuietMs, VERIFY_SLICE_MS, VERIFY_POLL_MS, verifying, unrefTurn, isSignature, isIndex, IMPORT_KEYS, IMPORT_MAX_VALUES, LIST_ATTEMPTS, pageZeroInFlight, OWN_READ_TIMEOUT_MS, BOUNDED_OWN_METHODS, P03_COMPACT_TAIL_GRID, TAIL_RETRY_REFUSALS, tailFrom, sameJson, SPOT_HEDGE_AFTER_MS, SPOT_HEDGE_LANE, spotReadId, POOL_HEDGE_AFTER_MS, POOL_HEDGE_LANE, poolReadId, compactReportsRunning, compactHistoryReadsRunning;
58308
58544
  var init_compact = __esm({
58309
58545
  "wallet/src/history/compact.mjs"() {
58310
58546
  "use strict";
@@ -58421,6 +58657,11 @@ var init_compact = __esm({
58421
58657
  preFolds = /* @__PURE__ */ new Map();
58422
58658
  hex64 = (value) => value.toString(16).padStart(64, "0");
58423
58659
  droppedPools = /* @__PURE__ */ new Set();
58660
+ servedWithoutRecords = (served) => ({
58661
+ records: [],
58662
+ initialization: served.initialization,
58663
+ ...served.syncedSlot !== void 0 ? { syncedSlot: served.syncedSlot } : {}
58664
+ });
58424
58665
  verifyQuietMs = 3e3;
58425
58666
  VERIFY_SLICE_MS = 20;
58426
58667
  VERIFY_POLL_MS = 250;
@@ -58437,6 +58678,17 @@ var init_compact = __esm({
58437
58678
  pageZeroInFlight = /* @__PURE__ */ new Map();
58438
58679
  OWN_READ_TIMEOUT_MS = HISTORY_RPC_REQUEST_TIMEOUT_MS;
58439
58680
  BOUNDED_OWN_METHODS = /* @__PURE__ */ new Set(["getAccountInfo", "getAccountInfoAndContext", "getMultipleAccountsInfo", "getMultipleAccountsInfoAndContext"]);
58681
+ P03_COMPACT_TAIL_GRID = 512;
58682
+ TAIL_RETRY_REFUSALS = /* @__PURE__ */ new Set([
58683
+ "p03_compact_history_root_mismatch",
58684
+ "p03_compact_history_kind_count_mismatch",
58685
+ "p03_compact_history_record_order_invalid",
58686
+ "p03_compact_history_record_invalid",
58687
+ "p03_compact_history_record_after_snapshot",
58688
+ "p03_compact_history_source_answer_invalid"
58689
+ ]);
58690
+ tailFrom = (count2, grid) => Math.floor((count2 - 1) / grid) * grid;
58691
+ sameJson = (left, right) => JSON.stringify(left) === JSON.stringify(right);
58440
58692
  SPOT_HEDGE_AFTER_MS = 4e3;
58441
58693
  SPOT_HEDGE_LANE = "spot-hedge";
58442
58694
  spotReadId = 0;
@@ -59241,7 +59493,7 @@ async function walkWalletP03HistoryNow({ store, connection: given, programId, po
59241
59493
  } else if (earlier !== void 0) {
59242
59494
  markPhase("refresh-start");
59243
59495
  let slicedPreRead;
59244
- let page0Stop;
59496
+ let page0Asked = false;
59245
59497
  let page0Answered = false;
59246
59498
  if (compactNext) {
59247
59499
  const started = startCompactPoolSnapshot({ connection, programId, pool: pool2 });
@@ -59249,8 +59501,8 @@ async function walkWalletP03HistoryNow({ store, connection: given, programId, po
59249
59501
  poolPreRead = started.full;
59250
59502
  markPhase("compact-pool-prestarted");
59251
59503
  if (inTorRun() || Date.now() - remembered.at > PAGE0_PREREAD_AFTER_MS) {
59252
- page0Stop = inTorRun() ? new AbortController() : void 0;
59253
- firstPreRead = requestCompactFirstPage({ history: connection.historyRpcSource, pool: pool2, signal: page0Stop?.signal });
59504
+ page0Asked = true;
59505
+ firstPreRead = requestCompactFirstPage({ history: connection.historyRpcSource, pool: pool2 });
59254
59506
  firstPreRead.then(() => {
59255
59507
  page0Answered = true;
59256
59508
  });
@@ -59278,19 +59530,23 @@ async function walkWalletP03HistoryNow({ store, connection: given, programId, po
59278
59530
  })();
59279
59531
  try {
59280
59532
  const refreshed = await refreshing;
59281
- markPhase("walk-refreshed");
59282
- if (page0Stop !== void 0) {
59283
- markPhase(page0Answered ? "compact-page0-answered-before-hit" : "compact-page0-stopped-on-hit");
59284
- page0Stop.abort();
59533
+ markPhase("walk-refreshed", poolReadOutcome(refreshed?.finalizedPoolSnapshot?.slot));
59534
+ if (page0Asked) {
59535
+ markPhase(page0Answered ? "compact-page0-answered-before-hit" : "compact-page0-read-on-after-hit");
59536
+ const currentIndex = refreshed?.finalizedPoolSnapshot?.currentIndex;
59537
+ if (typeof currentIndex === "bigint" && currentIndex > 0n) {
59538
+ requestCompactTailPage({ history: connection.historyRpcSource, pool: pool2, currentIndex });
59539
+ markPhase("compact-tail-sent-on-hit");
59540
+ }
59285
59541
  }
59286
59542
  rememberRunReport(runKey, refreshed);
59287
59543
  writeStderr("history: the pool made no transition since this run's last walk; that history was moved to the current pool account " + (fromSliced ? "(your own RPC read the pool account and the program's deployment account in one request)\n" : "(your own RPC read the pool account and the program's deployment account; the history source answered one signature read)\n"));
59288
59544
  return refreshed;
59289
59545
  } catch (error) {
59290
- markPhase("refresh-missed");
59291
- if (error?.message === "p03_history_refresh_transition_detected" && typeof error.currentIndex === "bigint" && error.snapshotBinding?.programId === String(programId) && error.snapshotBinding?.pool === String(pool2)) {
59292
- movedIndex = error.currentIndex;
59293
- }
59546
+ const moved = error?.message === "p03_history_refresh_transition_detected" && typeof error.currentIndex === "bigint" && error.snapshotBinding?.programId === String(programId) && error.snapshotBinding?.pool === String(pool2);
59547
+ const earlierIndex = earlier.finalizedPoolSnapshot?.currentIndex;
59548
+ markPhase("refresh-missed", moved ? poolReadOutcome(error.snapshotSlot, typeof earlierIndex === "bigint" ? Number((error.currentIndex - earlierIndex) / SUBTREE_LEAVES4) : void 0) : void 0);
59549
+ if (moved) movedIndex = error.currentIndex;
59294
59550
  floor?.reset();
59295
59551
  if (strict) floor?.strict();
59296
59552
  }
@@ -73866,6 +74122,10 @@ async function runRecoveredP03NoteExit({
73866
74122
  photonUrl,
73867
74123
  // Parsed --recovered-note-file (recoveredNotesFromFile): notes the walk cannot find.
73868
74124
  recoveredNoteFile,
74125
+ // Relay path only (realSpeed3 2026-10-22, mcp55 Part 1): return once the relay answered (it answers only after the
74126
+ // exit is finalized) and the "confirmed" record holds the signature, with `settled: false` and `finish`, the post-exit
74127
+ // walk and postcondition below. The caller runs `finish` after its own answer; a rerun of this exit resumes the same way.
74128
+ answerAtRelay = false,
73869
74129
  deps = {}
73870
74130
  }) {
73871
74131
  const state = store.state;
@@ -74295,39 +74555,46 @@ async function runRecoveredP03NoteExit({
74295
74555
  }
74296
74556
  wal = { ...wal, state: "confirmed", signature: signature2 };
74297
74557
  store.writeWal(walName2, wal);
74298
- for (let attempt = 1; ; attempt += 1) {
74299
- history = await walkHistory();
74300
- markPhase("exit-post-walk");
74301
- poolState2 = await readPool2(connection, pool2, "finalized");
74302
- if (!poolState2 || !poolState2.owner.equals(chainProgramId) || poolState2.version !== 3) {
74303
- throw new Error("finalized note-exit pool postcondition invalid");
74558
+ const preExitHistory = history;
74559
+ const finish = async () => {
74560
+ for (let attempt = 1; ; attempt += 1) {
74561
+ history = await walkHistory();
74562
+ markPhase("exit-post-walk");
74563
+ poolState2 = await readPool2(connection, pool2, "finalized");
74564
+ if (!poolState2 || !poolState2.owner.equals(chainProgramId) || poolState2.version !== 3) {
74565
+ throw new Error("finalized note-exit pool postcondition invalid");
74566
+ }
74567
+ if (attempt >= 4 || history.finalizedPoolSnapshot.currentIndex === poolState2.currentIndex && history.finalizedPoolSnapshot.currentRoot === poolState2.currentRoot) break;
74304
74568
  }
74305
- if (attempt >= 4 || history.finalizedPoolSnapshot.currentIndex === poolState2.currentIndex && history.finalizedPoolSnapshot.currentRoot === poolState2.currentRoot) break;
74306
- }
74307
- await prepareFold(history);
74308
- const spentPath = buildRecoveryPath({
74309
- history,
74310
- recoveredOutput: recoveredForPath,
74311
- circuitSource: p03CircuitSource,
74312
- anchor: recoveryPath.anchor
74313
- });
74314
- if (spentPath.spendStatus !== "spent" || poolState2.currentIndex < frontier.nextIndex + 128n || history.finalizedPoolSnapshot.currentIndex !== poolState2.currentIndex || history.finalizedPoolSnapshot.currentRoot !== poolState2.currentRoot) {
74315
- throw new Error("finalized note-exit postcondition failed");
74569
+ await prepareFold(history);
74570
+ const spentPath = buildRecoveryPath({
74571
+ history,
74572
+ recoveredOutput: recoveredForPath,
74573
+ circuitSource: p03CircuitSource,
74574
+ anchor: recoveryPath.anchor
74575
+ });
74576
+ if (spentPath.spendStatus !== "spent" || poolState2.currentIndex < frontier.nextIndex + 128n || history.finalizedPoolSnapshot.currentIndex !== poolState2.currentIndex || history.finalizedPoolSnapshot.currentRoot !== poolState2.currentRoot) {
74577
+ throw new Error("finalized note-exit postcondition failed");
74578
+ }
74579
+ store.writeWal(walName2, { ...wal, state: "settled" });
74580
+ return Object.freeze({
74581
+ signature: signature2,
74582
+ replayed: false,
74583
+ noteCommitment: selected.commitment,
74584
+ amountAtomic: amount,
74585
+ poolIndex: poolState2.currentIndex,
74586
+ manifestSha256: proved.manifestSha256,
74587
+ ...denomination.record ? { denominationPolicy: denomination.record } : {},
74588
+ ...denominationWarnings.length > 0 ? { denominationWarnings } : {},
74589
+ ...foreignRecipient ? { recipientOwner: expectedRecipientOwner.toBase58() } : {},
74590
+ ...relayed ? { broadcaster: broadcasterKey } : {},
74591
+ ...relaySubmit ? { facilitatorRelay: true } : {}
74592
+ });
74593
+ };
74594
+ if (answerAtRelay === true && relaySubmit) {
74595
+ return Object.freeze({ signature: signature2, settled: false, noteCommitment: selected.commitment, amountAtomic: amount, preExitHistory, finish });
74316
74596
  }
74317
- store.writeWal(walName2, { ...wal, state: "settled" });
74318
- return Object.freeze({
74319
- signature: signature2,
74320
- replayed: false,
74321
- noteCommitment: selected.commitment,
74322
- amountAtomic: amount,
74323
- poolIndex: poolState2.currentIndex,
74324
- manifestSha256: proved.manifestSha256,
74325
- ...denomination.record ? { denominationPolicy: denomination.record } : {},
74326
- ...denominationWarnings.length > 0 ? { denominationWarnings } : {},
74327
- ...foreignRecipient ? { recipientOwner: expectedRecipientOwner.toBase58() } : {},
74328
- ...relayed ? { broadcaster: broadcasterKey } : {},
74329
- ...relaySubmit ? { facilitatorRelay: true } : {}
74330
- });
74597
+ return finish();
74331
74598
  }
74332
74599
  function loadBroadcasterKeypairFile(path) {
74333
74600
  let text3;
@@ -95809,11 +96076,11 @@ function dropFloatConfigs() {
95809
96076
  configCache.clear();
95810
96077
  configReads.clear();
95811
96078
  }
95812
- function warmFloatConfig({ origin, fetchImpl = fetch } = {}) {
96079
+ function warmFloatConfig({ origin, fetchImpl = fetch, timing } = {}) {
95813
96080
  try {
95814
96081
  const kept = configCache.get(origin);
95815
96082
  if (kept && Date.now() - kept.readAtMs < CONFIG_CACHE_MS && floatPoolRecentlyUsed(FLOAT_POOL_WARM_MS) || configReads.has(origin)) return;
95816
- const read = readFloatConfig({ origin, fetchImpl });
96083
+ const read = timedConfigRead({ origin, fetchImpl, timing, route: "warm" });
95817
96084
  configReads.set(origin, read);
95818
96085
  read.then(
95819
96086
  () => {
@@ -95826,6 +96093,18 @@ function warmFloatConfig({ origin, fetchImpl = fetch } = {}) {
95826
96093
  } catch {
95827
96094
  }
95828
96095
  }
96096
+ function timedConfigRead({ origin, fetchImpl, timing, route }) {
96097
+ const mark = (phase) => {
96098
+ try {
96099
+ timing?.(phase);
96100
+ } catch {
96101
+ }
96102
+ };
96103
+ mark("float-config-start");
96104
+ const read = readFloatConfig({ origin, fetchImpl });
96105
+ read.then(() => mark(`float-config-answered-${route}`), () => mark(`float-config-failed-${route}`));
96106
+ return read;
96107
+ }
95829
96108
  function refreshFloatConfig(origin, fetchImpl) {
95830
96109
  try {
95831
96110
  if (configRefreshes.has(origin) || configReads.has(origin)) return;
@@ -95842,19 +96121,32 @@ function refreshKeptFloatConfig({ origin, fetchImpl = fetch } = {}) {
95842
96121
  } catch {
95843
96122
  }
95844
96123
  }
95845
- async function readFloatOffer({ origin, paymentRequired, store, resourceUrl, fetchImpl = fetch }) {
96124
+ async function readFloatOffer({ origin, paymentRequired, store, resourceUrl, fetchImpl = fetch, timing }) {
96125
+ const mark = (phase) => {
96126
+ try {
96127
+ timing?.(phase);
96128
+ } catch {
96129
+ }
96130
+ };
95846
96131
  const inFlight = configReads.get(origin);
95847
96132
  if (inFlight) await inFlight.catch(() => void 0);
95848
96133
  const kept = configCache.get(origin);
95849
96134
  if (kept && Date.now() - kept.readAtMs < CONFIG_CACHE_MS) {
95850
96135
  try {
95851
- return { offer: floatOffer({ paymentRequired, store, config: kept.config, resourceUrl }), config: kept.config };
96136
+ const offer = floatOffer({ paymentRequired, store, config: kept.config, resourceUrl });
96137
+ mark(inFlight ? "float-config-joined-warm" : "float-config-joined-kept");
96138
+ return { offer, config: kept.config };
95852
96139
  } catch {
95853
96140
  configCache.delete(origin);
95854
96141
  }
95855
96142
  }
95856
96143
  const warm = configReads.get(origin);
95857
- const config = (warm ? await warm.catch(() => void 0) : void 0) ?? await readFloatConfig({ origin, fetchImpl });
96144
+ let config = warm ? await warm.catch(() => void 0) : void 0;
96145
+ if (config !== void 0) mark("float-config-joined-warm");
96146
+ else {
96147
+ config = await timedConfigRead({ origin, fetchImpl, timing, route: "read" });
96148
+ mark("float-config-joined-read");
96149
+ }
95858
96150
  return { offer: floatOffer({ paymentRequired, store, config, resourceUrl }), config };
95859
96151
  }
95860
96152
  function keepSellerTerms(origin, url, paymentRequired) {
@@ -98572,6 +98864,15 @@ async function openPayerSession({
98572
98864
  notePayHost(memoService);
98573
98865
  const connection = connectRelay(rpc2, "finalized");
98574
98866
  const timingLog = createTimingLog(`${path}.timing.log`, { component: "payer" });
98867
+ takeSiteOnionRaces().then((races) => {
98868
+ for (const race of races) {
98869
+ const mark = timingLog.start({ startedAt: race.startedAt });
98870
+ mark("onion:race-start", void 0, race.startedAt);
98871
+ for (const probe of race.probes) mark(`onion:${probe.role}-${probe.event}`, void 0, probe.at);
98872
+ mark("onion:race-end", race.answered ? "answered" : "none", race.endedAt);
98873
+ }
98874
+ }).catch(() => {
98875
+ });
98575
98876
  let closed = false;
98576
98877
  let queue = Promise.resolve();
98577
98878
  const session = {
@@ -98631,6 +98932,7 @@ var init_session = __esm({
98631
98932
  init_processLease();
98632
98933
  init_proxy();
98633
98934
  init_keepAlive();
98935
+ init_facilitatorOnion();
98634
98936
  RELAY_READ_TIMEOUT_MS = 3e4;
98635
98937
  READ_ONLY_METHOD = /^(get[A-Za-z]+|simulateTransaction|isBlockhashValid|minimumLedgerSlot)$/;
98636
98938
  connectRelay = (rpcUrl, commitment = "finalized") => connect2(rpcUrl, commitment, { wrapFetch: (fetch2) => relayRpcFetch(fetch2) });
@@ -103892,7 +104194,7 @@ function hostConfigText({ home, env = process.env, platform = process.platform }
103892
104194
  function isOlderSetupEntry(existing, entry) {
103893
104195
  if (!isPlainObject3(existing) || entry.command !== "npx" || !Array.isArray(entry.args)) return false;
103894
104196
  const older = { ...entry, args: entry.args.filter((arg) => !NPX_INSTALL_FLAGS.includes(arg)) };
103895
- return older.args.length !== entry.args.length && sameJson(existing, older);
104197
+ return older.args.length !== entry.args.length && sameJson2(existing, older);
103896
104198
  }
103897
104199
  function restrictToOwnerWindows(file, { run: run5 = execFileSync2 } = {}) {
103898
104200
  const system32 = join18(process.env.SystemRoot || "C:/Windows", "System32");
@@ -103925,7 +104227,7 @@ function addToJsonHost(file, entry, { now = /* @__PURE__ */ new Date(), force =
103925
104227
  }
103926
104228
  const existing = current.mcpServers?.[MCP_SERVER_NAME];
103927
104229
  if (existing !== void 0) {
103928
- if (sameJson(existing, entry)) return { file, changed: false };
104230
+ if (sameJson2(existing, entry)) return { file, changed: false };
103929
104231
  if (!force && !isOlderSetupEntry(existing, entry)) {
103930
104232
  throw new Error(`setup_host_config_conflict: the host's config file already has a different "${MCP_SERVER_NAME}" server, so setup left it alone; rerun setup with --force to replace it (the old file is backed up first), or edit it by hand`);
103931
104233
  }
@@ -103973,7 +104275,7 @@ function addToJsonHost(file, entry, { now = /* @__PURE__ */ new Date(), force =
103973
104275
  }
103974
104276
  return { file, changed: true, ...backup ? { backup } : {} };
103975
104277
  }
103976
- var import_web3118, CONFIG_SCHEMA, CONFIG_FILE, NPX_PACKAGE, NPX_INSTALL_FLAGS, MCP_SERVER_NAME, P03_BINDING2, PRODUCTION, RETIRED_POOLS, NO_LIMIT_ATOMIC2, DEFAULT_CAPS, OLD_DEFAULT_SETUP_CAPS, OLD_DEFAULT_WALLET_BUDGETS, capText, POSITIVE_ATOMIC2, configPath, homeWalletPath, invalid3, isPubkey, HOST_TARGETS, WINDOWS_SHELL_CODE, isPlainObject3, sameJson;
104278
+ var import_web3118, CONFIG_SCHEMA, CONFIG_FILE, NPX_PACKAGE, NPX_INSTALL_FLAGS, MCP_SERVER_NAME, P03_BINDING2, PRODUCTION, RETIRED_POOLS, NO_LIMIT_ATOMIC2, DEFAULT_CAPS, OLD_DEFAULT_SETUP_CAPS, OLD_DEFAULT_WALLET_BUDGETS, capText, POSITIVE_ATOMIC2, configPath, homeWalletPath, invalid3, isPubkey, HOST_TARGETS, WINDOWS_SHELL_CODE, isPlainObject3, sameJson2;
103977
104279
  var init_config2 = __esm({
103978
104280
  "wallet/src/setup/config.mjs"() {
103979
104281
  "use strict";
@@ -104024,7 +104326,7 @@ var init_config2 = __esm({
104024
104326
  HOST_TARGETS = Object.freeze(["claude-code", "claude-desktop", "cursor"]);
104025
104327
  WINDOWS_SHELL_CODE = /[%$`"\r\n]/;
104026
104328
  isPlainObject3 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
104027
- sameJson = (a, b) => {
104329
+ sameJson2 = (a, b) => {
104028
104330
  const canonical5 = (value) => Array.isArray(value) ? value.map(canonical5) : isPlainObject3(value) ? Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonical5(value[key])])) : value;
104029
104331
  return JSON.stringify(canonical5(a)) === JSON.stringify(canonical5(b));
104030
104332
  };
@@ -104399,6 +104701,20 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
104399
104701
  const inputs = provenInputs(byName.get(`resource-proof-${entry.name.slice("resource-payment-".length)}`)?.record);
104400
104702
  return Boolean(digest2) && snapshotDigests.has(digest2) && inputs !== void 0 && inputs.length > 0 && inputs.every((commitment) => spentInSnapshot.has(commitment));
104401
104703
  };
104704
+ const answeredExits = /* @__PURE__ */ new Map();
104705
+ for (const entry of records) {
104706
+ if (entry.type !== "unshield" || entry.name.startsWith(POCKET_FILL_RECORD) || entry.name.startsWith(MERGE_RECORD) || !Number.isSafeInteger(entry.record.exitAnsweredAtMs) || typeof entry.record.exitTransaction !== "string" || !canonicalAtomic(String(entry.record.noteCommitment ?? ""))) continue;
104707
+ answeredExits.set(String(entry.record.noteCommitment), {
104708
+ changeAtomic: canonicalAtomic(entry.record.changeAtomic) ? BigInt(entry.record.changeAtomic) : 0n,
104709
+ unconfirmed: entry.record.exitUnconfirmed === true
104710
+ });
104711
+ }
104712
+ const answeredSettling = (note, answered) => {
104713
+ const output2 = unspent.get(note);
104714
+ if (output2 === void 0) return void 0;
104715
+ const exited = BigInt(output2.valueAtomic) - answered.changeAtomic;
104716
+ return exited > 0n ? { inputs: [note], amountAtomic: exited, join: false } : void 0;
104717
+ };
104402
104718
  for (const entry of records) {
104403
104719
  const before = Object.hasOwn(snapshot.walStates, entry.name) ? snapshot.walStates[entry.name] : void 0;
104404
104720
  const beforePhase = before === void 0 ? "absent" : entry.type === "payment-proof" ? entry.phase : classify(entry.name, { ...entry.record, state: before }).phase;
@@ -104406,6 +104722,18 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
104406
104722
  const join37 = entry.type === "payment" && joinedAccountOf(entry, byName) !== void 0;
104407
104723
  if (entry.phase === "done" && beforePhase !== "done") {
104408
104724
  if (join37 && snapshotDigests.has(digestKey(entry.record.memoDigest))) continue;
104725
+ const answeredExit = ["unshield", "note-exit"].includes(entry.type) ? answeredExits.get(String(entry.record.noteCommitment)) : void 0;
104726
+ if (answeredExit !== void 0) {
104727
+ const note = String(entry.record.noteCommitment);
104728
+ if (spentInSnapshot.has(note) || entry.type === "note-exit" && unspent.has(note)) continue;
104729
+ const exiting = answeredSettling(note, answeredExit);
104730
+ if (exiting) {
104731
+ settling.push(exiting);
104732
+ noteSettling(entry.name);
104733
+ settlingReasons.push(`unshield record ${entry.name} was answered after the last refresh: its exit is final on chain and the balance counts it as sent; refresh again in a minute`);
104734
+ continue;
104735
+ }
104736
+ }
104409
104737
  const paying = settlingPayment(entry);
104410
104738
  if (paying) {
104411
104739
  settling.push(paying);
@@ -104483,6 +104811,24 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
104483
104811
  listedNotes.add(note);
104484
104812
  const fill = entry.name.startsWith(POCKET_FILL_RECORD);
104485
104813
  const merge = entry.name.startsWith(MERGE_RECORD);
104814
+ const answered = answeredExits.get(note);
104815
+ if (answered !== void 0) {
104816
+ const exiting = answered.changeAtomic > 0n && !spentInWalk.has(note) ? answeredSettling(note, answered) : void 0;
104817
+ if (exiting) {
104818
+ settling.push(exiting);
104819
+ noteSettling(entry.name);
104820
+ settlingReasons.push(`unshield record ${entry.name} was answered: its exit is final on chain and the balance counts it as sent; its change becomes spendable at the next refresh`);
104821
+ }
104822
+ detail.inFlight.push({
104823
+ kind: "unshield-exit",
104824
+ state: entry.state,
104825
+ amountAtomic: entry.record.amountAtomic ?? null,
104826
+ answered: true,
104827
+ inputs: exiting ? "settling" : reserve([note], `unshield ${entry.name}`),
104828
+ next: answered.unconfirmed ? "rerun the same unshield command; it resumes the exit (the pool history has not shown this exit as spent for 10 minutes)" : "nothing to do: the exit is final on chain and this wallet checks it against the pool history by itself"
104829
+ });
104830
+ continue;
104831
+ }
104486
104832
  const inputs = reserve(
104487
104833
  [note],
104488
104834
  `unshield ${entry.name}`,
@@ -104526,7 +104872,8 @@ function computeBalance({ store, snapshotRead, source = "local", now = Date.now(
104526
104872
  }
104527
104873
  const anchorIndex = canonicalAtomic(snapshot.anchorIndex) ? BigInt(snapshot.anchorIndex) : void 0;
104528
104874
  detail.fundedAccounts = unknownInputs ? 0 : [...unspent.values()].filter((output2) => output2.kind === "account" && BigInt(output2.valueAtomic) > 0n && !reserved.has(output2.commitment) && !joining.has(output2.commitment) && !settlingInputs.has(output2.commitment) && (anchorIndex === void 0 || BigInt(output2.leafIndex) < anchorIndex)).length;
104529
- const next = detail.inFlight.length ? "finish or cancel the work in detail.inFlight; the wallet runs one payment at a time" : pendingDeposits.length ? `deposit ${pendingDeposits[0].depositId} is not finished: rerun the same deposit command with --submit --service <facilitator URL>` : pocketsUnscanned ? scanPockets2 : unspentAtomic === 0n && successionMovableAtomic > 0n ? "this pool was succeeded by a new pool: its value moves there (a move per account, the facilitator pays the fee) and counts in this balance until then" : unspentAtomic === 0n ? "nothing to spend: deposit (shinjuku-wallet help deposit), or run balance --refresh if a payment to this wallet settled" : void 0;
104875
+ const answeredOnly = detail.inFlight.length > 0 && detail.inFlight.every((item) => item.answered === true);
104876
+ const next = answeredOnly ? detail.inFlight.find((item) => item.next.startsWith("rerun"))?.next ?? "nothing to do: an answered unshield finishes its check by itself" : detail.inFlight.length ? "finish or cancel the work in detail.inFlight; the wallet runs one payment at a time" : pendingDeposits.length ? `deposit ${pendingDeposits[0].depositId} is not finished: rerun the same deposit command with --submit --service <facilitator URL>` : pocketsUnscanned ? scanPockets2 : unspentAtomic === 0n && successionMovableAtomic > 0n ? "this pool was succeeded by a new pool: its value moves there (a move per account, the facilitator pays the fee) and counts in this balance until then" : unspentAtomic === 0n ? "nothing to spend: deposit (shinjuku-wallet help deposit), or run balance --refresh if a payment to this wallet settled" : void 0;
104530
104877
  if (movedInAtomic > 0n) {
104531
104878
  settlingReasons.push(`${formatAtomic(movedInAtomic, decimals)} moved into this pool from the old pool after the last refresh: the balance counts it; refresh again in a minute`);
104532
104879
  }
@@ -104661,7 +105008,7 @@ function requestBody(method, body) {
104661
105008
  if (bytes2.length > 1024 * 1024) fail24("fetch_options_invalid", "the body is over 1 MiB");
104662
105009
  return bytes2;
104663
105010
  }
104664
- async function offeredPrice(paymentRequired, store, serviceUrl2, target) {
105011
+ async function offeredPrice(paymentRequired, store, serviceUrl2, target, timing) {
104665
105012
  let rail = paymentRail(paymentRequired);
104666
105013
  if (rail === "confidential" && paymentRequired.accepts.some((offer) => offer?.scheme === "exact")) rail = "exact";
104667
105014
  if (rail === "none") {
@@ -104672,7 +105019,7 @@ async function offeredPrice(paymentRequired, store, serviceUrl2, target) {
104672
105019
  }
104673
105020
  if (rail === "exact" && store) {
104674
105021
  const origin = new URL(serviceUrl2).origin;
104675
- const { offer, config } = await readFloatOffer({ origin, paymentRequired, store, resourceUrl: target });
105022
+ const { offer, config } = await readFloatOffer({ origin, paymentRequired, store, resourceUrl: target, timing });
104676
105023
  return { rail, amountAtomic: offer.paidAtomic, priceAtomic: offer.priceAtomic, floatConfig: { origin, config } };
104677
105024
  }
104678
105025
  let highest;
@@ -104782,7 +105129,7 @@ async function payAndFetch(session, url, init = {}, opts = {}) {
104782
105129
  if (ahead) {
104783
105130
  mark("leg1-ahead");
104784
105131
  } else {
104785
- warmFloatConfig({ origin: new URL(session.memoServiceUrl).origin });
105132
+ warmFloatConfig({ origin: new URL(session.memoServiceUrl).origin, timing: mark });
104786
105133
  }
104787
105134
  startRecoveredPaymentTools(lazyProofTools(session));
104788
105135
  }
@@ -104806,7 +105153,7 @@ async function payAndFetch(session, url, init = {}, opts = {}) {
104806
105153
  }
104807
105154
  challenge3 = { encoded, atMs: Date.now() };
104808
105155
  mark("fetch-challenge");
104809
- price = await offeredPrice(paymentRequired, store ?? session.openStore(), session.memoServiceUrl, target);
105156
+ price = await offeredPrice(paymentRequired, store ?? session.openStore(), session.memoServiceUrl, target, mark);
104810
105157
  mark("fetch-price");
104811
105158
  if (maxPrice !== void 0 && BigInt(price.priceAtomic) > BigInt(maxPrice)) {
104812
105159
  fail24(
@@ -108908,7 +109255,8 @@ function build(a, tool) {
108908
109255
  if (a.stage === "confirming") return "Waiting for you to confirm the send in your app.";
108909
109256
  const amount = usdcText(a.done === true ? a.sentAtomic ?? a.amountAtomic : a.amountAtomic);
108910
109257
  const what = amount ? `${amount} USDC` : "the USDC";
108911
- return a.done === true ? `Sent ${what} to ${a.to}.` : `Sending ${what} privately to ${a.to}.`;
109258
+ if (a.done !== true) return `Sending ${what} privately to ${a.to}.`;
109259
+ return typeof a.exitTransaction === "string" && a.privateSend === void 0 ? `Sent ${what} to ${a.to} (transaction ${a.exitTransaction}). Your private balance shows it in a few seconds; nothing to do.` : `Sent ${what} to ${a.to}.`;
108912
109260
  }
108913
109261
  if (typeof a.address === "string" && a.address && typeof a.stage === "string") return linkSay(a);
108914
109262
  if (a.stage === "waiting_for_funds" && typeof a.depositAddress === "string") {
@@ -117403,7 +117751,9 @@ async function runUnshield({
117403
117751
  profile,
117404
117752
  facilitatorOrigin: facilitatorOrigin2,
117405
117753
  onStage,
117406
- selfPayStep = selfPay
117754
+ selfPayStep = selfPay,
117755
+ onAnswered,
117756
+ onCheckPending
117407
117757
  }) {
117408
117758
  const owner = new import_web3133.PublicKey(recipientOwner);
117409
117759
  const mint = new import_web3133.PublicKey(store.state.asset);
@@ -117460,7 +117810,9 @@ async function runUnshield({
117460
117810
  onStage,
117461
117811
  walk,
117462
117812
  mark,
117463
- scoped
117813
+ scoped,
117814
+ onAnswered,
117815
+ onCheckPending
117464
117816
  });
117465
117817
  }
117466
117818
  if (record?.mode !== void 0) throw new Error("mcp_unshield_failed: this unshield's record names an exit mode this version does not know; nothing new was signed. Update the wallet");
@@ -117503,12 +117855,50 @@ async function runUnshield({
117503
117855
  allowForeignRecipient: true,
117504
117856
  relaySubmit: facilitatorExitRelay(facilitatorOrigin2),
117505
117857
  requestAnchor: markedAnchorRequest(facilitatorAnchor(facilitatorOrigin2)),
117858
+ answerAtRelay: true,
117506
117859
  deps: { walkHistory: walk(exitProofTools.deploymentIdentity) }
117507
117860
  }), { ring: { connection: session.connection, pool: store.state.pool } }));
117861
+ const answer = { selfPayTransaction: paid.transaction ?? null, exitTransaction: exit.signature ?? null };
117862
+ if (exit.settled === false && !await finishAnsweredExit({ session, store, name, exit, answer, onAnswered, onCheckPending, mark, scoped })) return answer;
117508
117863
  mark("unshield-exited");
117509
117864
  const exited = store.readWal(`self-pay-${unshieldId}`);
117510
117865
  store.writeWal(`self-pay-${unshieldId}`, { ...exited, state: "exited", exitTransaction: exit.signature, exitedAtMs: exited?.exitedAtMs ?? Date.now() });
117511
- return { selfPayTransaction: paid.transaction ?? null, exitTransaction: exit.signature ?? null };
117866
+ return answer;
117867
+ }
117868
+ async function finishAnsweredExit({ session, store, name, exit, answer, changeAtomic, onAnswered, onCheckPending, mark, scoped }) {
117869
+ if (typeof onAnswered !== "function") {
117870
+ await scoped("unshield-exit", () => exit.finish());
117871
+ return true;
117872
+ }
117873
+ const record = store.readWal(name);
117874
+ store.writeWal(name, {
117875
+ ...record,
117876
+ exitTransaction: exit.signature,
117877
+ exitAnsweredAtMs: record?.exitAnsweredAtMs ?? Date.now(),
117878
+ ...changeAtomic !== void 0 ? { changeAtomic } : {}
117879
+ });
117880
+ try {
117881
+ await scoped("unshield-exit", () => refreshWallet({ session, store, history: exit.preExitHistory }));
117882
+ } catch {
117883
+ mark("unshield-exit-snapshot-failed");
117884
+ }
117885
+ mark("unshield-exit-answered");
117886
+ try {
117887
+ onAnswered(answer);
117888
+ } catch {
117889
+ }
117890
+ try {
117891
+ await scoped("unshield-exit", () => exit.finish());
117892
+ return true;
117893
+ } catch (error) {
117894
+ mark("unshield-exit-check-pending");
117895
+ store.writeWal(name, { ...store.readWal(name), exitCheckPendingAtMs: Date.now() });
117896
+ try {
117897
+ onCheckPending?.(error);
117898
+ } catch {
117899
+ }
117900
+ return false;
117901
+ }
117512
117902
  }
117513
117903
  async function runDirectExit({
117514
117904
  session,
@@ -117525,7 +117915,9 @@ async function runDirectExit({
117525
117915
  walk,
117526
117916
  mark = () => {
117527
117917
  },
117528
- scoped = (_prefix, fn) => fn()
117918
+ scoped = (_prefix, fn) => fn(),
117919
+ onAnswered,
117920
+ onCheckPending
117529
117921
  }) {
117530
117922
  const name = `self-pay-${unshieldId}`;
117531
117923
  if (record.amountAtomic !== String(amountAtomic) || record.exitAmountAtomic !== String(amountAtomic)) {
@@ -117563,6 +117955,7 @@ async function runDirectExit({
117563
117955
  allowForeignRecipient: true,
117564
117956
  relaySubmit: facilitatorExitRelay(facilitatorOrigin2),
117565
117957
  requestAnchor: markedAnchorRequest(facilitatorAnchor(facilitatorOrigin2)),
117958
+ answerAtRelay: true,
117566
117959
  deps: { walkHistory: walk(exitProofTools.deploymentIdentity), proveExit }
117567
117960
  }), { ring: { connection: session.connection, pool: store.state.pool } }));
117568
117961
  } catch (error) {
@@ -117571,10 +117964,13 @@ async function runDirectExit({
117571
117964
  }
117572
117965
  throw error;
117573
117966
  }
117967
+ const answer = { selfPayTransaction: null, exitTransaction: exit.signature ?? null };
117968
+ const changeAtomic = exitAmountAtomic === void 0 ? void 0 : (BigInt(record.noteValueAtomic) - BigInt(record.exitAmountAtomic)).toString();
117969
+ if (exit.settled === false && !await finishAnsweredExit({ session, store, name, exit, answer, changeAtomic, onAnswered, onCheckPending, mark, scoped })) return answer;
117574
117970
  mark("unshield-exited");
117575
117971
  const exited = store.readWal(name);
117576
117972
  store.writeWal(name, { ...exited, state: "exited", exitTransaction: exit.signature, exitedAtMs: exited?.exitedAtMs ?? Date.now() });
117577
- return { selfPayTransaction: null, exitTransaction: exit.signature ?? null };
117973
+ return answer;
117578
117974
  }
117579
117975
  function movingAtomic(store) {
117580
117976
  let moving = 0n;
@@ -119394,6 +119790,12 @@ ${canonical5}`;
119394
119790
  watcher?.kick?.();
119395
119791
  } catch {
119396
119792
  }
119793
+ if (resumeSends) {
119794
+ try {
119795
+ finishAnsweredExits();
119796
+ } catch {
119797
+ }
119798
+ }
119397
119799
  let store;
119398
119800
  try {
119399
119801
  store = session.openStore();
@@ -119907,7 +120309,7 @@ ${canonical5}`;
119907
120309
  amountAtomic: typeof record.amountAtomic === "string" ? record.amountAtomic : null,
119908
120310
  to: record.mcpTo,
119909
120311
  // One word per state (real-user pass F3): "sent" as wallet_cancel and openSendState say, never the record's "exited".
119910
- state: retry ? "retrying" : joining ? "joining" : record.state === "exited" ? "sent" : live === "sending" ? "sending" : idle ?? record.state,
120312
+ state: retry ? "retrying" : joining ? "joining" : record.state === "exited" || live === "sent" ? "sent" : live === "sending" ? "sending" : idle ?? record.state,
119911
120313
  transaction: sigOrNull(record.exitTransaction),
119912
120314
  at,
119913
120315
  ...record.state === "exited" && started !== void 0 ? { startedAt: isoOf(started) } : {},
@@ -120353,7 +120755,8 @@ ${canonical5}`;
120353
120755
  return sendDoneNext(job.to, job.result.privateSend) + `${job.result?.backupPath ? `. An encrypted backup of the wallet was saved at ${job.result.backupPath}` : ""}`;
120354
120756
  }
120355
120757
  if (kind === "unshield") {
120356
- return `the USDC was sent to ${job.to}; tell the user (exitTransaction). wallet_balance shows the new shielded balance${job.result?.backupPath ? `. An encrypted backup of the wallet was saved at ${job.result.backupPath}` : ""}`;
120758
+ const tx = job.result?.exitTransaction;
120759
+ return `Sent ${usdcText2(job.amountAtomic)} USDC to ${job.to}${typeof tx === "string" ? ` (transaction ${tx})` : ""}. Tell the user; nothing to do: the private balance shows it in a few seconds (wallet_balance)${job.result?.backupPath ? `. An encrypted backup of the wallet was saved at ${job.result.backupPath}` : ""}`;
120357
120760
  }
120358
120761
  const post = job.post;
120359
120762
  if (!post || post.state === "running") {
@@ -120806,7 +121209,9 @@ ${canonical5}`;
120806
121209
  }
120807
121210
  function singleSendState(store, requestId, intent) {
120808
121211
  const unshieldId = moneyId(requestId);
120809
- if (store.readWal(`self-pay-${unshieldId}`)?.state === "exited") return "sent";
121212
+ const record = store.readWal(`self-pay-${unshieldId}`);
121213
+ if (record?.state === "exited") return "sent";
121214
+ if (Number.isSafeInteger(record?.exitAnsweredAtMs) && typeof record.exitTransaction === "string" && record.exitUnconfirmed !== true) return "sent";
120810
121215
  if (jobs.get(`unshield:${toRecordId(requestId)}`)?.state === "running" || selfPayMayHaveSigned(store, unshieldId)) return "sending";
120811
121216
  return intent ? "joining" : void 0;
120812
121217
  }
@@ -121289,7 +121694,7 @@ ${canonical5}`;
121289
121694
  if (known && known.state !== "failed" && known.state !== "waiting" && sameRequest(known, request)) {
121290
121695
  const wait3 = waitFor();
121291
121696
  if (known.state === "running" && wait3 > 0) {
121292
- const ends = (kind === "shield" || kind === "unshield") && known.stage !== "confirming" ? [known.promise] : [known.promise, stageChanged(known)];
121697
+ const ends = (kind === "shield" || kind === "unshield") && known.stage !== "confirming" ? [known.answered ?? known.promise] : [known.promise, stageChanged(known)];
121293
121698
  await statusWait(
121294
121699
  ends,
121295
121700
  wait3,
@@ -121664,6 +122069,10 @@ ${canonical5}`;
121664
122069
  if (kind !== "unshield") afterShield(job);
121665
122070
  else refreshAtFinality();
121666
122071
  }, async (error) => {
122072
+ if (job.answeredEarly) {
122073
+ log(`MCP_UNSHIELD_AFTER_ANSWER_FAILED requestId=${job.requestId} cause=${JSON.stringify(String(error?.message ?? error ?? "").split("\n", 1)[0].slice(0, 240))}`);
122074
+ return;
122075
+ }
121667
122076
  const linkHeld = /* @__PURE__ */ new Map();
121668
122077
  if (kind === "unshield" && /p03_merchant_respend_no_spendable_account|insufficient_recovered_value|insufficient_value/.test(String(error?.message ?? error))) {
121669
122078
  try {
@@ -121707,7 +122116,14 @@ ${canonical5}`;
121707
122116
  recorded = true;
121708
122117
  }
121709
122118
  if (!recorded) uncount(kind, key);
121710
- }).finally(() => signalStage(job));
122119
+ }).finally(() => {
122120
+ job.finishing = false;
122121
+ signalStage(job);
122122
+ });
122123
+ job.answered = new Promise((resolve38) => {
122124
+ job.answerNow = resolve38;
122125
+ job.promise.then(resolve38, resolve38);
122126
+ });
121711
122127
  if (needsConfirm) {
121712
122128
  await statusWait([confirmation], fullWait ? Infinity : budgetLeft(startedAt), progress3, "waiting for the user to confirm the send");
121713
122129
  if (job.stage === "confirming" || job.state === "failed") return jobAnswer(kind, job);
@@ -121716,7 +122132,7 @@ ${canonical5}`;
121716
122132
  if (wait2 === Infinity) await job.promise.catch(() => {
121717
122133
  });
121718
122134
  else if (wait2 > 0) {
121719
- await statusWait([job.promise], wait2, progress3, kind === "unshield" ? "sending privately; it takes a few minutes" : kind === "elsewhere" ? "checking the one-time address; a deposit takes a few minutes" : "making the USDC private; it takes a few minutes");
122135
+ await statusWait([job.answered], wait2, progress3, kind === "unshield" ? "sending privately; it takes a few minutes" : kind === "elsewhere" ? "checking the one-time address; a deposit takes a few minutes" : "making the USDC private; it takes a few minutes");
121720
122136
  }
121721
122137
  return jobAnswer(kind, job);
121722
122138
  }
@@ -121774,9 +122190,10 @@ ${canonical5}`;
121774
122190
  }
121775
122191
  return session.openStore();
121776
122192
  }
121777
- async function unshieldPart(opened, job, unshieldId, amountAtomic, onStage) {
122193
+ async function unshieldPart(opened, job, unshieldId, amountAtomic, onStage, onAnswered) {
121778
122194
  const store = await refreshStaleBeforeUnshield(opened, onStage);
121779
122195
  bindUnshieldRecipient(store, unshieldId, amountAtomic, job.to, unshieldId === moneyId(job.requestId) ? job.requestId : void 0);
122196
+ let backup;
121780
122197
  const result = await (ops.unshield ?? runUnshield)({
121781
122198
  session,
121782
122199
  store,
@@ -121787,17 +122204,40 @@ ${canonical5}`;
121787
122204
  profile: money.profile,
121788
122205
  facilitatorOrigin: money.facilitatorOrigin,
121789
122206
  onStage,
121790
- ...ops.selfPay ? { selfPayStep: ops.selfPay } : {}
122207
+ ...ops.selfPay ? { selfPayStep: ops.selfPay } : {},
122208
+ ...typeof onAnswered === "function" ? { onAnswered: (answer) => {
122209
+ backup = backupNow(session.openStore());
122210
+ onAnswered({ ...answer, ...backup });
122211
+ } } : {},
122212
+ onCheckPending: (error) => {
122213
+ log(`MCP_UNSHIELD_CHECK_PENDING requestId=${job.requestId} cause=${JSON.stringify(String(error?.message ?? error ?? "").split("\n", 1)[0].slice(0, 240))}`);
122214
+ answeredExitTries.set(`self-pay-${unshieldId}`, { running: false, atMs: Date.now() });
122215
+ armAnsweredExitRetry();
122216
+ }
121791
122217
  });
121792
- return { ...result, ...backupNow(session.openStore()) };
122218
+ return { ...result, ...backup ?? backupNow(session.openStore()) };
121793
122219
  }
121794
122220
  function runUnshieldJob(job, onStage) {
121795
122221
  if (isSplitSend(job)) return runPrivateSendJob(job, onStage);
121796
122222
  return underLease((opened) => {
121797
122223
  if (!opened.readWal(`self-pay-${moneyId(job.requestId)}`)) assertUnshieldNotPromised(opened, job.amountAtomic);
121798
- return unshieldPart(opened, job, moneyId(job.requestId), job.amountAtomic, onStage);
122224
+ return unshieldPart(opened, job, moneyId(job.requestId), job.amountAtomic, onStage, (answer) => answerUnshieldEarly(job, answer));
121799
122225
  });
121800
122226
  }
122227
+ function answerUnshieldEarly(job, answer) {
122228
+ job.state = "done";
122229
+ job.doneAtMs = Date.now();
122230
+ job.result = answer;
122231
+ job.answeredEarly = true;
122232
+ job.finishing = true;
122233
+ try {
122234
+ closeJoinIntent(session.openStore(), job.requestId, "done");
122235
+ } catch {
122236
+ }
122237
+ log(`MCP_UNSHIELD_ANSWERED requestId=${job.requestId}`);
122238
+ signalStage(job);
122239
+ job.answerNow?.();
122240
+ }
121801
122241
  async function runPrivateSendJob(job, onStage) {
121802
122242
  const sendId = moneyId(job.requestId);
121803
122243
  let store;
@@ -122009,6 +122449,89 @@ ${canonical5}`;
122009
122449
  }
122010
122450
  }
122011
122451
  const resumeRefusals = /* @__PURE__ */ new Map();
122452
+ const answeredExitTries = /* @__PURE__ */ new Map();
122453
+ let answeredExitRetryTimer;
122454
+ function finishAnsweredExits() {
122455
+ if (!unshieldEnabled || finalityClosed || session.closed === true) return;
122456
+ let store;
122457
+ try {
122458
+ store = session.openStore();
122459
+ } catch {
122460
+ return;
122461
+ }
122462
+ let names = [];
122463
+ try {
122464
+ names = store.listWal().filter((name) => name.startsWith("self-pay-") && !name.startsWith("self-pay-merge-") && !name.startsWith("self-pay-pocket-"));
122465
+ } catch {
122466
+ return;
122467
+ }
122468
+ for (const name of names) {
122469
+ let record;
122470
+ try {
122471
+ record = store.readWal(name);
122472
+ } catch {
122473
+ continue;
122474
+ }
122475
+ if (record?.schema !== SELF_PAY_WAL_SCHEMA || record.state === "exited" || record.exitUnconfirmed === true || typeof record.exitTransaction !== "string" || typeof record.mcpTo !== "string" || !ATOMIC_TEXT.test(String(record.amountAtomic))) continue;
122476
+ const tried = answeredExitTries.get(name);
122477
+ if (tried !== void 0 && (tried.running || Date.now() - tried.atMs < ANSWERED_EXIT_RETRY_MS)) continue;
122478
+ const attempt = { running: true, atMs: Date.now() };
122479
+ answeredExitTries.set(name, attempt);
122480
+ const unshieldId = name.slice("self-pay-".length);
122481
+ log(`MCP_UNSHIELD_CHECK_RESUME unshieldId=${unshieldId}`);
122482
+ underLease((opened) => {
122483
+ const current = opened.readWal(name);
122484
+ if (current?.state === "exited" || current?.exitUnconfirmed === true || typeof current?.exitTransaction !== "string") return void 0;
122485
+ return (ops.unshield ?? runUnshield)({
122486
+ session,
122487
+ store: opened,
122488
+ unshieldId,
122489
+ amountAtomic: current.amountAtomic,
122490
+ recipientOwner: current.mcpTo,
122491
+ exitProofTools: money.exitProofTools,
122492
+ profile: money.profile,
122493
+ facilitatorOrigin: money.facilitatorOrigin,
122494
+ onStage: () => {
122495
+ },
122496
+ ...ops.selfPay ? { selfPayStep: ops.selfPay } : {}
122497
+ });
122498
+ }, { orClosed: ANSWERED_EXIT_CLOSED }).then((result) => {
122499
+ if (result === ANSWERED_EXIT_CLOSED) return;
122500
+ const now = session.openStore().readWal(name);
122501
+ log(now?.state === "exited" ? `MCP_UNSHIELD_CHECK_DONE unshieldId=${unshieldId}` : `MCP_UNSHIELD_CHECK_PENDING unshieldId=${unshieldId}`);
122502
+ if (now?.state === "exited") refreshAtFinality();
122503
+ }, (error) => {
122504
+ const cause = String(error?.message ?? error ?? "").split("\n", 1)[0];
122505
+ attempt.provenUnspent = /confirmed note-exit is not finalized spent/.test(cause);
122506
+ log(`MCP_UNSHIELD_CHECK_PENDING unshieldId=${unshieldId} cause=${JSON.stringify(cause.slice(0, 240))}`);
122507
+ }).then(() => underLease((opened) => {
122508
+ const now = opened.readWal(name);
122509
+ if (attempt.provenUnspent === true && now?.state !== "exited" && now?.exitUnconfirmed !== true && Number.isSafeInteger(now?.exitAnsweredAtMs) && Date.now() - now.exitAnsweredAtMs > ANSWERED_EXIT_UNCONFIRMED_MS) {
122510
+ opened.writeWal(name, { ...now, exitUnconfirmed: true });
122511
+ log(`MCP_UNSHIELD_UNCONFIRMED unshieldId=${unshieldId}`);
122512
+ }
122513
+ }, { orClosed: ANSWERED_EXIT_CLOSED })).catch(() => {
122514
+ }).finally(() => {
122515
+ attempt.running = false;
122516
+ let now;
122517
+ try {
122518
+ now = session.openStore().readWal(name);
122519
+ } catch {
122520
+ now = void 0;
122521
+ }
122522
+ if (now?.state === "exited" || now?.exitUnconfirmed === true || !Number.isSafeInteger(now?.exitAnsweredAtMs) || Date.now() - now.exitAnsweredAtMs > ANSWERED_EXIT_RETRY_WINDOW_MS) return;
122523
+ armAnsweredExitRetry();
122524
+ });
122525
+ }
122526
+ }
122527
+ function armAnsweredExitRetry() {
122528
+ if (finalityClosed || session.closed === true || answeredExitRetryTimer !== void 0) return;
122529
+ answeredExitRetryTimer = setTimeout(() => {
122530
+ answeredExitRetryTimer = void 0;
122531
+ finishAnsweredExits();
122532
+ }, ANSWERED_EXIT_RETRY_MS + 100);
122533
+ answeredExitRetryTimer.unref?.();
122534
+ }
122012
122535
  async function cancelElsewhere(args) {
122013
122536
  const spec = KINDS.elsewhere;
122014
122537
  if (!spec.enabled()) return refuse11("mcp_tool_disabled", `${spec.tool} is off: the owner did not enable it when starting this MCP server`, spec.disabledNext);
@@ -124032,6 +124555,8 @@ ${JSON.stringify(args ?? {})}`;
124032
124555
  ));
124033
124556
  const resumeTimer = resumeSends ? setTimeout(resumePrivateSends, 0) : void 0;
124034
124557
  resumeTimer?.unref?.();
124558
+ const answeredExitTimer = resumeSends ? setTimeout(finishAnsweredExits, 0) : void 0;
124559
+ answeredExitTimer?.unref?.();
124035
124560
  const tempCleanTimer = typeof session.walletPath === "string" ? setTimeout(() => {
124036
124561
  const pool2 = dirname24(session.walletPath);
124037
124562
  removeStaleTemporaryFiles([pool2, join28(pool2, "wal"), join28(pool2, "move-counts")]).then((removed) => {
@@ -124057,6 +124582,8 @@ ${JSON.stringify(args ?? {})}`;
124057
124582
  for (const timer of joinTimers) clearTimeout(timer);
124058
124583
  joinTimers.clear();
124059
124584
  clearTimeout(resumeTimer);
124585
+ clearTimeout(answeredExitTimer);
124586
+ clearTimeout(answeredExitRetryTimer);
124060
124587
  clearTimeout(tempCleanTimer);
124061
124588
  clearTimeout(intentStartTimer);
124062
124589
  clearTimeout(intentTimer);
@@ -124080,6 +124607,7 @@ ${JSON.stringify(args ?? {})}`;
124080
124607
  */
124081
124608
  moving: { value: () => {
124082
124609
  if (moneyCallsInFlight - joinWaitingCalls > 0 || [...jobs.values()].some((job) => job.state === "running")) return true;
124610
+ if ([...jobs.values()].some((job) => job.finishing === true) || [...answeredExitTries.values()].some((attempt) => attempt.running)) return true;
124083
124611
  try {
124084
124612
  return mover.running?.() === true || successionRunning() || transferRuns.size > 0 || Boolean(joiner.status().running) || adopter.busy?.() === true || autoPockets.busy?.() === true || watcher?.depositing?.() === true;
124085
124613
  } catch {
@@ -124388,7 +124916,7 @@ function createSetupTools({
124388
124916
  };
124389
124917
  return { tools: [setupTool, statusTool, ...MCP_TOOL_NAMES.map((name) => name === "x402_discover" ? discoverTool(discoverCacheDir) : notSetUp(name))], doneTool };
124390
124918
  }
124391
- 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, SELF_PAY_ORIGIN3, REQUEST_ID4, POSITIVE_ATOMIC6, PRIVATE_HOST3, FORBIDDEN_HEADERS3, MAX_BODY_BYTES2, PREVIEW_TIMEOUT_MS, PREVIEW_SELLER_READ_MS, CALL_BUDGET_MS, budgetLeft, MONEY_FIRST_ANSWER_MS, JOIN_CALL_MAX_MS, JOIN_STEP_MS, sleep2, PROGRESS_EVERY_MS2, READ_BUDGET_MS, UNPAID_TOR_TRY_MS, UNPAID_CLEAR_MS, READ_BUDGET_TOOLS, FLOAT_BUSY_CODES, RETRY_MARGIN_MS, ASK_PAY_CODES, limitNext, SAFE_TEXT, MCP_TOOL_ALIASES, POOL_SWITCH_WHY, POOL_SWITCH_NEXT, SWITCH_STATES, quietSwitch, switchKey, ATOMIC_TEXT, switchAmount, switchEta, switchRows, OWNER_CAP_CODES, JOIN_END_MCP, JOIN_END_TERMINAL_LIBRARY, JOIN_RETRY_BACKOFF_MS, WALLET_CAP_FIX, capFix, ownerOnlyNext, mcpNext, CONFIDENTIAL_RETIRED, json2, answerResult, discoverTool, refuse11, publicOffer, FLOAT_PAID_CODES, floatStillSettling, MONEY_ID2, toRecordId, linkKindOf, moneyId, REFUSED_CODES, refusedCodeOf, PLAIN_REASONS, plainReason, KEPT_SEND_TRIES, shownId, isSplitSend, sendPartId, PROMISED_NEXT, PUBLIC_READ_TIMEOUT_MS, withTimeout, ownKey, readFailure, OWN_KEY_TOKENS_MAX, OWN_TOKENS_GRACE_MS, tokenAmountText, elsewhereId, AGENT_ID_SCHEMA, agentIdName, minutesText2, 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, TEMP_CLEAN_DELAY_MS, KEPT_ON_DISK, MAX_SHIELDED_BALANCE_ATOMIC, receiveGenerator, MCP_TOOL_NAMES, MCP_HIDDEN_TOOL_NAMES, SETUP_TERMINAL_NEXT, SETUP_POLL_NEXT, PRIVATE_INFERENCE_WHY, PRIVATE_INFERENCE_NEXT;
124919
+ 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, SELF_PAY_ORIGIN3, REQUEST_ID4, POSITIVE_ATOMIC6, PRIVATE_HOST3, FORBIDDEN_HEADERS3, MAX_BODY_BYTES2, PREVIEW_TIMEOUT_MS, PREVIEW_SELLER_READ_MS, CALL_BUDGET_MS, budgetLeft, MONEY_FIRST_ANSWER_MS, JOIN_CALL_MAX_MS, JOIN_STEP_MS, sleep2, PROGRESS_EVERY_MS2, READ_BUDGET_MS, UNPAID_TOR_TRY_MS, UNPAID_CLEAR_MS, READ_BUDGET_TOOLS, FLOAT_BUSY_CODES, RETRY_MARGIN_MS, ASK_PAY_CODES, limitNext, SAFE_TEXT, MCP_TOOL_ALIASES, POOL_SWITCH_WHY, POOL_SWITCH_NEXT, SWITCH_STATES, quietSwitch, switchKey, ATOMIC_TEXT, 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, capFix, ownerOnlyNext, mcpNext, CONFIDENTIAL_RETIRED, json2, answerResult, discoverTool, refuse11, publicOffer, FLOAT_PAID_CODES, floatStillSettling, MONEY_ID2, toRecordId, linkKindOf, moneyId, REFUSED_CODES, refusedCodeOf, PLAIN_REASONS, plainReason, KEPT_SEND_TRIES, shownId, isSplitSend, sendPartId, PROMISED_NEXT, PUBLIC_READ_TIMEOUT_MS, withTimeout, ownKey, readFailure, OWN_KEY_TOKENS_MAX, OWN_TOKENS_GRACE_MS, tokenAmountText, elsewhereId, AGENT_ID_SCHEMA, agentIdName, minutesText2, 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, TEMP_CLEAN_DELAY_MS, KEPT_ON_DISK, MAX_SHIELDED_BALANCE_ATOMIC, receiveGenerator, MCP_TOOL_NAMES, MCP_HIDDEN_TOOL_NAMES, SETUP_TERMINAL_NEXT, SETUP_POLL_NEXT, PRIVATE_INFERENCE_WHY, PRIVATE_INFERENCE_NEXT;
124392
124920
  var init_tools = __esm({
124393
124921
  "wallet/src/mcp/tools.mjs"() {
124394
124922
  "use strict";
@@ -124523,6 +125051,10 @@ var init_tools = __esm({
124523
125051
  quietSwitch = (item) => item.state === "done" && item.tellUser !== true;
124524
125052
  switchKey = (item) => `${item.from}:${item.state}`;
124525
125053
  ATOMIC_TEXT = /^[0-9]{1,31}$/;
125054
+ ANSWERED_EXIT_RETRY_MS = 3e4;
125055
+ ANSWERED_EXIT_UNCONFIRMED_MS = 10 * 6e4;
125056
+ ANSWERED_EXIT_RETRY_WINDOW_MS = 30 * 6e4;
125057
+ ANSWERED_EXIT_CLOSED = /* @__PURE__ */ Symbol("answered-exit-closed");
124526
125058
  switchAmount = (atomic4, decimals) => {
124527
125059
  try {
124528
125060
  return ATOMIC_TEXT.test(String(atomic4)) ? formatAtomic(BigInt(atomic4), decimals) : null;