@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 @@
1
+ export {};
@@ -0,0 +1,43 @@
1
+ import { cloneLocalSession, createLocalSessionBase, isLocalSessionExpired, tokenMatchesHash } from "./localSession.js";
2
+ export class SettingsSessionManager {
3
+ options;
4
+ sessions = new Map();
5
+ constructor(options) {
6
+ this.options = options;
7
+ }
8
+ async create(now) {
9
+ const { base, token } = createLocalSessionBase(now, this.options.ttlMs);
10
+ const session = {
11
+ ...base,
12
+ type: "local_settings"
13
+ };
14
+ this.sessions.set(session.id, session);
15
+ await this.options.appendEventLog({
16
+ type: "settings_session.created",
17
+ sessionId: session.id,
18
+ at: now.toISOString()
19
+ });
20
+ return { session: cloneLocalSession(session), token };
21
+ }
22
+ async get(id, now) {
23
+ const session = this.sessions.get(id);
24
+ if (!session) {
25
+ return undefined;
26
+ }
27
+ if (isLocalSessionExpired(session, now)) {
28
+ this.sessions.delete(id);
29
+ return undefined;
30
+ }
31
+ return cloneLocalSession(session);
32
+ }
33
+ async validateToken(id, token, now) {
34
+ const session = await this.get(id, now);
35
+ if (!session) {
36
+ return false;
37
+ }
38
+ return tokenMatchesHash(session.tokenHash, token);
39
+ }
40
+ clear() {
41
+ this.sessions.clear();
42
+ }
43
+ }
@@ -0,0 +1,86 @@
1
+ export const EXECUTION_POLLING_INTERVAL_SECONDS = 3;
2
+ export const EXECUTION_STATUS_CATEGORIES = [
3
+ "final",
4
+ "user_action_required",
5
+ "awaiting_chain_result",
6
+ "non_terminal"
7
+ ];
8
+ export function isFinalSessionStatus(status) {
9
+ return status === "success" || status === "failure" || status === "expired";
10
+ }
11
+ export function executionPollingHint() {
12
+ return {
13
+ nonTerminalStatuses: [
14
+ "pending",
15
+ "awaiting_wallet",
16
+ "awaiting_signature",
17
+ "signed_pending_result"
18
+ ],
19
+ waitStoppingStatuses: ["success", "failure", "refresh_required", "blocked", "expired"],
20
+ finalStatuses: ["success", "failure", "expired"],
21
+ userActionRequiredStatuses: ["refresh_required", "blocked"],
22
+ recommendedIntervalSeconds: EXECUTION_POLLING_INTERVAL_SECONDS
23
+ };
24
+ }
25
+ export function executionStatusCategory(status) {
26
+ switch (status) {
27
+ case "success":
28
+ case "failure":
29
+ case "expired":
30
+ return "final";
31
+ case "blocked":
32
+ case "refresh_required":
33
+ return "user_action_required";
34
+ case "signed_pending_result":
35
+ return "awaiting_chain_result";
36
+ case "pending":
37
+ case "awaiting_wallet":
38
+ case "awaiting_signature":
39
+ return "non_terminal";
40
+ default:
41
+ return assertNever(status);
42
+ }
43
+ }
44
+ export function isWaitStoppingExecutionStatus(status) {
45
+ const category = executionStatusCategory(status);
46
+ return category === "final" || category === "user_action_required";
47
+ }
48
+ export function isInteractionPendingReviewStatus(status) {
49
+ const category = executionStatusCategory(status);
50
+ return category !== "final";
51
+ }
52
+ export function getExecutionPollingStatus(session) {
53
+ if (session.status === "expired") {
54
+ return "expired";
55
+ }
56
+ if (session.executionResult) {
57
+ return executionResultStatus(session.executionResult);
58
+ }
59
+ switch (session.status) {
60
+ case "proposed":
61
+ return "pending";
62
+ case "awaiting_wallet":
63
+ return "awaiting_wallet";
64
+ case "wallet_connected":
65
+ case "ready_for_wallet_review":
66
+ return "awaiting_signature";
67
+ case "refresh_required":
68
+ return "refresh_required";
69
+ case "blocked":
70
+ return "blocked";
71
+ case "signed_pending_result":
72
+ return "signed_pending_result";
73
+ case "success":
74
+ return "success";
75
+ case "failure":
76
+ return "failure";
77
+ default:
78
+ return assertNever(session.status);
79
+ }
80
+ }
81
+ function executionResultStatus(result) {
82
+ return result.status;
83
+ }
84
+ function assertNever(value) {
85
+ throw new Error(`Unhandled session status: ${String(value)}`);
86
+ }
@@ -0,0 +1,205 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { Transaction } from "@mysten/sui/transactions";
3
+ import { z } from "zod";
4
+ import { normalizedSuiAddressSchema, parseSuiAddress, suiTransactionDigestSchema } from "../suiAddress.js";
5
+ export const LOCAL_TRANSACTION_MATERIAL_KINDS = [
6
+ "deepbook_swap_transaction_data",
7
+ "flowx_swap_transaction_data"
8
+ ];
9
+ export const LOCAL_TRANSACTION_MATERIAL_SOURCES = [
10
+ "say_ur_intent_built",
11
+ "say_ur_intent_verified"
12
+ ];
13
+ const localTransactionMaterialKindSchema = z.enum(LOCAL_TRANSACTION_MATERIAL_KINDS);
14
+ const localTransactionMaterialSourceSchema = z.enum(LOCAL_TRANSACTION_MATERIAL_SOURCES);
15
+ const isoUtcStringSchema = z.string().refine((value) => {
16
+ const parsed = new Date(value);
17
+ return Number.isFinite(parsed.getTime()) && parsed.toISOString() === value;
18
+ }, "Expected ISO 8601 UTC timestamp");
19
+ export const localTransactionMaterialHandleSchema = z.object({
20
+ materialId: z.string().min(1),
21
+ reviewSessionId: z.string().min(1),
22
+ planId: z.string().min(1),
23
+ account: normalizedSuiAddressSchema,
24
+ kind: localTransactionMaterialKindSchema,
25
+ source: localTransactionMaterialSourceSchema,
26
+ createdAt: isoUtcStringSchema,
27
+ expiresAt: isoUtcStringSchema
28
+ }).superRefine((value, ctx) => {
29
+ if (Date.parse(value.expiresAt) <= Date.parse(value.createdAt)) {
30
+ ctx.addIssue({
31
+ code: "custom",
32
+ path: ["expiresAt"],
33
+ message: "expiresAt must be after createdAt"
34
+ });
35
+ }
36
+ });
37
+ export const localTransactionMaterialDigestCommitmentSchema = z.object({
38
+ materialId: z.string().min(1),
39
+ reviewSessionId: z.string().min(1),
40
+ planId: z.string().min(1),
41
+ account: normalizedSuiAddressSchema,
42
+ kind: localTransactionMaterialKindSchema,
43
+ source: localTransactionMaterialSourceSchema,
44
+ digestKind: z.literal("sui_transaction_digest"),
45
+ transactionDigest: suiTransactionDigestSchema,
46
+ computedAt: isoUtcStringSchema,
47
+ expiresAt: isoUtcStringSchema
48
+ });
49
+ export function parseLocalTransactionMaterialHandle(value) {
50
+ return localTransactionMaterialHandleSchema.parse(value);
51
+ }
52
+ export function parseLocalTransactionMaterialDigestCommitment(value) {
53
+ return localTransactionMaterialDigestCommitmentSchema.parse(value);
54
+ }
55
+ function parseLocalTransactionMaterialArtifacts(value, now = new Date()) {
56
+ const transactionMaterial = parseLocalTransactionMaterialHandle(value.transactionMaterial);
57
+ const transactionMaterialDigest = parseLocalTransactionMaterialDigestCommitment(value.transactionMaterialDigest);
58
+ const materialCreatedAtMs = Date.parse(transactionMaterial.createdAt);
59
+ const materialExpiresAtMs = Date.parse(transactionMaterial.expiresAt);
60
+ const digestComputedAtMs = Date.parse(transactionMaterialDigest.computedAt);
61
+ const nowMs = now.getTime();
62
+ if (!Number.isFinite(nowMs)) {
63
+ throw new LocalTransactionMaterialStoreError("now must be a valid Date");
64
+ }
65
+ if (materialCreatedAtMs > nowMs) {
66
+ throw new LocalTransactionMaterialStoreError("transaction material createdAt must not be in the future");
67
+ }
68
+ if (materialExpiresAtMs <= nowMs) {
69
+ throw new LocalTransactionMaterialStoreError("transaction material must not be expired");
70
+ }
71
+ if (digestComputedAtMs < materialCreatedAtMs || digestComputedAtMs > nowMs) {
72
+ throw new LocalTransactionMaterialStoreError("transaction material digest computedAt must be between createdAt and now");
73
+ }
74
+ if (digestComputedAtMs >= materialExpiresAtMs) {
75
+ throw new LocalTransactionMaterialStoreError("transaction material digest must be computed before material expiry");
76
+ }
77
+ if (transactionMaterialDigest.materialId !== transactionMaterial.materialId ||
78
+ transactionMaterialDigest.reviewSessionId !== transactionMaterial.reviewSessionId ||
79
+ transactionMaterialDigest.planId !== transactionMaterial.planId ||
80
+ transactionMaterialDigest.account !== transactionMaterial.account ||
81
+ transactionMaterialDigest.kind !== transactionMaterial.kind ||
82
+ transactionMaterialDigest.source !== transactionMaterial.source ||
83
+ transactionMaterialDigest.expiresAt !== transactionMaterial.expiresAt) {
84
+ throw new LocalTransactionMaterialStoreError("transaction material digest must match the material handle");
85
+ }
86
+ return { transactionMaterial, transactionMaterialDigest };
87
+ }
88
+ export async function verifyLocalTransactionMaterialArtifacts(value) {
89
+ const now = value.now ?? new Date();
90
+ const parsed = parseLocalTransactionMaterialArtifacts({
91
+ transactionMaterial: value.transactionMaterial,
92
+ transactionMaterialDigest: value.transactionMaterialDigest
93
+ }, now);
94
+ const material = value.materialStore.getTransactionMaterial(parsed.transactionMaterial, now);
95
+ if (!material) {
96
+ throw new LocalTransactionMaterialStoreError("transaction material is unavailable");
97
+ }
98
+ let transactionDigest;
99
+ try {
100
+ transactionDigest = await Transaction.from(material.transactionBytes).getDigest();
101
+ }
102
+ catch {
103
+ throw new LocalTransactionMaterialStoreError("transaction material bytes cannot produce a Sui transaction digest");
104
+ }
105
+ const parsedDigest = suiTransactionDigestSchema.safeParse(transactionDigest);
106
+ if (!parsedDigest.success) {
107
+ throw new LocalTransactionMaterialStoreError("computed transaction digest is invalid");
108
+ }
109
+ if (parsedDigest.data !== parsed.transactionMaterialDigest.transactionDigest) {
110
+ throw new LocalTransactionMaterialStoreError("transaction material digest does not match stored transaction bytes");
111
+ }
112
+ return parsed;
113
+ }
114
+ export class LocalTransactionMaterialStoreError extends Error {
115
+ constructor(message) {
116
+ super(message);
117
+ }
118
+ }
119
+ export class InMemoryLocalTransactionMaterialStore {
120
+ records = new Map();
121
+ recordTransactionMaterial(input, now = new Date()) {
122
+ const account = parseSuiAddress(input.account);
123
+ if (!account) {
124
+ throw new LocalTransactionMaterialStoreError("Invalid transaction material account");
125
+ }
126
+ if (!input.reviewSessionId) {
127
+ throw new LocalTransactionMaterialStoreError("reviewSessionId is required");
128
+ }
129
+ if (!input.planId) {
130
+ throw new LocalTransactionMaterialStoreError("planId is required");
131
+ }
132
+ if (!LOCAL_TRANSACTION_MATERIAL_KINDS.includes(input.kind)) {
133
+ throw new LocalTransactionMaterialStoreError("Invalid transaction material kind");
134
+ }
135
+ if (!LOCAL_TRANSACTION_MATERIAL_SOURCES.includes(input.source)) {
136
+ throw new LocalTransactionMaterialStoreError("Invalid transaction material source");
137
+ }
138
+ if (input.transactionBytes.byteLength === 0) {
139
+ throw new LocalTransactionMaterialStoreError("transactionBytes must not be empty");
140
+ }
141
+ const createdAt = now.toISOString();
142
+ const expiresAt = input.expiresAt.toISOString();
143
+ if (Date.parse(expiresAt) <= Date.parse(createdAt)) {
144
+ throw new LocalTransactionMaterialStoreError("expiresAt must be after createdAt");
145
+ }
146
+ const handle = {
147
+ materialId: `txmat_${randomUUID()}`,
148
+ reviewSessionId: input.reviewSessionId,
149
+ planId: input.planId,
150
+ account,
151
+ kind: input.kind,
152
+ source: input.source,
153
+ createdAt,
154
+ expiresAt
155
+ };
156
+ this.records.set(handle.materialId, {
157
+ ...handle,
158
+ transactionBytes: cloneBytes(input.transactionBytes),
159
+ ...(input.redactedDiagnostics === undefined
160
+ ? {}
161
+ : { redactedDiagnostics: structuredClone(input.redactedDiagnostics) })
162
+ });
163
+ return { ...handle };
164
+ }
165
+ getTransactionMaterial(handle, now = new Date()) {
166
+ const record = this.records.get(handle.materialId);
167
+ if (!record) {
168
+ return undefined;
169
+ }
170
+ if (Date.parse(record.expiresAt) <= now.getTime()) {
171
+ this.records.delete(handle.materialId);
172
+ return undefined;
173
+ }
174
+ if (!sameHandle(record, handle)) {
175
+ return undefined;
176
+ }
177
+ return {
178
+ ...record,
179
+ transactionBytes: cloneBytes(record.transactionBytes),
180
+ ...(record.redactedDiagnostics === undefined
181
+ ? {}
182
+ : { redactedDiagnostics: structuredClone(record.redactedDiagnostics) })
183
+ };
184
+ }
185
+ deleteReviewSessionTransactionMaterials(reviewSessionId) {
186
+ for (const [materialId, record] of this.records) {
187
+ if (record.reviewSessionId === reviewSessionId) {
188
+ this.records.delete(materialId);
189
+ }
190
+ }
191
+ }
192
+ }
193
+ function sameHandle(record, handle) {
194
+ return record.materialId === handle.materialId &&
195
+ record.reviewSessionId === handle.reviewSessionId &&
196
+ record.planId === handle.planId &&
197
+ record.account === handle.account &&
198
+ record.kind === handle.kind &&
199
+ record.source === handle.source &&
200
+ record.createdAt === handle.createdAt &&
201
+ record.expiresAt === handle.expiresAt;
202
+ }
203
+ function cloneBytes(bytes) {
204
+ return new Uint8Array(bytes);
205
+ }
@@ -0,0 +1,102 @@
1
+ import { EXECUTION_POLLING_INTERVAL_SECONDS, getExecutionPollingStatus, isWaitStoppingExecutionStatus } from "./status.js";
2
+ import { SessionStoreError } from "./sessionStore.js";
3
+ import { isTerminalWalletIdentityStatus, WALLET_IDENTITY_POLLING_INTERVAL_SECONDS } from "./walletIdentity.js";
4
+ export const DEFAULT_WAIT_TIMEOUT_MS = 45_000;
5
+ export const MAX_WAIT_TIMEOUT_MS = 55_000;
6
+ export class WaitRequestAbortedError extends Error {
7
+ reason;
8
+ constructor(reason = "host_abort") {
9
+ super("Wait request aborted");
10
+ this.reason = reason;
11
+ }
12
+ }
13
+ export async function waitForWalletIdentitySession(sessions, walletSessionId, options = {}) {
14
+ const timeoutMs = options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
15
+ const sleep = options.sleep ?? sleepMs;
16
+ const startedAt = Date.now();
17
+ assertNotAborted(options.signal);
18
+ let session = await getRequiredWalletIdentitySession(sessions, walletSessionId, options.now, "missing");
19
+ if (isTerminalWalletIdentityStatus(session.status)) {
20
+ return walletWaitResult("status_reached", session);
21
+ }
22
+ while (Date.now() - startedAt < timeoutMs) {
23
+ assertNotAborted(options.signal);
24
+ const remainingMs = timeoutMs - (Date.now() - startedAt);
25
+ await sleep(Math.min(remainingMs, WALLET_IDENTITY_POLLING_INTERVAL_SECONDS * 1000), options.signal);
26
+ session = await getRequiredWalletIdentitySession(sessions, walletSessionId, options.now, "session_removed_during_wait");
27
+ if (isTerminalWalletIdentityStatus(session.status)) {
28
+ return walletWaitResult("status_reached", session);
29
+ }
30
+ }
31
+ return walletWaitResult("timed_out", session);
32
+ }
33
+ export async function waitForExecutionResult(sessions, reviewSessionId, options = {}) {
34
+ const timeoutMs = options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
35
+ const sleep = options.sleep ?? sleepMs;
36
+ const startedAt = Date.now();
37
+ assertNotAborted(options.signal);
38
+ let session = await getRequiredReviewSession(sessions, reviewSessionId, options.now, "missing");
39
+ let status = getExecutionPollingStatus(session);
40
+ if (isWaitStoppingExecutionStatus(status)) {
41
+ return { waitOutcome: "status_reached", session, status };
42
+ }
43
+ while (Date.now() - startedAt < timeoutMs) {
44
+ assertNotAborted(options.signal);
45
+ const remainingMs = timeoutMs - (Date.now() - startedAt);
46
+ await sleep(Math.min(remainingMs, EXECUTION_POLLING_INTERVAL_SECONDS * 1000), options.signal);
47
+ session = await getRequiredReviewSession(sessions, reviewSessionId, options.now, "session_removed_during_wait");
48
+ status = getExecutionPollingStatus(session);
49
+ if (isWaitStoppingExecutionStatus(status)) {
50
+ return { waitOutcome: "status_reached", session, status };
51
+ }
52
+ }
53
+ return { waitOutcome: "timed_out", session, status };
54
+ }
55
+ export function walletStatusCategory(status) {
56
+ return isTerminalWalletIdentityStatus(status) ? "terminal" : "non_terminal";
57
+ }
58
+ function walletWaitResult(waitOutcome, session) {
59
+ return {
60
+ waitOutcome,
61
+ session,
62
+ statusCategory: walletStatusCategory(session.status)
63
+ };
64
+ }
65
+ async function getRequiredWalletIdentitySession(sessions, walletSessionId, now, reason) {
66
+ const session = await sessions.getWalletIdentitySession(walletSessionId, now?.());
67
+ if (!session) {
68
+ throw new SessionStoreError("session_not_found", `Wallet identity session not found: ${walletSessionId}`, {
69
+ reason
70
+ });
71
+ }
72
+ return session;
73
+ }
74
+ async function getRequiredReviewSession(sessions, reviewSessionId, now, reason) {
75
+ const session = await sessions.getReviewSession(reviewSessionId, now?.());
76
+ if (!session) {
77
+ throw new SessionStoreError("session_not_found", `Review session not found: ${reviewSessionId}`, { reason });
78
+ }
79
+ return session;
80
+ }
81
+ function sleepMs(ms, signal) {
82
+ return new Promise((resolve, reject) => {
83
+ if (signal?.aborted) {
84
+ reject(new WaitRequestAbortedError());
85
+ return;
86
+ }
87
+ const timer = setTimeout(() => {
88
+ signal?.removeEventListener("abort", onAbort);
89
+ resolve();
90
+ }, ms);
91
+ function onAbort() {
92
+ clearTimeout(timer);
93
+ reject(new WaitRequestAbortedError());
94
+ }
95
+ signal?.addEventListener("abort", onAbort, { once: true });
96
+ });
97
+ }
98
+ function assertNotAborted(signal) {
99
+ if (signal?.aborted) {
100
+ throw new WaitRequestAbortedError();
101
+ }
102
+ }
@@ -0,0 +1,103 @@
1
+ import { z } from "zod";
2
+ import { normalizedSuiAddressSchema } from "../suiAddress.js";
3
+ export const SUI_MAINNET_WALLET_CHAIN = "sui:mainnet";
4
+ export const WALLET_IDENTITY_STATUSES = [
5
+ "pending",
6
+ "opened",
7
+ "connecting",
8
+ "connected",
9
+ "rejected",
10
+ "failed",
11
+ "expired"
12
+ ];
13
+ export const WALLET_IDENTITY_FAILURE_REASONS = [
14
+ "user_rejected",
15
+ "no_compatible_wallet",
16
+ "no_accounts_authorized",
17
+ "unsupported_chain",
18
+ "wallet_provider_error"
19
+ ];
20
+ const isoDateStringSchema = z.string().refine((value) => {
21
+ const parsed = new Date(value);
22
+ return !Number.isNaN(parsed.getTime()) && parsed.toISOString() === value;
23
+ }, "Expected ISO 8601 UTC timestamp");
24
+ const walletSessionBaseSchema = {
25
+ id: z.string(),
26
+ tokenHash: z.string(),
27
+ createdAt: isoDateStringSchema,
28
+ expiresAt: isoDateStringSchema,
29
+ lastActivityAt: isoDateStringSchema
30
+ };
31
+ const failureDetailSchema = z.string().min(1).max(500).optional();
32
+ export const walletIdentityStatusSchema = z.enum(WALLET_IDENTITY_STATUSES);
33
+ export const walletIdentityFailureReasonSchema = z.enum(WALLET_IDENTITY_FAILURE_REASONS);
34
+ export const nonUserWalletIdentityFailureReasonSchema = z.enum([
35
+ "no_compatible_wallet",
36
+ "no_accounts_authorized",
37
+ "unsupported_chain",
38
+ "wallet_provider_error"
39
+ ]);
40
+ export const walletIdentitySessionSchema = z.discriminatedUnion("status", [
41
+ z.object({ ...walletSessionBaseSchema, status: z.literal("pending") }),
42
+ z.object({ ...walletSessionBaseSchema, status: z.literal("opened") }),
43
+ z.object({ ...walletSessionBaseSchema, status: z.literal("connecting") }),
44
+ z.object({
45
+ ...walletSessionBaseSchema,
46
+ status: z.literal("connected"),
47
+ account: normalizedSuiAddressSchema,
48
+ chain: z.literal(SUI_MAINNET_WALLET_CHAIN),
49
+ walletName: z.string().min(1).optional(),
50
+ walletId: z.string().min(1).optional()
51
+ }),
52
+ z.object({
53
+ ...walletSessionBaseSchema,
54
+ status: z.literal("rejected"),
55
+ failureReason: z.literal("user_rejected"),
56
+ failureDetail: failureDetailSchema,
57
+ walletName: z.string().min(1).optional(),
58
+ walletId: z.string().min(1).optional()
59
+ }),
60
+ z.object({
61
+ ...walletSessionBaseSchema,
62
+ status: z.literal("failed"),
63
+ failureReason: nonUserWalletIdentityFailureReasonSchema,
64
+ failureDetail: failureDetailSchema,
65
+ walletName: z.string().min(1).optional(),
66
+ walletId: z.string().min(1).optional()
67
+ }),
68
+ z.object({ ...walletSessionBaseSchema, status: z.literal("expired") })
69
+ ]);
70
+ export const walletIdentityResultInputSchema = z.discriminatedUnion("status", [
71
+ z.object({
72
+ status: z.literal("connected"),
73
+ account: normalizedSuiAddressSchema,
74
+ chain: z.literal(SUI_MAINNET_WALLET_CHAIN),
75
+ walletName: z.string().min(1).optional(),
76
+ walletId: z.string().min(1).optional()
77
+ }),
78
+ z.object({
79
+ status: z.literal("rejected"),
80
+ failureReason: z.literal("user_rejected"),
81
+ failureDetail: failureDetailSchema,
82
+ walletName: z.string().min(1).optional(),
83
+ walletId: z.string().min(1).optional()
84
+ }),
85
+ z.object({
86
+ status: z.literal("failed"),
87
+ failureReason: nonUserWalletIdentityFailureReasonSchema,
88
+ failureDetail: failureDetailSchema,
89
+ walletName: z.string().min(1).optional(),
90
+ walletId: z.string().min(1).optional()
91
+ })
92
+ ]);
93
+ export function isTerminalWalletIdentityStatus(status) {
94
+ return status === "connected" || status === "rejected" || status === "failed" || status === "expired";
95
+ }
96
+ export const WALLET_IDENTITY_POLLING_INTERVAL_SECONDS = 5;
97
+ export function walletIdentityPollingHint() {
98
+ return {
99
+ nonTerminalStatuses: ["pending", "opened", "connecting"],
100
+ terminalStatuses: ["connected", "rejected", "failed", "expired"],
101
+ recommendedIntervalSeconds: WALLET_IDENTITY_POLLING_INTERVAL_SECONDS
102
+ };
103
+ }