@stelis/say-ur-intent 0.0.0 → 0.0.2

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 (234) hide show
  1. package/README.md +4 -39
  2. package/dist/adapters/adapterLifecycleValidators.js +7 -0
  3. package/dist/adapters/adapterPromptSurfaces.js +71 -0
  4. package/dist/adapters/deepbook/deepbookHumanReviewProducer.js +175 -0
  5. package/dist/adapters/deepbook/deepbookQuotePolicy.js +112 -0
  6. package/dist/adapters/deepbook/deepbookReviewEvidence.js +507 -0
  7. package/dist/adapters/deepbook/deepbookReviewLifecycle.js +85 -0
  8. package/dist/adapters/deepbook/deepbookSwapIntent.js +79 -0
  9. package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +269 -0
  10. package/dist/adapters/flowx/flowxSwapHumanReviewProducer.js +176 -0
  11. package/dist/adapters/flowx/flowxSwapIntent.js +79 -0
  12. package/dist/adapters/flowx/flowxSwapQuotePolicy.js +104 -0
  13. package/dist/adapters/flowx/flowxSwapReviewEvidence.js +468 -0
  14. package/dist/adapters/flowx/flowxSwapReviewLifecycle.js +85 -0
  15. package/dist/adapters/flowx/flowxSwapTransactionMaterialProducer.js +362 -0
  16. package/dist/adapters/intentPlanFactories.js +59 -0
  17. package/dist/adapters/reviewAdapters.js +81 -0
  18. package/dist/core/action/adapterLifecycleValidation.js +12 -0
  19. package/dist/core/action/forbiddenFields.js +43 -0
  20. package/dist/core/action/humanReadableReviewEvidence.js +203 -0
  21. package/dist/core/action/humanReadableReviewProjectionVerifier.js +29 -0
  22. package/dist/core/action/ptbVisualizationProducer.js +66 -0
  23. package/dist/core/action/reviewCheckResults.js +6 -0
  24. package/dist/core/action/reviewStateValidation.js +11 -0
  25. package/dist/core/action/reviewTimeSimulationEvidence.js +471 -0
  26. package/dist/core/action/schemas.js +529 -0
  27. package/dist/core/action/signableAdapterContract.js +993 -0
  28. package/dist/core/action/swapHumanReadableReviewProjection.js +124 -0
  29. package/dist/core/action/swapQuotePolicyEvidence.js +278 -0
  30. package/dist/core/action/transactionObjectOwnershipEvidence.js +247 -0
  31. package/dist/core/action/transactionObjectOwnershipProducer.js +329 -0
  32. package/dist/core/action/types.js +35 -0
  33. package/dist/core/action/walletReviewContractAssembler.js +282 -0
  34. package/dist/core/activity/activityStore.js +15 -0
  35. package/dist/core/activity/localDataService.js +258 -0
  36. package/dist/core/activity/localDataTypes.js +11 -0
  37. package/dist/core/activity/localDataValidation.js +396 -0
  38. package/dist/core/activity/schemaVersion.js +1 -0
  39. package/dist/core/activity/sqliteActivityStore.js +820 -0
  40. package/dist/core/activity/sqliteActivityStoreRows.js +430 -0
  41. package/dist/core/activity/sqliteActivityStoreSchema.js +258 -0
  42. package/dist/core/activity/sqliteActivityStoreTypes.js +5 -0
  43. package/dist/core/activity/suiFunctionTarget.js +43 -0
  44. package/dist/core/activity/transactionActivityAccountEffects.js +189 -0
  45. package/dist/core/activity/transactionActivityAnalysis.js +295 -0
  46. package/dist/core/activity/transactionActivityClassifier.js +306 -0
  47. package/dist/core/activity/transactionActivityDetails.js +229 -0
  48. package/dist/core/activity/transactionActivityProtocolRules.js +218 -0
  49. package/dist/core/activity/transactionActivityScanPolicy.js +170 -0
  50. package/dist/core/activity/transactionActivityService.js +379 -0
  51. package/dist/core/activity/transactionActivityTypes.js +18 -0
  52. package/dist/core/eventlog/sink.js +35 -0
  53. package/dist/core/evidence/settlementFamilies.js +87 -0
  54. package/dist/core/evidence/userAnswerUse.js +1 -0
  55. package/dist/core/numeric/rawU64.js +63 -0
  56. package/dist/core/preferences/preferencesStore.js +26 -0
  57. package/dist/core/preferences/sqlitePreferencesRepository.js +136 -0
  58. package/dist/core/proposal/externalProposalReview.js +347 -0
  59. package/dist/core/proposal/schemas.js +208 -0
  60. package/dist/core/proposal/types.js +35 -0
  61. package/dist/core/read/amounts.js +14 -0
  62. package/dist/core/read/coinMetadata.js +60 -0
  63. package/dist/core/read/deepbookRawQuoteClient.js +86 -0
  64. package/dist/core/read/deepbookReadHelpers.js +265 -0
  65. package/dist/core/read/deepbookRegistry.js +133 -0
  66. package/dist/core/read/flowxQuoteClient.js +117 -0
  67. package/dist/core/read/flowxReadHelpers.js +145 -0
  68. package/dist/core/read/flowxRegistry.js +174 -0
  69. package/dist/core/read/intentEvidenceResponseFormatting.js +228 -0
  70. package/dist/core/read/readResponseGuidance.js +451 -0
  71. package/dist/core/read/readService.js +1164 -0
  72. package/dist/core/read/readServiceTypes.js +59 -0
  73. package/dist/core/read/settlementParityFormatting.js +82 -0
  74. package/dist/core/read/walletReadHelpers.js +99 -0
  75. package/dist/core/review/reviewChecks.js +54 -0
  76. package/dist/core/review/reviewComputation.js +38 -0
  77. package/dist/core/review/reviewComputationResult.js +87 -0
  78. package/dist/core/session/localSession.js +31 -0
  79. package/dist/core/session/privateReviewArtifacts.js +73 -0
  80. package/dist/core/session/sessionErrors.js +9 -0
  81. package/dist/core/session/sessionStore.js +821 -0
  82. package/dist/core/session/settingsSession.js +1 -0
  83. package/dist/core/session/settingsSessions.js +43 -0
  84. package/dist/core/session/status.js +86 -0
  85. package/dist/core/session/transactionMaterialStore.js +205 -0
  86. package/dist/core/session/wait.js +102 -0
  87. package/dist/core/session/walletIdentity.js +103 -0
  88. package/dist/core/session/walletIdentitySessions.js +189 -0
  89. package/dist/core/suiAddress.js +18 -0
  90. package/dist/core/suiEndpoint.js +72 -0
  91. package/dist/mcp/activeAccountResponse.js +24 -0
  92. package/dist/mcp/prompts.js +146 -0
  93. package/dist/mcp/registerTool.js +19 -0
  94. package/dist/mcp/resources.js +72 -0
  95. package/dist/mcp/responseGuidance.js +381 -0
  96. package/dist/mcp/result.js +17 -0
  97. package/dist/mcp/schemas.js +8 -0
  98. package/dist/mcp/server.js +30 -0
  99. package/dist/mcp/serverInfo.js +123 -0
  100. package/dist/mcp/toolErrors.js +105 -0
  101. package/dist/mcp/toolNames.js +50 -0
  102. package/dist/mcp/tools/account/index.js +44 -0
  103. package/dist/mcp/tools/action/prepareSuiActionReview.js +120 -0
  104. package/dist/mcp/tools/read/commonSchemas.js +43 -0
  105. package/dist/mcp/tools/read/deepbookReadTools.js +453 -0
  106. package/dist/mcp/tools/read/flowxReadTools.js +135 -0
  107. package/dist/mcp/tools/read/index.js +16 -0
  108. package/dist/mcp/tools/read/readToolHelpers.js +68 -0
  109. package/dist/mcp/tools/read/reviewActivityTools.js +176 -0
  110. package/dist/mcp/tools/read/serverStatusTools.js +103 -0
  111. package/dist/mcp/tools/read/transactionActivityOutput.js +300 -0
  112. package/dist/mcp/tools/read/transactionActivityTools.js +544 -0
  113. package/dist/mcp/tools/read/walletReadTools.js +733 -0
  114. package/dist/mcp/tools/session/executionResultTools.js +92 -0
  115. package/dist/mcp/tools/session/index.js +8 -0
  116. package/dist/mcp/tools/session/shared.js +79 -0
  117. package/dist/mcp/tools/session/statusTools.js +134 -0
  118. package/dist/mcp/tools/session/walletIdentityTools.js +119 -0
  119. package/dist/mcp/tools/settings/index.js +64 -0
  120. package/dist/review-app/analysis.css +1 -0
  121. package/dist/review-app/analysis.js +1 -0
  122. package/dist/review-app/arc-BjIacwQm.js +1 -0
  123. package/dist/review-app/architecture-U656AL7Q-aSB9x1OK.js +1 -0
  124. package/dist/review-app/architectureDiagram-VXUJARFQ-C5W6re2I.js +36 -0
  125. package/dist/review-app/array-BmXUUrU6.js +1 -0
  126. package/dist/review-app/blockDiagram-VD42YOAC-20MLNcUm.js +122 -0
  127. package/dist/review-app/c4Diagram-YG6GDRKO-BZXRrcck.js +10 -0
  128. package/dist/review-app/channel-lk2p_CUu.js +1 -0
  129. package/dist/review-app/chunk-4BX2VUAB-BPITOdjX.js +1 -0
  130. package/dist/review-app/chunk-55IACEB6-Dz-pyw5k.js +1 -0
  131. package/dist/review-app/chunk-76Q3JFCE-cK_X1P_l.js +1 -0
  132. package/dist/review-app/chunk-ABZYJK2D-Dt4W53JI.js +81 -0
  133. package/dist/review-app/chunk-ATLVNIR6-fZHLXURb.js +1 -0
  134. package/dist/review-app/chunk-B4BG7PRW-BbgcjusC.js +165 -0
  135. package/dist/review-app/chunk-BJD4TVEz.js +1 -0
  136. package/dist/review-app/chunk-CVBHYZKI-CViawAKX.js +1 -0
  137. package/dist/review-app/chunk-DI55MBZ5-C5aoul-d.js +220 -0
  138. package/dist/review-app/chunk-FMBD7UC4-Chxmw62A.js +15 -0
  139. package/dist/review-app/chunk-FPAJGGOC-DDHjQ09H.js +80 -0
  140. package/dist/review-app/chunk-FWNWRKHM-CVVQUptk.js +1 -0
  141. package/dist/review-app/chunk-HN2XXSSU-yzNpjaSZ.js +1 -0
  142. package/dist/review-app/chunk-JA3XYJ7Z-C5ZJdU01.js +70 -0
  143. package/dist/review-app/chunk-JZLCHNYA-BBST4Cnk.js +54 -0
  144. package/dist/review-app/chunk-LBM3YZW2-CdwAPuHr.js +1 -0
  145. package/dist/review-app/chunk-LHMN2FUI-BtB5uDcp.js +1 -0
  146. package/dist/review-app/chunk-O7ZBX7Z2-pxdK4Sa3.js +1 -0
  147. package/dist/review-app/chunk-QN33PNHL-CbVv3uGK.js +1 -0
  148. package/dist/review-app/chunk-QXUST7PY-DKM2-t2c.js +7 -0
  149. package/dist/review-app/chunk-QZHKN3VN-C5ni2pN_.js +1 -0
  150. package/dist/review-app/chunk-S3R3BYOJ-BWvOhDs0.js +2 -0
  151. package/dist/review-app/chunk-S6J4BHB3-D9Fk0YeD.js +1 -0
  152. package/dist/review-app/chunk-T53DSG4Q-C1qEyzyV.js +1 -0
  153. package/dist/review-app/chunk-TZMSLE5B-B--7eU69.js +1 -0
  154. package/dist/review-app/classDiagram-2ON5EDUG-DlL1m2bp.js +1 -0
  155. package/dist/review-app/classDiagram-v2-WZHVMYZB-FXRskT1j.js +1 -0
  156. package/dist/review-app/clone-BZZb7gpZ.js +1 -0
  157. package/dist/review-app/cose-bilkent-S5V4N54A-CRIb8XEO.js +1 -0
  158. package/dist/review-app/cytoscape.esm-C7jYqDP5.js +321 -0
  159. package/dist/review-app/dagre-6UL2VRFP-FNCAXbdE.js +4 -0
  160. package/dist/review-app/dagre-Be46QtUd.js +1 -0
  161. package/dist/review-app/defaultLocale-BaWNtAUL.js +1 -0
  162. package/dist/review-app/diagram-PSM6KHXK-ylLWjiNM.js +24 -0
  163. package/dist/review-app/diagram-QEK2KX5R-BCDcESxs.js +43 -0
  164. package/dist/review-app/diagram-S2PKOQOG-Vdrc-vrO.js +24 -0
  165. package/dist/review-app/dist-WPc74x_f.js +1 -0
  166. package/dist/review-app/erDiagram-Q2GNP2WA-E5ZsUbDF.js +60 -0
  167. package/dist/review-app/flatten-DHf9IeNI.js +1 -0
  168. package/dist/review-app/flowDiagram-NV44I4VS-DBSQuj6x.js +162 -0
  169. package/dist/review-app/ganttDiagram-LVOFAZNH-CKUOsqwl.js +267 -0
  170. package/dist/review-app/gitGraph-F6HP7TQM-DsAD6qK1.js +1 -0
  171. package/dist/review-app/gitGraphDiagram-NY62KEGX-BCeIMWdl.js +65 -0
  172. package/dist/review-app/graphlib-CiX5CXxR.js +1 -0
  173. package/dist/review-app/http-DMvwuuFk.js +1 -0
  174. package/dist/review-app/identity-DY8PXc6t.js +1 -0
  175. package/dist/review-app/info-NVLQJR56-Dlx1nZic.js +1 -0
  176. package/dist/review-app/infoDiagram-F6ZHWCRC-CAuANIrz.js +2 -0
  177. package/dist/review-app/init-BvqephKz.js +1 -0
  178. package/dist/review-app/journeyDiagram-XKPGCS4Q-C-Z9phnx.js +139 -0
  179. package/dist/review-app/kanban-definition-3W4ZIXB7-DufgZABq.js +89 -0
  180. package/dist/review-app/katex-B-Z-NXXN.js +257 -0
  181. package/dist/review-app/line-DiIv3Jgw.js +1 -0
  182. package/dist/review-app/linear-Cv-UPvo1.js +1 -0
  183. package/dist/review-app/math-kmyYrkHL.js +1 -0
  184. package/dist/review-app/mermaid-parser.core-DkwUYTPl.js +4 -0
  185. package/dist/review-app/mindmap-definition-VGOIOE7T-TM_CqdmV.js +68 -0
  186. package/dist/review-app/ordinal-BliTlkoG.js +1 -0
  187. package/dist/review-app/packet-BFZMPI3H-DqbnU92v.js +1 -0
  188. package/dist/review-app/path-AEo9W6mQ.js +1 -0
  189. package/dist/review-app/pie-7BOR55EZ-LJzaLkgr.js +1 -0
  190. package/dist/review-app/pieDiagram-ADFJNKIX-BAs8OfRS.js +30 -0
  191. package/dist/review-app/quadrantDiagram-AYHSOK5B-CyUDZP5S.js +7 -0
  192. package/dist/review-app/radar-NHE76QYJ-DBpHc8_Y.js +1 -0
  193. package/dist/review-app/reduce-B-HuPpdd.js +1 -0
  194. package/dist/review-app/requirementDiagram-UZGBJVZJ-BEHix78P.js +64 -0
  195. package/dist/review-app/review.css +1 -0
  196. package/dist/review-app/review.js +43 -0
  197. package/dist/review-app/sankeyDiagram-TZEHDZUN-B2bKbmsm.js +10 -0
  198. package/dist/review-app/sequenceDiagram-WL72ISMW-DVLOORFJ.js +145 -0
  199. package/dist/review-app/settings.css +1 -0
  200. package/dist/review-app/settings.js +1 -0
  201. package/dist/review-app/src-Buml7cM5.js +1 -0
  202. package/dist/review-app/stateDiagram-FKZM4ZOC-sFGGp2kV.js +1 -0
  203. package/dist/review-app/stateDiagram-v2-4FDKWEC3-BHfCF4dX.js +1 -0
  204. package/dist/review-app/timeline-definition-IT6M3QCI-BESnBijC.js +61 -0
  205. package/dist/review-app/treemap-KMMF4GRG-wnVLBDeQ.js +1 -0
  206. package/dist/review-app/walletStatus-CcojOdGy.js +7 -0
  207. package/dist/review-app/xychartDiagram-PRI3JC2R-BGWVfCx4.js +7 -0
  208. package/dist/review-server/assets.js +48 -0
  209. package/dist/review-server/html.js +66 -0
  210. package/dist/review-server/http.js +47 -0
  211. package/dist/review-server/middleware/hostOrigin.js +48 -0
  212. package/dist/review-server/middleware/reviewToken.js +7 -0
  213. package/dist/review-server/reviewServerPolicy.js +10 -0
  214. package/dist/review-server/server.js +568 -0
  215. package/dist/review-server/settingsApi.js +182 -0
  216. package/dist/review-server/walletIdentityResponse.js +13 -0
  217. package/dist/runtime/config.js +103 -0
  218. package/dist/runtime/localSettingsService.js +198 -0
  219. package/dist/runtime/logger.js +50 -0
  220. package/dist/runtime/reviewServerAcquire.js +128 -0
  221. package/dist/runtime/smokeMainnetRead.js +529 -0
  222. package/dist/runtime/smokeMainnetReadAssertions.js +308 -0
  223. package/dist/runtime/start.js +295 -0
  224. package/dist/runtime/suiEndpoint.js +97 -0
  225. package/dist/runtime/suiTransactionGraphqlMapping.js +200 -0
  226. package/dist/runtime/suiTransactionGraphqlQueries.js +231 -0
  227. package/dist/runtime/suiTransactionGraphqlSource.js +148 -0
  228. package/docs/AGENT_BEHAVIOR.md +1 -1
  229. package/docs/AGENT_DEVELOPMENT_POLICY.md +20 -0
  230. package/docs/FRONTEND_POLICY.md +4 -3
  231. package/docs/MCP_SETUP.md +59 -7
  232. package/docs/MCP_TOOLS.md +1 -1
  233. package/docs/SDK_API.md +5 -1
  234. package/package.json +3 -2
@@ -0,0 +1,430 @@
1
+ import { actionPlanSchema } from "../action/schemas.js";
2
+ import { assertNoForbiddenMcpFields } from "../action/forbiddenFields.js";
3
+ import { ActivityStoreReadError, EXTERNAL_ACTIVITY_SCAN_MAX_LIMIT, REVIEW_ACTIVITY_LIST_DEFAULT_LIMIT, REVIEW_ACTIVITY_LIST_MAX_LIMIT, REVIEW_ACTIVITY_LOW_SAMPLE_THRESHOLD } from "./activityStore.js";
4
+ import { EXTERNAL_ACTIVITY_TRANSACTION_DETAIL_JSON_MAX_BYTES, externalActivityTransactionDetailJsonByteLength, externalActivityTransactionDetailSchema, externalActivityTransactionDetailsReferenceOnlyAccount } from "./transactionActivityDetails.js";
5
+ import { ActivityStoreError } from "./sqliteActivityStoreTypes.js";
6
+ export const INTERNAL_SESSION_STATUSES = [
7
+ "proposed",
8
+ "awaiting_wallet",
9
+ "wallet_connected",
10
+ "ready_for_wallet_review",
11
+ "refresh_required",
12
+ "blocked",
13
+ "signed_pending_result",
14
+ "success",
15
+ "failure",
16
+ "expired"
17
+ ];
18
+ export const REVIEW_STATE_STATUSES = [
19
+ "ready_for_wallet_review",
20
+ "blocked",
21
+ "refresh_required"
22
+ ];
23
+ export const REVIEW_TRANSITION_EVENTS = [
24
+ "created",
25
+ "opened",
26
+ "wallet_connected",
27
+ "state_computed",
28
+ "result_recorded",
29
+ "expired"
30
+ ];
31
+ export const EXTERNAL_ACTIVITY_RELATIONSHIPS = ["affected", "sent"];
32
+ export const EXTERNAL_ACTIVITY_STATUSES = ["success", "failure", "unknown"];
33
+ const EXTERNAL_ACTIVITY_INCOMPLETE_REASONS = [
34
+ "limit_reached",
35
+ "ordering_unverified",
36
+ "cursor_invalid",
37
+ "provider_error"
38
+ ];
39
+ export function parseIsoTimestamp(value, field) {
40
+ const parsed = new Date(value);
41
+ if (Number.isNaN(parsed.getTime()) || parsed.toISOString() !== value) {
42
+ throw new ActivityStoreReadError("input_invalid", `${field} must be an ISO 8601 UTC timestamp`, {
43
+ field
44
+ });
45
+ }
46
+ return value;
47
+ }
48
+ export function parseOptionalIsoTimestamp(value, field) {
49
+ if (value === undefined) {
50
+ return undefined;
51
+ }
52
+ return parseIsoTimestamp(value, field);
53
+ }
54
+ export function assertDateRange(from, to) {
55
+ if (from && to && from > to) {
56
+ throw new ActivityStoreReadError("input_invalid", "from must be before or equal to to", {
57
+ from,
58
+ to
59
+ });
60
+ }
61
+ }
62
+ export function normalizeListLimit(limit) {
63
+ if (limit === undefined) {
64
+ return REVIEW_ACTIVITY_LIST_DEFAULT_LIMIT;
65
+ }
66
+ if (!Number.isInteger(limit) || limit < 1 || limit > REVIEW_ACTIVITY_LIST_MAX_LIMIT) {
67
+ throw new ActivityStoreReadError("input_invalid", "limit must be an integer from 1 to 100", {
68
+ limit,
69
+ min: 1,
70
+ max: REVIEW_ACTIVITY_LIST_MAX_LIMIT
71
+ });
72
+ }
73
+ return limit;
74
+ }
75
+ export function normalizeExternalActivityLimit(limit) {
76
+ if (limit === undefined) {
77
+ return EXTERNAL_ACTIVITY_SCAN_MAX_LIMIT;
78
+ }
79
+ if (!Number.isInteger(limit) || limit < 1 || limit > EXTERNAL_ACTIVITY_SCAN_MAX_LIMIT) {
80
+ throw new ActivityStoreReadError("input_invalid", "limit must be an integer from 1 to 100", {
81
+ limit,
82
+ min: 1,
83
+ max: EXTERNAL_ACTIVITY_SCAN_MAX_LIMIT
84
+ });
85
+ }
86
+ return limit;
87
+ }
88
+ export function reviewSessionWhere(accountId, from, to, status) {
89
+ const conditions = ["rs.account_id = ?"];
90
+ const params = [accountId];
91
+ if (from) {
92
+ conditions.push("rs.created_at >= ?");
93
+ params.push(from);
94
+ }
95
+ if (to) {
96
+ conditions.push("rs.created_at <= ?");
97
+ params.push(to);
98
+ }
99
+ if (status) {
100
+ conditions.push("rs.current_status = ?");
101
+ params.push(status);
102
+ }
103
+ return {
104
+ whereSql: `WHERE ${conditions.join(" AND ")}`,
105
+ params
106
+ };
107
+ }
108
+ export function reviewActivityRowFromStorage(row) {
109
+ return {
110
+ reviewSessionId: asString(row.review_session_id),
111
+ planId: asString(row.plan_id),
112
+ actionKind: asString(row.action_kind),
113
+ adapterId: asString(row.adapter_id),
114
+ protocol: asString(row.protocol),
115
+ currentStatus: asInternalSessionStatus(row.current_status),
116
+ account: asString(row.account),
117
+ createdAt: asString(row.created_at),
118
+ updatedAt: asString(row.updated_at),
119
+ executionStatus: row.execution_status === null ? undefined : asString(row.execution_status),
120
+ txDigest: row.tx_digest === null ? undefined : asString(row.tx_digest),
121
+ snapshotCount: row.snapshot_count,
122
+ transitionCount: row.transition_count
123
+ };
124
+ }
125
+ export function reviewActivityListResult(scope, from, to, activities, truncated, recordCount) {
126
+ return {
127
+ dataScope: dataScope(scope.account, from, to, recordCount),
128
+ accountSource: scope.accountSource,
129
+ lowSampleWarning: recordCount < REVIEW_ACTIVITY_LOW_SAMPLE_THRESHOLD,
130
+ lowSampleThreshold: REVIEW_ACTIVITY_LOW_SAMPLE_THRESHOLD,
131
+ truncated: {
132
+ activities: truncated,
133
+ snapshots: false,
134
+ transitions: false
135
+ },
136
+ activities
137
+ };
138
+ }
139
+ export function reviewFunnelResult(scope, from, to, summary, recordCount) {
140
+ return {
141
+ dataScope: dataScope(scope.account, from, to, recordCount),
142
+ accountSource: scope.accountSource,
143
+ lowSampleWarning: recordCount < REVIEW_ACTIVITY_LOW_SAMPLE_THRESHOLD,
144
+ lowSampleThreshold: REVIEW_ACTIVITY_LOW_SAMPLE_THRESHOLD,
145
+ truncated: {
146
+ activities: false,
147
+ snapshots: false,
148
+ transitions: false
149
+ },
150
+ summary
151
+ };
152
+ }
153
+ function dataScope(account, from, to, recordCount) {
154
+ const scope = {
155
+ account,
156
+ recordCount
157
+ };
158
+ if (from !== undefined) {
159
+ scope.from = from;
160
+ }
161
+ if (to !== undefined) {
162
+ scope.to = to;
163
+ }
164
+ return scope;
165
+ }
166
+ export function externalActivityScanFromRow(row) {
167
+ return {
168
+ scanId: asString(row.scan_id),
169
+ kind: asExternalActivityScanKind(row.kind),
170
+ accountId: row.account_id,
171
+ account: asString(row.account),
172
+ relationship: asExternalActivityRelationship(row.relationship),
173
+ inputDigest: row.input_digest === null ? undefined : asString(row.input_digest),
174
+ fromCheckpoint: row.from_checkpoint === null ? undefined : asString(row.from_checkpoint),
175
+ toCheckpoint: row.to_checkpoint === null ? undefined : asString(row.to_checkpoint),
176
+ fromTimestamp: row.from_timestamp === null ? undefined : asString(row.from_timestamp),
177
+ toTimestamp: row.to_timestamp === null ? undefined : asString(row.to_timestamp),
178
+ limit: row.limit_count,
179
+ requestCursor: row.request_cursor === null ? undefined : asString(row.request_cursor),
180
+ responseCursor: row.response_cursor === null ? undefined : asString(row.response_cursor),
181
+ endpointHost: asString(row.endpoint_host),
182
+ chainIdentifier: asString(row.chain_identifier),
183
+ fetchedAt: asString(row.fetched_at),
184
+ storedCount: row.stored_count,
185
+ skippedCount: row.skipped_count,
186
+ hasMore: row.has_more === 1,
187
+ windowComplete: row.window_complete === null ? null : row.window_complete === 1,
188
+ incompleteReason: row.incomplete_reason === null
189
+ ? undefined
190
+ : asExternalActivityIncompleteReason(row.incomplete_reason)
191
+ };
192
+ }
193
+ export function externalActivityTransactionFromRow(row) {
194
+ return {
195
+ accountId: row.account_id,
196
+ account: asString(row.account),
197
+ digest: asString(row.digest),
198
+ relationship: asExternalActivityRelationship(row.relationship),
199
+ checkpoint: row.checkpoint === null ? undefined : asString(row.checkpoint),
200
+ timestamp: row.timestamp === null ? undefined : asString(row.timestamp),
201
+ status: asExternalActivityStatus(row.status),
202
+ knownSenderAccountId: row.known_sender_account_id === null ? undefined : row.known_sender_account_id,
203
+ firstScanId: asString(row.first_scan_id),
204
+ lastScanId: asString(row.last_scan_id),
205
+ firstFetchedAt: asString(row.first_fetched_at),
206
+ lastFetchedAt: asString(row.last_fetched_at),
207
+ lastScanIncompleteReason: row.last_scan_incomplete_reason === null
208
+ ? undefined
209
+ : asExternalActivityIncompleteReason(row.last_scan_incomplete_reason),
210
+ details: row.detail_json === null
211
+ ? undefined
212
+ : parseEvidenceJson(row.detail_json, asString(row.digest), "external_activity_detail", externalActivityTransactionDetailSchema)
213
+ };
214
+ }
215
+ export function externalActivitySummaryResult(scope, from, to, transactions, truncated, stats) {
216
+ const summary = {
217
+ transactionCount: stats.transactionCount,
218
+ statusCounts: stats.statusCounts,
219
+ relationshipCounts: stats.relationshipCounts
220
+ };
221
+ if (stats.earliestTimestamp !== undefined) {
222
+ summary.earliestTimestamp = stats.earliestTimestamp;
223
+ }
224
+ if (stats.latestTimestamp !== undefined) {
225
+ summary.latestTimestamp = stats.latestTimestamp;
226
+ }
227
+ return {
228
+ dataScope: dataScope(scope.account, from, to, stats.transactionCount),
229
+ accountSource: scope.accountSource,
230
+ lowSampleWarning: stats.transactionCount < REVIEW_ACTIVITY_LOW_SAMPLE_THRESHOLD,
231
+ lowSampleThreshold: REVIEW_ACTIVITY_LOW_SAMPLE_THRESHOLD,
232
+ truncated,
233
+ summary,
234
+ transactions
235
+ };
236
+ }
237
+ export function emptyExternalActivitySummaryStats() {
238
+ return {
239
+ transactionCount: 0,
240
+ statusCounts: Object.fromEntries(EXTERNAL_ACTIVITY_STATUSES.map((status) => [status, 0])),
241
+ relationshipCounts: Object.fromEntries(EXTERNAL_ACTIVITY_RELATIONSHIPS.map((relationship) => [relationship, 0]))
242
+ };
243
+ }
244
+ export function emptyReviewFunnelSummary() {
245
+ return {
246
+ total: 0,
247
+ opened: 0,
248
+ walletConnected: 0,
249
+ stateComputed: 0,
250
+ currentStatusCounts: countMap(INTERNAL_SESSION_STATUSES, []),
251
+ everReachedReviewStateCounts: countMap(REVIEW_STATE_STATUSES, []),
252
+ signedPending: 0,
253
+ success: 0,
254
+ failure: 0,
255
+ expiredBeforeResult: 0,
256
+ avgCreatedToSignedSeconds: null,
257
+ avgOpenedToSignedSeconds: null
258
+ };
259
+ }
260
+ export function countMap(keys, rows) {
261
+ const result = Object.fromEntries(keys.map((key) => [key, 0]));
262
+ for (const row of rows) {
263
+ if (keys.includes(row.key)) {
264
+ result[row.key] = row.count;
265
+ }
266
+ }
267
+ return result;
268
+ }
269
+ export function nullableSeconds(value) {
270
+ return value === null ? null : Math.round(value * 1000) / 1000;
271
+ }
272
+ export function parseEvidenceJson(value, reviewSessionId, evidenceField, schema) {
273
+ if (value === null) {
274
+ throw new ActivityStoreReadError("internal_error", "Malformed activity JSON evidence", {
275
+ reviewSessionId,
276
+ evidenceField
277
+ });
278
+ }
279
+ let parsed;
280
+ try {
281
+ parsed = JSON.parse(value);
282
+ }
283
+ catch {
284
+ throw new ActivityStoreReadError("internal_error", "Malformed activity JSON evidence", {
285
+ reviewSessionId,
286
+ evidenceField
287
+ });
288
+ }
289
+ if (schema !== undefined) {
290
+ const result = schema.safeParse(parsed);
291
+ if (!result.success) {
292
+ throw new ActivityStoreReadError("internal_error", "Malformed activity JSON evidence", {
293
+ reviewSessionId,
294
+ evidenceField
295
+ });
296
+ }
297
+ return result.data;
298
+ }
299
+ return parsed;
300
+ }
301
+ export function serializeJson(value) {
302
+ assertNoForbiddenMcpFields(value);
303
+ return JSON.stringify(value);
304
+ }
305
+ export function serializeExternalActivityTransactionDetail(details, account) {
306
+ const parsed = externalActivityTransactionDetailSchema.safeParse(details);
307
+ if (!parsed.success || !externalActivityTransactionDetailsReferenceOnlyAccount(parsed.data, account)) {
308
+ throw new ActivityStoreReadError("input_invalid", "Invalid external activity transaction detail", {
309
+ reason: "invalid_external_activity_detail_json"
310
+ });
311
+ }
312
+ const json = serializeJson(parsed.data);
313
+ if (externalActivityTransactionDetailJsonByteLength(json) > EXTERNAL_ACTIVITY_TRANSACTION_DETAIL_JSON_MAX_BYTES) {
314
+ throw new ActivityStoreReadError("input_invalid", "External activity transaction detail JSON is too large", {
315
+ reason: "external_activity_detail_too_large",
316
+ maxBytes: EXTERNAL_ACTIVITY_TRANSACTION_DETAIL_JSON_MAX_BYTES
317
+ });
318
+ }
319
+ return json;
320
+ }
321
+ export function serializeOptionalJson(value) {
322
+ if (value === undefined) {
323
+ return null;
324
+ }
325
+ return serializeJson(value);
326
+ }
327
+ export function parseActionPlanEvidence(plan) {
328
+ const parsed = actionPlanSchema.safeParse(plan);
329
+ if (!parsed.success) {
330
+ throw new ActivityStoreError("Invalid review session action plan evidence");
331
+ }
332
+ return parsed.data;
333
+ }
334
+ export function extractRequestedIntent(plan) {
335
+ // Adapter convention: full plan_json stays canonical, while requestedIntent
336
+ // is materialized only to make activity queries avoid nested JSON paths.
337
+ return plan.adapterData.requestedIntent;
338
+ }
339
+ export function reasonForReviewState(state) {
340
+ if ("blockedReason" in state) {
341
+ return state.blockedReason;
342
+ }
343
+ if ("refreshReason" in state) {
344
+ return state.refreshReason;
345
+ }
346
+ return undefined;
347
+ }
348
+ export function asString(value) {
349
+ if (typeof value !== "string") {
350
+ throw new ActivityStoreError("Unexpected SQLite value type");
351
+ }
352
+ return value;
353
+ }
354
+ export function coinMetadataCacheRecordFromRow(row) {
355
+ return {
356
+ coinType: asString(row.coin_type),
357
+ chainIdentifier: asString(row.chain_identifier),
358
+ decimals: row.decimals,
359
+ symbol: asString(row.symbol),
360
+ name: asString(row.name),
361
+ fetchedAt: asString(row.fetched_at),
362
+ expiresAt: asString(row.expires_at)
363
+ };
364
+ }
365
+ export function asInternalSessionStatus(value) {
366
+ if (typeof value === "string" && INTERNAL_SESSION_STATUSES.includes(value)) {
367
+ return value;
368
+ }
369
+ throw new ActivityStoreError("Unexpected review status");
370
+ }
371
+ export function asReviewTransitionEvent(value) {
372
+ if (typeof value === "string" && REVIEW_TRANSITION_EVENTS.includes(value)) {
373
+ return value;
374
+ }
375
+ throw new ActivityStoreError("Unexpected review transition event");
376
+ }
377
+ export function asAccountSource(value) {
378
+ if (value === "wallet_identity" || value === "review_execution") {
379
+ return value;
380
+ }
381
+ throw new ActivityStoreError("Unexpected account source");
382
+ }
383
+ function asExternalActivityScanKind(value) {
384
+ if (value === "digest_lookup" || value === "account_scan" || value === "function_scan") {
385
+ return value;
386
+ }
387
+ throw new ActivityStoreError("Unexpected external activity scan kind");
388
+ }
389
+ function asExternalActivityRelationship(value) {
390
+ if (typeof value === "string" && EXTERNAL_ACTIVITY_RELATIONSHIPS.includes(value)) {
391
+ return value;
392
+ }
393
+ throw new ActivityStoreError("Unexpected external activity relationship");
394
+ }
395
+ function asExternalActivityStatus(value) {
396
+ if (typeof value === "string" && EXTERNAL_ACTIVITY_STATUSES.includes(value)) {
397
+ return value;
398
+ }
399
+ throw new ActivityStoreError("Unexpected external activity status");
400
+ }
401
+ function asExternalActivityIncompleteReason(value) {
402
+ if (typeof value === "string" && EXTERNAL_ACTIVITY_INCOMPLETE_REASONS.includes(value)) {
403
+ return value;
404
+ }
405
+ throw new ActivityStoreError("Unexpected external activity incomplete reason");
406
+ }
407
+ export function isSameReviewExecution(existing, accountId, input) {
408
+ return (existing.plan_id === input.planId &&
409
+ existing.account_id === accountId &&
410
+ existing.status === input.status &&
411
+ nullableString(existing.tx_digest) === nullableString(input.txDigest) &&
412
+ nullableString(existing.explorer_url) === nullableString(input.explorerUrl) &&
413
+ nullableString(existing.failure_reason) === nullableString(input.failureReason));
414
+ }
415
+ export function canAdvanceReviewExecution(existing, accountId, input) {
416
+ if (existing.plan_id !== input.planId || existing.account_id !== accountId) {
417
+ return false;
418
+ }
419
+ if (existing.status !== "signed_pending_result") {
420
+ return false;
421
+ }
422
+ if (input.status === "signed_pending_result") {
423
+ return false;
424
+ }
425
+ const nextDigest = nullableString(input.txDigest);
426
+ return existing.tx_digest === null || existing.tx_digest === nextDigest;
427
+ }
428
+ function nullableString(value) {
429
+ return value ?? null;
430
+ }
@@ -0,0 +1,258 @@
1
+ import { EXTERNAL_ACTIVITY_SCAN_MAX_LIMIT } from "./activityStore.js";
2
+ import { DB_USER_VERSION } from "./schemaVersion.js";
3
+ import { ActivityStoreError } from "./sqliteActivityStoreTypes.js";
4
+ const EXTERNAL_ACTIVITY_SCAN_INDEX_SQL = `CREATE INDEX IF NOT EXISTS idx_external_activity_scans_account_fetched
5
+ ON external_activity_scans(account_id, fetched_at)`;
6
+ export function configureDatabase(db) {
7
+ db.exec("PRAGMA journal_mode=WAL");
8
+ db.exec("PRAGMA synchronous=NORMAL");
9
+ db.exec("PRAGMA foreign_keys=ON");
10
+ }
11
+ export function initializeDatabase(db) {
12
+ const currentUserVersion = db.pragma("user_version", { simple: true });
13
+ if (currentUserVersion > DB_USER_VERSION) {
14
+ throw new ActivityStoreError(`Local activity database version ${currentUserVersion} is newer than this runtime supports (${DB_USER_VERSION}).`);
15
+ }
16
+ db.exec(`
17
+ CREATE TABLE IF NOT EXISTS accounts (
18
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
19
+ sui_address TEXT NOT NULL UNIQUE,
20
+ first_seen_at TEXT NOT NULL,
21
+ last_used_at TEXT NOT NULL,
22
+ first_source TEXT NOT NULL CHECK (first_source IN ('wallet_identity', 'review_execution')),
23
+ last_source TEXT NOT NULL CHECK (last_source IN ('wallet_identity', 'review_execution'))
24
+ );
25
+
26
+ CREATE TABLE IF NOT EXISTS active_account_context (
27
+ id INTEGER PRIMARY KEY CHECK (id = 1),
28
+ account_id INTEGER REFERENCES accounts(id) ON DELETE SET NULL,
29
+ source TEXT NOT NULL CHECK (source IN ('wallet_identity', 'cleared')),
30
+ set_at TEXT NOT NULL,
31
+ wallet_name TEXT,
32
+ wallet_id TEXT,
33
+ CHECK (
34
+ (source = 'cleared' AND account_id IS NULL)
35
+ OR (source = 'wallet_identity' AND account_id IS NOT NULL)
36
+ )
37
+ );
38
+
39
+ CREATE TABLE IF NOT EXISTS review_sessions (
40
+ id TEXT PRIMARY KEY,
41
+ plan_id TEXT NOT NULL,
42
+ action_kind TEXT NOT NULL,
43
+ adapter_id TEXT NOT NULL,
44
+ protocol TEXT NOT NULL,
45
+ account_id INTEGER REFERENCES accounts(id) ON DELETE SET NULL,
46
+ current_status TEXT NOT NULL,
47
+ plan_json TEXT NOT NULL,
48
+ intent_json TEXT,
49
+ created_at TEXT NOT NULL,
50
+ updated_at TEXT NOT NULL
51
+ );
52
+
53
+ CREATE INDEX IF NOT EXISTS idx_review_sessions_account_created
54
+ ON review_sessions(account_id, created_at);
55
+
56
+ CREATE INDEX IF NOT EXISTS idx_review_sessions_status_account
57
+ ON review_sessions(current_status, account_id, created_at);
58
+
59
+ CREATE TABLE IF NOT EXISTS review_state_snapshots (
60
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
61
+ review_session_id TEXT NOT NULL REFERENCES review_sessions(id) ON DELETE RESTRICT,
62
+ plan_id TEXT NOT NULL,
63
+ account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT,
64
+ status TEXT NOT NULL CHECK (status IN ('ready_for_wallet_review', 'refresh_required', 'blocked')),
65
+ blocked_reason TEXT,
66
+ refresh_reason TEXT,
67
+ state_json TEXT NOT NULL,
68
+ updated_at TEXT NOT NULL,
69
+ recorded_at TEXT NOT NULL,
70
+ CHECK (
71
+ (status = 'blocked' AND blocked_reason IS NOT NULL AND refresh_reason IS NULL)
72
+ OR (status = 'refresh_required' AND refresh_reason IS NOT NULL AND blocked_reason IS NULL)
73
+ OR (status = 'ready_for_wallet_review' AND blocked_reason IS NULL AND refresh_reason IS NULL)
74
+ )
75
+ );
76
+
77
+ CREATE INDEX IF NOT EXISTS idx_review_state_snapshots_session_recorded
78
+ ON review_state_snapshots(review_session_id, recorded_at);
79
+
80
+ CREATE TABLE IF NOT EXISTS review_status_transitions (
81
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
82
+ review_session_id TEXT NOT NULL REFERENCES review_sessions(id) ON DELETE RESTRICT,
83
+ event TEXT NOT NULL CHECK (
84
+ event IN ('created', 'opened', 'wallet_connected', 'state_computed', 'result_recorded', 'expired')
85
+ ),
86
+ from_status TEXT,
87
+ to_status TEXT NOT NULL,
88
+ account_id INTEGER REFERENCES accounts(id) ON DELETE SET NULL,
89
+ reason TEXT,
90
+ transitioned_at TEXT NOT NULL
91
+ );
92
+
93
+ CREATE INDEX IF NOT EXISTS idx_review_transitions_session_time
94
+ ON review_status_transitions(review_session_id, transitioned_at);
95
+
96
+ CREATE TABLE IF NOT EXISTS review_executions (
97
+ review_session_id TEXT PRIMARY KEY REFERENCES review_sessions(id) ON DELETE RESTRICT,
98
+ plan_id TEXT NOT NULL,
99
+ account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT,
100
+ status TEXT NOT NULL CHECK (status IN ('signed_pending_result', 'success', 'failure')),
101
+ tx_digest TEXT,
102
+ explorer_url TEXT,
103
+ failure_reason TEXT,
104
+ result_json TEXT NOT NULL,
105
+ recorded_at TEXT NOT NULL,
106
+ updated_at TEXT NOT NULL,
107
+ CHECK (
108
+ (status = 'failure' AND failure_reason IS NOT NULL)
109
+ OR (status != 'failure' AND failure_reason IS NULL)
110
+ )
111
+ );
112
+
113
+ CREATE INDEX IF NOT EXISTS idx_review_executions_account_updated
114
+ ON review_executions(account_id, updated_at);
115
+
116
+ CREATE INDEX IF NOT EXISTS idx_review_executions_digest
117
+ ON review_executions(tx_digest);
118
+
119
+ ${externalActivityScansTableSql("external_activity_scans", { ifNotExists: true })};
120
+
121
+ ${EXTERNAL_ACTIVITY_SCAN_INDEX_SQL};
122
+
123
+ CREATE TABLE IF NOT EXISTS external_activity_transactions (
124
+ account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT,
125
+ digest TEXT NOT NULL,
126
+ relationship TEXT NOT NULL CHECK (relationship IN ('affected', 'sent')),
127
+ checkpoint TEXT,
128
+ timestamp TEXT,
129
+ status TEXT NOT NULL CHECK (status IN ('success', 'failure', 'unknown')),
130
+ known_sender_account_id INTEGER REFERENCES accounts(id) ON DELETE SET NULL,
131
+ first_scan_id TEXT NOT NULL REFERENCES external_activity_scans(scan_id) ON DELETE RESTRICT,
132
+ last_scan_id TEXT NOT NULL REFERENCES external_activity_scans(scan_id) ON DELETE RESTRICT,
133
+ first_fetched_at TEXT NOT NULL,
134
+ last_fetched_at TEXT NOT NULL,
135
+ detail_json TEXT,
136
+ PRIMARY KEY (account_id, digest, relationship)
137
+ );
138
+
139
+ CREATE INDEX IF NOT EXISTS idx_external_activity_transactions_account_time
140
+ ON external_activity_transactions(account_id, timestamp);
141
+
142
+ CREATE INDEX IF NOT EXISTS idx_external_activity_transactions_digest
143
+ ON external_activity_transactions(digest);
144
+
145
+ CREATE TABLE IF NOT EXISTS local_settings (
146
+ key TEXT PRIMARY KEY,
147
+ value_json TEXT NOT NULL,
148
+ updated_at TEXT NOT NULL
149
+ );
150
+
151
+ CREATE TABLE IF NOT EXISTS coin_metadata_cache (
152
+ coin_type TEXT NOT NULL,
153
+ chain_identifier TEXT NOT NULL,
154
+ decimals INTEGER NOT NULL CHECK (decimals >= 0 AND decimals <= 255),
155
+ symbol TEXT NOT NULL,
156
+ name TEXT NOT NULL,
157
+ fetched_at TEXT NOT NULL,
158
+ expires_at TEXT NOT NULL,
159
+ PRIMARY KEY (coin_type, chain_identifier)
160
+ );
161
+
162
+ `);
163
+ migrateDatabase(db, currentUserVersion);
164
+ if (db.pragma("user_version", { simple: true }) !== DB_USER_VERSION) {
165
+ db.pragma(`user_version = ${DB_USER_VERSION}`);
166
+ }
167
+ }
168
+ function migrateDatabase(db, currentUserVersion) {
169
+ if (currentUserVersion < 3 && !tableHasColumn(db, "external_activity_transactions", "detail_json")) {
170
+ db.exec("ALTER TABLE external_activity_transactions ADD COLUMN detail_json TEXT");
171
+ }
172
+ if (currentUserVersion > 0 && currentUserVersion < 4 && tableExists(db, "external_activity_scans")) {
173
+ rebuildExternalActivityScansForFunctionScan(db);
174
+ }
175
+ if (currentUserVersion > 0 && currentUserVersion < 5 && tableExists(db, "active_account_context")) {
176
+ if (!tableHasColumn(db, "active_account_context", "wallet_name")) {
177
+ db.exec("ALTER TABLE active_account_context ADD COLUMN wallet_name TEXT");
178
+ }
179
+ if (!tableHasColumn(db, "active_account_context", "wallet_id")) {
180
+ db.exec("ALTER TABLE active_account_context ADD COLUMN wallet_id TEXT");
181
+ }
182
+ }
183
+ }
184
+ function tableHasColumn(db, table, column) {
185
+ const rows = db.prepare(`PRAGMA table_info(${table})`).all();
186
+ return rows.some((row) => row.name === column);
187
+ }
188
+ function tableExists(db, table) {
189
+ const row = db
190
+ .prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?")
191
+ .get(table);
192
+ return row !== undefined;
193
+ }
194
+ function rebuildExternalActivityScansForFunctionScan(db) {
195
+ const previousForeignKeys = db.pragma("foreign_keys", { simple: true });
196
+ const rebuild = db.transaction(() => {
197
+ db.exec(`
198
+ DROP TABLE IF EXISTS external_activity_scans_new;
199
+
200
+ ${externalActivityScansTableSql("external_activity_scans_new", { ifNotExists: false })};
201
+
202
+ INSERT INTO external_activity_scans_new
203
+ (scan_id, kind, account_id, relationship, input_digest, from_checkpoint, to_checkpoint,
204
+ from_timestamp, to_timestamp, limit_count, request_cursor, response_cursor, endpoint_host,
205
+ chain_identifier, fetched_at, stored_count, skipped_count, has_more, window_complete,
206
+ incomplete_reason)
207
+ SELECT
208
+ scan_id, kind, account_id, relationship, input_digest, from_checkpoint, to_checkpoint,
209
+ from_timestamp, to_timestamp, limit_count, request_cursor, response_cursor, endpoint_host,
210
+ chain_identifier, fetched_at, stored_count, skipped_count, has_more, window_complete,
211
+ incomplete_reason
212
+ FROM external_activity_scans;
213
+
214
+ DROP TABLE external_activity_scans;
215
+ ALTER TABLE external_activity_scans_new RENAME TO external_activity_scans;
216
+ ${EXTERNAL_ACTIVITY_SCAN_INDEX_SQL};
217
+ `);
218
+ const foreignKeyFailures = db.prepare("PRAGMA foreign_key_check").all();
219
+ if (foreignKeyFailures.length > 0) {
220
+ throw new ActivityStoreError("Local activity database migration failed foreign key check");
221
+ }
222
+ db.pragma(`user_version = ${DB_USER_VERSION}`);
223
+ });
224
+ db.pragma("foreign_keys = OFF");
225
+ try {
226
+ rebuild();
227
+ }
228
+ finally {
229
+ db.pragma(`foreign_keys = ${previousForeignKeys === 0 ? "OFF" : "ON"}`);
230
+ }
231
+ }
232
+ function externalActivityScansTableSql(tableName, options) {
233
+ return `CREATE TABLE ${options.ifNotExists ? "IF NOT EXISTS " : ""}${tableName} (
234
+ scan_id TEXT PRIMARY KEY,
235
+ kind TEXT NOT NULL CHECK (kind IN ('digest_lookup', 'account_scan', 'function_scan')),
236
+ account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT,
237
+ relationship TEXT NOT NULL CHECK (relationship IN ('affected', 'sent')),
238
+ input_digest TEXT,
239
+ from_checkpoint TEXT,
240
+ to_checkpoint TEXT,
241
+ from_timestamp TEXT,
242
+ to_timestamp TEXT,
243
+ limit_count INTEGER NOT NULL CHECK (limit_count >= 1 AND limit_count <= ${EXTERNAL_ACTIVITY_SCAN_MAX_LIMIT}),
244
+ request_cursor TEXT,
245
+ response_cursor TEXT,
246
+ endpoint_host TEXT NOT NULL,
247
+ chain_identifier TEXT NOT NULL,
248
+ fetched_at TEXT NOT NULL,
249
+ stored_count INTEGER NOT NULL CHECK (stored_count >= 0),
250
+ skipped_count INTEGER NOT NULL CHECK (skipped_count >= 0),
251
+ has_more INTEGER NOT NULL CHECK (has_more IN (0, 1)),
252
+ window_complete INTEGER CHECK (window_complete IN (0, 1)),
253
+ incomplete_reason TEXT CHECK (
254
+ incomplete_reason IS NULL
255
+ OR incomplete_reason IN ('limit_reached', 'ordering_unverified', 'cursor_invalid', 'provider_error')
256
+ )
257
+ )`;
258
+ }
@@ -0,0 +1,5 @@
1
+ export class ActivityStoreError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ }
5
+ }