ethagent 2.2.0 → 2.4.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 (168) hide show
  1. package/README.md +11 -0
  2. package/package.json +2 -1
  3. package/src/app/FirstRun.tsx +3 -7
  4. package/src/app/FirstRunTimeline.tsx +1 -1
  5. package/src/chat/ChatBottomPane.tsx +29 -11
  6. package/src/chat/ChatScreen.tsx +169 -38
  7. package/src/chat/ConversationStack.tsx +1 -1
  8. package/src/chat/MessageList.tsx +185 -72
  9. package/src/chat/SessionStatus.tsx +3 -1
  10. package/src/chat/chatScreenUtils.ts +11 -15
  11. package/src/chat/chatSessionState.ts +5 -2
  12. package/src/chat/chatTurnOrchestrator.ts +7 -9
  13. package/src/chat/commands.ts +26 -26
  14. package/src/chat/display/DiffView.tsx +193 -0
  15. package/src/chat/display/SyntaxText.tsx +192 -0
  16. package/src/chat/display/toolCallDisplay.ts +103 -0
  17. package/src/chat/display/toolResultDisplay.ts +19 -0
  18. package/src/chat/{ChatInput.tsx → input/ChatInput.tsx} +61 -25
  19. package/src/chat/input/imageRefs.ts +30 -0
  20. package/src/chat/{TranscriptView.tsx → transcript/TranscriptView.tsx} +24 -50
  21. package/src/chat/{transcriptViewport.ts → transcript/transcriptViewport.ts} +12 -30
  22. package/src/chat/{ContextLimitView.tsx → views/ContextLimitView.tsx} +3 -3
  23. package/src/chat/{ContinuityEditReviewView.tsx → views/ContinuityEditReviewView.tsx} +11 -3
  24. package/src/chat/{CopyPicker.tsx → views/CopyPicker.tsx} +4 -5
  25. package/src/chat/{PermissionPrompt.tsx → views/PermissionPrompt.tsx} +16 -17
  26. package/src/chat/{PermissionsView.tsx → views/PermissionsView.tsx} +6 -6
  27. package/src/chat/{PlanApprovalView.tsx → views/PlanApprovalView.tsx} +4 -4
  28. package/src/chat/{ResumeView.tsx → views/ResumeView.tsx} +50 -41
  29. package/src/chat/views/RewindView.tsx +410 -0
  30. package/src/identity/continuity/privateEdit/diff.ts +2 -78
  31. package/src/identity/hub/OperationalRoutes.tsx +21 -21
  32. package/src/identity/hub/Routes.tsx +13 -13
  33. package/src/identity/hub/{flows/continuity → continuity}/ContinuityDashboardScreen.tsx +9 -9
  34. package/src/identity/hub/{flows/continuity → continuity}/RebackupStorageScreen.tsx +2 -2
  35. package/src/identity/hub/{flows/continuity → continuity}/RecoveryConfirmScreen.tsx +5 -5
  36. package/src/identity/hub/{flows/continuity → continuity}/SavePromptScreen.tsx +5 -5
  37. package/src/identity/hub/{effects/rebackup/runRebackup.ts → continuity/effects.ts} +17 -17
  38. package/src/identity/hub/{effects/rebackup → continuity}/index.ts +1 -1
  39. package/src/identity/hub/{effects/shared → continuity}/snapshot.ts +8 -8
  40. package/src/identity/hub/{effects/rebackup → continuity}/vault.ts +15 -15
  41. package/src/identity/hub/{flows/create → create}/CreateFlow.tsx +13 -13
  42. package/src/identity/hub/{effects/create.ts → create/effects.ts} +4 -4
  43. package/src/identity/hub/{flows/custody → custody}/CustodyEditFlow.tsx +9 -9
  44. package/src/identity/hub/{flows/custody/custodyFlowActions.ts → custody/actions.ts} +6 -6
  45. package/src/identity/hub/{flows/custody/custodyFlowHelpers.ts → custody/helpers.ts} +4 -4
  46. package/src/identity/hub/{effects/vault → custody}/preflight.ts +5 -5
  47. package/src/identity/hub/{flows/custody/custodyFlowRoutes.tsx → custody/routes.tsx} +8 -8
  48. package/src/identity/hub/{flows/custody/custodyEffects.ts → custody/transactions.ts} +9 -9
  49. package/src/identity/hub/{flows/custody/custodyFlowTypes.ts → custody/types.ts} +5 -5
  50. package/src/identity/hub/{flows/custody/custodyFlowEffects.ts → custody/useCustodyEffects.ts} +7 -7
  51. package/src/identity/hub/{flows/custody → custody}/useCustodyFlow.tsx +5 -5
  52. package/src/identity/hub/{flows/ens → ens}/EnsEditAdvancedScreens.tsx +13 -13
  53. package/src/identity/hub/{flows/ens → ens}/EnsEditFlow.tsx +7 -7
  54. package/src/identity/hub/{flows/ens → ens}/EnsEditMaintenanceScreens.tsx +10 -10
  55. package/src/identity/hub/{flows/ens → ens}/EnsEditReviewScreens.tsx +12 -12
  56. package/src/identity/hub/{flows/ens → ens}/EnsEditRunners.tsx +5 -5
  57. package/src/identity/hub/{flows/ens → ens}/EnsEditShared.tsx +10 -10
  58. package/src/identity/hub/{flows/ens → ens}/EnsEditSimpleScreens.tsx +14 -14
  59. package/src/identity/hub/{flows/ens/IdentityHubEnsFlow.tsx → ens/EnsFlow.tsx} +12 -12
  60. package/src/identity/hub/{flows/ens/OperatorWalletsScreen.tsx → ens/EnsOperatorWalletsScreen.tsx} +17 -17
  61. package/src/identity/hub/{advancedEnsValidation.ts → ens/advancedEnsValidation.ts} +2 -2
  62. package/src/identity/hub/{flows/ens/ensEditCopy.ts → ens/editCopy.ts} +3 -3
  63. package/src/identity/hub/{effects/ens/flows.ts → ens/effects.ts} +7 -7
  64. package/src/identity/hub/{effects/ens → ens}/index.ts +1 -1
  65. package/src/identity/hub/{model/ens.ts → ens/state.ts} +1 -1
  66. package/src/identity/hub/{effects/ens → ens}/transactions.ts +239 -239
  67. package/src/identity/hub/{flows/ens/ensEditTypes.ts → ens/types.ts} +7 -7
  68. package/src/identity/hub/identityHubReducer.ts +3 -3
  69. package/src/identity/hub/{flows/profile → profile}/EditProfileFlow.tsx +11 -11
  70. package/src/identity/hub/{effects/publicProfile/runPublicProfileSave.ts → profile/effects.ts} +18 -18
  71. package/src/identity/hub/{model → profile}/identity.ts +3 -3
  72. package/src/identity/hub/{effects/profile/profileState.ts → profile/state.ts} +181 -181
  73. package/src/identity/hub/{flows/restore → restore}/RestoreFlow.tsx +16 -16
  74. package/src/identity/hub/{effects/restore → restore}/apply.ts +10 -10
  75. package/src/identity/hub/{effects/restore → restore}/auth.ts +7 -7
  76. package/src/identity/hub/{effects/restore → restore}/discover.ts +6 -6
  77. package/src/identity/hub/{effects/restore → restore}/envelopes.ts +2 -2
  78. package/src/identity/hub/{effects/restore → restore}/fetch.ts +3 -3
  79. package/src/identity/hub/{effects/restore/shared.ts → restore/helpers.ts} +6 -6
  80. package/src/identity/hub/{effects/restore → restore}/recovery.ts +10 -10
  81. package/src/identity/hub/{effects/restore → restore}/resolve.ts +4 -4
  82. package/src/identity/hub/{effects → restore}/restoreAdmin.ts +1 -1
  83. package/src/identity/hub/{flows/restore/useRestoreFlowEffects.ts → restore/useRestoreEffects.ts} +5 -5
  84. package/src/identity/hub/{flows/settings → settings}/StorageCredentialScreen.tsx +5 -5
  85. package/src/identity/hub/{components → shared/components}/BusyScreen.tsx +4 -4
  86. package/src/identity/hub/{components → shared/components}/DetailsScreen.tsx +4 -4
  87. package/src/identity/hub/{components → shared/components}/ErrorScreen.tsx +4 -4
  88. package/src/identity/hub/{components → shared/components}/FlowTimeline.tsx +1 -1
  89. package/src/identity/hub/{components → shared/components}/IdentitySummary.tsx +8 -8
  90. package/src/identity/hub/{components → shared/components}/MenuScreen.tsx +7 -7
  91. package/src/identity/hub/{components → shared/components}/NetworkScreen.tsx +4 -4
  92. package/src/identity/hub/{components → shared/components}/PinataJwtInput.tsx +4 -4
  93. package/src/identity/hub/{components → shared/components}/UnlinkedIdentityScreen.tsx +5 -5
  94. package/src/identity/hub/{components → shared/components}/WalletApprovalScreen.tsx +6 -6
  95. package/src/identity/hub/{components → shared/components}/menuFlagsFromReconciliation.ts +1 -1
  96. package/src/identity/hub/{effects/shared → shared/effects}/profilePrep.ts +1 -1
  97. package/src/identity/hub/{effects → shared/effects}/receipts.ts +2 -2
  98. package/src/identity/hub/{effects/shared → shared/effects}/sync.ts +4 -4
  99. package/src/identity/hub/{effects → shared/effects}/types.ts +3 -3
  100. package/src/identity/hub/{model → shared/model}/copy.ts +2 -2
  101. package/src/identity/hub/{model → shared/model}/errors.ts +5 -5
  102. package/src/identity/hub/{model → shared/model}/network.ts +3 -3
  103. package/src/identity/hub/{operatorWallets.ts → shared/operatorWallets.ts} +1 -1
  104. package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/hook.ts +1 -1
  105. package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/ownership.ts +2 -2
  106. package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/run.ts +6 -6
  107. package/src/identity/hub/{utils.ts → shared/utils.ts} +5 -5
  108. package/src/identity/hub/{flows/token-transfer/IdentityHubTokenTransferFlow.tsx → transfer/TokenTransferFlow.tsx} +8 -8
  109. package/src/identity/hub/{flows/token-transfer → transfer}/TokenTransferScreens.tsx +14 -14
  110. package/src/identity/hub/{effects/token-transfer/runTokenTransfer.ts → transfer/effects.ts} +16 -16
  111. package/src/identity/hub/{effects/token-transfer → transfer}/progress.ts +1 -1
  112. package/src/identity/hub/useIdentityHubController.ts +11 -11
  113. package/src/identity/hub/useIdentityHubSideEffects.ts +11 -11
  114. package/src/models/ModelPicker.tsx +143 -9
  115. package/src/models/catalog.ts +2 -1
  116. package/src/models/huggingface.ts +180 -2
  117. package/src/models/llamacpp.ts +110 -15
  118. package/src/models/llamacppPreflight.ts +30 -11
  119. package/src/models/modelPickerOptions.ts +16 -15
  120. package/src/models/providerDisplay.ts +16 -0
  121. package/src/providers/anthropic.ts +36 -5
  122. package/src/providers/contracts.ts +9 -1
  123. package/src/providers/errors.ts +6 -4
  124. package/src/providers/gemini.ts +29 -3
  125. package/src/providers/openai-chat.ts +83 -3
  126. package/src/providers/openai-responses-format.ts +29 -8
  127. package/src/providers/openai-responses.ts +22 -7
  128. package/src/providers/registry.ts +1 -0
  129. package/src/runtime/sessionMode.ts +1 -1
  130. package/src/runtime/systemPrompt.ts +3 -1
  131. package/src/runtime/toolExecution.ts +9 -6
  132. package/src/runtime/turn.ts +29 -0
  133. package/src/storage/config.ts +1 -0
  134. package/src/storage/rewind.ts +20 -0
  135. package/src/storage/sessions.ts +16 -3
  136. package/src/tools/bashSafety.ts +7 -3
  137. package/src/tools/bashTool.ts +1 -1
  138. package/src/tools/contracts.ts +3 -0
  139. package/src/tools/deleteFileTool.ts +8 -3
  140. package/src/tools/editTool.ts +10 -5
  141. package/src/tools/fileDiff.ts +261 -0
  142. package/src/tools/privateContinuityEditTool.ts +5 -1
  143. package/src/tools/writeFileTool.ts +8 -3
  144. package/src/ui/Spinner.tsx +39 -5
  145. package/src/ui/TextInput.tsx +2 -2
  146. package/src/ui/theme.ts +19 -0
  147. package/src/utils/clipboard.ts +10 -7
  148. package/src/utils/images.ts +140 -0
  149. package/src/utils/messages.ts +2 -0
  150. package/src/chat/RewindView.tsx +0 -386
  151. package/src/chat/toolResultDisplay.ts +0 -8
  152. package/src/identity/hub/effects/index.ts +0 -73
  153. package/src/identity/hub/effects/publicProfile/index.ts +0 -5
  154. package/src/identity/hub/effects/restore/restoreEffects.ts +0 -22
  155. package/src/identity/hub/effects/token-transfer/index.ts +0 -6
  156. /package/src/chat/{chatInputState.ts → input/chatInputState.ts} +0 -0
  157. /package/src/chat/{chatPaste.ts → input/chatPaste.ts} +0 -0
  158. /package/src/chat/{textCursor.ts → input/textCursor.ts} +0 -0
  159. /package/src/identity/hub/{model/continuity.ts → continuity/state.ts} +0 -0
  160. /package/src/identity/hub/{model/custody.ts → custody/state.ts} +0 -0
  161. /package/src/identity/hub/{effects/restore → restore}/index.ts +0 -0
  162. /package/src/identity/hub/{model → shared/model}/format.ts +0 -0
  163. /package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/types.ts +0 -0
  164. /package/src/identity/hub/{reconciliation → shared/reconciliation}/index.ts +0 -0
  165. /package/src/identity/hub/{reconciliation → shared/reconciliation}/useAgentReconciliation.ts +0 -0
  166. /package/src/identity/hub/{reconciliation → shared/reconciliation}/walletSetup.ts +0 -0
  167. /package/src/identity/hub/{txGuard.ts → shared/txGuard.ts} +0 -0
  168. /package/src/identity/hub/{model/transfer.ts → transfer/state.ts} +0 -0
@@ -1,239 +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: '' })
88
- }
89
- return changedRecords(args.currentRecords ?? { token: '' }, 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
- }
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 '../shared/effects/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: '' })
88
+ }
89
+ return changedRecords(args.currentRecords ?? { token: '' }, 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
+ }
@@ -1,20 +1,20 @@
1
1
  import type { Address } from 'viem'
2
- import type { EthagentIdentity } from '../../../../storage/config.js'
2
+ import type { EthagentIdentity } from '../../../storage/config.js'
3
3
  import type {
4
4
  AgentEnsRecordState,
5
5
  AgentEnsRecords,
6
6
  AgentRecordDiff,
7
- } from '../../../ens/agentRecords.js'
8
- import type { EnsValidation } from '../../../ens/ensLookup.js'
7
+ } from '../../ens/agentRecords.js'
8
+ import type { EnsValidation } from '../../ens/ensLookup.js'
9
9
  import type {
10
10
  EnsRegistryAction,
11
11
  EnsSetupBlockedPlan,
12
12
  EnsSetupPlan,
13
13
  EnsSubdomainDeletePlan,
14
- } from '../../../ens/ensAutomation.js'
15
- import type { Erc8004RegistryConfig } from '../../../registry/erc8004.js'
16
- import type { AgentReconciliation } from '../../reconciliation/index.js'
17
- import type { EnsLinkOptions } from './ensEditCopy.js'
14
+ } from '../../ens/ensAutomation.js'
15
+ import type { Erc8004RegistryConfig } from '../../registry/erc8004.js'
16
+ import type { AgentReconciliation } from '../shared/reconciliation/index.js'
17
+ import type { EnsLinkOptions } from './editCopy.js'
18
18
 
19
19
  export type EnsIssueValidation = Extract<EnsValidation, { ok: false }>
20
20
 
@@ -7,9 +7,9 @@ import type { ContinuitySnapshotEnvelope } from '../continuity/envelope.js'
7
7
  import type { AgentEnsRecordState, AgentEnsRecords } from '../ens/agentRecords.js'
8
8
  import type { EnsSetupPlan } from '../ens/ensAutomation.js'
9
9
  import type { WalletPurpose } from '../wallet/browserWallet.js'
10
- import type { IdentityHubErrorView } from './model/errors.js'
11
- import type { ApprovedOperatorWalletInput } from './operatorWallets.js'
12
- import type { CustodyMode } from './model/custody.js'
10
+ import type { IdentityHubErrorView } from './shared/model/errors.js'
11
+ import type { ApprovedOperatorWalletInput } from './shared/operatorWallets.js'
12
+ import type { CustodyMode } from './custody/state.js'
13
13
 
14
14
  export type RestorePurpose = 'restore' | 'switch'
15
15
  type RestoreNotFoundReason = 'no-owner-or-operator' | 'search-incomplete' | 'cancelled'
@@ -1,17 +1,17 @@
1
1
  import React from 'react'
2
2
  import { Box, Text } from 'ink'
3
- import { Surface } from '../../../../ui/Surface.js'
4
- import { Select } from '../../../../ui/Select.js'
5
- import { TextInput } from '../../../../ui/TextInput.js'
6
- import { theme } from '../../../../ui/theme.js'
7
- import type { AgentEnsRecordState, AgentEnsRecords } from '../../../ens/agentRecords.js'
8
- import type { EnsSetupPlan } from '../../../ens/ensAutomation.js'
9
- import type { Step } from '../../identityHubReducer.js'
10
- import { readIdentityStateString } from '../../model/custody.js'
11
- import { FlowTimeline } from '../../components/FlowTimeline.js'
12
- import { validateAgentIconReference } from '../../../profile/agentIcon.js'
3
+ import { Surface } from '../../../ui/Surface.js'
4
+ import { Select } from '../../../ui/Select.js'
5
+ import { TextInput } from '../../../ui/TextInput.js'
6
+ import { theme } from '../../../ui/theme.js'
7
+ import type { AgentEnsRecordState, AgentEnsRecords } from '../../ens/agentRecords.js'
8
+ import type { EnsSetupPlan } from '../../ens/ensAutomation.js'
9
+ import type { Step } from '../identityHubReducer.js'
10
+ import { readIdentityStateString } from '../custody/state.js'
11
+ import { FlowTimeline } from '../shared/components/FlowTimeline.js'
12
+ import { validateAgentIconReference } from '../../profile/agentIcon.js'
13
13
  import { EnsEditFlow, type EnsLinkOptions } from '../ens/EnsEditFlow.js'
14
- import type { AgentReconciliation } from '../../reconciliation/index.js'
14
+ import type { AgentReconciliation } from '../shared/reconciliation/index.js'
15
15
 
16
16
  type EditProfileFlowProps = {
17
17
  step: Extract<Step, { kind: 'edit-profile-name' | 'edit-profile-description' | 'edit-profile-image' | 'edit-profile-review' | 'edit-profile-ens' }>
@@ -1,56 +1,56 @@
1
1
  import { getAddress, type Address } from 'viem'
2
- import type { EthagentIdentity } from '../../../../storage/config.js'
2
+ import type { EthagentIdentity } from '../../../storage/config.js'
3
3
  import {
4
4
  createWalletRestoreAccessChallenge,
5
5
  serializeContinuitySnapshotEnvelope,
6
6
  type WalletChallengePurpose,
7
- } from '../../../continuity/envelope.js'
7
+ } from '../../continuity/envelope.js'
8
8
  import {
9
9
  prepareSyncedPublicSkillsJson,
10
10
  readContinuityFiles,
11
11
  writePublicSkillsFile,
12
- } from '../../../continuity/storage.js'
12
+ } from '../../continuity/storage.js'
13
13
  import {
14
14
  createAgentCard,
15
15
  defaultPublicSkillsProfile,
16
16
  serializeAgentCard,
17
- } from '../../../continuity/publicSkills.js'
18
- import { recordPublishedContinuitySnapshot } from '../../../continuity/snapshots.js'
19
- import { addToIpfs, DEFAULT_IPFS_API_URL, isPinataUploadUrl } from '../../../storage/ipfs.js'
17
+ } from '../../continuity/publicSkills.js'
18
+ import { recordPublishedContinuitySnapshot } from '../../continuity/snapshots.js'
19
+ import { addToIpfs, DEFAULT_IPFS_API_URL, isPinataUploadUrl } from '../../storage/ipfs.js'
20
20
  import {
21
21
  createErc8004PublicClient,
22
22
  encodeSetAgentUri,
23
23
  preflightSetAgentUri,
24
24
  withEthagentPointers,
25
25
  type Erc8004RegistryConfig,
26
- } from '../../../registry/erc8004.js'
26
+ } from '../../registry/erc8004.js'
27
27
  import {
28
28
  VAULT_ABI,
29
29
  encodeRotateAgentURI,
30
- } from '../../../registry/vault.js'
31
- import { resolveValidatedPinataJwt, savePinataJwt } from '../../../storage/pinataJwt.js'
30
+ } from '../../registry/vault.js'
31
+ import { resolveValidatedPinataJwt, savePinataJwt } from '../../storage/pinataJwt.js'
32
32
  import {
33
33
  openBrowserWalletSession,
34
34
  requestBrowserWalletSignatureAndTransaction,
35
35
  type BrowserWalletSignature,
36
36
  type WalletPurpose,
37
- } from '../../../wallet/browserWallet.js'
38
- import type { ProfileUpdates, Step } from '../../identityHubReducer.js'
39
- import { acquireTxGuard, releaseTxGuard } from '../../txGuard.js'
40
- import type { EffectCallbacks } from '../types.js'
41
- import { awaitConfirmedReceipt } from '../receipts.js'
37
+ } from '../../wallet/browserWallet.js'
38
+ import type { ProfileUpdates, Step } from '../identityHubReducer.js'
39
+ import { acquireTxGuard, releaseTxGuard } from '../shared/txGuard.js'
40
+ import type { EffectCallbacks } from '../shared/effects/types.js'
41
+ import { awaitConfirmedReceipt } from '../shared/effects/receipts.js'
42
42
  import {
43
43
  assertVerifiedPin,
44
44
  deriveAgentName,
45
45
  prepareProfileStateForSave,
46
46
  readEnsOkFromState,
47
- } from '../shared/profilePrep.js'
47
+ } from '../shared/effects/profilePrep.js'
48
48
  import {
49
49
  appendResolverSyncWarning,
50
50
  markCurrentContinuityFilesPublished,
51
51
  resolverSyncWarningMessage,
52
52
  syncVaultOperatorsAfterOwnerSave,
53
- } from '../shared/sync.js'
53
+ } from '../shared/effects/sync.js'
54
54
  import {
55
55
  assertSnapshotSaveSignerAuthorized,
56
56
  createContinuityEnvelopeForSave,
@@ -59,8 +59,8 @@ import {
59
59
  ownerAddressForSnapshotSave,
60
60
  snapshotSaveWalletRole,
61
61
  walletRestoreAccessContext,
62
- } from '../shared/snapshot.js'
63
- import { rebackupCompletionMessage } from '../rebackup/runRebackup.js'
62
+ } from '../continuity/snapshot.js'
63
+ import { rebackupCompletionMessage } from '../continuity/effects.js'
64
64
 
65
65
  type BackupMetadata = NonNullable<EthagentIdentity['backup']>
66
66
  type PublicSkillsMetadata = NonNullable<EthagentIdentity['publicSkills']>
@@ -1,8 +1,8 @@
1
1
  import type { EthagentConfig, EthagentIdentity } from '../../../storage/config.js'
2
2
  import { supportedErc8004ChainForId, type Erc8004AgentCandidate } from '../../registry/erc8004.js'
3
- import { readCustodyMode, type CustodyMode } from './custody.js'
4
- import { formatDate, shortAddress, shortCid } from './format.js'
5
- import { chainSummaryRow, networkLabel } from './network.js'
3
+ import { readCustodyMode, type CustodyMode } from '../custody/state.js'
4
+ import { formatDate, shortAddress, shortCid } from '../shared/model/format.js'
5
+ import { chainSummaryRow, networkLabel } from '../shared/model/network.js'
6
6
 
7
7
  export const PREFLIGHT_AGENT_URI = 'ipfs://bafybeigdyrztma2dbfczw7q6ooozbxlqzyw5r7w4f3qw2axvvxqg3w6y7q'
8
8