@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,858 @@
1
+ import { z } from "zod";
2
+ import { buildAccountAssetTimeline } from "../../../core/activity/accountAssetTimeline.js";
3
+ import { attachDeepbookUsdcReferencesToTimeline } from "../../../core/activity/accountAssetTimelineUsdcReferences.js";
4
+ import { EXTERNAL_ACTIVITY_SCAN_MAX_LIMIT, REVIEW_ACTIVITY_LIST_MAX_LIMIT, REVIEW_ACTIVITY_LOW_SAMPLE_THRESHOLD } from "../../../core/activity/activityStore.js";
5
+ import { externalActivityGasCostFactSchema, externalActivityTransactionCompactFactsSchema, externalActivityTransactionDetailSchema } from "../../../core/activity/transactionActivityDetails.js";
6
+ import { suiActivityAnalysisLimitations } from "../../../core/activity/transactionActivityAnalysis.js";
7
+ import { deepbookOfficialIndexerCandleSchema, deepbookOfficialIndexerIntervalSchema } from "../../../core/read/deepbookOfficialIndexerSource.js";
8
+ import { DEEPBOOK_OFFICIAL_INDEXER_CANDLE_USE, DEEPBOOK_OFFICIAL_INDEXER_RESPONSE_TEXT, DEEPBOOK_OFFICIAL_INDEXER_SOURCE_BASE, DEEPBOOK_OFFICIAL_INDEXER_USDC_REFERENCE } from "../../../core/read/deepbookSourceOwners.js";
9
+ import { DEEPBOOK_USDC_PRICE_HISTORY_UNSUPPORTED_CLAIMS } from "../../../core/read/readServiceTypes.js";
10
+ import { suiTransactionDigestSchema } from "../../../core/suiAddress.js";
11
+ import { okToolResult } from "../../result.js";
12
+ import { successOutputSchema } from "../../schemas.js";
13
+ import { transactionActivityToolError } from "../../toolErrors.js";
14
+ import { TOOL_NAMES } from "../../toolNames.js";
15
+ import { fetchedAtSchema, reviewActivityAccountSourceSchema, reviewActivityDataScopeSchema, reviewActivityInputSchema, userAnswerUseSchema } from "./commonSchemas.js";
16
+ import { accountAssetTimelineQuantitySemantics, accountAssetTimelineUserAnswerUse, externalActivityTransactionRecordOutput, inspectSuiTransactionUserAnswerUse, liveSuiActivityUserAnswerUse, requestedAccountTransactionFactOutput, storedSuiActivityUserAnswerUse, suiActivityQuantitySemantics, transactionDetailAvailability, transactionFactAuditOutput, transactionFactOutput } from "./transactionActivityOutput.js";
17
+ const transactionDigestSchema = suiTransactionDigestSchema;
18
+ const externalActivityRelationshipSchema = z.enum(["affected", "sent"]);
19
+ const externalActivityStatusSchema = z.enum(["success", "failure", "unknown"]);
20
+ const externalActivityScanIncompleteReasonSchema = z.enum([
21
+ "limit_reached",
22
+ "ordering_unverified",
23
+ "cursor_invalid",
24
+ "provider_error"
25
+ ]);
26
+ const suiActivityScanInputSchema = {
27
+ account: z.string().min(1).optional(),
28
+ relationship: externalActivityRelationshipSchema.optional(),
29
+ limit: z.number().int().min(1).max(EXTERNAL_ACTIVITY_SCAN_MAX_LIMIT).optional(),
30
+ cursor: z.string().min(1).optional(),
31
+ fromCheckpoint: z.string().regex(/^\d+$/).optional(),
32
+ toCheckpoint: z.string().regex(/^\d+$/).optional(),
33
+ fromTimestamp: fetchedAtSchema.optional(),
34
+ toTimestamp: fetchedAtSchema.optional()
35
+ };
36
+ const suiFunctionActivityScanInputSchema = {
37
+ function: z.string().min(1),
38
+ account: z.string().min(1).optional(),
39
+ limit: z.number().int().min(1).max(EXTERNAL_ACTIVITY_SCAN_MAX_LIMIT).optional(),
40
+ cursor: z.string().min(1).optional(),
41
+ fromCheckpoint: z.string().regex(/^\d+$/).optional(),
42
+ toCheckpoint: z.string().regex(/^\d+$/).optional(),
43
+ fromTimestamp: fetchedAtSchema.optional(),
44
+ toTimestamp: fetchedAtSchema.optional()
45
+ };
46
+ const accountAssetTimelineInputSchema = {
47
+ account: z.string().min(1).optional(),
48
+ start: fetchedAtSchema,
49
+ end: fetchedAtSchema,
50
+ interval: deepbookOfficialIndexerIntervalSchema.optional()
51
+ };
52
+ const suiTransactionActivitySourceSchema = z.discriminatedUnion("transport", [
53
+ z.object({
54
+ transport: z.literal("graphql"),
55
+ endpointHost: z.string(),
56
+ chainIdentifier: z.string(),
57
+ method: z.enum(["Query.transaction", "Query.transactions"])
58
+ }),
59
+ z.object({
60
+ transport: z.literal("local_db"),
61
+ method: z.literal("stored_normalized_facts")
62
+ })
63
+ ]);
64
+ const accountBalanceChangeCompletenessSchema = z.enum(["complete", "truncated", "unavailable"]);
65
+ const accountBalanceChangeEvidenceSchema = z.enum([
66
+ "account_balance_changes_returned",
67
+ "no_account_balance_changes_returned",
68
+ "incomplete_account_balance_changes",
69
+ "account_balance_changes_unavailable"
70
+ ]);
71
+ const accountBalanceChangeInferencePolicySchema = z.enum([
72
+ "use_returned_account_balance_changes",
73
+ "no_account_balance_changes_in_complete_details",
74
+ "do_not_infer_from_transaction_context"
75
+ ]);
76
+ const transactionAccountRoleSchema = z.enum(["sender", "affected_only"]);
77
+ const transactionAccountEffectLimitationSchema = z.enum([
78
+ "provider_balance_changes_truncated",
79
+ "transaction_details_unavailable"
80
+ ]);
81
+ const requestedAccountCoinFlowSchema = z.object({
82
+ coinType: z.string(),
83
+ increaseRaw: z.string().regex(/^\d+$/),
84
+ decreaseRaw: z.string().regex(/^\d+$/),
85
+ netRaw: z.string().regex(/^-?\d+$/),
86
+ transactionCount: z.number().int().positive()
87
+ }).strict();
88
+ const transactionAccountEffectsSchema = z.object({
89
+ account: z.string(),
90
+ scope: z.literal("requested_account"),
91
+ role: transactionAccountRoleSchema,
92
+ sentByAccount: z.boolean(),
93
+ balanceChangeEvidence: accountBalanceChangeEvidenceSchema,
94
+ accountBalanceChangeInferencePolicy: accountBalanceChangeInferencePolicySchema,
95
+ balanceChangeCompleteness: accountBalanceChangeCompletenessSchema,
96
+ balanceChanges: z.array(z.object({
97
+ index: z.number().int().nonnegative(),
98
+ coinType: z.string(),
99
+ amountRaw: z.string().regex(/^-?\d+$/),
100
+ direction: z.enum(["increase", "decrease", "zero"])
101
+ }).strict()),
102
+ coinFlows: z.array(z.object({
103
+ coinType: z.string(),
104
+ increaseRaw: z.string().regex(/^\d+$/),
105
+ decreaseRaw: z.string().regex(/^\d+$/),
106
+ netRaw: z.string().regex(/^-?\d+$/)
107
+ }).strict()),
108
+ limitations: z.array(transactionAccountEffectLimitationSchema)
109
+ }).strict();
110
+ const requestedAccountActivitySchema = z.object({
111
+ account: z.string(),
112
+ relationship: externalActivityRelationshipSchema,
113
+ sentCount: z.number().int().nonnegative(),
114
+ affectedOnlyCount: z.number().int().nonnegative(),
115
+ balanceChangeCompleteness: accountBalanceChangeCompletenessSchema,
116
+ coinFlows: z.array(requestedAccountCoinFlowSchema)
117
+ }).strict();
118
+ const suiActivityQuantitySemanticsSchema = z.object({
119
+ kind: z.literal("sui_activity_raw_amounts"),
120
+ rawAmountsOnly: z.literal(true),
121
+ displayConversionRequires: z.literal("verified_coin_metadata_decimals"),
122
+ gasRawUnit: z.literal("MIST"),
123
+ gasUnitSource: z.literal("@mysten/sui MIST_PER_SUI"),
124
+ gasDisplayFields: z.tuple([
125
+ z.literal("transaction.compact.gasCost.display"),
126
+ z.literal("requestedAccountTransactionFacts[].transactionContext.gasCost.display"),
127
+ z.literal("transactions[].transactionContext.gasCost.display"),
128
+ z.literal("analysis.gas.netGasCost.display")
129
+ ]),
130
+ rawAmountFields: z.tuple([
131
+ z.literal("transaction.details.balanceChanges[].amountRaw"),
132
+ z.literal("transaction.requestedAccountEffect.balanceChanges[].amountRaw"),
133
+ z.literal("transaction.requestedAccountEffect.coinFlows[].*Raw"),
134
+ z.literal("requestedAccount.coinFlows[].*Raw"),
135
+ z.literal("requestedAccountTransactionFacts[].accountBalanceChanges[].amountRaw"),
136
+ z.literal("requestedAccountTransactionFacts[].accountCoinFlows[].*Raw"),
137
+ z.literal("requestedAccountTransactionFacts[].requestedAccountEffect.balanceChanges[].amountRaw"),
138
+ z.literal("requestedAccountTransactionFacts[].requestedAccountEffect.coinFlows[].*Raw"),
139
+ z.literal("transactions[].requestedAccountEffect.balanceChanges[].amountRaw"),
140
+ z.literal("transactions[].requestedAccountEffect.coinFlows[].*Raw"),
141
+ z.literal("transactions[].details.balanceChanges[].amountRaw"),
142
+ z.literal("transactions[].compact.balanceChanges[].amountRaw"),
143
+ z.literal("analysis.coinFlows[].*Raw")
144
+ ]),
145
+ notFor: z.tuple([
146
+ z.literal("display_conversion_without_verified_decimals"),
147
+ z.literal("fiat_usd_cash_out"),
148
+ z.literal("profit_or_pnl"),
149
+ z.literal("position_valuation")
150
+ ])
151
+ }).strict();
152
+ const suiTransactionFactSchema = z.object({
153
+ digest: z.string(),
154
+ checkpoint: z.string().optional(),
155
+ timestamp: fetchedAtSchema.optional(),
156
+ status: externalActivityStatusSchema,
157
+ sender: z.string().optional(),
158
+ requestedAccountEffect: transactionAccountEffectsSchema.optional(),
159
+ compact: externalActivityTransactionCompactFactsSchema.optional(),
160
+ details: externalActivityTransactionDetailSchema.optional()
161
+ });
162
+ const requestedAccountTransactionContextSchema = externalActivityTransactionCompactFactsSchema.omit({
163
+ balanceChanges: true
164
+ });
165
+ const suiTransactionAuditFactSchema = z.object({
166
+ digest: z.string(),
167
+ checkpoint: z.string().optional(),
168
+ timestamp: fetchedAtSchema.optional(),
169
+ status: externalActivityStatusSchema,
170
+ sender: z.string().optional(),
171
+ requestedAccountEffect: transactionAccountEffectsSchema.optional(),
172
+ transactionContext: requestedAccountTransactionContextSchema.optional(),
173
+ detailLookup: z.object({
174
+ tool: z.literal("read.inspect_sui_transaction"),
175
+ digest: z.string()
176
+ })
177
+ });
178
+ const requestedAccountTransactionFactSchema = z.object({
179
+ digest: z.string(),
180
+ checkpoint: z.string().optional(),
181
+ timestamp: fetchedAtSchema.optional(),
182
+ status: externalActivityStatusSchema,
183
+ sender: z.string().optional(),
184
+ requestedAccount: z.string(),
185
+ accountScope: z.literal("requested_account"),
186
+ accountRole: transactionAccountRoleSchema,
187
+ sentByAccount: z.boolean(),
188
+ accountBalanceChangeEvidence: accountBalanceChangeEvidenceSchema,
189
+ accountBalanceChangeInferencePolicy: accountBalanceChangeInferencePolicySchema,
190
+ accountBalanceChangeCompleteness: accountBalanceChangeCompletenessSchema,
191
+ accountBalanceChanges: transactionAccountEffectsSchema.shape.balanceChanges,
192
+ accountCoinFlows: transactionAccountEffectsSchema.shape.coinFlows,
193
+ accountEffectLimitations: z.array(transactionAccountEffectLimitationSchema),
194
+ requestedAccountEffect: transactionAccountEffectsSchema,
195
+ transactionContext: requestedAccountTransactionContextSchema.optional(),
196
+ detailLookup: z.object({
197
+ tool: z.literal("read.inspect_sui_transaction"),
198
+ digest: z.string()
199
+ })
200
+ });
201
+ const suiActivityAnalysisSchema = z.object({
202
+ overview: z.object({
203
+ transactionCount: z.number().int().nonnegative(),
204
+ analyzedTransactionCount: z.number().int().nonnegative(),
205
+ statusCounts: z.object({
206
+ success: z.number().int().nonnegative(),
207
+ failure: z.number().int().nonnegative(),
208
+ unknown: z.number().int().nonnegative()
209
+ }).strict(),
210
+ relationshipCounts: z.object({
211
+ affected: z.number().int().nonnegative(),
212
+ sent: z.number().int().nonnegative()
213
+ }).strict(),
214
+ earliestTimestamp: fetchedAtSchema.optional(),
215
+ latestTimestamp: fetchedAtSchema.optional(),
216
+ earliestCheckpoint: z.string().optional(),
217
+ latestCheckpoint: z.string().optional()
218
+ }).strict(),
219
+ moveCallTargets: z.array(z.object({
220
+ target: z.string(),
221
+ count: z.number().int().positive()
222
+ }).strict()),
223
+ protocols: z.array(z.object({
224
+ protocolId: z.string(),
225
+ displayName: z.string().optional(),
226
+ count: z.number().int().positive()
227
+ }).strict()),
228
+ coinFlows: z.array(z.object({
229
+ coinType: z.string(),
230
+ increaseRaw: z.string().regex(/^\d+$/),
231
+ decreaseRaw: z.string().regex(/^\d+$/),
232
+ netRaw: z.string().regex(/^-?\d+$/),
233
+ transactionCount: z.number().int().positive()
234
+ }).strict()),
235
+ objectChanges: z.object({
236
+ created: z.number().int().nonnegative(),
237
+ mutated: z.number().int().nonnegative(),
238
+ deleted: z.number().int().nonnegative()
239
+ }).strict(),
240
+ eventTypes: z.array(z.object({
241
+ eventType: z.string(),
242
+ count: z.number().int().positive()
243
+ }).strict()),
244
+ gas: z.object({
245
+ transactionCount: z.number().int().positive(),
246
+ netGasCostRaw: z.string().regex(/^-?\d+$/),
247
+ netGasCost: externalActivityGasCostFactSchema
248
+ }).strict().optional(),
249
+ failures: z.array(z.object({
250
+ message: z.string(),
251
+ count: z.number().int().positive(),
252
+ abortCodeRaw: z.string().regex(/^\d+$/).optional(),
253
+ package: z.string().optional(),
254
+ module: z.string().optional(),
255
+ function: z.string().optional()
256
+ }).strict()),
257
+ limitations: z.array(z.enum(suiActivityAnalysisLimitations))
258
+ }).strict();
259
+ const externalActivityScanRecordSchema = z.object({
260
+ scanId: z.string(),
261
+ kind: z.enum(["digest_lookup", "account_scan", "function_scan"]),
262
+ accountId: z.number().int().positive(),
263
+ account: z.string(),
264
+ relationship: externalActivityRelationshipSchema,
265
+ inputDigest: z.string().optional(),
266
+ fromCheckpoint: z.string().optional(),
267
+ toCheckpoint: z.string().optional(),
268
+ fromTimestamp: fetchedAtSchema.optional(),
269
+ toTimestamp: fetchedAtSchema.optional(),
270
+ limit: z.number().int().min(1).max(EXTERNAL_ACTIVITY_SCAN_MAX_LIMIT),
271
+ requestCursor: z.string().optional(),
272
+ responseCursor: z.string().optional(),
273
+ endpointHost: z.string(),
274
+ chainIdentifier: z.string(),
275
+ fetchedAt: fetchedAtSchema,
276
+ storedCount: z.number().int().nonnegative(),
277
+ skippedCount: z.number().int().nonnegative(),
278
+ hasMore: z.boolean(),
279
+ windowComplete: z.boolean().nullable(),
280
+ incompleteReason: externalActivityScanIncompleteReasonSchema.optional()
281
+ });
282
+ const externalActivityCoverageLimitationSchema = z.enum([
283
+ "no_stored_activity_scans",
284
+ "no_complete_affected_account_scan",
285
+ "sent_only_scan_not_full_account_coverage",
286
+ "scan_window_incomplete",
287
+ "scan_window_unbounded",
288
+ "scan_records_truncated"
289
+ ]);
290
+ const externalActivityCoverageSchema = z.object({
291
+ dataScope: reviewActivityDataScopeSchema,
292
+ accountSource: reviewActivityAccountSourceSchema,
293
+ accountKnown: z.boolean(),
294
+ requestedRange: z.object({
295
+ from: fetchedAtSchema,
296
+ to: fetchedAtSchema
297
+ }).strict(),
298
+ coverageStatus: z.enum(["complete", "partial", "no_stored_scans"]),
299
+ scanCount: z.number().int().nonnegative(),
300
+ returnedScanCount: z.number().int().nonnegative(),
301
+ scansTruncated: z.boolean(),
302
+ storedTransactionCount: z.number().int().nonnegative(),
303
+ storedTransactionRange: z.object({
304
+ earliestTimestamp: fetchedAtSchema.optional(),
305
+ latestTimestamp: fetchedAtSchema.optional(),
306
+ earliestCheckpoint: z.string().optional(),
307
+ latestCheckpoint: z.string().optional()
308
+ }).strict().optional(),
309
+ coverageEvidence: z.object({
310
+ completeAffectedAccountScanIds: z.array(z.string()),
311
+ incompleteScanIds: z.array(z.string()),
312
+ sentOnlyScanIds: z.array(z.string())
313
+ }).strict(),
314
+ limitations: z.array(externalActivityCoverageLimitationSchema),
315
+ scans: z.array(externalActivityScanRecordSchema)
316
+ }).strict();
317
+ const externalActivityTransactionRecordSchema = z.object({
318
+ accountId: z.number().int().positive(),
319
+ account: z.string(),
320
+ digest: z.string(),
321
+ relationship: externalActivityRelationshipSchema,
322
+ checkpoint: z.string().optional(),
323
+ timestamp: fetchedAtSchema.optional(),
324
+ status: externalActivityStatusSchema,
325
+ knownSenderAccountId: z.number().int().positive().optional(),
326
+ firstScanId: z.string(),
327
+ lastScanId: z.string(),
328
+ firstFetchedAt: fetchedAtSchema,
329
+ lastFetchedAt: fetchedAtSchema,
330
+ lastScanIncompleteReason: externalActivityScanIncompleteReasonSchema.optional(),
331
+ compact: externalActivityTransactionCompactFactsSchema.optional(),
332
+ details: externalActivityTransactionDetailSchema.optional(),
333
+ detailLookup: z.object({
334
+ tool: z.literal("read.inspect_sui_transaction"),
335
+ digest: z.string()
336
+ }).optional()
337
+ });
338
+ const transactionDetailAvailabilitySchema = z.object({
339
+ totalTransactions: z.number().int().nonnegative(),
340
+ withDetails: z.number().int().nonnegative(),
341
+ withoutDetails: z.number().int().nonnegative(),
342
+ detailAvailability: z.enum(["none", "some", "all"]),
343
+ allReturnedTransactionsHaveDetails: z.boolean()
344
+ }).strict();
345
+ const accountAssetTimelineQuantitySemanticsSchema = z.object({
346
+ kind: z.literal("sui_account_asset_timeline_raw_net_flows"),
347
+ rawAmountsOnly: z.literal(true),
348
+ netFlowFields: z.tuple([
349
+ z.literal("netFlowBars[].increaseRaw"),
350
+ z.literal("netFlowBars[].decreaseRaw"),
351
+ z.literal("netFlowBars[].netRaw")
352
+ ]),
353
+ balanceBarsAvailable: z.literal(false),
354
+ balanceStatusField: z.literal("balanceStatus"),
355
+ balanceBarRule: z.literal("balanceBars are unavailable unless a stored balance anchor exists"),
356
+ notFor: z.tuple([
357
+ z.literal("held_balance_without_balance_anchor"),
358
+ z.literal("complete_wallet_history"),
359
+ z.literal("display_conversion_without_verified_decimals"),
360
+ z.literal("fiat_usd_cash_out"),
361
+ z.literal("usd_peg_assumption"),
362
+ z.literal("profit_or_pnl"),
363
+ z.literal("cost_basis"),
364
+ z.literal("route_recommendation"),
365
+ z.literal("transaction_building"),
366
+ z.literal("signing_data_or_readiness")
367
+ ])
368
+ }).strict();
369
+ const accountAssetTimelineLimitationSchema = z.enum([
370
+ "no_balance_anchor",
371
+ "account_not_known",
372
+ "no_stored_activity_scans",
373
+ "no_complete_affected_account_scan",
374
+ "sent_only_scan_not_full_account_coverage",
375
+ "scan_window_incomplete",
376
+ "scan_window_unbounded",
377
+ "scan_records_truncated",
378
+ "source_transactions_truncated",
379
+ "transaction_timestamp_unavailable",
380
+ "transaction_details_unavailable",
381
+ "provider_balance_changes_truncated",
382
+ "no_observed_account_balance_changes"
383
+ ]);
384
+ const accountAssetTimelineNetFlowBarSchema = z.object({
385
+ bucketStart: fetchedAtSchema,
386
+ bucketEnd: fetchedAtSchema,
387
+ coinType: z.string(),
388
+ increaseRaw: z.string().regex(/^\d+$/),
389
+ decreaseRaw: z.string().regex(/^\d+$/),
390
+ netRaw: z.string().regex(/^-?\d+$/),
391
+ transactionCount: z.number().int().positive()
392
+ }).strict();
393
+ const accountAssetTimelineUsdcReferenceSummarySchema = z.object({
394
+ status: z.enum(["available", "partial", "unavailable", "no_timeline_bars"]),
395
+ quoteAsset: z.literal(DEEPBOOK_OFFICIAL_INDEXER_USDC_REFERENCE.quoteAsset),
396
+ priceConvention: z.literal(DEEPBOOK_OFFICIAL_INDEXER_USDC_REFERENCE.priceConvention),
397
+ usdcIsFiatUsd: z.literal(DEEPBOOK_OFFICIAL_INDEXER_USDC_REFERENCE.usdcIsFiatUsd),
398
+ usdPegGuaranteeAvailable: z.literal(DEEPBOOK_OFFICIAL_INDEXER_USDC_REFERENCE.usdPegGuaranteeAvailable),
399
+ source: z.literal(DEEPBOOK_OFFICIAL_INDEXER_SOURCE_BASE.kind),
400
+ chainRecomputedBySuiMcp: z.literal(DEEPBOOK_OFFICIAL_INDEXER_SOURCE_BASE.chainRecomputedBySuiMcp),
401
+ quantitySemantics: z.object({
402
+ kind: z.literal("deepbook_official_indexer_candles"),
403
+ allowedUse: z.literal(DEEPBOOK_OFFICIAL_INDEXER_CANDLE_USE.allowedUse),
404
+ source: z.literal(DEEPBOOK_OFFICIAL_INDEXER_CANDLE_USE.source),
405
+ quoteAsset: z.literal(DEEPBOOK_OFFICIAL_INDEXER_CANDLE_USE.quoteAsset),
406
+ priceConvention: z.literal(DEEPBOOK_OFFICIAL_INDEXER_CANDLE_USE.priceConvention),
407
+ usdcIsFiatUsd: z.literal(DEEPBOOK_OFFICIAL_INDEXER_CANDLE_USE.usdcIsFiatUsd),
408
+ usdPegGuaranteeAvailable: z.literal(DEEPBOOK_OFFICIAL_INDEXER_CANDLE_USE.usdPegGuaranteeAvailable),
409
+ chainRecomputedBySuiMcp: z.literal(DEEPBOOK_OFFICIAL_INDEXER_CANDLE_USE.chainRecomputedBySuiMcp),
410
+ liveQuoteAvailable: z.literal(false),
411
+ historicalMidPriceAvailable: z.literal(false),
412
+ globalMarketPriceAvailable: z.literal(false),
413
+ fiatUsdCashOutAvailable: z.literal(false),
414
+ routeRecommendationAvailable: z.literal(false),
415
+ transactionBuildingAvailable: z.literal(false),
416
+ signingReadinessAvailable: z.literal(false),
417
+ profitAndLossAvailable: z.literal(false),
418
+ costBasisAvailable: z.literal(false),
419
+ notFor: z.array(z.string())
420
+ }).strict(),
421
+ responseSummary: z.object({
422
+ questionKind: z.literal("deepbook_usdc_price_history"),
423
+ evidenceKind: z.literal("official_deepbook_indexer_candles"),
424
+ sourceStatement: z.string(),
425
+ usdcDisclaimer: z.literal(DEEPBOOK_OFFICIAL_INDEXER_RESPONSE_TEXT.usdcDisclaimer),
426
+ candleMeaning: z.literal("Each candle is returned by the DeepBookV3 official Indexer for the requested interval."),
427
+ excludedFromConclusion: z.array(z.enum(DEEPBOOK_USDC_PRICE_HISTORY_UNSUPPORTED_CLAIMS))
428
+ }).strict(),
429
+ unsupportedClaims: z.array(z.enum(DEEPBOOK_USDC_PRICE_HISTORY_UNSUPPORTED_CLAIMS)),
430
+ coinReferences: z.array(z.union([
431
+ z.object({
432
+ coinType: z.string(),
433
+ status: z.enum(["available", "partial"]),
434
+ pair: z.object({
435
+ poolName: z.string(),
436
+ poolId: z.string(),
437
+ baseAsset: z.object({
438
+ symbol: z.string(),
439
+ coinType: z.string(),
440
+ decimals: z.number().int().nonnegative()
441
+ }).strict(),
442
+ quoteAsset: z.object({
443
+ symbol: z.literal("USDC"),
444
+ coinType: z.string(),
445
+ decimals: z.number().int().nonnegative()
446
+ }).strict(),
447
+ priceConvention: z.literal(DEEPBOOK_OFFICIAL_INDEXER_USDC_REFERENCE.priceConvention)
448
+ }).strict(),
449
+ candleAvailability: z.enum(["available", "no_candles_in_range"]),
450
+ source: z.object({
451
+ kind: z.literal(DEEPBOOK_OFFICIAL_INDEXER_SOURCE_BASE.kind),
452
+ baseUrl: z.string(),
453
+ sourceStatement: z.string(),
454
+ poolList: z.object({
455
+ url: z.string(),
456
+ fetchedAt: fetchedAtSchema
457
+ }).strict(),
458
+ candles: z.object({
459
+ url: z.string(),
460
+ fetchedAt: fetchedAtSchema,
461
+ poolName: z.string(),
462
+ interval: deepbookOfficialIndexerIntervalSchema,
463
+ startTimeMs: z.number().int().nonnegative(),
464
+ endTimeMs: z.number().int().nonnegative(),
465
+ limit: z.number().int().positive()
466
+ }).strict(),
467
+ chainRecomputedBySuiMcp: z.literal(DEEPBOOK_OFFICIAL_INDEXER_SOURCE_BASE.chainRecomputedBySuiMcp)
468
+ }).strict(),
469
+ barReferences: z.array(z.union([
470
+ z.object({
471
+ bucketStart: fetchedAtSchema,
472
+ bucketEnd: fetchedAtSchema,
473
+ status: z.literal("available"),
474
+ candle: deepbookOfficialIndexerCandleSchema
475
+ }).strict(),
476
+ z.object({
477
+ bucketStart: fetchedAtSchema,
478
+ bucketEnd: fetchedAtSchema,
479
+ status: z.literal("missing_candle")
480
+ }).strict()
481
+ ]))
482
+ }).strict(),
483
+ z.object({
484
+ coinType: z.string(),
485
+ status: z.literal("unsupported_asset"),
486
+ reason: z.enum(["selector_not_in_official_indexer", "selector_resolves_to_multiple_usdc_pools"]),
487
+ matchingPoolNames: z.array(z.string()),
488
+ availablePoolNames: z.array(z.string())
489
+ }).strict(),
490
+ z.object({
491
+ coinType: z.string(),
492
+ status: z.literal("unsupported_range"),
493
+ reason: z.literal("requested_range_exceeds_max_bars"),
494
+ requested: z.unknown()
495
+ }).strict(),
496
+ z.object({
497
+ coinType: z.string(),
498
+ status: z.literal("source_unavailable"),
499
+ reason: z.enum(["official_indexer_not_configured", "pool_list_unavailable", "candle_fetch_failed", "official_indexer_invalid_payload"]),
500
+ pair: z.unknown().optional(),
501
+ source: z.unknown().optional()
502
+ }).strict()
503
+ ]))
504
+ }).strict();
505
+ const liveActivityScanPersistenceSchema = z.object({
506
+ stored: z.boolean(),
507
+ reason: z.enum(["account_not_known"]).optional(),
508
+ scan: externalActivityScanRecordSchema.optional()
509
+ });
510
+ const liveActivityScanOutputBase = {
511
+ status: z.literal("ok"),
512
+ fetchedAt: fetchedAtSchema,
513
+ account: z.string(),
514
+ accountKnown: z.boolean(),
515
+ accountSource: reviewActivityAccountSourceSchema,
516
+ relationship: externalActivityRelationshipSchema,
517
+ userAnswerUse: userAnswerUseSchema,
518
+ requestedAccount: requestedAccountActivitySchema,
519
+ requestedAccountTransactionFacts: z.array(requestedAccountTransactionFactSchema),
520
+ transactionDetailAvailability: transactionDetailAvailabilitySchema,
521
+ quantitySemantics: suiActivityQuantitySemanticsSchema,
522
+ source: suiTransactionActivitySourceSchema,
523
+ hasMore: z.boolean(),
524
+ continuationCursor: z.string().optional(),
525
+ windowComplete: z.boolean().nullable(),
526
+ orderingVerified: z.boolean(),
527
+ incompleteReason: externalActivityScanIncompleteReasonSchema.optional(),
528
+ persistence: liveActivityScanPersistenceSchema
529
+ };
530
+ const liveFunctionActivityScanOutputBase = {
531
+ ...liveActivityScanOutputBase,
532
+ function: z.string(),
533
+ relationship: z.literal("sent")
534
+ };
535
+ export function registerTransactionActivityTools(server, deps) {
536
+ server.registerTool(TOOL_NAMES.readInspectSuiTransaction, {
537
+ title: "Inspect Sui transaction",
538
+ description: "Lookup one Sui transaction digest. Stores only when sender or balance owner matches a known local wallet.",
539
+ inputSchema: {
540
+ digest: transactionDigestSchema,
541
+ account: z.string().min(1).optional()
542
+ },
543
+ outputSchema: successOutputSchema({
544
+ status: z.literal("ok"),
545
+ fetchedAt: fetchedAtSchema,
546
+ source: suiTransactionActivitySourceSchema,
547
+ userAnswerUse: userAnswerUseSchema,
548
+ quantitySemantics: suiActivityQuantitySemanticsSchema,
549
+ transaction: suiTransactionFactSchema,
550
+ persistence: z.object({
551
+ stored: z.boolean(),
552
+ reason: z.enum(["no_known_wallet_relation", "transaction_not_found"]).optional(),
553
+ account: z.string().optional(),
554
+ relationship: externalActivityRelationshipSchema.optional(),
555
+ scan: externalActivityScanRecordSchema.optional()
556
+ })
557
+ }),
558
+ annotations: { readOnlyHint: false, openWorldHint: false }
559
+ }, async ({ digest, account }) => {
560
+ try {
561
+ const result = await deps.transactionActivityService.inspectSuiTransaction({ digest, account });
562
+ const transaction = transactionFactOutput(result.transaction);
563
+ return okToolResult({
564
+ ...result,
565
+ userAnswerUse: inspectSuiTransactionUserAnswerUse({
566
+ hasSender: transaction.sender !== undefined,
567
+ hasRequestedAccountEffect: transaction.requestedAccountEffect !== undefined,
568
+ hasDetails: transaction.details !== undefined
569
+ }),
570
+ quantitySemantics: suiActivityQuantitySemantics(),
571
+ transaction
572
+ });
573
+ }
574
+ catch (error) {
575
+ return transactionActivityToolError(error, deps.logger);
576
+ }
577
+ });
578
+ server.registerTool(TOOL_NAMES.readScanSuiAccountActivity, {
579
+ title: "Scan Sui account activity rows",
580
+ description: "Bounded GraphQL account activity scan with requested-account facts and digest detail lookups. Not background indexing or complete history.",
581
+ inputSchema: suiActivityScanInputSchema,
582
+ outputSchema: successOutputSchema({
583
+ ...liveActivityScanOutputBase,
584
+ transactions: z.array(suiTransactionAuditFactSchema),
585
+ }),
586
+ annotations: { readOnlyHint: false, openWorldHint: false }
587
+ }, async (input) => {
588
+ try {
589
+ const result = await deps.transactionActivityService.scanSuiAccountActivity(input);
590
+ return okToolResult(liveActivityScanToolOutput(result, transactionFactAuditOutput));
591
+ }
592
+ catch (error) {
593
+ return transactionActivityToolError(error, deps.logger);
594
+ }
595
+ });
596
+ server.registerTool(TOOL_NAMES.readScanSuiFunctionActivity, {
597
+ title: "Scan sent Sui function activity rows",
598
+ description: "Bounded GraphQL scan for transactions the account sent that called one full package::module::function. Returns requested-account facts and digest detail lookups.",
599
+ inputSchema: suiFunctionActivityScanInputSchema,
600
+ outputSchema: successOutputSchema({
601
+ ...liveFunctionActivityScanOutputBase,
602
+ transactions: z.array(suiTransactionAuditFactSchema),
603
+ }),
604
+ annotations: { readOnlyHint: false, openWorldHint: false }
605
+ }, async (input) => {
606
+ try {
607
+ const result = await deps.transactionActivityService.scanSuiFunctionActivity(input);
608
+ return okToolResult(liveActivityScanToolOutput(result, transactionFactAuditOutput));
609
+ }
610
+ catch (error) {
611
+ return transactionActivityToolError(error, deps.logger);
612
+ }
613
+ });
614
+ server.registerTool(TOOL_NAMES.readSummarizeSuiAccountActivity, {
615
+ title: "Summarize Sui account activity",
616
+ description: "Summarize stored normalized Sui activity facts. Not complete history or P&L.",
617
+ inputSchema: {
618
+ ...reviewActivityInputSchema,
619
+ limit: z.number().int().min(1).max(REVIEW_ACTIVITY_LIST_MAX_LIMIT).optional()
620
+ },
621
+ outputSchema: successOutputSchema({
622
+ status: z.literal("ok"),
623
+ dataScope: reviewActivityDataScopeSchema,
624
+ accountSource: reviewActivityAccountSourceSchema,
625
+ userAnswerUse: userAnswerUseSchema,
626
+ quantitySemantics: suiActivityQuantitySemanticsSchema,
627
+ lowSampleWarning: z.boolean(),
628
+ lowSampleThreshold: z.literal(REVIEW_ACTIVITY_LOW_SAMPLE_THRESHOLD),
629
+ truncated: z.boolean(),
630
+ source: suiTransactionActivitySourceSchema,
631
+ summary: z.object({
632
+ transactionCount: z.number().int().nonnegative(),
633
+ statusCounts: z.object({
634
+ success: z.number().int().nonnegative(),
635
+ failure: z.number().int().nonnegative(),
636
+ unknown: z.number().int().nonnegative()
637
+ }),
638
+ relationshipCounts: z.object({
639
+ affected: z.number().int().nonnegative(),
640
+ sent: z.number().int().nonnegative()
641
+ }),
642
+ earliestTimestamp: fetchedAtSchema.optional(),
643
+ latestTimestamp: fetchedAtSchema.optional()
644
+ }),
645
+ analysis: suiActivityAnalysisSchema,
646
+ transactionDetailAvailability: transactionDetailAvailabilitySchema,
647
+ transactions: z.array(externalActivityTransactionRecordSchema)
648
+ }),
649
+ annotations: { readOnlyHint: true, openWorldHint: false }
650
+ }, async ({ account, from, to, limit }) => {
651
+ try {
652
+ const summary = await deps.transactionActivityService.summarizeSuiAccountActivity({
653
+ account,
654
+ from,
655
+ to,
656
+ limit
657
+ });
658
+ const detailAvailability = transactionDetailAvailability(summary.transactions);
659
+ return okToolResult({
660
+ ...summary,
661
+ userAnswerUse: storedSuiActivityUserAnswerUse(detailAvailability),
662
+ quantitySemantics: suiActivityQuantitySemantics(),
663
+ source: {
664
+ transport: "local_db",
665
+ method: "stored_normalized_facts"
666
+ },
667
+ transactionDetailAvailability: detailAvailability,
668
+ transactions: summary.transactions.map(externalActivityTransactionRecordOutput)
669
+ });
670
+ }
671
+ catch (error) {
672
+ return transactionActivityToolError(error, deps.logger);
673
+ }
674
+ });
675
+ server.registerTool(TOOL_NAMES.readGetAccountAssetTimeline, {
676
+ title: "Get account asset timeline",
677
+ description: "Return stored local account asset net-flow timeline evidence. Not complete wallet history, P&L, or held balances.",
678
+ inputSchema: accountAssetTimelineInputSchema,
679
+ outputSchema: successOutputSchema({
680
+ status: z.enum(["ok", "partial_coverage", "scan_needed", "account_not_known"]),
681
+ fetchedAt: fetchedAtSchema,
682
+ account: z.string(),
683
+ requestedRange: z.object({
684
+ from: fetchedAtSchema,
685
+ to: fetchedAtSchema
686
+ }).strict(),
687
+ bucket: z.object({
688
+ interval: deepbookOfficialIndexerIntervalSchema,
689
+ durationMs: z.number().int().positive(),
690
+ alignment: z.literal("utc_epoch")
691
+ }).strict(),
692
+ balanceStatus: z.literal("unavailable_no_balance_anchor"),
693
+ coverage: externalActivityCoverageSchema,
694
+ source: suiTransactionActivitySourceSchema,
695
+ sourceTransactions: z.object({
696
+ storedTransactionCount: z.number().int().nonnegative(),
697
+ returnedTransactionCount: z.number().int().nonnegative(),
698
+ truncated: z.boolean(),
699
+ detailAvailability: transactionDetailAvailabilitySchema
700
+ }).strict(),
701
+ sourceTransactionCount: z.number().int().nonnegative(),
702
+ analyzedTransactionCount: z.number().int().nonnegative(),
703
+ skippedTransactionCount: z.number().int().nonnegative(),
704
+ netFlowBars: z.array(accountAssetTimelineNetFlowBarSchema),
705
+ balanceBars: z.tuple([]),
706
+ quantitySemantics: accountAssetTimelineQuantitySemanticsSchema,
707
+ usdcReferences: accountAssetTimelineUsdcReferenceSummarySchema,
708
+ limitations: z.array(accountAssetTimelineLimitationSchema),
709
+ userAnswerUse: userAnswerUseSchema,
710
+ scanNeeded: z.object({
711
+ tool: z.literal(TOOL_NAMES.readScanSuiAccountActivity),
712
+ account: z.string(),
713
+ relationship: z.literal("affected"),
714
+ fromTimestamp: fetchedAtSchema,
715
+ toTimestamp: fetchedAtSchema
716
+ }).strict().optional()
717
+ }),
718
+ annotations: { readOnlyHint: true, openWorldHint: false }
719
+ }, async ({ account, start, end, interval }) => {
720
+ try {
721
+ const coverage = await deps.activityStore.getExternalActivityCoverage({
722
+ account,
723
+ from: start,
724
+ to: end
725
+ });
726
+ const activityTransactions = await deps.activityStore.listExternalActivityEffectTransactions({
727
+ account: coverage.dataScope.account,
728
+ from: start,
729
+ to: end,
730
+ limit: REVIEW_ACTIVITY_LIST_MAX_LIMIT
731
+ });
732
+ const timeline = buildAccountAssetTimeline({
733
+ account: coverage.dataScope.account,
734
+ from: start,
735
+ to: end,
736
+ interval,
737
+ coverage,
738
+ transactions: activityTransactions.transactions,
739
+ transactionsTruncated: activityTransactions.truncated
740
+ });
741
+ const withUsdcReferences = await attachDeepbookUsdcReferencesToTimeline({
742
+ timeline,
743
+ getPriceHistory: (priceInput) => deps.readService.getDeepbookUsdcPriceHistory(priceInput)
744
+ });
745
+ const detailAvailability = transactionDetailAvailability(activityTransactions.transactions);
746
+ const scanNeeded = withUsdcReferences.status === "scan_needed";
747
+ const accountKnown = withUsdcReferences.status !== "account_not_known";
748
+ return okToolResult({
749
+ ...withUsdcReferences,
750
+ fetchedAt: new Date().toISOString(),
751
+ source: {
752
+ transport: "local_db",
753
+ method: "stored_normalized_facts"
754
+ },
755
+ sourceTransactions: {
756
+ storedTransactionCount: activityTransactions.transactionCount,
757
+ returnedTransactionCount: activityTransactions.transactions.length,
758
+ truncated: activityTransactions.truncated,
759
+ detailAvailability
760
+ },
761
+ quantitySemantics: accountAssetTimelineQuantitySemantics(),
762
+ userAnswerUse: accountAssetTimelineUserAnswerUse({
763
+ hasNetFlowBars: withUsdcReferences.netFlowBars.length > 0,
764
+ hasUsdcReferenceCandles: withUsdcReferences.usdcReferences.coinReferences.some((reference) => reference.status === "available" || reference.status === "partial"),
765
+ scanNeeded,
766
+ accountKnown
767
+ }),
768
+ ...(scanNeeded && accountKnown
769
+ ? {
770
+ scanNeeded: {
771
+ tool: TOOL_NAMES.readScanSuiAccountActivity,
772
+ account: coverage.dataScope.account,
773
+ relationship: "affected",
774
+ fromTimestamp: start,
775
+ toTimestamp: end
776
+ }
777
+ }
778
+ : {})
779
+ });
780
+ }
781
+ catch (error) {
782
+ return transactionActivityToolError(error, deps.logger);
783
+ }
784
+ });
785
+ server.registerTool(TOOL_NAMES.readSummarizeSuiActivityScan, {
786
+ title: "Summarize Sui activity scan",
787
+ description: "Bounded GraphQL account activity summary with requested-account facts and deterministic normalized-fact analysis. Not complete history or P&L.",
788
+ inputSchema: suiActivityScanInputSchema,
789
+ outputSchema: successOutputSchema({
790
+ ...liveActivityScanOutputBase,
791
+ analysis: suiActivityAnalysisSchema,
792
+ transactions: z.array(suiTransactionAuditFactSchema),
793
+ }),
794
+ annotations: { readOnlyHint: false, openWorldHint: false }
795
+ }, async (input) => {
796
+ try {
797
+ const result = await deps.transactionActivityService.summarizeSuiActivityScan(input);
798
+ return okToolResult(liveActivityScanToolOutput(result, transactionFactAuditOutput));
799
+ }
800
+ catch (error) {
801
+ return transactionActivityToolError(error, deps.logger);
802
+ }
803
+ });
804
+ server.registerTool(TOOL_NAMES.readSummarizeSuiFunctionActivityScan, {
805
+ title: "Summarize sent Sui function activity",
806
+ description: "Bounded sent-function activity summary with requested-account facts and deterministic normalized-fact analysis. Not route, P&L, or position analysis.",
807
+ inputSchema: suiFunctionActivityScanInputSchema,
808
+ outputSchema: successOutputSchema({
809
+ ...liveFunctionActivityScanOutputBase,
810
+ analysis: suiActivityAnalysisSchema,
811
+ transactions: z.array(suiTransactionAuditFactSchema),
812
+ }),
813
+ annotations: { readOnlyHint: false, openWorldHint: false }
814
+ }, async (input) => {
815
+ try {
816
+ const result = await deps.transactionActivityService.summarizeSuiFunctionActivityScan(input);
817
+ return okToolResult(liveActivityScanToolOutput(result, transactionFactAuditOutput));
818
+ }
819
+ catch (error) {
820
+ return transactionActivityToolError(error, deps.logger);
821
+ }
822
+ });
823
+ }
824
+ function liveActivityScanToolOutput(result, transactionOutput) {
825
+ const detailAvailability = transactionDetailAvailability(result.transactions);
826
+ return {
827
+ status: result.status,
828
+ fetchedAt: result.fetchedAt,
829
+ account: result.account,
830
+ accountKnown: result.accountKnown,
831
+ accountSource: result.accountSource,
832
+ ...("function" in result ? { function: result.function } : {}),
833
+ relationship: result.relationship,
834
+ userAnswerUse: liveSuiActivityUserAnswerUse({
835
+ includeAnalysis: "analysis" in result,
836
+ transactionDetailAvailability: detailAvailability
837
+ }),
838
+ requestedAccount: result.requestedAccount,
839
+ requestedAccountTransactionFacts: requestedAccountTransactionFacts(result.transactions),
840
+ transactionDetailAvailability: detailAvailability,
841
+ quantitySemantics: suiActivityQuantitySemantics(),
842
+ source: result.source,
843
+ ...("analysis" in result ? { analysis: result.analysis } : {}),
844
+ transactions: result.transactions.map(transactionOutput),
845
+ hasMore: result.hasMore,
846
+ ...(result.continuationCursor === undefined ? {} : { continuationCursor: result.continuationCursor }),
847
+ windowComplete: result.windowComplete,
848
+ orderingVerified: result.orderingVerified,
849
+ ...(result.incompleteReason === undefined ? {} : { incompleteReason: result.incompleteReason }),
850
+ persistence: result.persistence
851
+ };
852
+ }
853
+ function requestedAccountTransactionFacts(transactions) {
854
+ return transactions.flatMap((transaction) => {
855
+ const fact = requestedAccountTransactionFactOutput(transaction);
856
+ return fact === undefined ? [] : [fact];
857
+ });
858
+ }