ethagent 2.1.1 → 2.3.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 (177) hide show
  1. package/package.json +2 -1
  2. package/src/app/FirstRun.tsx +1 -7
  3. package/src/app/FirstRunTimeline.tsx +1 -1
  4. package/src/auth/openaiOAuth/credentials.ts +47 -0
  5. package/src/auth/openaiOAuth/crypto.ts +23 -0
  6. package/src/auth/openaiOAuth/index.ts +238 -0
  7. package/src/auth/openaiOAuth/landingPage.ts +125 -0
  8. package/src/auth/openaiOAuth/listener.ts +151 -0
  9. package/src/auth/openaiOAuth/refresh.ts +70 -0
  10. package/src/auth/openaiOAuth/shared.ts +115 -0
  11. package/src/chat/ChatBottomPane.tsx +20 -11
  12. package/src/chat/ChatScreen.tsx +160 -35
  13. package/src/chat/ConversationStack.tsx +1 -1
  14. package/src/chat/MessageList.tsx +185 -72
  15. package/src/chat/SessionStatus.tsx +3 -1
  16. package/src/chat/chatScreenUtils.ts +11 -15
  17. package/src/chat/chatSessionState.ts +3 -2
  18. package/src/chat/chatTurnOrchestrator.ts +1 -7
  19. package/src/chat/commands.ts +28 -27
  20. package/src/chat/display/DiffView.tsx +193 -0
  21. package/src/chat/display/SyntaxText.tsx +192 -0
  22. package/src/chat/display/toolCallDisplay.ts +103 -0
  23. package/src/chat/display/toolResultDisplay.ts +19 -0
  24. package/src/chat/{ChatInput.tsx → input/ChatInput.tsx} +36 -23
  25. package/src/chat/{TranscriptView.tsx → transcript/TranscriptView.tsx} +24 -50
  26. package/src/chat/{transcriptViewport.ts → transcript/transcriptViewport.ts} +12 -30
  27. package/src/chat/{ContextLimitView.tsx → views/ContextLimitView.tsx} +3 -3
  28. package/src/chat/{ContinuityEditReviewView.tsx → views/ContinuityEditReviewView.tsx} +11 -3
  29. package/src/chat/{CopyPicker.tsx → views/CopyPicker.tsx} +4 -5
  30. package/src/chat/{PermissionPrompt.tsx → views/PermissionPrompt.tsx} +16 -17
  31. package/src/chat/{PermissionsView.tsx → views/PermissionsView.tsx} +6 -6
  32. package/src/chat/{PlanApprovalView.tsx → views/PlanApprovalView.tsx} +4 -4
  33. package/src/chat/{ResumeView.tsx → views/ResumeView.tsx} +35 -35
  34. package/src/chat/views/RewindView.tsx +410 -0
  35. package/src/identity/continuity/privateEdit/diff.ts +2 -78
  36. package/src/identity/ens/agentRecords.ts +5 -19
  37. package/src/identity/ens/ensAutomation/setup.ts +0 -1
  38. package/src/identity/ens/ensAutomation/types.ts +0 -1
  39. package/src/identity/hub/OperationalRoutes.tsx +23 -32
  40. package/src/identity/hub/Routes.tsx +13 -13
  41. package/src/identity/hub/{flows/continuity → continuity}/ContinuityDashboardScreen.tsx +9 -9
  42. package/src/identity/hub/{flows/continuity → continuity}/RebackupStorageScreen.tsx +2 -2
  43. package/src/identity/hub/{flows/continuity → continuity}/RecoveryConfirmScreen.tsx +5 -5
  44. package/src/identity/hub/{flows/continuity → continuity}/SavePromptScreen.tsx +5 -5
  45. package/src/identity/hub/{effects/rebackup/runRebackup.ts → continuity/effects.ts} +19 -19
  46. package/src/identity/hub/{effects/rebackup → continuity}/index.ts +1 -1
  47. package/src/identity/hub/{effects/shared → continuity}/snapshot.ts +8 -8
  48. package/src/identity/hub/{effects/rebackup → continuity}/vault.ts +15 -15
  49. package/src/identity/hub/{flows/create → create}/CreateFlow.tsx +13 -13
  50. package/src/identity/hub/{effects/create.ts → create/effects.ts} +4 -4
  51. package/src/identity/hub/{flows/custody → custody}/CustodyEditFlow.tsx +10 -48
  52. package/src/identity/hub/{flows/custody/custodyFlowActions.ts → custody/actions.ts} +11 -9
  53. package/src/identity/hub/{flows/custody/custodyFlowHelpers.ts → custody/helpers.ts} +4 -4
  54. package/src/identity/hub/{effects/vault → custody}/preflight.ts +5 -5
  55. package/src/identity/hub/{flows/custody/custodyFlowRoutes.tsx → custody/routes.tsx} +8 -8
  56. package/src/identity/hub/{flows/custody/custodyEffects.ts → custody/transactions.ts} +9 -9
  57. package/src/identity/hub/{flows/custody/custodyFlowTypes.ts → custody/types.ts} +6 -6
  58. package/src/identity/hub/{flows/custody/custodyFlowEffects.ts → custody/useCustodyEffects.ts} +7 -7
  59. package/src/identity/hub/{flows/custody → custody}/useCustodyFlow.tsx +5 -5
  60. package/src/identity/hub/ens/EnsEditAdvancedScreens.tsx +241 -0
  61. package/src/identity/hub/{flows/ens → ens}/EnsEditFlow.tsx +27 -82
  62. package/src/identity/hub/{flows/ens → ens}/EnsEditMaintenanceScreens.tsx +25 -65
  63. package/src/identity/hub/{flows/ens → ens}/EnsEditReviewScreens.tsx +12 -30
  64. package/src/identity/hub/ens/EnsEditRunners.tsx +62 -0
  65. package/src/identity/hub/{flows/ens → ens}/EnsEditShared.tsx +15 -14
  66. package/src/identity/hub/{flows/ens → ens}/EnsEditSimpleScreens.tsx +68 -217
  67. package/src/identity/hub/{flows/ens/IdentityHubEnsFlow.tsx → ens/EnsFlow.tsx} +18 -11
  68. package/src/identity/hub/{flows/ens/OperatorWalletsScreen.tsx → ens/EnsOperatorWalletsScreen.tsx} +17 -48
  69. package/src/identity/hub/{advancedEnsValidation.ts → ens/advancedEnsValidation.ts} +2 -2
  70. package/src/identity/hub/{flows/ens/ensEditCopy.ts → ens/editCopy.ts} +4 -4
  71. package/src/identity/hub/{effects/ens/flows.ts → ens/effects.ts} +7 -7
  72. package/src/identity/hub/{effects/ens → ens}/index.ts +1 -1
  73. package/src/identity/hub/{model/ens.ts → ens/state.ts} +1 -1
  74. package/src/identity/hub/{effects/ens → ens}/transactions.ts +232 -232
  75. package/src/identity/hub/{flows/ens/ensEditTypes.ts → ens/types.ts} +12 -26
  76. package/src/identity/hub/identityHubReducer.ts +3 -3
  77. package/src/identity/hub/{flows/profile → profile}/EditProfileFlow.tsx +17 -10
  78. package/src/identity/hub/{effects/publicProfile/runPublicProfileSave.ts → profile/effects.ts} +55 -177
  79. package/src/identity/hub/{model → profile}/identity.ts +3 -3
  80. package/src/identity/hub/{effects/profile/profileState.ts → profile/state.ts} +181 -173
  81. package/src/identity/hub/{flows/restore → restore}/RestoreFlow.tsx +21 -21
  82. package/src/identity/hub/{effects/restore → restore}/apply.ts +10 -10
  83. package/src/identity/hub/{effects/restore → restore}/auth.ts +7 -7
  84. package/src/identity/hub/{effects/restore → restore}/discover.ts +6 -6
  85. package/src/identity/hub/{effects/restore → restore}/envelopes.ts +2 -2
  86. package/src/identity/hub/{effects/restore → restore}/fetch.ts +3 -3
  87. package/src/identity/hub/{effects/restore/shared.ts → restore/helpers.ts} +6 -6
  88. package/src/identity/hub/{effects/restore → restore}/recovery.ts +10 -10
  89. package/src/identity/hub/{effects/restore → restore}/resolve.ts +4 -4
  90. package/src/identity/hub/restore/restoreAdmin.ts +34 -0
  91. package/src/identity/hub/{flows/restore/useRestoreFlowEffects.ts → restore/useRestoreEffects.ts} +5 -5
  92. package/src/identity/hub/{flows/settings → settings}/StorageCredentialScreen.tsx +5 -5
  93. package/src/identity/hub/{components → shared/components}/BusyScreen.tsx +4 -4
  94. package/src/identity/hub/{components → shared/components}/DetailsScreen.tsx +4 -4
  95. package/src/identity/hub/{components → shared/components}/ErrorScreen.tsx +4 -4
  96. package/src/identity/hub/{components → shared/components}/FlowTimeline.tsx +1 -1
  97. package/src/identity/hub/{components → shared/components}/IdentitySummary.tsx +16 -11
  98. package/src/identity/hub/{components → shared/components}/MenuScreen.tsx +8 -9
  99. package/src/identity/hub/{components → shared/components}/NetworkScreen.tsx +4 -4
  100. package/src/identity/hub/{components → shared/components}/PinataJwtInput.tsx +4 -4
  101. package/src/identity/hub/{components → shared/components}/UnlinkedIdentityScreen.tsx +5 -5
  102. package/src/identity/hub/{components → shared/components}/WalletApprovalScreen.tsx +6 -6
  103. package/src/identity/hub/{components → shared/components}/menuFlagsFromReconciliation.ts +2 -4
  104. package/src/identity/hub/{effects/shared → shared/effects}/profilePrep.ts +1 -1
  105. package/src/identity/hub/{effects → shared/effects}/receipts.ts +2 -2
  106. package/src/identity/hub/{effects/shared → shared/effects}/sync.ts +6 -47
  107. package/src/identity/hub/{effects → shared/effects}/types.ts +3 -3
  108. package/src/identity/hub/{model → shared/model}/copy.ts +2 -2
  109. package/src/identity/hub/{model → shared/model}/errors.ts +5 -5
  110. package/src/identity/hub/{model → shared/model}/network.ts +3 -3
  111. package/src/identity/hub/{operatorWallets.ts → shared/operatorWallets.ts} +1 -1
  112. package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/hook.ts +1 -2
  113. package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/ownership.ts +2 -2
  114. package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/run.ts +7 -40
  115. package/src/identity/hub/{reconciliation → shared/reconciliation}/agentReconciliation/types.ts +0 -4
  116. package/src/identity/hub/{reconciliation → shared/reconciliation}/index.ts +0 -7
  117. package/src/identity/hub/shared/reconciliation/walletSetup.ts +27 -0
  118. package/src/identity/hub/{utils.ts → shared/utils.ts} +5 -5
  119. package/src/identity/hub/{flows/token-transfer/IdentityHubTokenTransferFlow.tsx → transfer/TokenTransferFlow.tsx} +8 -8
  120. package/src/identity/hub/{flows/token-transfer → transfer}/TokenTransferScreens.tsx +14 -14
  121. package/src/identity/hub/{effects/token-transfer/runTokenTransfer.ts → transfer/effects.ts} +16 -16
  122. package/src/identity/hub/{effects/token-transfer → transfer}/progress.ts +1 -1
  123. package/src/identity/hub/useIdentityHubController.ts +11 -11
  124. package/src/identity/hub/useIdentityHubSideEffects.ts +11 -11
  125. package/src/identity/wallet/browserWallet/types.ts +0 -5
  126. package/src/identity/wallet/page/copy.ts +1 -31
  127. package/src/identity/wallet/walletPurposeCompat.ts +0 -2
  128. package/src/models/ModelPicker.tsx +248 -8
  129. package/src/models/catalog.ts +29 -1
  130. package/src/models/modelPickerOptions.ts +12 -10
  131. package/src/models/providerDisplay.ts +16 -0
  132. package/src/providers/errors.ts +6 -4
  133. package/src/providers/openai-chat.ts +2 -1
  134. package/src/providers/openai-responses-format.ts +156 -0
  135. package/src/providers/openai-responses.ts +276 -0
  136. package/src/providers/registry.ts +85 -8
  137. package/src/runtime/sessionMode.ts +1 -1
  138. package/src/runtime/systemPrompt.ts +4 -2
  139. package/src/runtime/toolExecution.ts +9 -6
  140. package/src/runtime/turn.ts +29 -1
  141. package/src/storage/rewind.ts +20 -0
  142. package/src/storage/secrets.ts +4 -1
  143. package/src/storage/sessions.ts +2 -1
  144. package/src/tools/bashSafety.ts +7 -3
  145. package/src/tools/bashTool.ts +1 -1
  146. package/src/tools/contracts.ts +3 -0
  147. package/src/tools/deleteFileTool.ts +8 -3
  148. package/src/tools/editTool.ts +10 -5
  149. package/src/tools/fileDiff.ts +261 -0
  150. package/src/tools/privateContinuityEditTool.ts +11 -1
  151. package/src/tools/writeFileTool.ts +8 -3
  152. package/src/ui/Spinner.tsx +25 -3
  153. package/src/ui/TextInput.tsx +2 -2
  154. package/src/ui/theme.ts +17 -0
  155. package/src/utils/clipboard.ts +10 -7
  156. package/src/utils/openExternal.ts +20 -10
  157. package/src/chat/RewindView.tsx +0 -386
  158. package/src/chat/toolResultDisplay.ts +0 -8
  159. package/src/identity/ens/ensRegistration.ts +0 -199
  160. package/src/identity/hub/effects/index.ts +0 -74
  161. package/src/identity/hub/effects/publicProfile/index.ts +0 -5
  162. package/src/identity/hub/effects/restore/restoreEffects.ts +0 -22
  163. package/src/identity/hub/effects/restoreAdmin.ts +0 -93
  164. package/src/identity/hub/effects/token-transfer/index.ts +0 -6
  165. package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +0 -336
  166. package/src/identity/hub/flows/ens/EnsEditRunners.tsx +0 -198
  167. package/src/identity/hub/reconciliation/walletSetup.ts +0 -220
  168. /package/src/chat/{chatInputState.ts → input/chatInputState.ts} +0 -0
  169. /package/src/chat/{chatPaste.ts → input/chatPaste.ts} +0 -0
  170. /package/src/chat/{textCursor.ts → input/textCursor.ts} +0 -0
  171. /package/src/identity/hub/{model/continuity.ts → continuity/state.ts} +0 -0
  172. /package/src/identity/hub/{model/custody.ts → custody/state.ts} +0 -0
  173. /package/src/identity/hub/{effects/restore → restore}/index.ts +0 -0
  174. /package/src/identity/hub/{model → shared/model}/format.ts +0 -0
  175. /package/src/identity/hub/{reconciliation → shared/reconciliation}/useAgentReconciliation.ts +0 -0
  176. /package/src/identity/hub/{txGuard.ts → shared/txGuard.ts} +0 -0
  177. /package/src/identity/hub/{model/transfer.ts → transfer/state.ts} +0 -0
@@ -4,236 +4,236 @@ import {
4
4
  RegisterAgentPreflightError,
5
5
  createErc8004PublicClient,
6
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
-
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
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
- }
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,38 +1,27 @@
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 { 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'
17
18
 
18
19
  export type EnsIssueValidation = Extract<EnsValidation, { ok: false }>
19
20
 
20
- export type RegisterCommitPhase = {
21
- label: string
22
- secret: `0x${string}`
23
- price: { base: bigint; premium: bigint; total: bigint }
24
- }
25
-
26
21
  export type SimpleEnsPhase =
27
- | { kind: 'discovering' }
28
- | { kind: 'pick-parent' }
29
- | { kind: 'manual-parent' }
30
- | { kind: 'register-root-input'; error?: string }
31
- | { kind: 'register-root-pricing'; label: string; secret: `0x${string}`; busy: boolean; error?: string; price?: { base: bigint; premium: bigint; total: bigint } }
32
- | { kind: 'register-root-commit-tx'; label: string; secret: `0x${string}`; price: { base: bigint; premium: bigint; total: bigint } }
33
- | { kind: 'register-root-wait'; label: string; secret: `0x${string}`; price: { base: bigint; premium: bigint; total: bigint }; commitMinedAt: number }
34
- | { kind: 'register-root-tx'; label: string; secret: `0x${string}`; price: { base: bigint; premium: bigint; total: bigint } }
35
- | { kind: 'register-root-done'; fullName: string }
22
+ | { kind: 'discovering'; mode?: 'simple' | 'advanced' }
23
+ | { kind: 'pick-parent'; mode?: 'simple' | 'advanced'; error?: string }
24
+ | { kind: 'manual-parent'; mode?: 'simple' | 'advanced'; error?: string }
36
25
  | { kind: 'pick-subdomain'; parent: string; label?: string; error?: string }
37
26
  | { kind: 'simple-name-missing'; fullName: string; validation: EnsIssueValidation }
38
27
  | { kind: 'simple-create-preflight'; rootName: string; label: string; fullName: string }
@@ -43,14 +32,9 @@ export type SimpleEnsPhase =
43
32
 
44
33
  export type AdvancedEnsPhase =
45
34
  | { kind: 'advanced-transfer-check' }
46
- | { kind: 'advanced-root'; rootName?: string; error?: string }
47
35
  | { kind: 'advanced-root-check'; rootName: string }
48
36
  | { kind: 'advanced-subdomain'; rootName: string; label?: string; error?: string }
49
37
  | { kind: 'advanced-subdomain-check'; rootName: string; label: string }
50
- | { kind: 'advanced-operator-wallet'; rootName: string; label: string; registryAction?: EnsRegistryAction; error?: string }
51
- | { kind: 'advanced-operator-wallet-manual'; rootName: string; label: string; error?: string }
52
- | { kind: 'advanced-operator-wallet-connecting'; rootName: string; label: string }
53
- | { kind: 'advanced-preflight'; rootName: string; label: string; operatorWallet: Address }
54
38
  | { kind: 'advanced-review'; setup: EnsSetupPlan }
55
39
  | { kind: 'advanced-manual'; fallback: EnsSetupBlockedPlan }
56
40
 
@@ -81,11 +65,13 @@ export type DiscoveryState =
81
65
  export type EnsEditProps = {
82
66
  identity: EthagentIdentity
83
67
  registry: Erc8004RegistryConfig
68
+ reconciliation: AgentReconciliation
84
69
  onEnsLink: (fullName: string, options: EnsLinkOptions) => void
85
70
  onEnsUnlink: () => void
86
71
  onEnsRecordsUpdate: (fullName: string, records: AgentEnsRecords, options: EnsLinkOptions, clearRecords?: boolean, currentRecords?: AgentEnsRecordState) => void
87
72
  onEnsSetup: (setup: EnsSetupPlan) => void
88
73
  onManageOperatorWalletAccess: () => void
74
+ onWithdrawToken: () => void
89
75
  initialView?: 'advanced'
90
76
  onBack: () => void
91
77
  }
@@ -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,19 +1,21 @@
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 '../shared/reconciliation/index.js'
14
15
 
15
16
  type EditProfileFlowProps = {
16
17
  step: Extract<Step, { kind: 'edit-profile-name' | 'edit-profile-description' | 'edit-profile-image' | 'edit-profile-review' | 'edit-profile-ens' }>
18
+ reconciliation: AgentReconciliation
17
19
  onNameSubmit: (name: string) => void
18
20
  onDescriptionSubmit: (description: string) => void
19
21
  onIconSubmit: (iconPath?: string) => void
@@ -24,6 +26,7 @@ type EditProfileFlowProps = {
24
26
  onEnsRecordsUpdate: (fullName: string, records: AgentEnsRecords, options: EnsLinkOptions, clearRecords?: boolean, currentRecords?: AgentEnsRecordState) => void
25
27
  onEnsSetup: (setup: EnsSetupPlan) => void
26
28
  onManageOperatorWalletAccess: () => void
29
+ onWithdrawToken: () => void
27
30
  onBack: () => void
28
31
  onMenu: () => void
29
32
  }
@@ -35,6 +38,7 @@ const EDIT_DESCRIPTION_FOOTER = 'enter next · shift+enter newline · esc back'
35
38
 
36
39
  export const EditProfileFlow: React.FC<EditProfileFlowProps> = ({
37
40
  step,
41
+ reconciliation,
38
42
  onNameSubmit,
39
43
  onDescriptionSubmit,
40
44
  onIconSubmit,
@@ -45,6 +49,7 @@ export const EditProfileFlow: React.FC<EditProfileFlowProps> = ({
45
49
  onEnsRecordsUpdate,
46
50
  onEnsSetup,
47
51
  onManageOperatorWalletAccess,
52
+ onWithdrawToken,
48
53
  onBack,
49
54
  onMenu,
50
55
  }) => {
@@ -90,11 +95,13 @@ export const EditProfileFlow: React.FC<EditProfileFlowProps> = ({
90
95
  <EnsEditFlow
91
96
  identity={step.identity}
92
97
  registry={step.registry}
98
+ reconciliation={reconciliation}
93
99
  onEnsLink={onEnsLink}
94
100
  onEnsUnlink={onEnsUnlink}
95
101
  onEnsRecordsUpdate={onEnsRecordsUpdate}
96
102
  onEnsSetup={onEnsSetup}
97
103
  onManageOperatorWalletAccess={onManageOperatorWalletAccess}
104
+ onWithdrawToken={onWithdrawToken}
98
105
  initialView={step.initialView}
99
106
  onBack={onBack}
100
107
  />