@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,367 @@
1
+ import Database from "better-sqlite3";
2
+ import { EXTERNAL_ACTIVITY_SCAN_MAX_LIMIT } from "./activityStore.js";
3
+ import { DB_USER_VERSION } from "./schemaVersion.js";
4
+ import { ActivityStoreError } from "./sqliteActivityStoreTypes.js";
5
+ import { LIVE_REVIEW_SESSION_INSERT_TRIGGER, LIVE_REVIEW_SESSION_UPDATE_TRIGGER, LIVE_REVIEW_SESSION_WRITE_CONTRACT_VERSION } from "../session/liveReviewSessionContract.js";
6
+ const EXTERNAL_ACTIVITY_SCAN_INDEX_SQL = `CREATE INDEX IF NOT EXISTS idx_external_activity_scans_account_fetched
7
+ ON external_activity_scans(account_id, fetched_at)`;
8
+ const CURRENT_SCHEMA_SQL = `
9
+ CREATE TABLE IF NOT EXISTS accounts (
10
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
11
+ sui_address TEXT NOT NULL UNIQUE,
12
+ first_seen_at TEXT NOT NULL,
13
+ last_used_at TEXT NOT NULL,
14
+ first_source TEXT NOT NULL CHECK (first_source IN ('wallet_connection', 'review_execution')),
15
+ last_source TEXT NOT NULL CHECK (last_source IN ('wallet_connection', 'review_execution'))
16
+ );
17
+
18
+ CREATE TABLE IF NOT EXISTS active_account_context (
19
+ id INTEGER PRIMARY KEY CHECK (id = 1),
20
+ account_id INTEGER REFERENCES accounts(id) ON DELETE SET NULL,
21
+ source TEXT NOT NULL CHECK (source IN ('wallet_connection', 'cleared')),
22
+ set_at TEXT NOT NULL,
23
+ wallet_name TEXT,
24
+ wallet_id TEXT,
25
+ CHECK (
26
+ (source = 'cleared' AND account_id IS NULL)
27
+ OR (source = 'wallet_connection' AND account_id IS NOT NULL)
28
+ )
29
+ );
30
+
31
+ CREATE TABLE IF NOT EXISTS review_sessions (
32
+ id TEXT PRIMARY KEY,
33
+ plan_id TEXT NOT NULL,
34
+ action_kind TEXT NOT NULL,
35
+ adapter_id TEXT NOT NULL,
36
+ protocol TEXT NOT NULL,
37
+ account_id INTEGER REFERENCES accounts(id) ON DELETE SET NULL,
38
+ current_status TEXT NOT NULL CHECK (current_status IN ('proposed','awaiting_wallet','wallet_connected','ready_for_wallet_review','refresh_required','blocked','expired')),
39
+ current_attempt_id TEXT,
40
+ opened_at TEXT,
41
+ plan_json TEXT NOT NULL,
42
+ intent_json TEXT,
43
+ created_at TEXT NOT NULL,
44
+ updated_at TEXT NOT NULL
45
+ );
46
+
47
+ CREATE INDEX IF NOT EXISTS idx_review_sessions_account_created
48
+ ON review_sessions(account_id, created_at);
49
+
50
+ CREATE INDEX IF NOT EXISTS idx_review_sessions_status_account
51
+ ON review_sessions(current_status, account_id, created_at);
52
+
53
+ CREATE TABLE IF NOT EXISTS review_state_snapshots (
54
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
55
+ review_revision INTEGER NOT NULL CHECK (review_revision >= 0),
56
+ review_session_id TEXT NOT NULL REFERENCES review_sessions(id) ON DELETE RESTRICT,
57
+ plan_id TEXT NOT NULL,
58
+ account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT,
59
+ status TEXT NOT NULL CHECK (status IN ('ready_for_wallet_review', 'refresh_required', 'blocked')),
60
+ blocked_reason TEXT,
61
+ refresh_reason TEXT,
62
+ state_json TEXT NOT NULL,
63
+ updated_at TEXT NOT NULL,
64
+ recorded_at TEXT NOT NULL,
65
+ CHECK (
66
+ (status = 'blocked' AND blocked_reason IS NOT NULL AND refresh_reason IS NULL)
67
+ OR (status = 'refresh_required' AND refresh_reason IS NOT NULL AND blocked_reason IS NULL)
68
+ OR (status = 'ready_for_wallet_review' AND blocked_reason IS NULL AND refresh_reason IS NULL)
69
+ )
70
+ );
71
+
72
+ CREATE INDEX IF NOT EXISTS idx_review_state_snapshots_session_recorded
73
+ ON review_state_snapshots(review_session_id, recorded_at);
74
+
75
+ CREATE TABLE IF NOT EXISTS review_status_transitions (
76
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
77
+ review_session_id TEXT NOT NULL REFERENCES review_sessions(id) ON DELETE RESTRICT,
78
+ event TEXT NOT NULL CHECK (
79
+ event IN ('created', 'opened', 'wallet_connected', 'state_computed', 'request_admitted', 'request_status_changed', 'signature_verified', 'chain_result_recorded', 'review_update_failed', 'review_invalidated', 'expired')
80
+ ),
81
+ attempt_id TEXT,
82
+ domain TEXT NOT NULL DEFAULT 'review' CHECK (domain IN ('review', 'request')),
83
+ from_status TEXT,
84
+ to_status TEXT NOT NULL,
85
+ account_id INTEGER REFERENCES accounts(id) ON DELETE SET NULL,
86
+ reason TEXT,
87
+ transitioned_at TEXT NOT NULL
88
+ );
89
+
90
+ CREATE INDEX IF NOT EXISTS idx_review_transitions_session_time
91
+ ON review_status_transitions(review_session_id, transitioned_at);
92
+
93
+ CREATE TABLE IF NOT EXISTS review_requests (
94
+ attempt_id TEXT PRIMARY KEY,
95
+ review_session_id TEXT NOT NULL REFERENCES review_sessions(id) ON DELETE RESTRICT,
96
+ plan_id TEXT NOT NULL,
97
+ review_revision INTEGER NOT NULL CHECK (review_revision >= 0),
98
+ account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT,
99
+ transaction_digest TEXT NOT NULL,
100
+ review_state_json TEXT NOT NULL,
101
+ request_status TEXT NOT NULL CHECK (request_status IN ('awaiting_signature', 'submitting', 'awaiting_chain_result', 'stopped', 'request_failed', 'outcome_unknown', 'completed')),
102
+ revision INTEGER NOT NULL CHECK (revision >= 0),
103
+ reason TEXT,
104
+ created_at TEXT NOT NULL,
105
+ updated_at TEXT NOT NULL,
106
+ signature_verified_at TEXT,
107
+ submitted_at TEXT,
108
+ UNIQUE (review_session_id, review_revision)
109
+ );
110
+ CREATE INDEX IF NOT EXISTS idx_review_requests_session ON review_requests(review_session_id, created_at);
111
+ CREATE TABLE IF NOT EXISTS review_executions (
112
+ attempt_id TEXT PRIMARY KEY REFERENCES review_requests(attempt_id) ON DELETE RESTRICT,
113
+ review_session_id TEXT NOT NULL REFERENCES review_sessions(id) ON DELETE RESTRICT,
114
+ plan_id TEXT NOT NULL,
115
+ account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT,
116
+ status TEXT NOT NULL CHECK (status IN ('success', 'failure')),
117
+ tx_digest TEXT NOT NULL,
118
+ explorer_url TEXT,
119
+ failure_reason TEXT CHECK (failure_reason IS NULL OR failure_reason = 'chain_execution_failed'),
120
+ result_json TEXT NOT NULL,
121
+ recorded_at TEXT NOT NULL,
122
+ updated_at TEXT NOT NULL,
123
+ CHECK ((status = 'failure' AND failure_reason IS NOT NULL) OR (status = 'success' AND failure_reason IS NULL))
124
+ );
125
+ CREATE INDEX IF NOT EXISTS idx_review_executions_account_updated ON review_executions(account_id, updated_at);
126
+ CREATE INDEX IF NOT EXISTS idx_review_executions_digest ON review_executions(tx_digest);
127
+ CREATE TABLE IF NOT EXISTS live_execution_details (
128
+ attempt_id TEXT PRIMARY KEY REFERENCES review_requests(attempt_id) ON DELETE RESTRICT,
129
+ model_json TEXT NOT NULL,
130
+ display_json TEXT
131
+ );
132
+ CREATE TABLE IF NOT EXISTS live_wallet_connections (
133
+ id TEXT PRIMARY KEY,
134
+ owner_id TEXT NOT NULL,
135
+ status TEXT NOT NULL,
136
+ revision INTEGER NOT NULL CHECK (revision >= 0),
137
+ topic TEXT UNIQUE,
138
+ sdk_pending INTEGER NOT NULL CHECK (sdk_pending IN (0,1)),
139
+ connection_json TEXT NOT NULL
140
+ );
141
+ CREATE TABLE IF NOT EXISTS live_request_authority (
142
+ attempt_id TEXT PRIMARY KEY REFERENCES review_requests(attempt_id) ON DELETE RESTRICT,
143
+ owner_id TEXT NOT NULL,
144
+ connection_id TEXT NOT NULL REFERENCES live_wallet_connections(id) ON DELETE RESTRICT,
145
+ connection_revision INTEGER NOT NULL,
146
+ can_submit INTEGER NOT NULL CHECK (can_submit IN (0,1)),
147
+ sdk_pending INTEGER NOT NULL CHECK (sdk_pending IN (0,1)),
148
+ submit_pending INTEGER NOT NULL CHECK (submit_pending IN (0,1)),
149
+ lookup_pending INTEGER NOT NULL CHECK (lookup_pending IN (0,1)),
150
+ signature_deadline TEXT NOT NULL,
151
+ lookup_deadline TEXT,
152
+ observation_stopped INTEGER NOT NULL DEFAULT 0 CHECK (observation_stopped IN (0,1))
153
+ );
154
+
155
+ ${externalActivityScansTableSql()};
156
+
157
+ ${EXTERNAL_ACTIVITY_SCAN_INDEX_SQL};
158
+
159
+ CREATE TABLE IF NOT EXISTS external_activity_transactions (
160
+ account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT,
161
+ digest TEXT NOT NULL,
162
+ relationship TEXT NOT NULL CHECK (relationship IN ('affected', 'sent')),
163
+ checkpoint TEXT,
164
+ timestamp TEXT,
165
+ status TEXT NOT NULL CHECK (status IN ('success', 'failure', 'unknown')),
166
+ known_sender_account_id INTEGER REFERENCES accounts(id) ON DELETE SET NULL,
167
+ first_scan_id TEXT NOT NULL REFERENCES external_activity_scans(scan_id) ON DELETE RESTRICT,
168
+ last_scan_id TEXT NOT NULL REFERENCES external_activity_scans(scan_id) ON DELETE RESTRICT,
169
+ first_fetched_at TEXT NOT NULL,
170
+ last_fetched_at TEXT NOT NULL,
171
+ detail_json TEXT,
172
+ PRIMARY KEY (account_id, digest, relationship)
173
+ );
174
+
175
+ CREATE INDEX IF NOT EXISTS idx_external_activity_transactions_account_time
176
+ ON external_activity_transactions(account_id, timestamp);
177
+
178
+ CREATE INDEX IF NOT EXISTS idx_external_activity_transactions_digest
179
+ ON external_activity_transactions(digest);
180
+
181
+ CREATE TABLE IF NOT EXISTS local_settings (
182
+ key TEXT PRIMARY KEY,
183
+ value_json TEXT NOT NULL,
184
+ updated_at TEXT NOT NULL
185
+ );
186
+
187
+ CREATE TABLE IF NOT EXISTS coin_metadata_cache (
188
+ coin_type TEXT NOT NULL,
189
+ chain_identifier TEXT NOT NULL,
190
+ decimals INTEGER NOT NULL CHECK (decimals >= 0 AND decimals <= 255),
191
+ symbol TEXT NOT NULL,
192
+ name TEXT NOT NULL,
193
+ fetched_at TEXT NOT NULL,
194
+ expires_at TEXT NOT NULL,
195
+ PRIMARY KEY (coin_type, chain_identifier)
196
+ );
197
+
198
+
199
+ CREATE TABLE IF NOT EXISTS live_transaction_materials (
200
+ material_id TEXT PRIMARY KEY,
201
+ review_session_id TEXT NOT NULL,
202
+ plan_id TEXT NOT NULL,
203
+ account TEXT NOT NULL,
204
+ kind TEXT NOT NULL,
205
+ source TEXT NOT NULL,
206
+ transaction_bytes BLOB NOT NULL,
207
+ redacted_diagnostics_json TEXT,
208
+ created_at TEXT NOT NULL,
209
+ expires_at TEXT NOT NULL
210
+ );
211
+
212
+ CREATE INDEX IF NOT EXISTS idx_live_transaction_materials_session
213
+ ON live_transaction_materials(review_session_id);
214
+
215
+ CREATE TABLE IF NOT EXISTS live_review_sessions (
216
+ id TEXT PRIMARY KEY,
217
+ token_hash TEXT NOT NULL,
218
+ status TEXT NOT NULL,
219
+ account TEXT,
220
+ owner_id TEXT NOT NULL,
221
+ review_revision INTEGER NOT NULL DEFAULT 0,
222
+ preparation_id TEXT,
223
+ preparation_error TEXT,
224
+ wallet_connection_id TEXT,
225
+ wallet_connection_revision INTEGER,
226
+ current_attempt_id TEXT,
227
+ plans_json TEXT NOT NULL,
228
+ review_state_json TEXT,
229
+ created_at TEXT NOT NULL,
230
+ expires_at TEXT NOT NULL,
231
+ last_activity_at TEXT NOT NULL,
232
+ revision INTEGER NOT NULL,
233
+ write_contract_version TEXT NOT NULL
234
+ );
235
+
236
+ CREATE TABLE IF NOT EXISTS live_private_review_artifacts (
237
+ review_session_id TEXT PRIMARY KEY
238
+ REFERENCES live_review_sessions(id) ON DELETE CASCADE,
239
+ artifacts_json TEXT NOT NULL
240
+ );
241
+
242
+ CREATE TABLE IF NOT EXISTS live_settings_sessions (
243
+ id TEXT PRIMARY KEY,
244
+ session_json TEXT NOT NULL
245
+ );
246
+
247
+ CREATE TABLE IF NOT EXISTS live_read_cards (
248
+ id TEXT PRIMARY KEY,
249
+ owner_id TEXT NOT NULL,
250
+ token_hash TEXT NOT NULL,
251
+ kind TEXT NOT NULL CHECK (kind IN ('account', 'receipt', 'chart', 'connect', 'review')),
252
+ state TEXT NOT NULL CHECK (state IN ('ready', 'running', 'closed')),
253
+ reason TEXT CHECK (reason IN ('completed', 'expired', 'failed', 'server_restarted', 'cancelled')),
254
+ error TEXT,
255
+ revision INTEGER NOT NULL CHECK (revision >= 0),
256
+ created_at TEXT NOT NULL,
257
+ expires_at TEXT NOT NULL,
258
+ input_json TEXT NOT NULL,
259
+ scope TEXT NOT NULL DEFAULT 'read' CHECK (scope IN ('read', 'connect', 'review', 'review_manage')),
260
+ operation_id TEXT,
261
+ accepted_input_json TEXT,
262
+ result_json TEXT,
263
+ receipt_display_json TEXT,
264
+ CHECK ((state = 'closed' AND reason IS NOT NULL) OR (state != 'closed' AND reason IS NULL)),
265
+ CHECK (state != 'running' OR accepted_input_json IS NOT NULL),
266
+ CHECK (receipt_display_json IS NULL OR (kind = 'receipt' AND reason = 'completed'))
267
+ );
268
+ CREATE INDEX IF NOT EXISTS idx_live_read_cards_owner_state ON live_read_cards(owner_id, state);
269
+ CREATE TRIGGER IF NOT EXISTS live_read_cards_insert_revision
270
+ BEFORE INSERT ON live_read_cards WHEN NEW.revision != 0
271
+ BEGIN SELECT RAISE(ABORT, 'card insert requires revision zero'); END;
272
+ CREATE TRIGGER IF NOT EXISTS live_read_cards_update_revision
273
+ BEFORE UPDATE ON live_read_cards WHEN NEW.revision != OLD.revision + 1
274
+ BEGIN SELECT RAISE(ABORT, 'card update requires the next revision'); END;
275
+
276
+ CREATE TRIGGER IF NOT EXISTS ${LIVE_REVIEW_SESSION_INSERT_TRIGGER}
277
+ BEFORE INSERT ON live_review_sessions
278
+ FOR EACH ROW
279
+ WHEN NEW.revision IS NULL
280
+ OR NEW.revision != 0
281
+ OR NEW.write_contract_version IS NULL
282
+ OR NEW.write_contract_version != '${LIVE_REVIEW_SESSION_WRITE_CONTRACT_VERSION}'
283
+ BEGIN
284
+ SELECT RAISE(ABORT, 'live review session insert requires the hardened write contract');
285
+ END;
286
+
287
+ CREATE TRIGGER IF NOT EXISTS ${LIVE_REVIEW_SESSION_UPDATE_TRIGGER}
288
+ BEFORE UPDATE ON live_review_sessions
289
+ FOR EACH ROW
290
+ WHEN NEW.revision IS NULL
291
+ OR OLD.revision IS NULL
292
+ OR NEW.revision != OLD.revision + 1
293
+ OR NEW.write_contract_version IS NULL
294
+ OR NEW.write_contract_version != '${LIVE_REVIEW_SESSION_WRITE_CONTRACT_VERSION}'
295
+ BEGIN
296
+ SELECT RAISE(ABORT, 'live review session update requires the hardened write contract');
297
+ END;
298
+ `;
299
+ export function configureDatabase(db) {
300
+ db.exec("PRAGMA journal_mode=WAL");
301
+ db.exec("PRAGMA synchronous=NORMAL");
302
+ db.exec("PRAGMA foreign_keys=ON");
303
+ db.exec("PRAGMA busy_timeout=5000");
304
+ }
305
+ // SQLite normalizes its own DDL. Compare only schema metadata, never user rows.
306
+ // The reference is built once from the single current schema, not from the DB being checked.
307
+ let currentSchemaSignature;
308
+ function schemaSignature(db) {
309
+ return JSON.stringify(db.prepare("SELECT type, name, tbl_name, sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%' ORDER BY type, name").all());
310
+ }
311
+ function expectedSchemaSignature() {
312
+ if (currentSchemaSignature !== undefined)
313
+ return currentSchemaSignature;
314
+ const reference = new Database(":memory:");
315
+ try {
316
+ reference.exec(CURRENT_SCHEMA_SQL);
317
+ currentSchemaSignature = schemaSignature(reference);
318
+ return currentSchemaSignature;
319
+ }
320
+ finally {
321
+ reference.close();
322
+ }
323
+ }
324
+ export function assertCurrentDatabaseFormat(db) {
325
+ const version = db.pragma("user_version", { simple: true });
326
+ const signature = schemaSignature(db);
327
+ if (version === 0 && signature === "[]")
328
+ return "empty";
329
+ if (version === DB_USER_VERSION && signature === expectedSchemaSignature())
330
+ return "current";
331
+ throw new ActivityStoreError("Local database format does not match this runtime. Use a new empty SUI_MCP_DATA_DIR; existing data is not converted or deleted.");
332
+ }
333
+ export function initializeDatabase(db) {
334
+ if (assertCurrentDatabaseFormat(db) === "current")
335
+ return;
336
+ db.transaction(() => {
337
+ db.exec(CURRENT_SCHEMA_SQL);
338
+ db.pragma(`user_version = ${DB_USER_VERSION}`);
339
+ })();
340
+ }
341
+ function externalActivityScansTableSql() {
342
+ return `CREATE TABLE IF NOT EXISTS external_activity_scans (
343
+ scan_id TEXT PRIMARY KEY,
344
+ kind TEXT NOT NULL CHECK (kind IN ('digest_lookup', 'account_scan', 'function_scan')),
345
+ account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT,
346
+ relationship TEXT NOT NULL CHECK (relationship IN ('affected', 'sent')),
347
+ input_digest TEXT,
348
+ from_checkpoint TEXT,
349
+ to_checkpoint TEXT,
350
+ from_timestamp TEXT,
351
+ to_timestamp TEXT,
352
+ limit_count INTEGER NOT NULL CHECK (limit_count >= 1 AND limit_count <= ${EXTERNAL_ACTIVITY_SCAN_MAX_LIMIT}),
353
+ request_cursor TEXT,
354
+ response_cursor TEXT,
355
+ endpoint_host TEXT NOT NULL,
356
+ chain_identifier TEXT NOT NULL,
357
+ fetched_at TEXT NOT NULL,
358
+ stored_count INTEGER NOT NULL CHECK (stored_count >= 0),
359
+ skipped_count INTEGER NOT NULL CHECK (skipped_count >= 0),
360
+ has_more INTEGER NOT NULL CHECK (has_more IN (0, 1)),
361
+ window_complete INTEGER CHECK (window_complete IN (0, 1)),
362
+ incomplete_reason TEXT CHECK (
363
+ incomplete_reason IS NULL
364
+ OR incomplete_reason IN ('limit_reached', 'ordering_unverified', 'cursor_invalid', 'provider_error')
365
+ )
366
+ )`;
367
+ }
@@ -0,0 +1,5 @@
1
+ export class ActivityStoreError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ }
5
+ }
@@ -0,0 +1,43 @@
1
+ import { isValidSuiAddress, normalizeSuiAddress } from "@mysten/sui/utils";
2
+ import { parseSuiAddress } from "../suiAddress.js";
3
+ import { TransactionActivityError } from "./transactionActivityTypes.js";
4
+ const MOVE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
5
+ export function parseSuiFunctionTarget(value) {
6
+ if (value.length === 0 || value.trim() !== value || value.includes("<") || value.includes(">")) {
7
+ throw invalidFunctionTarget();
8
+ }
9
+ const parts = value.split("::");
10
+ if (parts.length !== 3) {
11
+ throw invalidFunctionTarget();
12
+ }
13
+ const [packageId, moduleName, functionName] = parts;
14
+ const normalizedPackage = parseFunctionPackageAddress(packageId);
15
+ if (normalizedPackage === undefined ||
16
+ !MOVE_IDENTIFIER_PATTERN.test(moduleName) ||
17
+ !MOVE_IDENTIFIER_PATTERN.test(functionName)) {
18
+ throw invalidFunctionTarget();
19
+ }
20
+ return {
21
+ package: normalizedPackage,
22
+ module: moduleName,
23
+ function: functionName,
24
+ target: `${normalizedPackage}::${moduleName}::${functionName}`
25
+ };
26
+ }
27
+ function parseFunctionPackageAddress(value) {
28
+ const normalized = parseSuiAddress(value);
29
+ if (normalized !== undefined) {
30
+ return normalized;
31
+ }
32
+ if (!/^0x[0-9a-fA-F]+$/.test(value)) {
33
+ return undefined;
34
+ }
35
+ const normalizedShort = normalizeSuiAddress(value);
36
+ return isValidSuiAddress(normalizedShort) ? normalizedShort : undefined;
37
+ }
38
+ function invalidFunctionTarget() {
39
+ return new TransactionActivityError("input_invalid", "Invalid Sui function target", {
40
+ field: "function",
41
+ reason: "invalid_function_target"
42
+ });
43
+ }
@@ -0,0 +1,186 @@
1
+ import { parseSuiAddress } from "../suiAddress.js";
2
+ export function attachRequestedAccountEffects(transactions, account) {
3
+ return transactions.map((transaction) => ({
4
+ ...transaction,
5
+ accountEffects: requestedAccountEffectsForTransaction(transaction, account)
6
+ }));
7
+ }
8
+ export function buildRequestedAccountActivity(input) {
9
+ const coinFlows = new Map();
10
+ let sentCount = 0;
11
+ let affectedOnlyCount = 0;
12
+ let completeness = "complete";
13
+ for (const transaction of input.transactions) {
14
+ const effects = transaction.accountEffects ?? requestedAccountEffectsForTransaction(transaction, input.account);
15
+ if (effects.sentByAccount) {
16
+ sentCount += 1;
17
+ }
18
+ else {
19
+ affectedOnlyCount += 1;
20
+ }
21
+ completeness = mergeCompleteness(completeness, effects.balanceChangeCompleteness);
22
+ for (const change of effects.balanceChanges) {
23
+ const entry = coinFlows.get(change.coinType) ?? {
24
+ increaseRaw: 0n,
25
+ decreaseRaw: 0n,
26
+ netRaw: 0n,
27
+ digests: new Set()
28
+ };
29
+ const amount = BigInt(change.amountRaw);
30
+ entry.netRaw += amount;
31
+ if (amount > 0n) {
32
+ entry.increaseRaw += amount;
33
+ }
34
+ else if (amount < 0n) {
35
+ entry.decreaseRaw += -amount;
36
+ }
37
+ entry.digests.add(transaction.digest);
38
+ coinFlows.set(change.coinType, entry);
39
+ }
40
+ }
41
+ return {
42
+ account: input.account,
43
+ relationship: input.relationship,
44
+ sentCount,
45
+ affectedOnlyCount,
46
+ balanceChangeCompleteness: completeness,
47
+ coinFlows: [...coinFlows.entries()]
48
+ .map(([coinType, value]) => ({
49
+ coinType,
50
+ increaseRaw: value.increaseRaw.toString(),
51
+ decreaseRaw: value.decreaseRaw.toString(),
52
+ netRaw: value.netRaw.toString(),
53
+ transactionCount: value.digests.size
54
+ }))
55
+ .sort(compareCoinFlowRows)
56
+ };
57
+ }
58
+ export function requestedAccountEffectsForTransaction(transaction, account) {
59
+ const sentByAccount = addressMatches(transaction.sender, account);
60
+ if (transaction.details === undefined) {
61
+ return {
62
+ account,
63
+ scope: "requested_account",
64
+ role: sentByAccount ? "sender" : "affected_only",
65
+ sentByAccount,
66
+ balanceChangeEvidence: "account_balance_changes_unavailable",
67
+ accountBalanceChangeInferencePolicy: "do_not_infer_from_transaction_context",
68
+ balanceChangeCompleteness: "unavailable",
69
+ balanceChanges: [],
70
+ coinFlows: [],
71
+ limitations: ["transaction_details_unavailable"]
72
+ };
73
+ }
74
+ const balanceChanges = transaction.details.balanceChanges
75
+ .filter((change) => addressMatches(change.owner, account))
76
+ .map((change) => ({
77
+ index: change.index,
78
+ coinType: change.coinType,
79
+ amountRaw: change.amountRaw,
80
+ direction: change.direction
81
+ }));
82
+ const balanceChangeCompleteness = transaction.details.truncation.balanceChanges ? "truncated" : "complete";
83
+ const balanceChangeEvidence = balanceChangeEvidenceFor(balanceChangeCompleteness, balanceChanges);
84
+ return {
85
+ account,
86
+ scope: "requested_account",
87
+ role: sentByAccount ? "sender" : "affected_only",
88
+ sentByAccount,
89
+ balanceChangeEvidence,
90
+ accountBalanceChangeInferencePolicy: accountBalanceChangeInferencePolicyFor(balanceChangeEvidence),
91
+ balanceChangeCompleteness,
92
+ balanceChanges,
93
+ coinFlows: coinFlowsForBalanceChanges(balanceChanges),
94
+ limitations: limitationsForCompleteness(balanceChangeCompleteness)
95
+ };
96
+ }
97
+ function accountBalanceChangeInferencePolicyFor(evidence) {
98
+ if (evidence === "account_balance_changes_returned") {
99
+ return "use_returned_account_balance_changes";
100
+ }
101
+ if (evidence === "no_account_balance_changes_returned") {
102
+ return "no_account_balance_changes_in_complete_details";
103
+ }
104
+ return "do_not_infer_from_transaction_context";
105
+ }
106
+ function balanceChangeEvidenceFor(completeness, balanceChanges) {
107
+ if (completeness === "truncated") {
108
+ return "incomplete_account_balance_changes";
109
+ }
110
+ if (completeness === "unavailable") {
111
+ return "account_balance_changes_unavailable";
112
+ }
113
+ return balanceChanges.length === 0
114
+ ? "no_account_balance_changes_returned"
115
+ : "account_balance_changes_returned";
116
+ }
117
+ function coinFlowsForBalanceChanges(balanceChanges) {
118
+ const flows = new Map();
119
+ for (const change of balanceChanges) {
120
+ const amount = BigInt(change.amountRaw);
121
+ const entry = flows.get(change.coinType) ?? {
122
+ increaseRaw: "0",
123
+ decreaseRaw: "0",
124
+ netRaw: "0",
125
+ increaseRawBigInt: 0n,
126
+ decreaseRawBigInt: 0n,
127
+ netRawBigInt: 0n
128
+ };
129
+ entry.netRawBigInt += amount;
130
+ if (amount > 0n) {
131
+ entry.increaseRawBigInt += amount;
132
+ }
133
+ else if (amount < 0n) {
134
+ entry.decreaseRawBigInt += -amount;
135
+ }
136
+ flows.set(change.coinType, entry);
137
+ }
138
+ return [...flows.entries()]
139
+ .map(([coinType, value]) => ({
140
+ coinType,
141
+ increaseRaw: value.increaseRawBigInt.toString(),
142
+ decreaseRaw: value.decreaseRawBigInt.toString(),
143
+ netRaw: value.netRawBigInt.toString()
144
+ }))
145
+ .sort((a, b) => {
146
+ const absoluteNetA = absBigInt(BigInt(a.netRaw));
147
+ const absoluteNetB = absBigInt(BigInt(b.netRaw));
148
+ if (absoluteNetA !== absoluteNetB) {
149
+ return absoluteNetA > absoluteNetB ? -1 : 1;
150
+ }
151
+ return a.coinType.localeCompare(b.coinType);
152
+ });
153
+ }
154
+ function limitationsForCompleteness(completeness) {
155
+ if (completeness === "truncated") {
156
+ return ["provider_balance_changes_truncated"];
157
+ }
158
+ if (completeness === "unavailable") {
159
+ return ["transaction_details_unavailable"];
160
+ }
161
+ return [];
162
+ }
163
+ function mergeCompleteness(current, next) {
164
+ if (current === "truncated" || next === "truncated") {
165
+ return "truncated";
166
+ }
167
+ if (current === "unavailable" || next === "unavailable") {
168
+ return "unavailable";
169
+ }
170
+ return "complete";
171
+ }
172
+ function compareCoinFlowRows(a, b) {
173
+ const absoluteNetA = absBigInt(BigInt(a.netRaw));
174
+ const absoluteNetB = absBigInt(BigInt(b.netRaw));
175
+ if (absoluteNetA !== absoluteNetB) {
176
+ return absoluteNetA > absoluteNetB ? -1 : 1;
177
+ }
178
+ return a.coinType.localeCompare(b.coinType);
179
+ }
180
+ function absBigInt(value) {
181
+ return value < 0n ? -value : value;
182
+ }
183
+ function addressMatches(candidate, account) {
184
+ const normalized = candidate === undefined ? undefined : parseSuiAddress(candidate);
185
+ return normalized !== undefined && normalized === account;
186
+ }