@zktx.io/sui-mcp 0.5.0

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 (218) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSES/@mysten-bcs-2.0.5-Apache-2.0.txt +205 -0
  3. package/LICENSES/@mysten-sui-2.17.0-Apache-2.0.txt +201 -0
  4. package/LICENSES/@mysten-utils-0.3.3-Apache-2.0.txt +205 -0
  5. package/LICENSES/fancy-canvas-2.1.0-MIT.txt +7 -0
  6. package/LICENSES/lightweight-charts-5.2.0-NOTICE.txt +2 -0
  7. package/README.md +288 -0
  8. package/dist/adapters/adapterLifecycleValidators.js +5 -0
  9. package/dist/adapters/adapterPromptSurfaces.js +60 -0
  10. package/dist/adapters/deepbook/deepbookHumanReviewProducer.js +178 -0
  11. package/dist/adapters/deepbook/deepbookQuotePolicy.js +112 -0
  12. package/dist/adapters/deepbook/deepbookReviewEvidence.js +507 -0
  13. package/dist/adapters/deepbook/deepbookReviewLifecycle.js +85 -0
  14. package/dist/adapters/deepbook/deepbookSwapIntent.js +79 -0
  15. package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +254 -0
  16. package/dist/adapters/intentPlanFactories.js +51 -0
  17. package/dist/adapters/reviewAdapters.js +47 -0
  18. package/dist/core/action/adapterLifecycleValidation.js +12 -0
  19. package/dist/core/action/contractNameRegistry.js +151 -0
  20. package/dist/core/action/forbiddenFields.js +43 -0
  21. package/dist/core/action/humanReadableReviewEvidence.js +193 -0
  22. package/dist/core/action/humanReadableReviewProjectionVerifier.js +29 -0
  23. package/dist/core/action/ptbVisualizationProducer.js +71 -0
  24. package/dist/core/action/receiptPtbGraph.js +29 -0
  25. package/dist/core/action/reviewCheckResults.js +6 -0
  26. package/dist/core/action/reviewStateValidation.js +11 -0
  27. package/dist/core/action/reviewTimeSimulationEvidence.js +470 -0
  28. package/dist/core/action/schemas.js +491 -0
  29. package/dist/core/action/signableAdapterContract.js +998 -0
  30. package/dist/core/action/suiChainReceiptEvidence.js +106 -0
  31. package/dist/core/action/suiChainReceiptMapping.js +107 -0
  32. package/dist/core/action/suiChainReceiptReader.js +211 -0
  33. package/dist/core/action/suiChainReceiptVerifier.js +178 -0
  34. package/dist/core/action/swapHumanReadableReviewProjection.js +130 -0
  35. package/dist/core/action/swapQuotePolicyEvidence.js +271 -0
  36. package/dist/core/action/transactionFunding.js +244 -0
  37. package/dist/core/action/transactionObjectOwnershipEvidence.js +256 -0
  38. package/dist/core/action/transactionObjectOwnershipProducer.js +323 -0
  39. package/dist/core/action/types.js +33 -0
  40. package/dist/core/action/walletReviewContractAssembler.js +286 -0
  41. package/dist/core/activity/accountAssetTimeline.js +157 -0
  42. package/dist/core/activity/accountAssetTimelineUsdcReferences.js +120 -0
  43. package/dist/core/activity/activityStore.js +16 -0
  44. package/dist/core/activity/externalActivityCoverage.js +76 -0
  45. package/dist/core/activity/externalActivityTransactionStream.js +82 -0
  46. package/dist/core/activity/localDataService.js +296 -0
  47. package/dist/core/activity/localDataTypes.js +11 -0
  48. package/dist/core/activity/localDataValidation.js +439 -0
  49. package/dist/core/activity/schemaVersion.js +2 -0
  50. package/dist/core/activity/sqliteActivityStore.js +1068 -0
  51. package/dist/core/activity/sqliteActivityStoreRows.js +389 -0
  52. package/dist/core/activity/sqliteActivityStoreSchema.js +367 -0
  53. package/dist/core/activity/sqliteActivityStoreTypes.js +5 -0
  54. package/dist/core/activity/suiFunctionTarget.js +43 -0
  55. package/dist/core/activity/transactionActivityAccountEffects.js +186 -0
  56. package/dist/core/activity/transactionActivityAnalysis.js +295 -0
  57. package/dist/core/activity/transactionActivityClassifier.js +306 -0
  58. package/dist/core/activity/transactionActivityDetails.js +229 -0
  59. package/dist/core/activity/transactionActivityProtocolRules.js +218 -0
  60. package/dist/core/activity/transactionActivityScanPolicy.js +170 -0
  61. package/dist/core/activity/transactionActivityService.js +379 -0
  62. package/dist/core/activity/transactionActivityTypes.js +18 -0
  63. package/dist/core/eventlog/sink.js +35 -0
  64. package/dist/core/evidence/settlementFamilies.js +85 -0
  65. package/dist/core/evidence/userAnswerUse.js +1 -0
  66. package/dist/core/numeric/rawU64.js +63 -0
  67. package/dist/core/preferences/preferencesStore.js +26 -0
  68. package/dist/core/preferences/sqlitePreferencesRepository.js +136 -0
  69. package/dist/core/proposal/externalProposalReview.js +347 -0
  70. package/dist/core/proposal/schemas.js +208 -0
  71. package/dist/core/proposal/types.js +35 -0
  72. package/dist/core/read/amounts.js +14 -0
  73. package/dist/core/read/coinMetadata.js +60 -0
  74. package/dist/core/read/deepbookOfficialIndexerSource.js +373 -0
  75. package/dist/core/read/deepbookRawQuoteClient.js +86 -0
  76. package/dist/core/read/deepbookReadHelpers.js +321 -0
  77. package/dist/core/read/deepbookRegistry.js +157 -0
  78. package/dist/core/read/deepbookSourceOwners.js +159 -0
  79. package/dist/core/read/deepbookUsdcChartService.js +342 -0
  80. package/dist/core/read/intentEvidenceResponseFormatting.js +228 -0
  81. package/dist/core/read/readCardInputs.js +19 -0
  82. package/dist/core/read/readCardResult.js +40 -0
  83. package/dist/core/read/readResponseGuidance.js +402 -0
  84. package/dist/core/read/readService.js +1639 -0
  85. package/dist/core/read/readServiceTypes.js +88 -0
  86. package/dist/core/read/settlementParityFormatting.js +82 -0
  87. package/dist/core/read/walletReadHelpers.js +99 -0
  88. package/dist/core/review/reviewChecks.js +45 -0
  89. package/dist/core/review/reviewComputation.js +38 -0
  90. package/dist/core/review/reviewComputationResult.js +87 -0
  91. package/dist/core/session/cardSession.js +31 -0
  92. package/dist/core/session/cardSessionStore.js +204 -0
  93. package/dist/core/session/keyedRecordStore.js +18 -0
  94. package/dist/core/session/liveReviewSessionContract.js +3 -0
  95. package/dist/core/session/localSession.js +31 -0
  96. package/dist/core/session/privateReviewArtifacts.js +75 -0
  97. package/dist/core/session/reviewValidity.js +49 -0
  98. package/dist/core/session/sessionErrors.js +9 -0
  99. package/dist/core/session/sessionRecordStore.js +40 -0
  100. package/dist/core/session/sessionStore.js +655 -0
  101. package/dist/core/session/settingsSession.js +1 -0
  102. package/dist/core/session/settingsSessions.js +45 -0
  103. package/dist/core/session/sqliteCardStore.js +88 -0
  104. package/dist/core/session/sqliteSessionStore.js +213 -0
  105. package/dist/core/session/sqliteTransactionMaterialStore.js +64 -0
  106. package/dist/core/session/sqliteWalletWorkflowStore.js +821 -0
  107. package/dist/core/session/status.js +60 -0
  108. package/dist/core/session/transactionMaterialStore.js +221 -0
  109. package/dist/core/session/transactionRequest.js +72 -0
  110. package/dist/core/session/wait.js +79 -0
  111. package/dist/core/session/walletConnection.js +92 -0
  112. package/dist/core/session/walletRuntime.js +38 -0
  113. package/dist/core/session/walletWorkflow.js +608 -0
  114. package/dist/core/session/workflowState.js +80 -0
  115. package/dist/core/session/workflowView.js +150 -0
  116. package/dist/core/suiAddress.js +18 -0
  117. package/dist/core/suiEndpoint.js +72 -0
  118. package/dist/mcp/activeAccountResponse.js +24 -0
  119. package/dist/mcp/prompts.js +146 -0
  120. package/dist/mcp/registerTool.js +19 -0
  121. package/dist/mcp/resources.js +72 -0
  122. package/dist/mcp/responseGuidance.js +326 -0
  123. package/dist/mcp/result.js +17 -0
  124. package/dist/mcp/schemas.js +11 -0
  125. package/dist/mcp/server.js +32 -0
  126. package/dist/mcp/serverInfo.js +131 -0
  127. package/dist/mcp/toolErrors.js +118 -0
  128. package/dist/mcp/toolNames.js +58 -0
  129. package/dist/mcp/tools/account/index.js +44 -0
  130. package/dist/mcp/tools/action/prepareSuiActionReview.js +126 -0
  131. package/dist/mcp/tools/read/commonSchemas.js +43 -0
  132. package/dist/mcp/tools/read/deepbookReadTools.js +686 -0
  133. package/dist/mcp/tools/read/index.js +14 -0
  134. package/dist/mcp/tools/read/readToolHelpers.js +97 -0
  135. package/dist/mcp/tools/read/reviewActivityTools.js +179 -0
  136. package/dist/mcp/tools/read/serverStatusTools.js +100 -0
  137. package/dist/mcp/tools/read/transactionActivityOutput.js +371 -0
  138. package/dist/mcp/tools/read/transactionActivityTools.js +858 -0
  139. package/dist/mcp/tools/read/walletReadTools.js +734 -0
  140. package/dist/mcp/tools/session/executionResultTools.js +44 -0
  141. package/dist/mcp/tools/session/index.js +8 -0
  142. package/dist/mcp/tools/session/shared.js +36 -0
  143. package/dist/mcp/tools/session/statusTools.js +69 -0
  144. package/dist/mcp/tools/session/walletConnectionTools.js +69 -0
  145. package/dist/mcp/tools/settings/index.js +66 -0
  146. package/dist/mcp-app/account.html +3133 -0
  147. package/dist/mcp-app/account.notices.txt +973 -0
  148. package/dist/mcp-app/chart.html +3354 -0
  149. package/dist/mcp-app/chart.notices.txt +1194 -0
  150. package/dist/mcp-app/connect.html +3179 -0
  151. package/dist/mcp-app/connect.notices.txt +1012 -0
  152. package/dist/mcp-app/receipt.html +8430 -0
  153. package/dist/mcp-app/receipt.notices.txt +3549 -0
  154. package/dist/mcp-app/review.html +8430 -0
  155. package/dist/mcp-app/review.notices.txt +3549 -0
  156. package/dist/mcp-ui/contracts.js +31 -0
  157. package/dist/mcp-ui/readCards.js +28 -0
  158. package/dist/mcp-ui/tools.js +134 -0
  159. package/dist/review-app/brand-dark.svg +1 -0
  160. package/dist/review-app/brand-light.svg +1 -0
  161. package/dist/review-app/favicon.svg +1 -0
  162. package/dist/review-app/settings.css +1 -0
  163. package/dist/review-app/settings.js +1 -0
  164. package/dist/review-app/ui.css +809 -0
  165. package/dist/review-server/assets.js +48 -0
  166. package/dist/review-server/html.js +45 -0
  167. package/dist/review-server/http.js +47 -0
  168. package/dist/review-server/middleware/hostOrigin.js +48 -0
  169. package/dist/review-server/middleware/reviewToken.js +7 -0
  170. package/dist/review-server/reviewServerPolicy.js +1 -0
  171. package/dist/review-server/server.js +72 -0
  172. package/dist/review-server/settingsApi.js +158 -0
  173. package/dist/runtime/application.js +203 -0
  174. package/dist/runtime/config.js +97 -0
  175. package/dist/runtime/localSettingsService.js +198 -0
  176. package/dist/runtime/logger.js +66 -0
  177. package/dist/runtime/reviewDependencies.js +58 -0
  178. package/dist/runtime/reviewServerAcquire.js +93 -0
  179. package/dist/runtime/runtimeTermination.js +60 -0
  180. package/dist/runtime/shared/authenticatedFetch.js +127 -0
  181. package/dist/runtime/shared/control.js +93 -0
  182. package/dist/runtime/shared/dataAccess.js +66 -0
  183. package/dist/runtime/shared/mcpHttp.js +84 -0
  184. package/dist/runtime/shared/ownerLease.js +50 -0
  185. package/dist/runtime/shared/server.js +98 -0
  186. package/dist/runtime/shared/stdio.js +113 -0
  187. package/dist/runtime/smokeMainnetRead.js +391 -0
  188. package/dist/runtime/smokeMainnetReadAssertions.js +306 -0
  189. package/dist/runtime/smokeReview.js +40 -0
  190. package/dist/runtime/start.js +90 -0
  191. package/dist/runtime/suiEndpoint.js +97 -0
  192. package/dist/runtime/suiTransactionGraphqlMapping.js +200 -0
  193. package/dist/runtime/suiTransactionGraphqlQueries.js +231 -0
  194. package/dist/runtime/suiTransactionGraphqlSource.js +148 -0
  195. package/dist/runtime/walletConnectConfig.js +3 -0
  196. package/dist/runtime/walletConnectStorage.js +99 -0
  197. package/dist/runtime/walletConnectTransport.js +128 -0
  198. package/dist/runtime/walletSdkChild.js +3 -0
  199. package/dist/runtime/walletSdkChildRuntime.js +160 -0
  200. package/dist/runtime/walletSdkIpc.js +26 -0
  201. package/dist/runtime/walletSdkProcess.js +358 -0
  202. package/docs/AGENT_BEHAVIOR.md +524 -0
  203. package/docs/AGENT_DEVELOPMENT_POLICY.md +815 -0
  204. package/docs/FRONTEND_POLICY.md +534 -0
  205. package/docs/LOCAL_DB_ARCHITECTURE.md +381 -0
  206. package/docs/MCP_SETUP.md +742 -0
  207. package/docs/MCP_TOOLS.md +1318 -0
  208. package/docs/SDK_API.md +206 -0
  209. package/docs/SIGNABLE_ADAPTER_CONTRACT.md +396 -0
  210. package/docs/TRANSACTION_ACTIVITY_LOG.md +194 -0
  211. package/docs/UTILITY_INDEX.md +275 -0
  212. package/docs/WALLET_CONNECTION.md +238 -0
  213. package/docs/golden-scenarios/BEHAVIOR_MATRIX.md +166 -0
  214. package/docs/golden-scenarios/INTENT_EVIDENCE_GOLDEN_ANSWERS.md +161 -0
  215. package/docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md +259 -0
  216. package/package.json +79 -0
  217. package/protocols/deepbook-margin.md +15 -0
  218. package/protocols/deepbook-v3.md +24 -0
@@ -0,0 +1,439 @@
1
+ import { transactionRequestStatusSchema, transactionRequestSchema } from "../session/transactionRequest.js";
2
+ import { REVIEW_TRANSITION_EVENTS } from "./sqliteActivityStoreRows.js";
3
+ import { DB_USER_VERSION } from "./schemaVersion.js";
4
+ import { z } from "zod";
5
+ import { assertNoForbiddenMcpFields } from "../action/forbiddenFields.js";
6
+ import { parseLifecycleValidatedReviewState } from "../action/reviewStateValidation.js";
7
+ import { actionPlanSchema, transactionExecutionSummarySchema, internalSessionStatusSchema } from "../action/schemas.js";
8
+ import { parseSuiAddress } from "../suiAddress.js";
9
+ import { parseGraphqlUrl, parseGrpcUrl } from "../suiEndpoint.js";
10
+ import { EXTERNAL_ACTIVITY_TRANSACTION_DETAIL_JSON_MAX_BYTES, externalActivityTransactionDetailJsonByteLength, externalActivityTransactionDetailSchema, externalActivityTransactionDetailsReferenceOnlyAccount } from "./transactionActivityDetails.js";
11
+ import { LOCAL_DATA_EXPORT_FORMAT, LOCAL_DATA_NETWORK, LocalDataError } from "./localDataTypes.js";
12
+ const isoTimestamp = z.string().refine((value) => {
13
+ const parsed = new Date(value);
14
+ return !Number.isNaN(parsed.getTime()) && parsed.toISOString() === value;
15
+ }, "must be an ISO 8601 UTC timestamp");
16
+ const nullableString = z.string().nullable();
17
+ const accountRowSchema = z.object({
18
+ id: z.number().int().positive(),
19
+ sui_address: z.string().refine((value) => parseSuiAddress(value) === value, "must be a normalized Sui address"),
20
+ first_seen_at: isoTimestamp,
21
+ last_used_at: isoTimestamp,
22
+ first_source: z.enum(["wallet_connection", "review_execution"]),
23
+ last_source: z.enum(["wallet_connection", "review_execution"])
24
+ }).strict();
25
+ const activeAccountContextRowSchema = z.object({
26
+ id: z.literal(1),
27
+ account_id: z.number().int().positive().nullable(),
28
+ source: z.enum(["wallet_connection", "cleared"]),
29
+ set_at: isoTimestamp,
30
+ wallet_name: z.string().min(1).max(200).nullish(),
31
+ wallet_id: z.string().min(1).max(200).nullish()
32
+ }).strict();
33
+ const reviewSessionRowSchema = z.object({
34
+ id: z.string().min(1),
35
+ plan_id: z.string().min(1),
36
+ action_kind: z.string().min(1),
37
+ adapter_id: z.string().min(1),
38
+ protocol: z.string().min(1),
39
+ account_id: z.number().int().positive().nullable(),
40
+ current_status: internalSessionStatusSchema,
41
+ current_attempt_id: z.string().min(1).nullable(),
42
+ opened_at: isoTimestamp.nullable(),
43
+ plan_json: z.string(),
44
+ intent_json: z.string().nullable(),
45
+ created_at: isoTimestamp,
46
+ updated_at: isoTimestamp
47
+ }).strict();
48
+ const reviewStateSnapshotRowSchema = z.object({
49
+ id: z.number().int().positive(),
50
+ review_revision: z.number().int().nonnegative(),
51
+ review_session_id: z.string().min(1),
52
+ plan_id: z.string().min(1),
53
+ account_id: z.number().int().positive(),
54
+ status: z.enum(["ready_for_wallet_review", "refresh_required", "blocked"]),
55
+ blocked_reason: nullableString,
56
+ refresh_reason: nullableString,
57
+ state_json: z.string(),
58
+ updated_at: isoTimestamp,
59
+ recorded_at: isoTimestamp
60
+ }).strict();
61
+ const reviewStatusTransitionRowSchema = z.object({
62
+ id: z.number().int().positive(),
63
+ review_session_id: z.string().min(1),
64
+ event: z.enum(REVIEW_TRANSITION_EVENTS),
65
+ domain: z.enum(["review", "request"]),
66
+ attempt_id: z.string().min(1).nullable(),
67
+ from_status: z.string().nullable(),
68
+ to_status: z.string(),
69
+ account_id: z.number().int().positive().nullable(),
70
+ reason: nullableString,
71
+ transitioned_at: isoTimestamp
72
+ }).strict();
73
+ const reviewExecutionRowSchema = z.object({
74
+ attempt_id: z.string().min(1),
75
+ review_session_id: z.string().min(1),
76
+ plan_id: z.string().min(1),
77
+ account_id: z.number().int().positive(),
78
+ status: z.enum(["success", "failure"]),
79
+ tx_digest: nullableString,
80
+ explorer_url: nullableString,
81
+ failure_reason: nullableString,
82
+ result_json: z.string(),
83
+ recorded_at: isoTimestamp,
84
+ updated_at: isoTimestamp
85
+ }).strict();
86
+ const reviewRequestRowSchema = z.object({
87
+ attempt_id: z.string().min(1), review_session_id: z.string().min(1), plan_id: z.string().min(1),
88
+ review_revision: z.number().int().nonnegative(), account_id: z.number().int().positive(), transaction_digest: z.string().min(1),
89
+ review_state_json: z.string(), request_status: transactionRequestStatusSchema, revision: z.number().int().nonnegative(),
90
+ reason: nullableString, created_at: isoTimestamp, updated_at: isoTimestamp,
91
+ signature_verified_at: isoTimestamp.nullable(), submitted_at: isoTimestamp.nullable()
92
+ }).strict();
93
+ const externalActivityScanRowSchema = z.object({
94
+ scan_id: z.string().min(1),
95
+ kind: z.enum(["digest_lookup", "account_scan", "function_scan"]),
96
+ account_id: z.number().int().positive(),
97
+ relationship: z.enum(["affected", "sent"]),
98
+ input_digest: nullableString,
99
+ from_checkpoint: nullableString,
100
+ to_checkpoint: nullableString,
101
+ from_timestamp: isoTimestamp.nullable(),
102
+ to_timestamp: isoTimestamp.nullable(),
103
+ limit_count: z.number().int().min(1).max(100),
104
+ request_cursor: nullableString,
105
+ response_cursor: nullableString,
106
+ endpoint_host: z.string().min(1),
107
+ chain_identifier: z.string().min(1),
108
+ fetched_at: isoTimestamp,
109
+ stored_count: z.number().int().nonnegative(),
110
+ skipped_count: z.number().int().nonnegative(),
111
+ has_more: z.union([z.literal(0), z.literal(1)]),
112
+ window_complete: z.union([z.literal(0), z.literal(1)]).nullable(),
113
+ incomplete_reason: z.enum(["limit_reached", "ordering_unverified", "cursor_invalid", "provider_error"]).nullable()
114
+ }).strict();
115
+ const externalActivityTransactionRowSchema = z.object({
116
+ account_id: z.number().int().positive(),
117
+ digest: z.string().min(1),
118
+ relationship: z.enum(["affected", "sent"]),
119
+ checkpoint: nullableString,
120
+ timestamp: isoTimestamp.nullable(),
121
+ status: z.enum(["success", "failure", "unknown"]),
122
+ known_sender_account_id: z.number().int().positive().nullable(),
123
+ first_scan_id: z.string().min(1),
124
+ last_scan_id: z.string().min(1),
125
+ first_fetched_at: isoTimestamp,
126
+ last_fetched_at: isoTimestamp,
127
+ detail_json: z.string().nullable()
128
+ }).strict();
129
+ const localSettingRowSchema = z.object({
130
+ key: z.enum(["suiGrpcUrl", "suiGraphqlUrl"]),
131
+ value_json: z.string(),
132
+ updated_at: isoTimestamp
133
+ }).strict();
134
+ const payloadSchema = z.object({
135
+ accounts: z.array(accountRowSchema),
136
+ activeAccountContext: z.array(activeAccountContextRowSchema).max(1),
137
+ reviewSessions: z.array(reviewSessionRowSchema),
138
+ reviewStateSnapshots: z.array(reviewStateSnapshotRowSchema),
139
+ reviewStatusTransitions: z.array(reviewStatusTransitionRowSchema),
140
+ reviewExecutions: z.array(reviewExecutionRowSchema),
141
+ reviewRequests: z.array(reviewRequestRowSchema),
142
+ externalActivityScans: z.array(externalActivityScanRowSchema),
143
+ externalActivityTransactions: z.array(externalActivityTransactionRowSchema),
144
+ localSettings: z.array(localSettingRowSchema).length(2)
145
+ }).strict();
146
+ const envelopeSchema = z.object({
147
+ schemaVersion: z.literal(DB_USER_VERSION),
148
+ format: z.literal(LOCAL_DATA_EXPORT_FORMAT),
149
+ network: z.literal(LOCAL_DATA_NETWORK),
150
+ exportedAt: isoTimestamp,
151
+ data: payloadSchema
152
+ }).strict();
153
+ export function parseLocalDataEnvelope(input, options) {
154
+ const parsed = envelopeSchema.safeParse(input);
155
+ if (!parsed.success) {
156
+ throw new LocalDataError("input_invalid", "Invalid local data backup", {
157
+ reason: "invalid_backup_shape"
158
+ });
159
+ }
160
+ validatePayloadSemantics(parsed.data.data, options.validateAdapterLifecycle);
161
+ return parsed.data;
162
+ }
163
+ function validatePayloadSemantics(data, validateAdapterLifecycle) {
164
+ ensureUnique(data.accounts.map((row) => row.id), "duplicate_account_id");
165
+ ensureUnique(data.accounts.map((row) => row.sui_address), "duplicate_account_address");
166
+ ensureUnique(data.reviewSessions.map((row) => row.id), "duplicate_review_session_id");
167
+ ensureUnique(data.reviewStateSnapshots.map((row) => row.id), "duplicate_review_state_snapshot_id");
168
+ ensureUnique(data.reviewStatusTransitions.map((row) => row.id), "duplicate_review_transition_id");
169
+ ensureUnique(data.reviewExecutions.map((row) => row.attempt_id), "duplicate_review_execution_id");
170
+ ensureUnique(data.externalActivityScans.map((row) => row.scan_id), "duplicate_external_activity_scan_id");
171
+ ensureUnique(data.externalActivityTransactions.map((row) => `${row.account_id}:${row.digest}:${row.relationship}`), "duplicate_external_activity_transaction");
172
+ ensureUnique(data.localSettings.map((row) => row.key), "duplicate_local_setting_key");
173
+ ensureUnique(data.reviewRequests.map((row) => row.attempt_id), "duplicate_review_request_id");
174
+ ensureUnique(data.reviewRequests.map((row) => `${row.review_session_id}:${row.review_revision}`), "duplicate_review_request_revision");
175
+ const accountIds = new Set(data.accounts.map((row) => row.id));
176
+ const accountAddressesById = new Map(data.accounts.map((row) => [row.id, row.sui_address]));
177
+ for (const row of data.activeAccountContext) {
178
+ if (row.source === "cleared" && row.account_id !== null) {
179
+ throw invalidBackup("invalid_active_account_context");
180
+ }
181
+ if (row.source === "wallet_connection" && (row.account_id === null || !accountIds.has(row.account_id))) {
182
+ throw invalidBackup("invalid_active_account_context");
183
+ }
184
+ }
185
+ for (const row of data.reviewSessions) {
186
+ validateJsonColumn(row.plan_json, "plan_json", actionPlanSchema);
187
+ validateOptionalJsonColumn(row.intent_json, "intent_json");
188
+ if (row.account_id !== null && !accountIds.has(row.account_id)) {
189
+ throw invalidBackup("invalid_review_session_account");
190
+ }
191
+ }
192
+ const reviewSessionIds = new Set(data.reviewSessions.map((row) => row.id));
193
+ for (const row of data.reviewStateSnapshots) {
194
+ if (!reviewSessionIds.has(row.review_session_id) || !accountIds.has(row.account_id)) {
195
+ throw invalidBackup("invalid_review_state_snapshot_reference");
196
+ }
197
+ if (row.status === "blocked" && (row.blocked_reason === null || row.refresh_reason !== null)) {
198
+ throw invalidBackup("invalid_review_state_snapshot_status");
199
+ }
200
+ if (row.status === "refresh_required" && (row.refresh_reason === null || row.blocked_reason !== null)) {
201
+ throw invalidBackup("invalid_review_state_snapshot_status");
202
+ }
203
+ if (row.status === "ready_for_wallet_review" && (row.blocked_reason !== null || row.refresh_reason !== null)) {
204
+ throw invalidBackup("invalid_review_state_snapshot_status");
205
+ }
206
+ validateReviewStateJsonColumn(row.state_json, "state_json", validateAdapterLifecycle);
207
+ }
208
+ const requestsById = new Map(data.reviewRequests.map((row) => [row.attempt_id, row]));
209
+ const sessionsById = new Map(data.reviewSessions.map((row) => [row.id, row]));
210
+ const executionsById = new Map(data.reviewExecutions.map((row) => [row.attempt_id, row]));
211
+ for (const row of data.reviewRequests) {
212
+ const session = sessionsById.get(row.review_session_id), account = accountAddressesById.get(row.account_id);
213
+ if (!session || !account || session.account_id !== row.account_id || session.plan_id !== row.plan_id)
214
+ throw invalidBackup("invalid_review_request_reference");
215
+ validateReviewStateJsonColumn(row.review_state_json, "review_requests.review_state_json", validateAdapterLifecycle);
216
+ const state = JSON.parse(row.review_state_json);
217
+ if (state.reviewSessionId !== row.review_session_id || state.planId !== row.plan_id || state.account !== account ||
218
+ state.transactionReviewData?.reviewedTransactionDigest !== row.transaction_digest)
219
+ throw invalidBackup("invalid_review_request_evidence");
220
+ const execution = executionsById.get(row.attempt_id);
221
+ const result = execution ? validateJsonColumn(execution.result_json, "result_json", transactionExecutionSummarySchema) : undefined;
222
+ const parsed = transactionRequestSchema.safeParse({ attemptId: row.attempt_id, reviewSessionId: row.review_session_id,
223
+ planId: row.plan_id, reviewRevision: row.review_revision, account, transactionDigest: row.transaction_digest,
224
+ requestStatus: row.request_status, revision: row.revision, createdAt: row.created_at, updatedAt: row.updated_at,
225
+ ...(row.reason === null ? {} : { reason: row.reason }), ...(row.signature_verified_at === null ? {} : { signatureVerifiedAt: row.signature_verified_at }),
226
+ ...(row.submitted_at === null ? {} : { submittedAt: row.submitted_at }), ...(result ? { execution: result } : {}) });
227
+ if (!parsed.success)
228
+ throw invalidBackup("invalid_review_request_state");
229
+ if (row.updated_at < row.created_at || (row.signature_verified_at !== null && row.signature_verified_at < row.created_at) ||
230
+ (row.submitted_at !== null && (row.signature_verified_at === null || row.submitted_at < row.signature_verified_at)))
231
+ throw invalidBackup("invalid_review_request_time");
232
+ }
233
+ for (const row of data.reviewSessions) {
234
+ const attempts = data.reviewRequests.filter((request) => request.review_session_id === row.id);
235
+ const latest = attempts.reduce((found, request) => !found || request.review_revision > found.review_revision ? request : found, undefined);
236
+ if ((latest?.attempt_id ?? null) !== row.current_attempt_id)
237
+ throw invalidBackup("invalid_current_request_reference");
238
+ if (row.current_attempt_id !== null) {
239
+ const request = requestsById.get(row.current_attempt_id);
240
+ if (!request || request.review_session_id !== row.id || request.account_id !== row.account_id)
241
+ throw invalidBackup("invalid_current_request_reference");
242
+ }
243
+ if (row.opened_at !== null && row.opened_at < row.created_at)
244
+ throw invalidBackup("invalid_review_open_time");
245
+ }
246
+ for (const row of data.reviewStatusTransitions) {
247
+ if (!reviewSessionIds.has(row.review_session_id) || (row.account_id !== null && !accountIds.has(row.account_id)))
248
+ throw invalidBackup("invalid_review_transition_reference");
249
+ const requestEvent = ["request_admitted", "request_status_changed", "signature_verified", "chain_result_recorded"].includes(row.event);
250
+ if ((row.domain === "request") !== requestEvent)
251
+ throw invalidBackup("invalid_review_transition_domain");
252
+ const statusSchema = row.domain === "request" ? transactionRequestStatusSchema : internalSessionStatusSchema;
253
+ if (!statusSchema.safeParse(row.to_status).success || (row.from_status !== null && !statusSchema.safeParse(row.from_status).success))
254
+ throw invalidBackup("invalid_review_transition_state");
255
+ if (row.domain === "request") {
256
+ const request = row.attempt_id ? requestsById.get(row.attempt_id) : undefined;
257
+ if (!request || request.review_session_id !== row.review_session_id || request.account_id !== row.account_id)
258
+ throw invalidBackup("invalid_review_transition_attempt");
259
+ }
260
+ else if (row.attempt_id !== null)
261
+ throw invalidBackup("invalid_review_transition_attempt");
262
+ }
263
+ for (const row of data.reviewExecutions) {
264
+ const request = requestsById.get(row.attempt_id);
265
+ if (!request || request.review_session_id !== row.review_session_id || request.plan_id !== row.plan_id || request.account_id !== row.account_id ||
266
+ request.transaction_digest !== row.tx_digest)
267
+ throw invalidBackup("invalid_review_execution_reference");
268
+ const result = transactionExecutionSummarySchema.parse(validateJsonColumn(row.result_json, "result_json", transactionExecutionSummarySchema));
269
+ if (result.status !== row.status || result.txDigest !== row.tx_digest || result.failureReason !== (row.failure_reason ?? undefined) ||
270
+ result.chainReceipt.sender !== accountAddressesById.get(row.account_id))
271
+ throw invalidBackup("invalid_review_execution_status");
272
+ }
273
+ const scanIds = new Set(data.externalActivityScans.map((row) => row.scan_id));
274
+ for (const row of data.externalActivityScans) {
275
+ if (!accountIds.has(row.account_id)) {
276
+ throw invalidBackup("invalid_external_activity_scan_account");
277
+ }
278
+ if (row.from_timestamp !== null && row.to_timestamp !== null && row.from_timestamp > row.to_timestamp) {
279
+ throw invalidBackup("invalid_external_activity_scan_window");
280
+ }
281
+ if (row.window_complete !== null && row.window_complete !== 0 && row.window_complete !== 1) {
282
+ throw invalidBackup("invalid_external_activity_scan_window_complete");
283
+ }
284
+ }
285
+ for (const row of data.externalActivityTransactions) {
286
+ if (!accountIds.has(row.account_id) || !scanIds.has(row.first_scan_id) || !scanIds.has(row.last_scan_id)) {
287
+ throw invalidBackup("invalid_external_activity_transaction_reference");
288
+ }
289
+ if (row.known_sender_account_id !== null && !accountIds.has(row.known_sender_account_id)) {
290
+ throw invalidBackup("invalid_external_activity_sender_reference");
291
+ }
292
+ if (row.detail_json !== null) {
293
+ if (externalActivityTransactionDetailJsonByteLength(row.detail_json) > EXTERNAL_ACTIVITY_TRANSACTION_DETAIL_JSON_MAX_BYTES) {
294
+ throw invalidBackup("external_activity_detail_too_large", {
295
+ maxBytes: EXTERNAL_ACTIVITY_TRANSACTION_DETAIL_JSON_MAX_BYTES
296
+ });
297
+ }
298
+ const detail = validateJsonColumn(row.detail_json, "external_activity_transactions.detail_json");
299
+ const parsedDetail = externalActivityTransactionDetailSchema.safeParse(detail);
300
+ if (!parsedDetail.success) {
301
+ throw invalidBackup("invalid_external_activity_detail_json");
302
+ }
303
+ const account = accountAddressesById.get(row.account_id);
304
+ if (account === undefined || !externalActivityTransactionDetailsReferenceOnlyAccount(parsedDetail.data, account)) {
305
+ throw invalidBackup("invalid_external_activity_detail_json");
306
+ }
307
+ }
308
+ }
309
+ const localSettingKeys = new Set(data.localSettings.map((row) => row.key));
310
+ if (!localSettingKeys.has("suiGrpcUrl")) {
311
+ throw invalidBackup("missing_sui_grpc_url");
312
+ }
313
+ for (const row of data.localSettings) {
314
+ const value = validateJsonColumn(row.value_json, "local_settings.value_json");
315
+ if (typeof value !== "string") {
316
+ throw invalidBackup(row.key === "suiGrpcUrl" ? "invalid_sui_grpc_url" : "invalid_sui_graphql_url");
317
+ }
318
+ try {
319
+ if (row.key === "suiGrpcUrl") {
320
+ parseGrpcUrl(value);
321
+ }
322
+ else {
323
+ parseGraphqlUrl(value);
324
+ }
325
+ }
326
+ catch {
327
+ throw invalidBackup(row.key === "suiGrpcUrl" ? "invalid_sui_grpc_url" : "invalid_sui_graphql_url");
328
+ }
329
+ }
330
+ }
331
+ function validateReviewStateJsonColumn(value, field, validateAdapterLifecycle) {
332
+ const parsed = validateJsonColumn(value, field);
333
+ try {
334
+ parseLifecycleValidatedReviewState(parsed, validateAdapterLifecycle);
335
+ }
336
+ catch {
337
+ throw invalidBackup("invalid_json_shape", { field });
338
+ }
339
+ }
340
+ export function suiGrpcUrlFromPayload(data) {
341
+ const row = data.localSettings.find((setting) => setting.key === "suiGrpcUrl");
342
+ if (!row) {
343
+ throw invalidBackup("missing_sui_grpc_url");
344
+ }
345
+ const value = validateJsonColumn(row.value_json, "local_settings.value_json");
346
+ if (typeof value !== "string") {
347
+ throw invalidBackup("invalid_sui_grpc_url");
348
+ }
349
+ try {
350
+ return parseGrpcUrl(value);
351
+ }
352
+ catch {
353
+ throw invalidBackup("invalid_sui_grpc_url");
354
+ }
355
+ }
356
+ export function suiGraphqlUrlFromPayload(data) {
357
+ const row = data.localSettings.find((setting) => setting.key === "suiGraphqlUrl");
358
+ if (!row) {
359
+ throw invalidBackup("missing_sui_graphql_url");
360
+ }
361
+ const value = validateJsonColumn(row.value_json, "local_settings.value_json");
362
+ if (typeof value !== "string") {
363
+ throw invalidBackup("invalid_sui_graphql_url");
364
+ }
365
+ try {
366
+ return parseGraphqlUrl(value);
367
+ }
368
+ catch {
369
+ throw invalidBackup("invalid_sui_graphql_url");
370
+ }
371
+ }
372
+ function ensureUnique(values, reason) {
373
+ if (new Set(values).size !== values.length) {
374
+ throw invalidBackup(reason);
375
+ }
376
+ }
377
+ function validateJsonColumn(value, field, schema) {
378
+ let parsed;
379
+ try {
380
+ parsed = JSON.parse(value);
381
+ }
382
+ catch {
383
+ throw invalidBackup("malformed_json", { field });
384
+ }
385
+ try {
386
+ assertNoForbiddenMcpFields(parsed);
387
+ }
388
+ catch {
389
+ throw invalidBackup("forbidden_field", { field });
390
+ }
391
+ if (schema) {
392
+ const result = schema.safeParse(parsed);
393
+ if (!result.success) {
394
+ throw invalidBackup("invalid_json_shape", { field });
395
+ }
396
+ return result.data;
397
+ }
398
+ return parsed;
399
+ }
400
+ function validateOptionalJsonColumn(value, field) {
401
+ if (value !== null) {
402
+ validateJsonColumn(value, field);
403
+ }
404
+ }
405
+ export function invalidBackup(reason, details = {}) {
406
+ return new LocalDataError("input_invalid", "Invalid local data backup", { reason, ...details });
407
+ }
408
+ export function countsForPayload(data) {
409
+ return {
410
+ accounts: data.accounts.length,
411
+ reviewSessions: data.reviewSessions.length,
412
+ reviewStateSnapshots: data.reviewStateSnapshots.length,
413
+ reviewStatusTransitions: data.reviewStatusTransitions.length,
414
+ reviewExecutions: data.reviewExecutions.length,
415
+ reviewRequests: data.reviewRequests.length,
416
+ outcomeUnknownRequests: data.reviewRequests.filter((row) => row.request_status === "outcome_unknown").length,
417
+ externalActivityScans: data.externalActivityScans.length,
418
+ externalActivityTransactions: data.externalActivityTransactions.length,
419
+ localSettings: data.localSettings.length
420
+ };
421
+ }
422
+ export function activeAccountChange(currentAccounts, current, incomingAccounts, incoming) {
423
+ const currentAccount = activeAccountAddress(currentAccounts, current);
424
+ const incomingAccount = activeAccountAddress(incomingAccounts, incoming);
425
+ if (currentAccount === incomingAccount) {
426
+ return "unchanged";
427
+ }
428
+ return incomingAccount === null ? "cleared" : "set";
429
+ }
430
+ function activeAccountAddress(accounts, context) {
431
+ const accountId = context[0]?.account_id ?? null;
432
+ if (accountId === null) {
433
+ return null;
434
+ }
435
+ return accounts.find((account) => account.id === accountId)?.sui_address ?? null;
436
+ }
437
+ export function maxNumber(values) {
438
+ return values.reduce((max, value) => Math.max(max, value), 0);
439
+ }
@@ -0,0 +1,2 @@
1
+ // Current-format-only storage; existing databases are never migrated.
2
+ export const DB_USER_VERSION = 9;