@zktx.io/sui-mcp 0.5.0 → 0.6.1

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.
Files changed (138) hide show
  1. package/README.md +21 -21
  2. package/dist/adapters/adapterPromptSurfaces.js +0 -20
  3. package/dist/adapters/deepbook/deepbookQuotePolicy.js +0 -4
  4. package/dist/adapters/deepbook/deepbookReviewEvidence.js +0 -9
  5. package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +0 -9
  6. package/dist/adapters/intentPlanFactories.js +0 -6
  7. package/dist/core/action/contractNameRegistry.js +0 -53
  8. package/dist/core/action/forbiddenFields.js +0 -4
  9. package/dist/core/action/ptbVisualizationProducer.js +0 -3
  10. package/dist/core/action/receiptPtbGraph.js +0 -17
  11. package/dist/core/action/reviewTimeSimulationEvidence.js +0 -4
  12. package/dist/core/action/signableAdapterContract.js +0 -5
  13. package/dist/core/action/suiChainReceiptMapping.js +0 -18
  14. package/dist/core/action/suiChainReceiptReader.js +1 -21
  15. package/dist/core/action/suiChainReceiptVerifier.js +0 -10
  16. package/dist/core/action/transactionFunding.js +0 -7
  17. package/dist/core/action/types.js +0 -1
  18. package/dist/core/action/walletReviewContractAssembler.js +0 -2
  19. package/dist/core/activity/localDataService.js +65 -52
  20. package/dist/core/activity/localDataValidation.js +1 -3
  21. package/dist/core/activity/sqliteActivityStore.js +24 -24
  22. package/dist/core/activity/sqliteActivityStoreRows.js +0 -2
  23. package/dist/core/activity/sqliteActivityStoreSchema.js +81 -14
  24. package/dist/core/preferences/sqlitePreferencesRepository.js +5 -3
  25. package/dist/core/read/deepbookReadHelpers.js +0 -2
  26. package/dist/core/read/deepbookRegistry.js +0 -19
  27. package/dist/core/read/deepbookUsdcChartService.js +0 -2
  28. package/dist/core/read/readCardResult.js +0 -2
  29. package/dist/core/read/readService.js +0 -19
  30. package/dist/core/read/readServiceTypes.js +0 -2
  31. package/dist/core/session/cardSession.js +1 -5
  32. package/dist/core/session/cardSessionStore.js +54 -7
  33. package/dist/core/session/reviewValidity.js +0 -3
  34. package/dist/core/session/sessionRecordStore.js +0 -1
  35. package/dist/core/session/sessionStore.js +0 -32
  36. package/dist/core/session/settingsFileIdentity.js +9 -0
  37. package/dist/core/session/settingsFileProtocol.js +20 -0
  38. package/dist/core/session/settingsFileStore.js +337 -0
  39. package/dist/core/session/settingsInputError.js +32 -0
  40. package/dist/core/session/settingsOperation.js +220 -0
  41. package/dist/core/session/settingsPreparationSql.js +13 -0
  42. package/dist/core/session/settingsWorkflow.js +1183 -0
  43. package/dist/core/session/sqliteCardStore.js +0 -2
  44. package/dist/core/session/sqliteSessionStore.js +0 -51
  45. package/dist/core/session/sqliteSettingsWorkflowStore.js +453 -0
  46. package/dist/core/session/sqliteTransactionMaterialStore.js +0 -11
  47. package/dist/core/session/sqliteWalletWorkflowStore.js +160 -66
  48. package/dist/core/session/status.js +2 -4
  49. package/dist/core/session/transactionMaterialStore.js +0 -4
  50. package/dist/core/session/transactionRequest.js +0 -4
  51. package/dist/core/session/walletConnection.js +26 -10
  52. package/dist/core/session/walletWorkflow.js +70 -30
  53. package/dist/core/session/workflowState.js +14 -14
  54. package/dist/core/session/workflowView.js +18 -8
  55. package/dist/core/suiAddress.js +0 -5
  56. package/dist/core/suiEndpoint.js +0 -3
  57. package/dist/mcp/prompts.js +0 -12
  58. package/dist/mcp/responseGuidance.js +10 -5
  59. package/dist/mcp/schemas.js +0 -3
  60. package/dist/mcp/serverInfo.js +3 -4
  61. package/dist/mcp/toolNames.js +2 -2
  62. package/dist/mcp/tools/account/index.js +1 -19
  63. package/dist/mcp/tools/action/prepareSuiActionReview.js +0 -6
  64. package/dist/mcp/tools/read/readToolHelpers.js +5 -4
  65. package/dist/mcp/tools/read/serverStatusTools.js +0 -1
  66. package/dist/mcp/tools/session/executionResultTools.js +2 -2
  67. package/dist/mcp/tools/session/shared.js +1 -2
  68. package/dist/mcp/tools/session/statusTools.js +4 -5
  69. package/dist/mcp/tools/settings/index.js +8 -28
  70. package/dist/mcp-app/account.html +14 -14
  71. package/dist/mcp-app/account.notices.txt +1 -1
  72. package/dist/mcp-app/chart.html +9 -9
  73. package/dist/mcp-app/chart.notices.txt +1 -1
  74. package/dist/mcp-app/connect.html +14 -14
  75. package/dist/mcp-app/connect.notices.txt +1 -1
  76. package/dist/mcp-app/receipt.html +1298 -1463
  77. package/dist/mcp-app/receipt.notices.txt +226 -1204
  78. package/dist/mcp-app/review.html +1298 -1463
  79. package/dist/mcp-app/review.notices.txt +226 -1204
  80. package/dist/mcp-app/settings.html +3133 -0
  81. package/dist/mcp-app/settings.notices.txt +973 -0
  82. package/dist/mcp-ui/contracts.js +8 -2
  83. package/dist/mcp-ui/readCards.js +1 -1
  84. package/dist/mcp-ui/tools.js +34 -4
  85. package/dist/review-server/server.js +3 -51
  86. package/dist/runtime/application.js +41 -14
  87. package/dist/runtime/config.js +0 -2
  88. package/dist/runtime/localSettingsService.js +23 -2
  89. package/dist/runtime/logger.js +0 -2
  90. package/dist/runtime/reviewServerAcquire.js +1 -18
  91. package/dist/runtime/runtimeTermination.js +1 -4
  92. package/dist/runtime/shared/authenticatedFetch.js +2 -4
  93. package/dist/runtime/shared/control.js +4 -7
  94. package/dist/runtime/shared/dataAccess.js +0 -2
  95. package/dist/runtime/shared/http.js +14 -0
  96. package/dist/runtime/shared/mcpHttp.js +6 -6
  97. package/dist/runtime/shared/ownerLease.js +0 -4
  98. package/dist/runtime/shared/server.js +1 -2
  99. package/dist/runtime/shared/stdio.js +2 -5
  100. package/dist/runtime/smokeMainnetRead.js +0 -3
  101. package/dist/runtime/smokeReview.js +0 -4
  102. package/dist/runtime/start.js +0 -2
  103. package/dist/runtime/suiTransactionGraphqlMapping.js +0 -2
  104. package/dist/runtime/suiTransactionGraphqlQueries.js +0 -3
  105. package/dist/runtime/walletConnectConfig.js +0 -2
  106. package/dist/runtime/walletConnectStorage.js +2 -10
  107. package/dist/runtime/walletConnectTransport.js +0 -11
  108. package/dist/runtime/walletSdkChildRuntime.js +2 -6
  109. package/dist/runtime/walletSdkIpc.js +2 -1
  110. package/dist/runtime/walletSdkProcess.js +3 -8
  111. package/docs/AGENT_BEHAVIOR.md +45 -15
  112. package/docs/LOCAL_DB_ARCHITECTURE.md +145 -22
  113. package/docs/MCP_SETUP.md +99 -39
  114. package/docs/MCP_TOOLS.md +288 -24
  115. package/docs/SDK_API.md +63 -10
  116. package/docs/UTILITY_INDEX.md +9 -6
  117. package/docs/WALLET_CONNECTION.md +32 -10
  118. package/package.json +22 -8
  119. package/dist/core/activity/schemaVersion.js +0 -2
  120. package/dist/core/session/keyedRecordStore.js +0 -18
  121. package/dist/core/session/settingsSession.js +0 -1
  122. package/dist/core/session/settingsSessions.js +0 -45
  123. package/dist/review-app/brand-dark.svg +0 -1
  124. package/dist/review-app/brand-light.svg +0 -1
  125. package/dist/review-app/favicon.svg +0 -1
  126. package/dist/review-app/settings.css +0 -1
  127. package/dist/review-app/settings.js +0 -1
  128. package/dist/review-app/ui.css +0 -809
  129. package/dist/review-server/assets.js +0 -48
  130. package/dist/review-server/html.js +0 -45
  131. package/dist/review-server/http.js +0 -47
  132. package/dist/review-server/middleware/reviewToken.js +0 -7
  133. package/dist/review-server/settingsApi.js +0 -158
  134. package/docs/AGENT_DEVELOPMENT_POLICY.md +0 -815
  135. package/docs/FRONTEND_POLICY.md +0 -534
  136. package/docs/golden-scenarios/BEHAVIOR_MATRIX.md +0 -166
  137. package/docs/golden-scenarios/INTENT_EVIDENCE_GOLDEN_ANSWERS.md +0 -161
  138. package/docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md +0 -259
@@ -3,8 +3,6 @@ import { computeReviewStateWithPrivateArtifacts } from "../core/review/reviewCom
3
3
  export class SmokeReviewConfigError extends Error {
4
4
  }
5
5
  const requiredSwapInputs = ["SMOKE_SWAP_PROTOCOL", "SMOKE_SWAP_FROM_SYMBOL", "SMOKE_SWAP_TO_SYMBOL", "SMOKE_SWAP_AMOUNT_DISPLAY"];
6
- /** Optional read-only review computation. This never establishes a wallet
7
- * connection; the caller owns the explicit temporary read-account fixture. */
8
6
  export async function computeSmokeReview(options) {
9
7
  const { env, sessions, computation, account } = options;
10
8
  const rawSlippage = env.SMOKE_SWAP_MAX_SLIPPAGE_BPS;
@@ -16,8 +14,6 @@ export async function computeSmokeReview(options) {
16
14
  return { status: "not_run", notRunReason: "missing_env", requiredEnv: [...requiredSwapInputs] };
17
15
  if (missing.length)
18
16
  throw new SmokeReviewConfigError(`Partial account-bound swap smoke configuration; missing: ${missing.join(", ")}`);
19
- // Preserve the utility's existing 50 bps CLI default; it is not a product
20
- // recommendation or a silently selected protocol/settlement asset.
21
17
  const parsed = swapIntentInputSchema.safeParse({ type: "swap", protocol: env.SMOKE_SWAP_PROTOCOL,
22
18
  from: { symbol: env.SMOKE_SWAP_FROM_SYMBOL, amount: env.SMOKE_SWAP_AMOUNT_DISPLAY },
23
19
  to: { symbol: env.SMOKE_SWAP_TO_SYMBOL }, maxSlippageBps: Number(env.SMOKE_SWAP_MAX_SLIPPAGE_BPS ?? "50") });
@@ -43,8 +43,6 @@ async function main() {
43
43
  grpcOverride: process.env.SUI_GRPC_URL ?? null, graphqlOverride: process.env.SUI_GRAPHQL_URL ?? null
44
44
  });
45
45
  void controlReady.catch(() => { });
46
- // The SDK owns stdin from the start, including fragmented initialization
47
- // and EOF while backend startup is pending. No second reader buffers bytes.
48
46
  stage = "stdio_start";
49
47
  bridge = await startSharedStdio({ stdio, port: bootConfig.reviewPort, control: controlReady, ready: backendReady,
50
48
  onError: (error) => logger.error("MCP transport failed", { error: error.message }) });
@@ -187,8 +187,6 @@ function executionStatusFromGraphql(value) {
187
187
  return "success";
188
188
  if (value === "FAILURE")
189
189
  return "failure";
190
- // Null status can occur before checkpointed effects are available; unmapped
191
- // provider enum values stay unknown until explicitly mapped.
192
190
  return "unknown";
193
191
  }
194
192
  function normalizeGraphqlTimestamp(value) {
@@ -211,9 +211,6 @@ async function queryGraphql(client, options) {
211
211
  const result = await client.query({ query: options.query, variables: options.variables ?? {} });
212
212
  if (result.errors && result.errors.length > 0) {
213
213
  const message = result.errors.map((error) => error.message).join("; ");
214
- // The pinned SDK exposes provider GraphQL errors as messages here. Cursor
215
- // rejection is intentionally detected conservatively and otherwise falls
216
- // back to provider_error.
217
214
  throw new TransactionActivitySourceError(message.toLowerCase().includes("cursor") ? "cursor_invalid" : "provider_error", "Sui GraphQL query returned errors", { message });
218
215
  }
219
216
  if (!result.data) {
@@ -1,3 +1 @@
1
- // Product-owned public dApp identifier. All installations share this Relay project.
2
- // This is not a wallet credential and has no user configuration override.
3
1
  export const WALLETCONNECT_PROJECT_ID = "62c5f7d7917d3936258d4b0746c99cab";
@@ -2,9 +2,6 @@ import { existsSync, lstatSync, mkdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { serialize, deserialize } from "node:v8";
4
4
  import Database from "better-sqlite3";
5
- // Restoration namespaces of pinned SignClient/Core 2.23.10. Request history,
6
- // pending requests, messages, unacknowledged envelopes and new namespaces are
7
- // intentionally volatile; they may contain transaction or signature material.
8
5
  const persistentKeys = new Set([
9
6
  "wc@2:core:0.3//keychain", "wc@2:core:0.3//pairing",
10
7
  "wc@2:core:0.3//subscription", "wc@2:core:0.3//expirer",
@@ -12,12 +9,11 @@ const persistentKeys = new Set([
12
9
  ]);
13
10
  const storageSchema = "CREATE TABLE sdk_sessions (key TEXT PRIMARY KEY, value BLOB NOT NULL)";
14
11
  function storageFormat(db) {
15
- const version = db.pragma("user_version", { simple: true });
16
12
  const schema = db.prepare("SELECT type,name,sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%' ORDER BY type,name")
17
13
  .all();
18
- if (version === 0 && schema.length === 0)
14
+ if (schema.length === 0)
19
15
  return "empty";
20
- if (version === 1 && schema.length === 1 && schema[0]?.type === "table" &&
16
+ if (schema.length === 1 && schema[0]?.type === "table" &&
21
17
  schema[0].name === "sdk_sessions" && schema[0].sql === storageSchema)
22
18
  return "current";
23
19
  throw new Error("WalletConnect storage format is unsupported.");
@@ -59,11 +55,8 @@ export function openWalletConnectStorage(dataDirectory) {
59
55
  if (storageFormat(db) === "empty") {
60
56
  db.transaction(() => {
61
57
  db.exec(storageSchema);
62
- db.pragma("user_version = 1");
63
58
  })();
64
59
  }
65
- // There is exactly one SDK owner. FULL protects the session material that
66
- // the SDK needs to resume an acknowledged connection after process exit.
67
60
  db.pragma("journal_mode = DELETE");
68
61
  db.pragma("synchronous = FULL");
69
62
  }
@@ -94,6 +87,5 @@ export function openWalletConnectStorage(dataDirectory) {
94
87
  volatile.delete(key);
95
88
  }
96
89
  };
97
- // Once injected, SDK timers own the storage until the owner process exits.
98
90
  return { storage, closeBeforeSdkUse: () => { volatile.clear(); db.close(); } };
99
91
  }
@@ -27,8 +27,6 @@ export async function createWalletConnectTransport(options) {
27
27
  if (!/^[0-9a-f]{32}$/i.test(options.projectId))
28
28
  throw new Error("WalletConnect project ID format is invalid.");
29
29
  const owner = openWalletConnectStorage(options.dataDirectory);
30
- // SDK log bodies can contain pairing credentials and serialized requests.
31
- // Product failure stages are recorded by the caller without these values.
32
30
  options.onSdkStart?.();
33
31
  const client = await (options.initialize ?? SignClient.init.bind(SignClient))({ projectId: options.projectId, metadata: { ...options.metadata, icons: [] },
34
32
  storage: owner.storage, logger: "silent", telemetryEnabled: false });
@@ -37,8 +35,6 @@ export async function createWalletConnectTransport(options) {
37
35
  const changed = (event) => { if (!stopped)
38
36
  for (const listener of listeners)
39
37
  listener(event.topic); };
40
- // Pinned SignClient emits session_event without changing session.namespaces.
41
- // Treat active account/chain changes as invalidation, not new authorization.
42
38
  const selectionChanged = (event) => {
43
39
  if (!stopped && ["accountsChanged", "chainChanged"].includes(event.params.event.name)) {
44
40
  for (const listener of listeners)
@@ -60,7 +56,6 @@ export async function createWalletConnectTransport(options) {
60
56
  const inspect = (topic) => {
61
57
  if (stopped)
62
58
  throw new Error("WalletConnect owner stopped.");
63
- // A store failure is not evidence that the session disappeared.
64
59
  const session = client.session.getAll().find((item) => item.topic === topic);
65
60
  return session ? inspectValue(session) : { topic, status: "absent" };
66
61
  };
@@ -75,8 +70,6 @@ export async function createWalletConnectTransport(options) {
75
70
  return {
76
71
  async restore() {
77
72
  requireActive();
78
- // A pending proposal is not an approved session and must not reappear as
79
- // a new approval ceremony after owner replacement.
80
73
  for (const pairing of client.core.pairing.getPairings()) {
81
74
  if (!pairing.active)
82
75
  await client.core.pairing.disconnect({ topic: pairing.topic });
@@ -89,8 +82,6 @@ export async function createWalletConnectTransport(options) {
89
82
  chains: [SUI_MAINNET_WALLET_CHAIN], methods: [SUI_SIGN_TRANSACTION_METHOD], events: ["accountsChanged", "chainChanged"]
90
83
  } } });
91
84
  const approval = connecting.approval().then(sessionValue).catch((error) => { throw transportError(error); });
92
- // Attach a rejection handler immediately; the backend records the same
93
- // promise's outcome once the private QR has been associated with its row.
94
85
  void approval.catch(() => undefined);
95
86
  if (!connecting.uri)
96
87
  throw new Error("The wallet pairing could not be prepared.");
@@ -121,8 +112,6 @@ export async function createWalletConnectTransport(options) {
121
112
  client.off("session_delete", changed);
122
113
  client.off("session_expire", changed);
123
114
  client.off("session_event", selectionChanged);
124
- // SignClient has no supported complete in-process disposal. The runtime
125
- // ends the owner process; it must not close injected storage under timers.
126
115
  }
127
116
  };
128
117
  }
@@ -1,10 +1,9 @@
1
+ import { SERVER_VERSION } from "../mcp/serverInfo.js";
1
2
  import { join } from "node:path";
2
3
  import { isDeepStrictEqual } from "node:util";
3
4
  import { WalletUserRejectedError } from "../core/session/walletConnection.js";
4
5
  import { acquireDataDirectoryOwner } from "./shared/ownerLease.js";
5
6
  import { walletCommandSchema, walletEventSchema } from "./walletSdkIpc.js";
6
- // Production and the real-SDK wire fixture run this same process lifecycle.
7
- // The factory is a code dependency, never a serialized command or setting.
8
7
  export function runWalletSdkChild(createTransport) {
9
8
  let init, transport;
10
9
  let sequence = 0, lastRequest = 0, ready = false, exiting = false;
@@ -19,7 +18,7 @@ export function runWalletSdkChild(createTransport) {
19
18
  process.send(walletEventSchema.parse(message), (error) => { if (error)
20
19
  exit(); });
21
20
  };
22
- const envelope = () => ({ protocolVersion: 1, runId: init.runId });
21
+ const envelope = () => ({ packageVersion: SERVER_VERSION, runId: init.runId });
23
22
  const exit = () => { if (!exiting) {
24
23
  exiting = true;
25
24
  void ownership;
@@ -29,7 +28,6 @@ export function runWalletSdkChild(createTransport) {
29
28
  ready = false;
30
29
  if (init && !exiting)
31
30
  send({ ...envelope(), type: "failure", reason });
32
- // Neither SDK cleanup nor a callback is a prerequisite for process exit.
33
31
  if (!exiting) {
34
32
  exiting = true;
35
33
  process.exit(1);
@@ -74,7 +72,6 @@ export function runWalletSdkChild(createTransport) {
74
72
  };
75
73
  const start = async (input) => {
76
74
  init = input;
77
- // The child alone opens the SDK store; OS exit releases this separate lock.
78
75
  ownership = acquireDataDirectoryOwner(join(input.dataDirectory, "walletconnect", "sessions.sqlite"));
79
76
  send({ ...envelope(), type: "stage", stage: "sdk_start" });
80
77
  transport = await createTransport(input);
@@ -153,7 +150,6 @@ export function runWalletSdkChild(createTransport) {
153
150
  }
154
151
  if (!init || input.runId !== init.runId || input.requestId <= lastRequest)
155
152
  return;
156
- // Reserve before awaiting anything, including a connect approval.
157
153
  lastRequest = input.requestId;
158
154
  void command(input).catch(() => fail("wallet_state_unavailable"));
159
155
  });
@@ -1,8 +1,9 @@
1
+ import { SERVER_VERSION } from "../mcp/serverInfo.js";
1
2
  import { z } from "zod";
2
3
  import { suiAddressStringSchema } from "../core/suiAddress.js";
3
4
  import { walletRunIdSchema, walletStartStageSchema, walletSnapshotSchema, walletSessionObservationSchema, walletSessionSchema } from "../core/session/walletRuntime.js";
4
5
  import { walletUnavailableReasonSchema } from "../core/session/walletConnection.js";
5
- const envelope = z.object({ protocolVersion: z.literal(1), runId: walletRunIdSchema });
6
+ const envelope = z.object({ packageVersion: z.literal(SERVER_VERSION), runId: walletRunIdSchema });
6
7
  const request = envelope.extend({ requestId: z.number().int().positive(), operationId: z.string().min(1) });
7
8
  export const walletCommandSchema = z.union([
8
9
  envelope.extend({ type: z.literal("init"), dataDirectory: z.string().min(1), projectId: z.string().regex(/^[0-9a-f]{32}$/i),
@@ -1,11 +1,10 @@
1
+ import { SERVER_VERSION } from "../mcp/serverInfo.js";
1
2
  import { fork } from "node:child_process";
2
3
  import { randomUUID } from "node:crypto";
3
4
  import { isDeepStrictEqual } from "node:util";
4
5
  import { walletUnavailable, WalletUnavailableError, WalletUserRejectedError } from "../core/session/walletConnection.js";
5
6
  import { WalletRunInterruptedError, WalletReplacementFailedError } from "../core/session/walletRuntime.js";
6
7
  import { walletCommandSchema, walletEventSchema } from "./walletSdkIpc.js";
7
- /** Owns one SDK process. Business authority and all database transitions remain
8
- * in the workflow; a process event is never transaction approval. */
9
8
  export class WalletSdkProcess {
10
9
  options;
11
10
  run = this.newRun();
@@ -74,13 +73,12 @@ export class WalletSdkProcess {
74
73
  child.once("exit", () => this.exited(run));
75
74
  child.once("error", () => {
76
75
  this.lose(run, this.failureReason());
77
- // Failed spawn has no process and therefore cannot emit exit.
78
76
  if (child.pid === undefined)
79
77
  this.exited(run);
80
78
  });
81
79
  child.once("disconnect", () => { if (!run.fenced && !run.didExit)
82
80
  this.lose(run, this.failureReason()); });
83
- this.send(run, { protocolVersion: 1, runId: run.id, type: "init", dataDirectory: this.options.dataDirectory,
81
+ this.send(run, { packageVersion: SERVER_VERSION, runId: run.id, type: "init", dataDirectory: this.options.dataDirectory,
84
82
  projectId: this.options.projectId, metadata: this.options.metadata });
85
83
  }
86
84
  catch {
@@ -92,7 +90,6 @@ export class WalletSdkProcess {
92
90
  send(run, message) {
93
91
  if (run !== this.run || run.fenced || this.closed || !run.child?.connected)
94
92
  throw new WalletRunInterruptedError(run.id);
95
- // A false return means backpressure, not permission to repeat the command.
96
93
  run.child.send(walletCommandSchema.parse(message), (error) => { if (error)
97
94
  this.lose(run, "wallet_state_unavailable"); });
98
95
  }
@@ -270,8 +267,6 @@ export class WalletSdkProcess {
270
267
  if (this.closed || this.run !== old || this.replacement !== replacement)
271
268
  throw new WalletRunInterruptedError(runId);
272
269
  const next = this.newRun();
273
- // A failed reservation ends this attempt. Reads may save its failure,
274
- // but only a new explicit admission may start another SDK process.
275
270
  beforeStart(next.id);
276
271
  this.run = next;
277
272
  this.state = this.starting(next.id, "process_start");
@@ -318,7 +313,7 @@ export class WalletSdkProcess {
318
313
  return new Promise((resolve, reject) => {
319
314
  run.pending.set(requestId, { operationId, type: input.type, resolve, reject, ...approval });
320
315
  try {
321
- this.send(run, { ...input, protocolVersion: 1, runId: run.id, requestId, operationId });
316
+ this.send(run, { ...input, packageVersion: SERVER_VERSION, runId: run.id, requestId, operationId });
322
317
  }
323
318
  catch (error) {
324
319
  run.pending.delete(requestId);
@@ -4,7 +4,7 @@ This document is the MCP-exposed answer playbook for AI clients. It owns user-qu
4
4
 
5
5
  It does not define tool schemas or field contracts. Use `docs/MCP_TOOLS.md` for the MCP API reference, response fields, statuses, and follow-up fields.
6
6
 
7
- It is not the contributor rulebook and it is not enforcement. Development rules live in `AGENTS.md` and `docs/AGENT_DEVELOPMENT_POLICY.md`. Hard product boundaries live in code, schemas, allowlists, mainnet guards, and the local review layer.
7
+ It is not the contributor rulebook and it is not enforcement. Development rules live in `AGENTS.md` and `docs/AGENT_DEVELOPMENT_POLICY.md` in the [source repository](https://github.com/zktx-mcp/sui-mcp). They are not installed MCP resources. Hard product boundaries live in code, schemas, allowlists, mainnet guards, and the local review layer.
8
8
 
9
9
  ## Support Matrix
10
10
 
@@ -311,7 +311,7 @@ For a connected-account-only read, use `session.get_interaction_status` to disti
311
311
  1. `account.get_active_account` reports the stored selection, with `source` and `setAt`. For current assets without a user-provided address, use `session.get_interaction_status.assetReadAccount` to check whether that selection is a usable default. If not, request an address only for tools that accept `account`; connected-account-only tools require the connection/account-selection flow. Do not infer current connection from the stored selection.
312
312
  2. An explicit connect/reconnect/disconnect/account replacement request opens an internal
313
313
  Connect card with `session.create_wallet_connection`: use `intent: connect` for requested pairing, `intent: disconnect` for requested disconnection, and `intent: manage` for inspection or account selection. The model must not call app-only actions on the user's behalf.
314
- For "disconnect my wallet", when the wallet service is available and the target is connected, open `session.create_wallet_connection` with `intent: disconnect`. For one connected target the card shows Confirm disconnect or Back directly; Back only returns to the connection view; it does not submit Cancel review or disconnect. The card starts no pairing. Do not substitute `account.clear_active_account`, claim disconnection is unsupported merely because there is no direct model-facing disconnect tool, or equate disconnection with revoking onchain permissions. If disconnection is already pending, read or wait on that exact card instead of opening another operation.
314
+ For "disconnect my wallet", when the wallet service is available and the target is connected, open `session.create_wallet_connection` with `intent: disconnect`. For one connected target the card shows Confirm disconnect or Back directly; Back only returns to the connection view; it does not submit Cancel review or disconnect. The card starts no pairing. Do not substitute the Settings action to clear the default read address, claim disconnection is unsupported merely because there is no direct model-facing disconnect tool, or equate disconnection with revoking onchain permissions. If disconnection is already pending, read or wait on that exact card instead of opening another operation.
315
315
 
316
316
  3. `session.get_wallet_connection` and `session.wait_wallet_connection` use the
317
317
  returned cardId. An unsubmitted card needs input; waiting does not create a
@@ -347,12 +347,28 @@ them. `allowedActions` describes permitted action types, not all buttons visible
347
347
  in a particular frame. In particular, a card without a target does not show
348
348
  Disconnect or Use account merely because a general action name was mentioned.
349
349
 
350
- There is no service-restart button in Connect or Settings. If the service is
351
- unavailable or disconnection remains unresponsive, explain the operational
352
- procedure in the setup guide: fully quit all apps using Sui MCP, then
353
- reopen them. Closing only a non-owning app or a window may leave the shared
354
- backend running. Do not kill processes or repeatedly send wallet commands for
355
- the user, and do not report restart success before the new service is observed.
350
+ Connection-service recovery belongs to Settings. When the service is unavailable
351
+ or a connection operation remains unresponsive, open
352
+ `settings.create_local_settings_session` on the user's request and direct them
353
+ to Troubleshooting. The user inspects the affected run/requests and explicitly
354
+ confirms Restart connection service; do not call app-only actions for them.
355
+ A new Settings card can explicitly supersede a startup still pending. Read the
356
+ same operation's result; neither a new available run nor restart completion
357
+ confirms an interrupted disconnect or a transaction result. Do not repeatedly
358
+ request wallet approval or report recovery before its new run is observed.
359
+ `manageableConnectionIds` names exact disconnect targets, not usable wallet
360
+ choices. A terminal record may remain manageable when its SDK session is still
361
+ present; do not describe the old failed operation as successful. If
362
+ `connectionReconciliationRequired` is true, explain that the service contains an
363
+ unrecorded connection. Ask the user to remove that retained connection in the
364
+ wallet app, then check its status. Settings/Troubleshooting can restart the
365
+ service to inspect it again; restarting does not approve an unrecorded session.
366
+ Do not infer a free pairing slot from a failed or expired record.
367
+
368
+ If the parent or Settings itself cannot be reached, explain the full app exit
369
+ procedure in the setup guide. All participating apps must quit; closing a peer
370
+ or window can leave the owner alive. Full app restart is also required for
371
+ stored endpoint or installation changes. Do not kill another app's processes.
356
372
 
357
373
  A failed `connectionAction: "disconnect"` means removal was not confirmed.
358
374
  Direct the user to inspect/remove the retained connection in their wallet app.
@@ -468,9 +484,27 @@ For `read.list_review_activity`, `dataScope.recordCount` is the full matching lo
468
484
 
469
485
  For `read.get_review_session_detail`, transition rows with `isNoOp: true` are repeated lifecycle observations where the stored status did not change. Mention them only as audit details; do not count them as extra funnel progress.
470
486
 
471
- For local settings or local data management, call `settings.create_local_settings_session` and tell the user to open the returned settings URL in the same machine's system browser.
472
-
473
- Do not call MCP tools to mutate settings directly; direct settings mutator tools are not exposed. Do not call clearing active account a wallet disconnect. Endpoint changes apply after MCP server restart.
487
+ For local settings, local records or connection-service recovery, call `settings.create_local_settings_session` to open the internal Settings card. Its name is unchanged; no external URL is returned. Explain Address for viewing assets, Local records, Troubleshooting or Advanced settings as needed and leave selection/confirmation to the user.
488
+
489
+ For a backup, use the exact label Download backup in Local records. It prepares
490
+ and verifies the snapshot internally and asks the app to save it in one user
491
+ action. Do not instruct a separate Create backup step or confuse Download backup
492
+ with Restore local records. If the actual download function is unsupported,
493
+ the same file is saved once in the Sui MCP backup folder. Use the verified
494
+ Backup saved result's Folder and File name for that local artifact. Do not
495
+ describe that folder as the Host download destination. Suggested file name and Host acknowledgment do not establish where
496
+ or whether a file was saved. Retry download is explicit and may create another
497
+ copy after an unconfirmed earlier request. It uses the same snapshot only during
498
+ its fixed transfer period; a new request after expiry or server restart captures
499
+ current records. Cancel download does not delete a file already saved by the app.
500
+ Use the same function-based product instructions across desktop apps. Missing
501
+ advertisement is not proof of non-support, and timeout, cancellation or denial
502
+ does not trigger local saving. Save a local copy is the explicit decision after
503
+ an earlier possibly handled request. Retry save follows only a confirmed failure
504
+ with no published file; Check status settles uncertain saving or cleanup without
505
+ another file effect. Report saved-file and cleanup facts separately.
506
+
507
+ Ordinary MCP tools never apply a Settings change. Do not call `ui.act_card` or `ui.read_settings_file` as model work. Never request/paste raw backup bytes or card permissions in chat. Clear default address only changes read context, not wallet connections or admitted transactions. Endpoint changes require quitting/reopening all apps; a connection-service restart does not apply them. Report operation result, temporary-file cleanup and Host download acknowledgment as separate facts. A saved result is not proof that the Host saved a downloaded file. For restore validation refusal, let the user correct input or choose another file and prepare a new confirmation; reading or returning to a frame never applies it.
474
508
 
475
509
  ## User Vocabulary
476
510
 
@@ -518,7 +552,3 @@ Avoid unqualified words such as best, recommended, guaranteed, or safe. Prefer:
518
552
  ## Language
519
553
 
520
554
  This reference document stays in English. Reply in the user's language. Translate response patterns as needed, but keep SDK names, tool names, object IDs, package IDs, and token symbols in their original form.
521
-
522
- ## Golden Scenarios
523
-
524
- Detailed expected response classes live in `docs/golden-scenarios/BEHAVIOR_MATRIX.md`. They are documentation for release review, not automated cross-client results.
@@ -10,7 +10,7 @@ The process that binds the configured loopback port initializes these local comp
10
10
 
11
11
  - a local SQLite store;
12
12
  - a mainnet guard for the configured Sui gRPC endpoint;
13
- - the local review HTTP server on `127.0.0.1`;
13
+ - the shared private HTTP transport on `127.0.0.1`;
14
14
  - authenticated internal MCP sessions, reached by each client through a stdio forwarder.
15
15
 
16
16
  The GraphQL endpoint is also mainnet-guarded when it is saved through settings, imported from a local-data backup, or first used by Sui activity tools.
@@ -33,7 +33,7 @@ User-requested bounded transaction scans can store normalized facts only when a
33
33
 
34
34
  The default Sui mainnet gRPC and GraphQL endpoints are stored in the local SQLite settings table on first run.
35
35
 
36
- To inspect settings or change local data, ask your AI client to create a Sui MCP local settings session and open the returned settings URL in the same machine's system browser.
36
+ To inspect settings or change local data, ask for the internal Settings card. Ordinary MCP creates an unsubmitted card or reads settings; scoped app-only preparation/confirmation owns changes. No external page or Settings HTTP API remains.
37
37
 
38
38
  Endpoint changes apply after the MCP server restarts.
39
39
 
@@ -105,7 +105,10 @@ The following live session tables hold runtime session state in the shared datab
105
105
  - `live_private_review_artifacts`: per-session private review evidence (the transaction-material handle, its digest commitment, and derived evidence), cascade-deleted with its review session.
106
106
  - `live_transaction_materials`: locally built unsigned transaction bytes stored as a BLOB behind a redacted handle, with a TTL; retained for the admitted request. Terminal request cleanup removes only material still owned by that revision, preserving newer preparation. Owner replacement deletes old private material while retaining immutable review and chain-result evidence.
107
107
  - `live_wallet_connections`: verified connection facts, private topic and callback state.
108
- - `live_settings_sessions`: short-lived Settings page authority.
108
+ - `live_read_cards`: public card state with private permission hash, scope, operation and CAS revision.
109
+ - `settings_operations`: one draft/upload/preparation/admission/result identity per Settings card, with owner/data epoch, user-data revision, fingerprint and SDK supersession/checkpoint facts. A download stores its typed snapshot lifetime and per-attempt delivery reports in private `preparation_json`, separately from the immutable result. Updating delivery never reserializes a terminal `result_json`.
110
+ - `settings_files`: private file identity, original operation, upload/export role, generated paths, lengths/hash, chunk reservation and seal/invalidation/cleanup state. `cleanup_operation_id` separately names the operation responsible for cleanup; replacement can transfer that responsibility without changing the file identity, public path or input eligibility.
111
+ - `settings_control`: current data epoch/user-data revision, one final-operation slot, one file-I/O reservation and the SDK input block owner. These are semantic state/authority facts, not release-version counters.
109
112
 
110
113
  These live tables are distinct from the append-only review evidence tables above: the evidence tables remain the durable activity/audit record, while the live tables hold the in-flight session state that the review server serves.
111
114
 
@@ -132,13 +135,92 @@ update triggers reject revision-unaware writers: inserts must use
132
135
  `revision = 0` and the current write-contract marker, and updates must increase
133
136
  the revision by exactly one and keep the current write-contract marker.
134
137
 
135
- Logical local data reset is the local settings page action that clears stored product state through the runtime without requiring manual database-file deletion. Replace-only import is the settings page import path that replaces local product state from a validated backup. When no unsettled financial request prevents replacement, both logical local data reset and replace-only import clear `coin_metadata_cache`, card records, live review/wallet/settings sessions, private review artifacts and transaction material in the same SQLite transaction. A rollback preserves the existing data and permissions. No separate HTTP cleanup call owns permission invalidation. Clearing active account context does not clear it because coin metadata is account-independent.
136
-
137
- Non-terminal review session expiry is recorded lazily when the session is read or mutated after its TTL. There is no background expiry worker.
138
+ Reset/import are explicitly confirmed Settings operations. They validate the
139
+ current backup shape and imported mainnet endpoints, check current prepared
140
+ impact and reject unsettled wallet/transaction work. One SQLite transaction
141
+ replaces user data, clears coin metadata and other live authorities, advances
142
+ data epoch and preserves the applying operation's read-only card/result. It
143
+ never recreates that result from a callback after deleting it. Rollback
144
+ preserves the prior data, permissions and admission state.
145
+ The private `live_wallet_connections` service journal remains because replacing
146
+ user data does not end SDK sessions. It cannot restore old card permissions or
147
+ transaction authority and is not imported from a backup. Reset clears the
148
+ default address; an imported default still needs its exact current connection
149
+ ID and approved address. Outstanding connection SDK callbacks also block replacement.
150
+
151
+ User-data and wallet/request writers invalidate affected preparations and
152
+ increment their card revision in the same transaction. The trigger and `settings_back`
153
+ reuse one SQL transition: a complete, sealed, non-invalidated upload belonging
154
+ to that operation returns to sealed; other preparations return to draft. Old
155
+ confirmations cannot apply. Backup checking and final I/O still verify physical content;
156
+ a manifest alone is not proof of a valid file.
157
+
158
+ A `download_backup` admission consumes one card input and captures one local-data
159
+ snapshot in the same SQLite transaction. The export role and exact generated
160
+ `.settings-staging/<fileId>.download` namespace identify its private file; an
161
+ export role or display name alone is insufficient. File bytes are not stored
162
+ in operation JSON or nested in exported local data. Physical write, fsync, length and SHA checks
163
+ finish before the snapshot becomes available. `result` is immutable preparation
164
+ evidence; delivery reports have separate typed state and atomic attempt identity.
165
+
166
+ An exact unsupported download report can atomically reserve final/file I/O and
167
+ admit local publication of that same snapshot. An earlier possibly handled
168
+ attempt instead records a pending local-copy decision. Explicit consent/retry
169
+ uses a distinct save-attempt identity and compare-and-set predecessor, without
170
+ creating another snapshot. Application names and absent capability advertisement
171
+ do not authorize this effect.
172
+
173
+ The private `download.localSave` journal in `preparation_json` records its source
174
+ manifest, fixed `backups/<operationId>.json` destination, attempt/consent and
175
+ physical source device/inode as decimal strings. The verified source identity
176
+ is committed before exclusive same-volume hard-link publication. The source and
177
+ target must name the recorded file and pass size/SHA checks before `saved` is
178
+ committed. Existing destinations are never overwritten or removed. Private
179
+ unlink follows that result and actual descriptor closure; the final file has
180
+ no automatic expiry. Historical export path validation remains distinct from
181
+ the private `.download` row identity.
182
+
183
+ Unknown file effects remain `saving`, retaining their source and reservations.
184
+ A failed DB result write retains the ended physical outcome for settlement.
185
+ A missing target is distinct from one that could not be read. Only a verified
186
+ absent target with a valid source permits an explicit retry within the original
187
+ period. Recovery after owner exit verifies an existing source-bound target only;
188
+ an absent or mismatching artifact is not regenerated. The new owner inherits
189
+ settlement responsibility without restoring the old card's action permission.
190
+ A committed saved result remains a past fact when its private file is gone.
191
+
192
+ The fixed thirty-minute transfer period begins at physical readiness, independently
193
+ of the original input expiry. The owner schedules its earliest live transfer
194
+ deadline; current DB time, owner and epoch decide expiry. An abandoned frame does
195
+ not own cleanup. A failed already-due journal cannot create a zero-delay loop or
196
+ cancel another snapshot's future deadline. Status reads and owner recovery can
197
+ retry cleanup. Current-generation access rejects stale callbacks after replacement.
198
+
199
+ Private staging files survive as cleanup journal entries when cards are
200
+ invalidated. Reset/import invalidate uploads and private downloads and transfer
201
+ cleanup ownership without changing file identity. Existing public backup files
202
+ remain unchanged. Acknowledgment, explicit cancellation, expiry, source failure
203
+ or owner restart ends private retention, except that an already-admitted local
204
+ publication retains the evidence needed to settle its result first. Its final
205
+ reservation blocks competing Settings application and data replacement until
206
+ physical I/O and result settlement end. Active descriptor reads/writes finish
207
+ before deletion; new ranges and retries are refused once authority ends.
208
+ `temporaryFileCleanup` includes physical cleanup and settlement of the operation's
209
+ ended I/O reservation. Stored results remain readable during pending cleanup,
210
+ including when expiry persistence fails; input remains closed and the failed
211
+ cleanup stays visible. Another operation's reservation is never released.
212
+
213
+ An explicit same-result read may finish its cleanup or verify an existing
214
+ published export, never regenerate a backup, repeat Host delivery, reapply data
215
+ or replace an SDK run. Files/permissions/SDK credentials/private transaction
216
+ material are excluded from public backups. Clearing the default address does
217
+ not invalidate account-independent coin metadata or disconnect the wallet.
218
+
219
+ Non-terminal review session expiry is recorded lazily when the session is read or mutated after its TTL. Review-session expiry has no background worker; private download retention uses the separate owner timer described above.
138
220
 
139
221
  ## Shared local server
140
222
 
141
- Exactly one parent runtime owner per data directory binds the configured loopback port and creates the shared product services and SQLite stores. Other clients authenticate the listener before sending control credentials and forward MCP messages to it. The authentication covers database identity, internal API version 7, configuration (including WalletConnect project configuration) and server instance. Proof and subsequent dispatch use the same TCP connection. Host/Origin validation is separate from authentication; neither protects against a malicious process running as the same OS user.
223
+ Exactly one parent runtime owner per data directory binds the configured loopback port and creates the shared product services and SQLite stores. Other clients authenticate the listener before sending control credentials and forward MCP messages to it. The authentication covers database identity, the package release version, configuration (including WalletConnect project configuration) and server instance. Proof and subsequent dispatch use the same TCP connection. Host/Origin validation is separate from authentication; neither protects against a malicious process running as the same OS user.
142
224
 
143
225
  The private `runtime-control.key` file is separate from UI permissions and wallet credentials and is excluded from product backups. Stdio closure and process signals close the owned server. No client signals a peer backend. The parent may terminate only its own SDK child. A peer can acquire the port after it becomes free. Failed calls are not replayed automatically. MCP framing, cancellation and EOF handling start before backend acquisition; wallet SDK readiness does not block parent service startup.
144
226
 
@@ -204,10 +286,12 @@ child exit, while parent verification, submission and lookup flags remain until
204
286
  their own work ends. SDK readiness does not clear those independent guards.
205
287
 
206
288
  Initial SDK restoration considers the entire known connection set, including
207
- prior-owner records, before publishing current eligibility. Only confirmed
208
- known connected records transfer to the new owner. Multiple valid records are
209
- management targets, not usable wallet choices: `connectionConflict` is derived
210
- from those rows with no additional table. Targeted disconnection remains
289
+ prior-owner records, before publishing current eligibility. Confirmed known
290
+ connected records transfer to the new owner. Known terminal records with a
291
+ present SDK session transfer management metadata without changing their outcome.
292
+ Multiple known occupied connections are management targets, not usable wallet
293
+ choices: `connectionConflict` is derived from the journal and current supervisor
294
+ snapshot, with no additional table. Targeted disconnection remains
211
295
  available while new pairing, account use, review preparation and signing are
212
296
  blocked. The final submission transaction applies the same single-connection
213
297
  rule. Failed/stopped records and unknown SDK sessions do not restore authority.
@@ -221,6 +305,17 @@ database evaluation time and published to cards as `usableConnectionId` and
221
305
  `assetReadAccount`; Views do not recount recorded connections. This adds no
222
306
  table or expiry writer. Stored status, disconnection outcomes and historical
223
307
  account records retain their separate meanings.
308
+ The store reads the current run's private supervisor snapshot inside evaluation
309
+ and immediate admission. Missing inspection does not mean an empty service.
310
+ `manageableConnectionIds` names exact nonpending recorded disconnect targets,
311
+ including known terminal records whose SDK sessions remain present. They are not
312
+ usable account choices. `connectionReconciliationRequired: true` means a present
313
+ SDK session has no journal target after ordinary registration/cleanup has ended;
314
+ it blocks pairing, account use and unsubmitted signing until the user removes the
315
+ retained connection in the wallet app and a complete current-run observation
316
+ confirms absence. Settings can explicitly restart the service to inspect it
317
+ again; restart alone does not resolve retained presence. Topics remain private. A pending registration
318
+ is not reported as a reconciliation failure.
224
319
 
225
320
  An admission that changes connection eligibility or targets also publishes the
226
321
  owner's ready Connect/Review card revisions in that same transaction, after
@@ -234,7 +329,8 @@ write; they do not start another SDK process. Repeated reads after a successful
234
329
  repair do not repeat the transition. Unrelated stored transaction results and
235
330
  already dispatched chain observation remain independent of that write.
236
331
  The supervisor's explicit replacement primitive requires fencing and actual
237
- child exit before a new run, but it has no current card or model entry point.
332
+ child exit before a new run. Its user entry point is a scoped, explicitly
333
+ prepared and confirmed Settings card; ordinary model calls cannot replace it.
238
334
 
239
335
  If a completed callback's flag write fails, the owner retains its exact operation
240
336
  identity as completion evidence. A current state read can retry that database
@@ -257,6 +353,25 @@ The target session/card's own required expiry writes still propagate errors.
257
353
  This is not availability during an unreadable database or a bypass of authority
258
354
  revocation failures.
259
355
 
356
+ Connection facts and workflow maintenance have separate internal contracts.
357
+ `readConnectionFacts(at)` collects recorded connections, current-run SDK presence,
358
+ pending work and read-address provenance from one SQLite read snapshot. The
359
+ shared selector and qualification projection compute eligibility from those
360
+ facts; they do not update requests, expire connections or repair callbacks.
361
+ Settings general facts use this diagnostic path and stored counts, so another
362
+ request's refused deadline write does not prevent opening or reading Settings.
363
+ Creating a card and checking its own permission, input expiry or cleanup retain
364
+ their own writes. Stored counts are not a promise about a later deletion impact.
365
+
366
+ `reconcileAndEvaluate` requires a current-wallet or server-bound review purpose.
367
+ Current wallet consumers use `reconcileConnectionContext`; exact request reads
368
+ retain their narrower target. Reset/import preparation and final admission still
369
+ settle deadlines and compare the current impact in their transaction. Snapshot
370
+ memory may precede its DB publication: initializing/state_sync does not grant
371
+ account use or establish an unrecorded-connection removal diagnosis. Only a
372
+ published available run can qualify that guidance; missing guidance does not
373
+ prove that the SDK is empty.
374
+
260
375
  Successful reset/import invalidates pending SDK startup or recovery in the old
261
376
  data generation and ends that child without automatically starting another.
262
377
  Rejected or rolled-back replacement does not change the run or generation.
@@ -335,13 +450,21 @@ unknown-outcome records may be removed after the explicit warning; removing
335
450
  records cannot cancel a transaction. A generation change rejects late writes.
336
451
 
337
452
  The wallet store advances expired signature and initial-observation deadlines
338
- through the same request transition writer used by the workflow. Current
339
- session/card reads, current local-data counts and preview counts, and the actual
340
- reset/import transaction use this operation even when no View is open. Expiry
453
+ through the same request transition writer used by the workflow. Explicit
454
+ current-wallet reconciliation settles current wallet work. A server-bound review
455
+ read settles its own request target unless it depends on current wallet work;
456
+ it does not use an unrelated completed request as a reason for global maintenance.
457
+ Reset/import preparation settles deadlines before calculating its impact, and
458
+ final admission settles them again and compares the current impact in the same
459
+ transaction as replacement, even when no View is open.
460
+
461
+ General Settings facts and stored local-data counts use the diagnostic read
462
+ contract; they do not advance live requests. Creating or reading a card still
463
+ checks that card's own permission, input expiry and operation cleanup. Expiry
341
464
  does not clear pending SDK/submission/lookup callbacks. Stored activity queries
342
- and public backup export only read recorded facts; they do not advance live
343
- requests or interpret imported history as authority. Replacement warnings do not
344
- claim an earlier counts snapshot is the number being deleted now.
465
+ and public backup export also read recorded facts without advancing live
466
+ requests or treating imported history as authority. Replacement warnings do not
467
+ claim an earlier stored-counts snapshot is the number being deleted now.
345
468
 
346
469
  Disconnect progress is the existing running Connect card's accepted disconnect
347
470
  action and connection reference. Connection updates preserve that operation
@@ -349,11 +472,11 @@ until its result is recorded; no additional persisted connection status is used.
349
472
 
350
473
  ### Current format and backups
351
474
 
352
- The current `user_version` is 9. It identifies one supported schema; there is no migration registry or older-format decoder. All clients sharing a data directory must use the same compatible runtime.
475
+ The runtime accepts one current SQLite structure, checked against its table, column, constraint, index and trigger definitions before writes. There is no separate database version counter, migration registry or older-format decoder. All clients sharing a data directory must use the same package release.
353
476
 
354
- A current backup carries `format`, `schemaVersion`, `network`, `exportedAt` and its product data. Import rejects a missing or different schema identifier, incomplete required fields, invalid references or invalid raw quantities. Missing settings or activity arrays are not filled from defaults. Supported `function_scan` provenance remains part of the current format. Validation failure leaves current data intact.
477
+ A current backup carries `format`, `network`, `exportedAt` and its product data. It has no numeric schema version. Import rejects a different format, unknown or missing fields, invalid references or invalid raw quantities. Missing settings or activity arrays are not filled from defaults. Supported `function_scan` provenance remains part of the current format. Validation failure leaves current data intact.
355
478
 
356
- New installations do not inherit older local review records, activity scans, known/active accounts or stored endpoints. Users configure the required context again; environment overrides retain their existing precedence. Empty local history does not imply an absence of on-chain transactions. Existing DB and backup files are not deleted by startup.
479
+ With a new empty data directory, users configure account context and endpoints; environment overrides retain their existing precedence. Empty local history does not imply an absence of on-chain transactions. Existing DB and backup files are not deleted by startup.
357
480
 
358
481
  ## Boundaries
359
482
 
@@ -373,7 +496,7 @@ Ownership and simulation private artifacts use their v2 evidence formats to
373
496
  bind transaction funding to the stored material. They cover real coin objects,
374
497
  address withdrawals, gas reservations, and their observed balance/epoch facts.
375
498
  They are deeply copied and revalidated before admission, and are excluded from
376
- public backup. They introduce no table, column, or DB format migration.
499
+ public backup.
377
500
 
378
501
  Old private evidence is not upgraded into funding proof. Existing owner recovery
379
502
  removes private material; a new review recomputes it. Historical public reviews,