ethagent 1.1.2 → 2.0.1

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 (268) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +124 -32
  3. package/package.json +8 -3
  4. package/src/app/FirstRun.tsx +190 -146
  5. package/src/app/FirstRunTimeline.tsx +47 -0
  6. package/src/app/input/AppInputProvider.tsx +1 -1
  7. package/src/app/keybindings/KeybindingProvider.tsx +1 -1
  8. package/src/chat/ChatBottomPane.tsx +0 -1
  9. package/src/chat/ChatInput.tsx +6 -6
  10. package/src/chat/ChatScreen.tsx +35 -15
  11. package/src/chat/ContextLimitView.tsx +4 -4
  12. package/src/chat/ContinuityEditReviewView.tsx +10 -22
  13. package/src/chat/CopyPicker.tsx +0 -1
  14. package/src/chat/MessageList.tsx +62 -45
  15. package/src/chat/PermissionPrompt.tsx +13 -9
  16. package/src/chat/PlanApprovalView.tsx +3 -3
  17. package/src/chat/ResumeView.tsx +1 -4
  18. package/src/chat/RewindView.tsx +2 -2
  19. package/src/chat/chatInputState.ts +1 -1
  20. package/src/chat/chatScreenUtils.ts +22 -11
  21. package/src/chat/chatSessionState.ts +2 -2
  22. package/src/chat/chatTurnOrchestrator.ts +16 -81
  23. package/src/chat/commands.ts +1 -1
  24. package/src/chat/textCursor.ts +1 -1
  25. package/src/chat/transcriptViewport.ts +2 -7
  26. package/src/cli/ResetConfirmView.tsx +1 -1
  27. package/src/cli/main.tsx +9 -3
  28. package/src/cli/preview.tsx +0 -5
  29. package/src/cli/updateNotice.ts +4 -2
  30. package/src/identity/continuity/editor.ts +7 -107
  31. package/src/identity/continuity/envelope.ts +1048 -40
  32. package/src/identity/continuity/history.ts +4 -4
  33. package/src/identity/continuity/localBackup.ts +249 -0
  34. package/src/identity/continuity/privateEdit/apply.ts +170 -0
  35. package/src/identity/continuity/privateEdit/diff.ts +82 -0
  36. package/src/identity/continuity/privateEdit/files.ts +23 -0
  37. package/src/identity/continuity/privateEdit/types.ts +28 -0
  38. package/src/identity/continuity/privateEdit.ts +10 -298
  39. package/src/identity/continuity/publicSkills.ts +8 -9
  40. package/src/identity/continuity/snapshots.ts +17 -6
  41. package/src/identity/continuity/storage/defaults.ts +111 -0
  42. package/src/identity/continuity/storage/files.ts +72 -0
  43. package/src/identity/continuity/storage/markdown.ts +81 -0
  44. package/src/identity/continuity/storage/paths.ts +24 -0
  45. package/src/identity/continuity/storage/scaffold.ts +124 -0
  46. package/src/identity/continuity/storage/status.ts +86 -0
  47. package/src/identity/continuity/storage/types.ts +27 -0
  48. package/src/identity/continuity/storage.ts +32 -507
  49. package/src/identity/continuity/zipWriter.ts +95 -0
  50. package/src/identity/crypto/backupEnvelope.ts +14 -247
  51. package/src/identity/crypto/eth.ts +7 -7
  52. package/src/identity/ens/agentRecords.ts +96 -0
  53. package/src/identity/ens/ensAutomation/contracts.ts +38 -0
  54. package/src/identity/ens/ensAutomation/delete.ts +80 -0
  55. package/src/identity/ens/ensAutomation/names.ts +14 -0
  56. package/src/identity/ens/ensAutomation/operators.ts +29 -0
  57. package/src/identity/ens/ensAutomation/read.ts +114 -0
  58. package/src/identity/ens/ensAutomation/root.ts +63 -0
  59. package/src/identity/ens/ensAutomation/setup.ts +284 -0
  60. package/src/identity/ens/ensAutomation/transactions.ts +107 -0
  61. package/src/identity/ens/ensAutomation/types.ts +126 -0
  62. package/src/identity/ens/ensAutomation.ts +29 -0
  63. package/src/identity/ens/ensLookup/client.ts +43 -0
  64. package/src/identity/ens/ensLookup/constants.ts +26 -0
  65. package/src/identity/ens/ensLookup/discovery.ts +70 -0
  66. package/src/identity/ens/ensLookup/names.ts +34 -0
  67. package/src/identity/ens/ensLookup/records.ts +45 -0
  68. package/src/identity/ens/ensLookup/resolve.ts +75 -0
  69. package/src/identity/ens/ensLookup/tokenReference.ts +17 -0
  70. package/src/identity/ens/ensLookup/types.ts +38 -0
  71. package/src/identity/ens/ensLookup/validation.ts +72 -0
  72. package/src/identity/ens/ensLookup.ts +19 -0
  73. package/src/identity/ens/ensRegistration.ts +199 -0
  74. package/src/identity/ens/resolverDelegation.ts +48 -0
  75. package/src/identity/hub/IdentityHub.tsx +13 -817
  76. package/src/identity/hub/OperationalRoutes.tsx +370 -0
  77. package/src/identity/hub/Routes.tsx +361 -0
  78. package/src/identity/hub/advancedEnsValidation.ts +45 -0
  79. package/src/identity/hub/{screens → components}/DetailsScreen.tsx +14 -8
  80. package/src/identity/hub/{screens → components}/ErrorScreen.tsx +15 -5
  81. package/src/identity/hub/components/FlowTimeline.tsx +27 -0
  82. package/src/identity/hub/components/IdentitySummary.tsx +190 -0
  83. package/src/identity/hub/components/MenuScreen.tsx +237 -0
  84. package/src/identity/hub/{screens → components}/NetworkScreen.tsx +3 -3
  85. package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx} +21 -18
  86. package/src/identity/hub/components/UnlinkedIdentityScreen.tsx +76 -0
  87. package/src/identity/hub/{screens → components}/WalletApprovalScreen.tsx +9 -8
  88. package/src/identity/hub/components/menuFlagsFromReconciliation.ts +68 -0
  89. package/src/identity/hub/effects/create.ts +310 -0
  90. package/src/identity/hub/effects/ens/flows.ts +218 -0
  91. package/src/identity/hub/effects/ens/index.ts +11 -0
  92. package/src/identity/hub/effects/ens/transactions.ts +239 -0
  93. package/src/identity/hub/effects/index.ts +74 -0
  94. package/src/identity/hub/effects/profile/profileState.ts +173 -0
  95. package/src/identity/hub/effects/publicProfile/index.ts +5 -0
  96. package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +646 -0
  97. package/src/identity/hub/effects/rebackup/index.ts +7 -0
  98. package/src/identity/hub/effects/rebackup/operatorVault.ts +378 -0
  99. package/src/identity/hub/effects/rebackup/runRebackup.ts +451 -0
  100. package/src/identity/hub/effects/receipts.ts +46 -0
  101. package/src/identity/hub/effects/restore/apply.ts +112 -0
  102. package/src/identity/hub/effects/restore/auth.ts +159 -0
  103. package/src/identity/hub/effects/restore/discover.ts +86 -0
  104. package/src/identity/hub/effects/restore/envelopes.ts +21 -0
  105. package/src/identity/hub/effects/restore/fetch.ts +25 -0
  106. package/src/identity/hub/effects/restore/index.ts +22 -0
  107. package/src/identity/hub/effects/restore/recovery.ts +135 -0
  108. package/src/identity/hub/effects/restore/resolve.ts +102 -0
  109. package/src/identity/hub/effects/restore/restoreEffects.ts +22 -0
  110. package/src/identity/hub/effects/restore/shared.ts +91 -0
  111. package/src/identity/hub/effects/restoreAdmin.ts +93 -0
  112. package/src/identity/hub/effects/shared/profilePrep.ts +139 -0
  113. package/src/identity/hub/effects/shared/snapshot.ts +336 -0
  114. package/src/identity/hub/effects/shared/sync.ts +190 -0
  115. package/src/identity/hub/effects/token-transfer/index.ts +6 -0
  116. package/src/identity/hub/effects/token-transfer/progress.ts +59 -0
  117. package/src/identity/hub/effects/token-transfer/runTokenTransfer.ts +299 -0
  118. package/src/identity/hub/effects/types.ts +53 -0
  119. package/src/identity/hub/effects/vault/preflight.ts +50 -0
  120. package/src/identity/hub/flows/continuity/ContinuityDashboardScreen.tsx +170 -0
  121. package/src/identity/hub/flows/continuity/RebackupStorageScreen.tsx +28 -0
  122. package/src/identity/hub/{screens → flows/continuity}/RecoveryConfirmScreen.tsx +28 -19
  123. package/src/identity/hub/flows/continuity/SavePromptScreen.tsx +49 -0
  124. package/src/identity/hub/{screens → flows/create}/CreateFlow.tsx +61 -62
  125. package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +347 -0
  126. package/src/identity/hub/flows/custody/custodyEffects.ts +321 -0
  127. package/src/identity/hub/flows/custody/custodyFlowActions.ts +236 -0
  128. package/src/identity/hub/flows/custody/custodyFlowEffects.ts +163 -0
  129. package/src/identity/hub/flows/custody/custodyFlowHelpers.ts +25 -0
  130. package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +239 -0
  131. package/src/identity/hub/flows/custody/custodyFlowTypes.ts +45 -0
  132. package/src/identity/hub/flows/custody/useCustodyFlow.tsx +25 -0
  133. package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +336 -0
  134. package/src/identity/hub/flows/ens/EnsEditFlow.tsx +397 -0
  135. package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +332 -0
  136. package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +471 -0
  137. package/src/identity/hub/flows/ens/EnsEditRunners.tsx +198 -0
  138. package/src/identity/hub/flows/ens/EnsEditShared.tsx +162 -0
  139. package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +518 -0
  140. package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +299 -0
  141. package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +398 -0
  142. package/src/identity/hub/flows/ens/ensEditCopy.ts +117 -0
  143. package/src/identity/hub/flows/ens/ensEditTypes.ts +91 -0
  144. package/src/identity/hub/flows/profile/EditProfileFlow.tsx +271 -0
  145. package/src/identity/hub/flows/restore/RestoreFlow.tsx +324 -0
  146. package/src/identity/hub/flows/restore/useRestoreFlowEffects.ts +77 -0
  147. package/src/identity/hub/{screens → flows/settings}/StorageCredentialScreen.tsx +23 -44
  148. package/src/identity/hub/flows/token-transfer/IdentityHubTokenTransferFlow.tsx +162 -0
  149. package/src/identity/hub/flows/token-transfer/TokenTransferScreens.tsx +256 -0
  150. package/src/identity/hub/identityHubReducer.ts +164 -99
  151. package/src/identity/hub/model/continuity.ts +94 -0
  152. package/src/identity/hub/model/copy.ts +35 -0
  153. package/src/identity/hub/model/custody.ts +54 -0
  154. package/src/identity/hub/model/ens.ts +49 -0
  155. package/src/identity/hub/model/errors.ts +140 -0
  156. package/src/identity/hub/model/format.ts +15 -0
  157. package/src/identity/hub/model/identity.ts +94 -0
  158. package/src/identity/hub/model/network.ts +32 -0
  159. package/src/identity/hub/model/transfer.ts +57 -0
  160. package/src/identity/hub/operatorWallets.ts +131 -0
  161. package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +46 -0
  162. package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +129 -0
  163. package/src/identity/hub/reconciliation/agentReconciliation/run.ts +302 -0
  164. package/src/identity/hub/reconciliation/agentReconciliation/types.ts +17 -0
  165. package/src/identity/hub/reconciliation/index.ts +21 -0
  166. package/src/identity/hub/reconciliation/useAgentReconciliation.ts +10 -0
  167. package/src/identity/hub/reconciliation/walletSetup.ts +220 -0
  168. package/src/identity/hub/txGuard.ts +51 -0
  169. package/src/identity/hub/types.ts +17 -0
  170. package/src/identity/hub/useIdentityHubContinuity.ts +136 -0
  171. package/src/identity/hub/useIdentityHubController.ts +396 -0
  172. package/src/identity/hub/useIdentityHubSideEffects.ts +309 -0
  173. package/src/identity/hub/utils.ts +79 -0
  174. package/src/identity/identityCompat.ts +34 -0
  175. package/src/identity/profile/agentIcon.ts +61 -0
  176. package/src/identity/profile/imagePicker.ts +12 -12
  177. package/src/identity/registry/erc8004/abi.ts +14 -0
  178. package/src/identity/registry/erc8004/chains.ts +150 -0
  179. package/src/identity/registry/erc8004/client.ts +11 -0
  180. package/src/identity/registry/erc8004/discovery.ts +511 -0
  181. package/src/identity/registry/erc8004/metadata.ts +335 -0
  182. package/src/identity/registry/erc8004/ownership.ts +121 -0
  183. package/src/identity/registry/erc8004/preflight.ts +123 -0
  184. package/src/identity/registry/erc8004/transactions.ts +77 -0
  185. package/src/identity/registry/erc8004/types.ts +88 -0
  186. package/src/identity/registry/erc8004/uri.ts +59 -0
  187. package/src/identity/registry/erc8004/utils.ts +58 -0
  188. package/src/identity/registry/erc8004.ts +53 -1106
  189. package/src/identity/registry/fieldParsers.ts +28 -0
  190. package/src/identity/registry/operatorVault/bytecode.ts +98 -0
  191. package/src/identity/registry/operatorVault/constants.ts +38 -0
  192. package/src/identity/registry/operatorVault/read.ts +246 -0
  193. package/src/identity/registry/operatorVault/transactions.ts +81 -0
  194. package/src/identity/registry/operatorVault.ts +44 -0
  195. package/src/identity/storage/ipfs.ts +26 -24
  196. package/src/identity/wallet/browserWallet/gas.ts +41 -0
  197. package/src/identity/wallet/browserWallet/html.ts +106 -0
  198. package/src/identity/wallet/browserWallet/http.ts +28 -0
  199. package/src/identity/wallet/browserWallet/requestServer.ts +106 -0
  200. package/src/identity/wallet/browserWallet/requests.ts +191 -0
  201. package/src/identity/wallet/browserWallet/session.ts +325 -0
  202. package/src/identity/wallet/browserWallet/types.ts +192 -0
  203. package/src/identity/wallet/browserWallet/validation.ts +74 -0
  204. package/src/identity/wallet/browserWallet.ts +30 -393
  205. package/src/identity/wallet/page/constants.ts +5 -0
  206. package/src/identity/wallet/page/controller.ts +251 -0
  207. package/src/identity/wallet/page/copy.ts +340 -0
  208. package/src/identity/wallet/page/grainient.ts +278 -0
  209. package/src/identity/wallet/page/html.ts +28 -0
  210. package/src/identity/wallet/page/markup.ts +50 -0
  211. package/src/identity/wallet/page/state.ts +9 -0
  212. package/src/identity/wallet/page/styles/base.ts +259 -0
  213. package/src/identity/wallet/page/styles/components.ts +262 -0
  214. package/src/identity/wallet/page/styles/index.ts +5 -0
  215. package/src/identity/wallet/page/styles/responsive.ts +247 -0
  216. package/src/identity/wallet/page/types.ts +47 -0
  217. package/src/identity/wallet/page/view.ts +535 -0
  218. package/src/identity/wallet/page/walletProvider.ts +70 -0
  219. package/src/identity/wallet/page.tsx +38 -0
  220. package/src/identity/wallet/walletPurposeCompat.ts +27 -0
  221. package/src/mcp/manager.ts +0 -1
  222. package/src/models/ModelPicker.tsx +36 -30
  223. package/src/models/catalog.ts +5 -2
  224. package/src/models/huggingface.ts +9 -9
  225. package/src/models/llamacpp.ts +13 -13
  226. package/src/models/modelDisplay.ts +75 -0
  227. package/src/models/modelPickerOptions.ts +16 -3
  228. package/src/models/modelRecommendation.ts +0 -1
  229. package/src/providers/errors.ts +16 -0
  230. package/src/providers/gemini.ts +252 -39
  231. package/src/providers/registry.ts +2 -2
  232. package/src/providers/retry.ts +1 -1
  233. package/src/runtime/sessionMode.ts +1 -1
  234. package/src/runtime/systemPrompt.ts +2 -0
  235. package/src/runtime/toolExecution.ts +18 -22
  236. package/src/runtime/toolIntent.ts +0 -20
  237. package/src/runtime/turn.ts +0 -92
  238. package/src/storage/atomicWrite.ts +4 -1
  239. package/src/storage/config.ts +181 -5
  240. package/src/storage/identity.ts +9 -3
  241. package/src/storage/secrets.ts +2 -2
  242. package/src/tools/bashSafety.ts +8 -0
  243. package/src/tools/changeDirectoryTool.ts +1 -1
  244. package/src/tools/deleteFileTool.ts +4 -4
  245. package/src/tools/editTool.ts +4 -4
  246. package/src/tools/editUtils.ts +5 -5
  247. package/src/tools/privateContinuityEditTool.ts +4 -5
  248. package/src/tools/privateContinuityReadTool.ts +1 -2
  249. package/src/tools/registry.ts +30 -0
  250. package/src/tools/writeFileTool.ts +5 -5
  251. package/src/ui/BrandSplash.tsx +20 -85
  252. package/src/ui/ProgressBar.tsx +3 -5
  253. package/src/ui/Select.tsx +20 -8
  254. package/src/ui/Spinner.tsx +38 -3
  255. package/src/ui/Surface.tsx +2 -2
  256. package/src/ui/TextInput.tsx +63 -20
  257. package/src/ui/theme.ts +7 -34
  258. package/src/utils/openExternal.ts +21 -0
  259. package/src/utils/withRetry.ts +47 -3
  260. package/src/identity/hub/identityHubEffects.ts +0 -937
  261. package/src/identity/hub/identityHubModel.ts +0 -371
  262. package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -156
  263. package/src/identity/hub/screens/EditProfileFlow.tsx +0 -146
  264. package/src/identity/hub/screens/IdentitySummary.tsx +0 -106
  265. package/src/identity/hub/screens/MenuScreen.tsx +0 -117
  266. package/src/identity/hub/screens/RestoreFlow.tsx +0 -206
  267. package/src/identity/wallet/wallet-page/wallet.html +0 -1202
  268. /package/src/identity/hub/{screens → components}/BusyScreen.tsx +0 -0
@@ -0,0 +1,239 @@
1
+ import type { Address, Hex, PublicClient } from 'viem'
2
+ import {
3
+ DEFAULT_ETHEREUM_RPC_URL,
4
+ RegisterAgentPreflightError,
5
+ createErc8004PublicClient,
6
+ supportedErc8004ChainForId,
7
+ } from '../../../registry/erc8004.js'
8
+ import { encodeSetEthagentTextRecords } from '../../../ens/ensLookup.js'
9
+ import { encodeEnsRecordsTransaction, encodeEnsRegistryTransaction, type EnsSetupPlan } from '../../../ens/ensAutomation.js'
10
+ import type { AgentEnsRecordState, AgentEnsRecords } from '../../../ens/agentRecords.js'
11
+ import { changedRecords } from '../../../ens/agentRecords.js'
12
+ import { sendBrowserWalletTransaction, type BrowserWalletSession, type WalletPurpose } from '../../../wallet/browserWallet.js'
13
+ import type { EffectCallbacks } from '../types.js'
14
+ function chainLabel(chainId: number): string {
15
+ return supportedErc8004ChainForId(chainId)?.name ?? `chain ${chainId}`
16
+ }
17
+
18
+ function ensTokenChainName(chainId: number): string | undefined {
19
+ return chainId !== 1 ? chainLabel(chainId) : undefined
20
+ }
21
+
22
+ export async function runUpdateEnsRecords(args: {
23
+ fullName: string
24
+ ownerAddress: Address
25
+ records: AgentEnsRecords
26
+ currentRecords?: AgentEnsRecordState
27
+ callbacks: EffectCallbacks
28
+ purpose?: WalletPurpose
29
+ clearRecords?: boolean
30
+ publicClient?: PublicClient
31
+ tokenChainId?: number
32
+ session?: BrowserWalletSession
33
+ flowId?: string
34
+ flowStep?: number
35
+ }): Promise<{ txHash: string }> {
36
+ const next = ensRecordWritesForUpdate({
37
+ records: args.records,
38
+ currentRecords: args.currentRecords,
39
+ clearRecords: args.clearRecords,
40
+ })
41
+ if (Object.keys(next).length === 0) {
42
+ throw new Error('No ENS records to update')
43
+ }
44
+ const publicClient = args.publicClient ?? createMainnetEnsPublicClient()
45
+ const encoded = await encodeSetEthagentTextRecords(args.fullName, next, { publicClient })
46
+ await preflightEnsRecordTransaction({
47
+ fullName: args.fullName,
48
+ account: args.ownerAddress,
49
+ to: encoded.resolverAddress,
50
+ data: encoded.data,
51
+ publicClient,
52
+ purpose: args.purpose ?? 'update-ens-records',
53
+ })
54
+ const tokenChainName = typeof args.tokenChainId === 'number' ? ensTokenChainName(args.tokenChainId) : undefined
55
+ const purpose = args.purpose ?? 'update-ens-records'
56
+ if (args.session) {
57
+ const result = await args.session.sendTransaction({
58
+ chainId: 1,
59
+ expectedAccount: args.ownerAddress,
60
+ to: encoded.resolverAddress,
61
+ data: encoded.data,
62
+ purpose,
63
+ ...(tokenChainName ? { tokenChainName } : {}),
64
+ ...(args.flowId ? { flowId: args.flowId } : {}),
65
+ })
66
+ return { txHash: result.txHash }
67
+ }
68
+ const result = await sendBrowserWalletTransaction({
69
+ chainId: 1,
70
+ expectedAccount: args.ownerAddress,
71
+ to: encoded.resolverAddress,
72
+ data: encoded.data,
73
+ purpose,
74
+ onReady: args.callbacks.onWalletReady,
75
+ ...(tokenChainName ? { tokenChainName } : {}),
76
+ })
77
+ args.callbacks.onWalletReady(null)
78
+ return { txHash: result.txHash }
79
+ }
80
+
81
+ export function ensRecordWritesForUpdate(args: {
82
+ records: AgentEnsRecords
83
+ currentRecords?: AgentEnsRecordState
84
+ clearRecords?: boolean
85
+ }): Record<string, string> {
86
+ if (args.clearRecords) {
87
+ return changedRecords(args.currentRecords ?? {}, { token: '', profile: '' })
88
+ }
89
+ return changedRecords(args.currentRecords ?? { token: '', profile: '' }, args.records)
90
+ }
91
+
92
+ export async function runEnsSetupRegistryTransaction(args: {
93
+ setup: EnsSetupPlan
94
+ callbacks: EffectCallbacks
95
+ publicClient?: PublicClient
96
+ tokenChainId?: number
97
+ session?: BrowserWalletSession
98
+ flowId?: string
99
+ flowStep?: number
100
+ }): Promise<{ txHash: string } | null> {
101
+ const encoded = encodeEnsRegistryTransaction(args.setup)
102
+ if (!encoded) return null
103
+ const publicClient = args.publicClient ?? createMainnetEnsPublicClient()
104
+ const purpose: WalletPurpose = args.setup.mode === 'simple' ? 'create-simple-ens-subdomain' : 'create-agent-ens-subdomain'
105
+ await preflightEnsRecordTransaction({
106
+ fullName: args.setup.fullName,
107
+ account: args.setup.ownerAddress,
108
+ to: encoded.to,
109
+ data: encoded.data,
110
+ publicClient,
111
+ purpose,
112
+ })
113
+ const tokenChainName = typeof args.tokenChainId === 'number' ? ensTokenChainName(args.tokenChainId) : undefined
114
+ if (args.session) {
115
+ const result = await args.session.sendTransaction({
116
+ chainId: 1,
117
+ expectedAccount: args.setup.ownerAddress,
118
+ to: encoded.to,
119
+ data: encoded.data,
120
+ purpose,
121
+ ...(tokenChainName ? { tokenChainName } : {}),
122
+ ...(args.flowId ? { flowId: args.flowId } : {}),
123
+ ...(typeof args.flowStep === 'number' ? { flowStep: args.flowStep } : {}),
124
+ })
125
+ await publicClient.waitForTransactionReceipt({ hash: result.txHash })
126
+ return { txHash: result.txHash }
127
+ }
128
+ const result = await sendBrowserWalletTransaction({
129
+ chainId: 1,
130
+ expectedAccount: args.setup.ownerAddress,
131
+ to: encoded.to,
132
+ data: encoded.data,
133
+ purpose,
134
+ onReady: args.callbacks.onWalletReady,
135
+ ...(tokenChainName ? { tokenChainName } : {}),
136
+ })
137
+ args.callbacks.onWalletReady(null)
138
+ await publicClient.waitForTransactionReceipt({ hash: result.txHash })
139
+ return { txHash: result.txHash }
140
+ }
141
+
142
+ export async function runEnsSetupRecordsTransaction(args: {
143
+ setup: EnsSetupPlan
144
+ callbacks: EffectCallbacks
145
+ publicClient?: PublicClient
146
+ tokenChainId?: number
147
+ session?: BrowserWalletSession
148
+ flowId?: string
149
+ flowStep?: number
150
+ }): Promise<{ txHash: string } | null> {
151
+ const encoded = encodeEnsRecordsTransaction(args.setup)
152
+ if (!encoded) return null
153
+ const publicClient = args.publicClient ?? createMainnetEnsPublicClient()
154
+ const purpose: WalletPurpose = args.setup.mode === 'simple' ? 'set-simple-ens-records' : 'set-agent-ens-records'
155
+ await preflightEnsRecordTransaction({
156
+ fullName: args.setup.fullName,
157
+ account: args.setup.ownerAddress,
158
+ to: encoded.to,
159
+ data: encoded.data,
160
+ publicClient,
161
+ purpose,
162
+ })
163
+ const tokenChainName = typeof args.tokenChainId === 'number' ? ensTokenChainName(args.tokenChainId) : undefined
164
+ if (args.session) {
165
+ const result = await args.session.sendTransaction({
166
+ chainId: 1,
167
+ expectedAccount: args.setup.ownerAddress,
168
+ to: encoded.to,
169
+ data: encoded.data,
170
+ purpose,
171
+ ...(tokenChainName ? { tokenChainName } : {}),
172
+ ...(args.flowId ? { flowId: args.flowId } : {}),
173
+ ...(typeof args.flowStep === 'number' ? { flowStep: args.flowStep } : {}),
174
+ })
175
+ return { txHash: result.txHash }
176
+ }
177
+ const result = await sendBrowserWalletTransaction({
178
+ chainId: 1,
179
+ expectedAccount: args.setup.ownerAddress,
180
+ to: encoded.to,
181
+ data: encoded.data,
182
+ purpose,
183
+ onReady: args.callbacks.onWalletReady,
184
+ ...(tokenChainName ? { tokenChainName } : {}),
185
+ })
186
+ args.callbacks.onWalletReady(null)
187
+ return { txHash: result.txHash }
188
+ }
189
+
190
+ export function createMainnetEnsPublicClient(): PublicClient {
191
+ return createErc8004PublicClient({
192
+ chainId: 1,
193
+ rpcUrl: DEFAULT_ETHEREUM_RPC_URL,
194
+ })
195
+ }
196
+
197
+ async function preflightEnsRecordTransaction(args: {
198
+ fullName: string
199
+ account: Address
200
+ to: Address
201
+ data: Hex
202
+ publicClient: Pick<PublicClient, 'estimateGas'>
203
+ purpose: WalletPurpose
204
+ }): Promise<void> {
205
+ try {
206
+ await args.publicClient.estimateGas({
207
+ account: args.account,
208
+ to: args.to,
209
+ data: args.data,
210
+ })
211
+ } catch (err: unknown) {
212
+ throw new RegisterAgentPreflightError({
213
+ code: 'simulation-failed',
214
+ title: ensPreflightErrorTitle(args.purpose),
215
+ detail: cleanPreflightError(err),
216
+ hint: ensPreflightErrorHint(args),
217
+ })
218
+ }
219
+ }
220
+
221
+ function ensPreflightErrorTitle(_purpose: WalletPurpose): string {
222
+ return 'ENS Record Update Blocked'
223
+ }
224
+
225
+ function ensPreflightErrorHint(args: {
226
+ fullName: string
227
+ purpose: WalletPurpose
228
+ }): string {
229
+ const wallet = args.purpose === 'set-simple-ens-records' || args.purpose === 'update-ens-records' || args.purpose === 'clear-ens-records'
230
+ ? 'ENS owner wallet'
231
+ : 'owner wallet'
232
+ return `No transaction was sent. Connect the ${wallet} and confirm it can write ENS resolver records on ${args.fullName}.`
233
+ }
234
+
235
+ function cleanPreflightError(err: unknown): string {
236
+ return (err instanceof Error ? err.message : String(err))
237
+ .replace(/\s+/g, ' ')
238
+ .slice(0, 220)
239
+ }
@@ -0,0 +1,74 @@
1
+ export type {
2
+ CustodySwitchAdvancedProgress,
3
+ EffectCallbacks,
4
+ EnsClearProgress,
5
+ EnsLinkProgress,
6
+ EnsUpdateProgress,
7
+ IdentityCompletionSource,
8
+ RestoreProgress,
9
+ TokenTransferProgress,
10
+ } from './types.js'
11
+ export { awaitConfirmedReceipt } from './receipts.js'
12
+ export {
13
+ ensRecordWritesForUpdate,
14
+ runEnsLinkFlow,
15
+ runEnsSetupRecordsTransaction,
16
+ runEnsSetupRegistryTransaction,
17
+ runEnsUnlinkFlow,
18
+ runEnsUpdateFlow,
19
+ runUpdateEnsRecords,
20
+ } from './ens/index.js'
21
+ export {
22
+ applyEnsValidationState,
23
+ applyOperatorProfileState,
24
+ } from './profile/profileState.js'
25
+ export {
26
+ canRestoreCandidate,
27
+ resolveAgentEnsToCandidate,
28
+ resolveAgentTokenIdToCandidate,
29
+ restoreSignatureRequestForStep,
30
+ restoreTokenSelectionStep,
31
+ runRecoveryRefetch,
32
+ runRestoreAuthorize,
33
+ runRestoreConnectWallet,
34
+ runRestoreDiscover,
35
+ runRestoreFetch,
36
+ } from './restore/restoreEffects.js'
37
+ export {
38
+ assertTokenNotInVault,
39
+ OperatorVaultUnavailableError,
40
+ TokenInVaultError,
41
+ } from './vault/preflight.js'
42
+ export {
43
+ createContinuityEnvelopeForSave,
44
+ expectedAccountForSnapshotSave,
45
+ snapshotSaveRequiresOwnerSigner,
46
+ snapshotSaveWalletRole,
47
+ } from './shared/snapshot.js'
48
+ export {
49
+ runCreatePreflight,
50
+ runCreateSigning,
51
+ runRegistrySubmit,
52
+ runStorageSubmit,
53
+ } from './create.js'
54
+ export {
55
+ runFixRecordsSubmit,
56
+ runRestoreRegistrySubmit,
57
+ } from './restoreAdmin.js'
58
+ export {
59
+ runPublicProfilePreflight,
60
+ runPublicProfileSigning,
61
+ runPublicProfileStorageSubmit,
62
+ } from './publicProfile/runPublicProfileSave.js'
63
+ export {
64
+ runRebackupPreflight,
65
+ runRebackupSigning,
66
+ runRebackupSigningInSession,
67
+ runRebackupStorageSubmit,
68
+ } from './rebackup/runRebackup.js'
69
+ export {
70
+ tokenTransferProgressForPhase,
71
+ runTokenTransferSigning,
72
+ runTokenTransferStorageSubmit,
73
+ runTokenTransferTargetSubmit,
74
+ } from './token-transfer/runTokenTransfer.js'
@@ -0,0 +1,173 @@
1
+ import { getAddress, isAddress, type Address } from 'viem'
2
+ import type { EthagentIdentity } from '../../../../storage/config.js'
3
+ import type { Erc8004RegistryConfig } from '../../../registry/erc8004.js'
4
+ import { validateAgentEnsLink, type EnsValidation } from '../../../ens/ensLookup.js'
5
+ import { clearOwnerAddressField, clearOperatorVaultAddressField, readOwnerAddressField, setOperatorVaultAddressField, setOwnerAddressField } from '../../../identityCompat.js'
6
+ import { validateAdvancedEnsRelationship } from '../../advancedEnsValidation.js'
7
+ import { readCustodyMode } from '../../model/custody.js'
8
+ import { ensValidationReasonText } from '../../model/ens.js'
9
+ import type { ProfileUpdates } from '../../identityHubReducer.js'
10
+ import {
11
+ assertActiveOperatorIsApproved,
12
+ mergeApprovedOperatorWallets,
13
+ normalizeApprovedOperatorWallets,
14
+ upsertApprovedOperatorWallet,
15
+ } from '../../operatorWallets.js'
16
+ function ensValidationToState(validation: EnsValidation): Record<string, unknown> {
17
+ const checkedAt = new Date().toISOString()
18
+ if (validation.ok) {
19
+ return { ok: true, resolvedAddress: validation.resolvedAddress, checkedAt }
20
+ }
21
+ return {
22
+ ok: false,
23
+ reason: validation.reason,
24
+ ...(validation.detail ? { detail: validation.detail } : {}),
25
+ checkedAt,
26
+ }
27
+ }
28
+
29
+ export async function validateEnsForProfileUpdate(
30
+ fullName: string,
31
+ walletAccount: Address,
32
+ profile: ProfileUpdates,
33
+ baseState: Record<string, unknown>,
34
+ identity: EthagentIdentity,
35
+ registry: Erc8004RegistryConfig,
36
+ ): Promise<EnsValidation> {
37
+ const mode = readCustodyModeForUpdate(profile, baseState)
38
+ if (mode === 'advanced') {
39
+ const ownerAddress = readOwnerAddressForUpdate(profile, baseState)
40
+ if (!ownerAddress) {
41
+ return { ok: false, reason: 'lookup-failed', detail: 'no owner address recorded for advanced custody' }
42
+ }
43
+ const validation = await validateAdvancedEnsRelationship({
44
+ fullName,
45
+ ownerAddress: getAddress(ownerAddress),
46
+ registry,
47
+ agentId: identity.agentId,
48
+ })
49
+ return validation
50
+ }
51
+ return validateAgentEnsLink(fullName, walletAccount)
52
+ }
53
+
54
+ export function applyEnsValidationState(
55
+ state: Record<string, unknown>,
56
+ validation: EnsValidation,
57
+ profile: ProfileUpdates,
58
+ baseState: Record<string, unknown>,
59
+ ): void {
60
+ state.ensValidation = ensValidationToState(validation)
61
+ if (!validation.ok) {
62
+ throw new Error(`${ensValidationReasonText(validation.reason)}${validation.detail ? `: ${validation.detail}` : ''}`)
63
+ }
64
+ const mode = readCustodyModeForUpdate(profile, baseState)
65
+ state.custodyMode = mode
66
+ delete state.ensMode
67
+ if (mode === 'advanced') {
68
+ const ownerAddressValue = readOwnerAddressForUpdate(profile, baseState)
69
+ const operatorWallet = readActiveOperatorForUpdate(profile, baseState)
70
+ if (!ownerAddressValue || !operatorWallet) {
71
+ throw new Error('Advanced custody requires owner wallet and operator wallet addresses')
72
+ }
73
+ const ownerAddress = getAddress(ownerAddressValue)
74
+ const existingOperators = mergeApprovedOperatorWallets(baseState.approvedOperatorWallets, profile.approvedOperatorWallets ?? [], { walletAddress: ownerAddress })
75
+ const approvedOperatorWallets = upsertApprovedOperatorWallet(existingOperators, getAddress(operatorWallet), { walletAddress: ownerAddress })
76
+ setOwnerAddressField(state, getAddress(ownerAddress))
77
+ state.approvedOperatorWallets = approvedOperatorWallets
78
+ state.activeOperatorAddress = getAddress(operatorWallet)
79
+ return
80
+ }
81
+ clearOwnerAddressField(state)
82
+ delete state.approvedOperatorWallets
83
+ delete state.activeOperatorAddress
84
+ }
85
+
86
+ export function applyOperatorProfileState(
87
+ state: Record<string, unknown>,
88
+ profile: ProfileUpdates,
89
+ baseState: Record<string, unknown>,
90
+ ): void {
91
+ const operatorFieldsTouched = profile.custodyMode !== undefined
92
+ || profile.ownerAddress !== undefined
93
+ || profile.approvedOperatorWallets !== undefined
94
+ || profile.activeOperatorAddress !== undefined
95
+ || profile.operatorVaultAddress !== undefined
96
+ || profile.restoreAccessEpoch !== undefined
97
+ if (!operatorFieldsTouched) return
98
+
99
+ if (profile.custodyMode === 'simple') {
100
+ state.custodyMode = 'simple'
101
+ delete state.ensMode
102
+ clearOwnerAddressField(state)
103
+ delete state.approvedOperatorWallets
104
+ delete state.activeOperatorAddress
105
+ if (profile.restoreAccessEpoch !== undefined) {
106
+ state.restoreAccessEpoch = profile.restoreAccessEpoch
107
+ }
108
+ return
109
+ }
110
+ if (profile.custodyMode === 'advanced') {
111
+ state.custodyMode = 'advanced'
112
+ delete state.ensMode
113
+ }
114
+
115
+ if (profile.operatorVaultAddress !== undefined) {
116
+ if (typeof profile.operatorVaultAddress === 'string' && profile.operatorVaultAddress.trim()) {
117
+ setOperatorVaultAddressField(state, getAddress(profile.operatorVaultAddress))
118
+ } else {
119
+ clearOperatorVaultAddressField(state)
120
+ }
121
+ }
122
+
123
+ if (typeof profile.ownerAddress === 'string' && profile.ownerAddress.trim()) {
124
+ setOwnerAddressField(state, getAddress(profile.ownerAddress))
125
+ }
126
+
127
+ const approvedOperatorWallets = profile.approvedOperatorWallets !== undefined
128
+ ? normalizeApprovedOperatorWallets(profile.approvedOperatorWallets)
129
+ : normalizeApprovedOperatorWallets(baseState.approvedOperatorWallets)
130
+ if (profile.approvedOperatorWallets !== undefined) {
131
+ const ownerForCheck = readOwnerAddressField(state) ?? readOwnerAddressField(baseState) ?? ''
132
+ if (ownerForCheck && isAddress(ownerForCheck, { strict: false })) {
133
+ const ownerLower = ownerForCheck.toLowerCase()
134
+ if (approvedOperatorWallets.some(record => record.address.toLowerCase() === ownerLower)) {
135
+ throw new Error('Operator wallet must be different from the owner wallet')
136
+ }
137
+ }
138
+ state.approvedOperatorWallets = approvedOperatorWallets
139
+ }
140
+
141
+ const active = profile.activeOperatorAddress !== undefined
142
+ ? assertActiveOperatorIsApproved(approvedOperatorWallets, profile.activeOperatorAddress)
143
+ : assertActiveOperatorIsApproved(approvedOperatorWallets, readActiveOperatorForUpdate({}, baseState))
144
+ if (active) {
145
+ state.activeOperatorAddress = active
146
+ } else if (profile.activeOperatorAddress !== undefined) {
147
+ delete state.activeOperatorAddress
148
+ }
149
+ if (profile.restoreAccessEpoch !== undefined) {
150
+ state.restoreAccessEpoch = profile.restoreAccessEpoch
151
+ }
152
+ }
153
+
154
+ function readCustodyModeForUpdate(profile: ProfileUpdates, baseState: Record<string, unknown>): 'simple' | 'advanced' {
155
+ if (profile.custodyMode === 'simple' || profile.custodyMode === 'advanced') return profile.custodyMode
156
+ return readCustodyMode(baseState) ?? 'simple'
157
+ }
158
+
159
+ function readOwnerAddressForUpdate(profile: ProfileUpdates, baseState: Record<string, unknown>): string | undefined {
160
+ if (typeof profile.ownerAddress === 'string' && profile.ownerAddress.trim()) return profile.ownerAddress.trim()
161
+ return readOwnerAddressField(baseState)
162
+ }
163
+
164
+ function readActiveOperatorForUpdate(profile: ProfileUpdates, baseState: Record<string, unknown>): string | undefined {
165
+ if (typeof profile.activeOperatorAddress === 'string' && profile.activeOperatorAddress.trim()) return profile.activeOperatorAddress.trim()
166
+ const profileApproved = normalizeApprovedOperatorWallets(profile.approvedOperatorWallets)
167
+ if (profileApproved[0]) return profileApproved[0].address
168
+ const storedActive = baseState.activeOperatorAddress
169
+ if (typeof storedActive === 'string' && storedActive.trim()) return storedActive.trim()
170
+ const storedApproved = normalizeApprovedOperatorWallets(baseState.approvedOperatorWallets)
171
+ if (storedApproved[0]) return storedApproved[0].address
172
+ return undefined
173
+ }
@@ -0,0 +1,5 @@
1
+ export {
2
+ runPublicProfilePreflight,
3
+ runPublicProfileSigning,
4
+ runPublicProfileStorageSubmit,
5
+ } from './runPublicProfileSave.js'