ethagent 3.3.3 → 4.0.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 (322) hide show
  1. package/.claude-plugin/marketplace.json +11 -0
  2. package/.claude-plugin/plugin.json +35 -0
  3. package/LICENSE +1 -1
  4. package/README.md +64 -104
  5. package/commands/ethagent.md +40 -0
  6. package/package.json +16 -16
  7. package/src/app/keybindings/KeybindingProvider.tsx +1 -6
  8. package/src/app/keybindings/types.ts +1 -6
  9. package/src/cli/ResetConfirmView.tsx +54 -53
  10. package/src/cli/demo.ts +86 -0
  11. package/src/cli/hookIo.ts +45 -0
  12. package/src/cli/main.tsx +94 -123
  13. package/src/cli/memoryGuard.ts +49 -0
  14. package/src/cli/reset.ts +28 -70
  15. package/src/cli/sessionStart.ts +33 -0
  16. package/src/cli/status.ts +46 -0
  17. package/src/cli/sync.ts +167 -0
  18. package/src/cli/syncAdapters/claude-code.ts +86 -0
  19. package/src/cli/syncAdapters/codex.ts +66 -0
  20. package/src/cli/syncAdapters/index.ts +45 -0
  21. package/src/cli/syncAdapters/managedBlock.ts +175 -0
  22. package/src/cli/syncAdapters/shared.ts +63 -0
  23. package/src/identity/continuity/envelopeParse.ts +20 -1
  24. package/src/identity/continuity/publicSkills.ts +3 -1
  25. package/src/identity/continuity/skills/publicSkillsSync.ts +2 -1
  26. package/src/identity/continuity/skills/scaffold.ts +5 -2
  27. package/src/identity/continuity/snapshots.ts +12 -5
  28. package/src/identity/continuity/storage/defaults.ts +20 -19
  29. package/src/identity/continuity/storage/status.ts +1 -1
  30. package/src/identity/ens/ensLookup/constants.ts +1 -1
  31. package/src/identity/manager/IdentityManager.tsx +33 -0
  32. package/src/identity/{hub → manager}/OperationalRoutes.tsx +37 -18
  33. package/src/identity/{hub → manager}/Routes.tsx +48 -34
  34. package/src/identity/{hub → manager}/continuity/ContinuityDashboardScreen.tsx +9 -19
  35. package/src/identity/{hub → manager}/continuity/RebackupStorageScreen.tsx +3 -3
  36. package/src/identity/manager/continuity/RecoveryConfirmScreen.tsx +102 -0
  37. package/src/identity/{hub → manager}/continuity/SavePromptScreen.tsx +2 -3
  38. package/src/identity/{hub → manager}/continuity/completion.ts +1 -1
  39. package/src/identity/{hub → manager}/continuity/effects.ts +1 -1
  40. package/src/identity/{hub → manager}/continuity/skills/DeleteSkillConfirmScreen.tsx +2 -2
  41. package/src/identity/{hub → manager}/continuity/skills/NewSkillScreen.tsx +0 -5
  42. package/src/identity/{hub → manager}/continuity/skills/NewSkillVisibilityScreen.tsx +4 -4
  43. package/src/identity/{hub → manager}/continuity/skills/SkillActionsScreen.tsx +6 -22
  44. package/src/identity/{hub → manager}/continuity/skills/SkillsTreeScreen.tsx +5 -17
  45. package/src/identity/{hub → manager}/continuity/snapshot.ts +1 -1
  46. package/src/identity/{hub → manager}/continuity/vault.ts +1 -1
  47. package/src/identity/{hub → manager}/create/CreateFlow.tsx +59 -32
  48. package/src/identity/{hub → manager}/create/effects.ts +19 -10
  49. package/src/identity/manager/create/importScan.ts +122 -0
  50. package/src/identity/{hub → manager}/custody/CustodyEditFlow.tsx +17 -61
  51. package/src/identity/{hub → manager}/custody/actions.ts +1 -15
  52. package/src/identity/{hub → manager}/custody/routes.tsx +20 -40
  53. package/src/identity/{hub → manager}/custody/transactions.ts +1 -0
  54. package/src/identity/{hub → manager}/custody/types.ts +1 -2
  55. package/src/identity/{hub → manager}/custody/useCustodyEffects.ts +1 -1
  56. package/src/identity/{hub → manager}/ens/EnsEditAdvancedScreens.tsx +2 -2
  57. package/src/identity/{hub → manager}/ens/EnsEditMaintenanceScreens.tsx +12 -23
  58. package/src/identity/{hub → manager}/ens/EnsEditReviewScreens.tsx +18 -42
  59. package/src/identity/{hub → manager}/ens/EnsEditRunners.tsx +1 -1
  60. package/src/identity/{hub → manager}/ens/EnsEditShared.tsx +0 -2
  61. package/src/identity/{hub → manager}/ens/EnsEditSimpleScreens.tsx +10 -19
  62. package/src/identity/{hub → manager}/ens/EnsFlow.tsx +133 -41
  63. package/src/identity/{hub → manager}/ens/EnsOperatorWalletsScreen.tsx +14 -19
  64. package/src/identity/{hub → manager}/ens/editCopy.ts +1 -14
  65. package/src/identity/{hub → manager}/profile/EditProfileFlow.tsx +99 -66
  66. package/src/identity/{hub → manager}/profile/effects.ts +1 -3
  67. package/src/identity/{hub → manager}/profile/operatorSave.ts +1 -1
  68. package/src/identity/{hub → manager}/profile/state.ts +1 -1
  69. package/src/identity/{hub/identityHubReducer.ts → manager/reducer.ts} +25 -26
  70. package/src/identity/{hub → manager}/restore/RestoreFlow.tsx +16 -24
  71. package/src/identity/{hub → manager}/restore/apply.ts +1 -1
  72. package/src/identity/{hub → manager}/restore/auth.ts +1 -1
  73. package/src/identity/{hub → manager}/restore/discover.ts +1 -1
  74. package/src/identity/{hub → manager}/restore/fetch.ts +1 -1
  75. package/src/identity/{hub → manager}/restore/restoreAdmin.ts +1 -1
  76. package/src/identity/{hub → manager}/restore/useRestoreEffects.ts +2 -9
  77. package/src/identity/{hub → manager}/settings/StorageCredentialScreen.tsx +10 -25
  78. package/src/identity/{hub → manager}/shared/components/DetailsScreen.tsx +5 -7
  79. package/src/identity/{hub → manager}/shared/components/ErrorScreen.tsx +6 -10
  80. package/src/identity/{hub → manager}/shared/components/FlowTimeline.tsx +4 -3
  81. package/src/identity/{hub → manager}/shared/components/IdentitySummary.tsx +19 -59
  82. package/src/identity/manager/shared/components/LazyMenu.tsx +147 -0
  83. package/src/identity/manager/shared/components/MenuScreen.tsx +220 -0
  84. package/src/identity/manager/shared/components/OperationCompleteScreen.tsx +28 -0
  85. package/src/identity/{hub → manager}/shared/components/UnlinkedIdentityScreen.tsx +9 -10
  86. package/src/identity/{hub → manager}/shared/components/WalletApprovalScreen.tsx +1 -2
  87. package/src/identity/manager/shared/components/Wordmark.tsx +54 -0
  88. package/src/identity/{hub → manager}/shared/components/menuFlagsFromReconciliation.ts +39 -15
  89. package/src/identity/{hub → manager}/shared/effects/profilePrep.ts +1 -1
  90. package/src/identity/manager/shared/effects/types.ts +30 -0
  91. package/src/identity/{hub → manager}/shared/model/copy.ts +0 -4
  92. package/src/identity/{hub → manager}/shared/model/errors.ts +32 -3
  93. package/src/identity/{hub → manager}/shared/model/network.ts +2 -2
  94. package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/hook.ts +5 -0
  95. package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/run.ts +1 -1
  96. package/src/identity/{hub/shared/reconciliation/useAgentReconciliation.ts → manager/shared/reconciliation/index.ts} +6 -0
  97. package/src/identity/{hub → manager}/shared/utils.ts +6 -10
  98. package/src/identity/{hub → manager}/transfer/TokenTransferFlow.tsx +3 -3
  99. package/src/identity/{hub → manager}/transfer/TokenTransferScreens.tsx +4 -10
  100. package/src/identity/{hub → manager}/transfer/effects.ts +1 -1
  101. package/src/identity/{hub → manager}/types.ts +5 -6
  102. package/src/identity/{hub/useIdentityHubContinuity.ts → manager/useContinuity.ts} +59 -27
  103. package/src/identity/{hub/useIdentityHubController.ts → manager/useController.ts} +38 -35
  104. package/src/identity/{hub/useIdentityHubSideEffects.ts → manager/useSideEffects.ts} +40 -4
  105. package/src/identity/registry/erc8004/discovery.ts +3 -17
  106. package/src/identity/registry/erc8004/utils.ts +1 -1
  107. package/src/identity/storage/ipfs.ts +21 -1
  108. package/src/identity/wallet/browserWallet/html.ts +10 -2
  109. package/src/identity/wallet/browserWallet/http.ts +18 -0
  110. package/src/identity/wallet/browserWallet/requestServer.ts +5 -1
  111. package/src/identity/wallet/browserWallet/requests.ts +10 -28
  112. package/src/identity/wallet/browserWallet/session.ts +26 -33
  113. package/src/identity/wallet/browserWallet/validation.ts +14 -0
  114. package/src/identity/wallet/browserWallet/walletPageSource.ts +22 -40
  115. package/src/identity/wallet/page/boot.ts +43 -0
  116. package/src/identity/wallet/page/config.ts +59 -0
  117. package/src/identity/wallet/page/constants.ts +12 -0
  118. package/src/identity/wallet/page/copy.ts +47 -68
  119. package/src/identity/wallet/page/css.ts +638 -0
  120. package/src/identity/wallet/page/{errorView.ts → errors.ts} +5 -14
  121. package/src/identity/wallet/page/{controller.ts → flow.ts} +4 -71
  122. package/src/identity/wallet/page/markup.ts +44 -34
  123. package/src/identity/wallet/page/{walletProvider.ts → provider.ts} +0 -3
  124. package/src/identity/wallet/page/resize.ts +95 -0
  125. package/src/identity/wallet/page/state.ts +135 -8
  126. package/src/identity/wallet/page/timeline.ts +161 -0
  127. package/src/identity/wallet/page/view.ts +22 -302
  128. package/src/storage/config.ts +30 -80
  129. package/src/storage/reset.ts +31 -0
  130. package/src/storage/secrets.ts +1 -16
  131. package/src/ui/Select.tsx +27 -5
  132. package/src/ui/Spinner.tsx +16 -15
  133. package/src/ui/Surface.tsx +21 -17
  134. package/src/ui/TextArea.tsx +173 -0
  135. package/src/ui/TextInput.tsx +31 -133
  136. package/src/ui/theme.ts +22 -13
  137. package/src/utils/clipboard.ts +0 -140
  138. package/src/app/FirstRun.tsx +0 -577
  139. package/src/app/FirstRunTimeline.tsx +0 -51
  140. package/src/app/firstRunConfig.ts +0 -26
  141. package/src/app/hooks/useCancelRequest.ts +0 -22
  142. package/src/app/hooks/useDoublePress.ts +0 -46
  143. package/src/app/hooks/useExitOnCtrlC.ts +0 -36
  144. package/src/auth/openaiOAuth/credentials.ts +0 -47
  145. package/src/auth/openaiOAuth/crypto.ts +0 -23
  146. package/src/auth/openaiOAuth/index.ts +0 -238
  147. package/src/auth/openaiOAuth/landingPage.ts +0 -116
  148. package/src/auth/openaiOAuth/listener.ts +0 -151
  149. package/src/auth/openaiOAuth/refresh.ts +0 -70
  150. package/src/auth/openaiOAuth/shared.ts +0 -115
  151. package/src/chat/ChatBottomPane.tsx +0 -296
  152. package/src/chat/ChatScreen.tsx +0 -1685
  153. package/src/chat/ConversationStack.tsx +0 -56
  154. package/src/chat/MessageList.tsx +0 -638
  155. package/src/chat/SessionStatus.tsx +0 -53
  156. package/src/chat/chatEnvironment.ts +0 -16
  157. package/src/chat/chatScreenUtils.ts +0 -194
  158. package/src/chat/chatSessionState.ts +0 -146
  159. package/src/chat/chatTurnContext.ts +0 -50
  160. package/src/chat/chatTurnOrchestrator.ts +0 -603
  161. package/src/chat/chatTurnRows.ts +0 -64
  162. package/src/chat/commands.ts +0 -494
  163. package/src/chat/continuityEditReview.ts +0 -42
  164. package/src/chat/display/DiffView.tsx +0 -193
  165. package/src/chat/display/SyntaxText.tsx +0 -192
  166. package/src/chat/display/toolCallDisplay.ts +0 -103
  167. package/src/chat/display/toolResultDisplay.ts +0 -19
  168. package/src/chat/input/ChatInput.tsx +0 -625
  169. package/src/chat/input/chatInputHelpers.ts +0 -62
  170. package/src/chat/input/chatInputState.ts +0 -247
  171. package/src/chat/input/chatPaste.ts +0 -49
  172. package/src/chat/input/imageRefs.ts +0 -30
  173. package/src/chat/input/inputRendering.tsx +0 -93
  174. package/src/chat/input/textCursor.ts +0 -212
  175. package/src/chat/messageMarkdown.ts +0 -220
  176. package/src/chat/messageRows.ts +0 -43
  177. package/src/chat/planImplementation.ts +0 -62
  178. package/src/chat/slashCommandHandlers.ts +0 -122
  179. package/src/chat/slashCommandViews.ts +0 -120
  180. package/src/chat/transcript/TranscriptView.tsx +0 -184
  181. package/src/chat/transcript/transcriptViewport.ts +0 -295
  182. package/src/chat/views/ContextLimitView.tsx +0 -95
  183. package/src/chat/views/ContinuityEditReviewView.tsx +0 -50
  184. package/src/chat/views/CopyPicker.tsx +0 -50
  185. package/src/chat/views/PermissionPrompt.tsx +0 -156
  186. package/src/chat/views/PermissionsView.tsx +0 -165
  187. package/src/chat/views/PlanApprovalView.tsx +0 -91
  188. package/src/chat/views/ResumeView.tsx +0 -273
  189. package/src/chat/views/RewindView.tsx +0 -412
  190. package/src/cli/preview.tsx +0 -14
  191. package/src/cli/updateNotice.ts +0 -54
  192. package/src/identity/continuity/privateEdit/apply.ts +0 -170
  193. package/src/identity/continuity/privateEdit/diff.ts +0 -6
  194. package/src/identity/continuity/privateEdit/files.ts +0 -23
  195. package/src/identity/continuity/privateEdit/types.ts +0 -28
  196. package/src/identity/continuity/privateEdit.ts +0 -46
  197. package/src/identity/hub/IdentityHub.tsx +0 -14
  198. package/src/identity/hub/continuity/RecoveryConfirmScreen.tsx +0 -104
  199. package/src/identity/hub/ens/effects.ts +0 -218
  200. package/src/identity/hub/shared/components/MenuScreen.tsx +0 -241
  201. package/src/identity/hub/shared/effects/types.ts +0 -53
  202. package/src/identity/hub/shared/reconciliation/index.ts +0 -14
  203. package/src/identity/wallet/page/grainient.ts +0 -278
  204. package/src/identity/wallet/page/html.ts +0 -28
  205. package/src/identity/wallet/page/styles/base.ts +0 -259
  206. package/src/identity/wallet/page/styles/components.ts +0 -262
  207. package/src/identity/wallet/page/styles/index.ts +0 -5
  208. package/src/identity/wallet/page/styles/responsive.ts +0 -247
  209. package/src/identity/wallet/page.tsx +0 -38
  210. package/src/mcp/approvals.ts +0 -113
  211. package/src/mcp/config.ts +0 -235
  212. package/src/mcp/manager.ts +0 -482
  213. package/src/mcp/managerHelpers.ts +0 -70
  214. package/src/mcp/names.ts +0 -19
  215. package/src/mcp/output.ts +0 -96
  216. package/src/models/ModelPicker.tsx +0 -1009
  217. package/src/models/catalog.ts +0 -327
  218. package/src/models/huggingface.ts +0 -712
  219. package/src/models/huggingfaceStorage.ts +0 -136
  220. package/src/models/llamacpp.ts +0 -848
  221. package/src/models/llamacppCommands.ts +0 -44
  222. package/src/models/llamacppConfig.ts +0 -34
  223. package/src/models/llamacppDiscovery.ts +0 -176
  224. package/src/models/llamacppOutput.ts +0 -65
  225. package/src/models/llamacppPreflight.ts +0 -158
  226. package/src/models/modelDisplay.ts +0 -180
  227. package/src/models/modelPickerCatalogFlow.ts +0 -56
  228. package/src/models/modelPickerCredentials.ts +0 -166
  229. package/src/models/modelPickerData.ts +0 -41
  230. package/src/models/modelPickerDisplay.tsx +0 -132
  231. package/src/models/modelPickerHfFlow.ts +0 -192
  232. package/src/models/modelPickerLocalRunnerFlow.ts +0 -115
  233. package/src/models/modelPickerOptions.ts +0 -457
  234. package/src/models/modelPickerTypes.ts +0 -69
  235. package/src/models/modelPickerUninstallFlow.ts +0 -48
  236. package/src/models/modelPickerViewHelpers.ts +0 -174
  237. package/src/models/modelRecommendation.ts +0 -139
  238. package/src/models/providerDisplay.ts +0 -16
  239. package/src/models/runtimeDetection.ts +0 -81
  240. package/src/models/uncensoredCatalog.ts +0 -86
  241. package/src/providers/anthropic.ts +0 -290
  242. package/src/providers/contracts.ts +0 -71
  243. package/src/providers/errors.ts +0 -80
  244. package/src/providers/gemini.ts +0 -391
  245. package/src/providers/openai-chat.ts +0 -474
  246. package/src/providers/openai-responses-format.ts +0 -177
  247. package/src/providers/openai-responses.ts +0 -306
  248. package/src/providers/openaiChatWire.ts +0 -124
  249. package/src/providers/registry.ts +0 -120
  250. package/src/providers/retry.ts +0 -58
  251. package/src/providers/sse.ts +0 -93
  252. package/src/runtime/compaction.ts +0 -395
  253. package/src/runtime/cwd.ts +0 -43
  254. package/src/runtime/providerTurn.ts +0 -38
  255. package/src/runtime/sessionMode.ts +0 -55
  256. package/src/runtime/systemPrompt.ts +0 -213
  257. package/src/runtime/textToolParser.ts +0 -161
  258. package/src/runtime/toolClaimGuards.ts +0 -143
  259. package/src/runtime/toolExecution.ts +0 -304
  260. package/src/runtime/toolIntent.ts +0 -143
  261. package/src/runtime/turn.ts +0 -369
  262. package/src/runtime/turnNudges.ts +0 -223
  263. package/src/runtime/turnTypes.ts +0 -86
  264. package/src/storage/factoryReset.ts +0 -127
  265. package/src/storage/history.ts +0 -58
  266. package/src/storage/permissions.ts +0 -76
  267. package/src/storage/rewind.ts +0 -266
  268. package/src/storage/sessionExport.ts +0 -49
  269. package/src/storage/sessions.ts +0 -495
  270. package/src/tools/bashSafety.ts +0 -186
  271. package/src/tools/bashTool.ts +0 -140
  272. package/src/tools/changeDirectoryTool.ts +0 -213
  273. package/src/tools/contracts.ts +0 -192
  274. package/src/tools/deleteFileTool.ts +0 -116
  275. package/src/tools/editTool.ts +0 -165
  276. package/src/tools/editUtils.ts +0 -170
  277. package/src/tools/fileDiff.ts +0 -261
  278. package/src/tools/listDirectoryTool.ts +0 -55
  279. package/src/tools/listSkillFilesTool.ts +0 -77
  280. package/src/tools/listSkillsTool.ts +0 -68
  281. package/src/tools/mcpResourceTools.ts +0 -95
  282. package/src/tools/permissionRules.ts +0 -85
  283. package/src/tools/privateContinuityEditTool.ts +0 -187
  284. package/src/tools/privateContinuityReadTool.ts +0 -106
  285. package/src/tools/readSkillTool.ts +0 -107
  286. package/src/tools/readTool.ts +0 -85
  287. package/src/tools/registry.ts +0 -103
  288. package/src/tools/writeFileTool.ts +0 -167
  289. package/src/ui/BrandSplash.tsx +0 -133
  290. package/src/ui/terminalTitle.ts +0 -30
  291. package/src/utils/images.ts +0 -140
  292. package/src/utils/markdownSegments.ts +0 -51
  293. package/src/utils/messages.ts +0 -37
  294. package/src/utils/withRetry.ts +0 -324
  295. /package/src/identity/{hub → manager}/continuity/state.ts +0 -0
  296. /package/src/identity/{hub → manager}/custody/helpers.ts +0 -0
  297. /package/src/identity/{hub → manager}/custody/preflight.ts +0 -0
  298. /package/src/identity/{hub → manager}/custody/state.ts +0 -0
  299. /package/src/identity/{hub → manager}/custody/useCustodyFlow.tsx +0 -0
  300. /package/src/identity/{hub → manager}/ens/EnsEditFlow.tsx +0 -0
  301. /package/src/identity/{hub → manager}/ens/advancedEnsValidation.ts +0 -0
  302. /package/src/identity/{hub → manager}/ens/state.ts +0 -0
  303. /package/src/identity/{hub → manager}/ens/transactions.ts +0 -0
  304. /package/src/identity/{hub → manager}/ens/types.ts +0 -0
  305. /package/src/identity/{hub → manager}/profile/identity.ts +0 -0
  306. /package/src/identity/{hub → manager}/restore/envelopes.ts +0 -0
  307. /package/src/identity/{hub → manager}/restore/helpers.ts +0 -0
  308. /package/src/identity/{hub → manager}/restore/recovery.ts +0 -0
  309. /package/src/identity/{hub → manager}/restore/resolve.ts +0 -0
  310. /package/src/identity/{hub → manager}/shared/components/BusyScreen.tsx +0 -0
  311. /package/src/identity/{hub → manager}/shared/components/NetworkScreen.tsx +0 -0
  312. /package/src/identity/{hub → manager}/shared/components/PinataJwtInput.tsx +0 -0
  313. /package/src/identity/{hub → manager}/shared/effects/receipts.ts +0 -0
  314. /package/src/identity/{hub → manager}/shared/effects/sync.ts +0 -0
  315. /package/src/identity/{hub → manager}/shared/model/format.ts +0 -0
  316. /package/src/identity/{hub → manager}/shared/operatorWallets.ts +0 -0
  317. /package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/ownership.ts +0 -0
  318. /package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/types.ts +0 -0
  319. /package/src/identity/{hub → manager}/shared/reconciliation/walletSetup.ts +0 -0
  320. /package/src/identity/{hub → manager}/shared/txGuard.ts +0 -0
  321. /package/src/identity/{hub → manager}/transfer/progress.ts +0 -0
  322. /package/src/identity/{hub → manager}/transfer/state.ts +0 -0
@@ -69,7 +69,7 @@ export function renderAdvancedEnsPhase({
69
69
  return (
70
70
  <Surface
71
71
  title="Checking ENS and Token Ownership"
72
- subtitle={`Verifying the connected wallet manages ${phase.rootName} and owns the ERC-8004 token.`}
72
+ subtitle={`Verifying ${phase.rootName}.`}
73
73
  footer={footerHint('esc back')}
74
74
  >
75
75
  <Box marginTop={1}>
@@ -117,7 +117,7 @@ export function renderAdvancedEnsPhase({
117
117
  return (
118
118
  <Surface
119
119
  title="Check Agent Subdomain"
120
- subtitle={`Verifying ${phase.label}.${phase.rootName} on Ethereum Mainnet.`}
120
+ subtitle={`Verifying ${phase.label}.${phase.rootName}.`}
121
121
  footer={footerHint('esc back')}
122
122
  >
123
123
  <Box marginTop={1}>
@@ -78,15 +78,15 @@ export function renderEnsMaintenancePhase({
78
78
  const multiNeedsCustodySetup = isAdvanced && !savedOwnerAddress
79
79
  const subtitle = currentEnsName
80
80
  ? `This agent resolves at ${currentEnsName}.`
81
- : 'Link an ENS name so others can find this agent by name instead of token ID.'
81
+ : 'Assign a name so others can find this agent.'
82
82
  const linkHint = multiNeedsCustodySetup
83
83
  ? 'Set Advanced custody first via Custody Mode'
84
84
  : isAdvanced
85
- ? 'Root → Name → Review → Apply'
86
- : 'Root → Name → Review → Apply'
85
+ ? 'Root → Name → Apply'
86
+ : 'Root → Name → Apply'
87
87
  const options: Array<{ value: EnsAction; role?: 'section' | 'utility'; label: string; hint?: string; disabled?: boolean }> = []
88
88
  if (currentEnsName) {
89
- options.push({ value: 'unlink', label: 'Unlink Name', hint: 'Removes the name from the token. Link a different one anytime.' })
89
+ options.push({ value: 'unlink', label: 'Unlink Name' })
90
90
  } else {
91
91
  options.push({
92
92
  value: 'link',
@@ -95,8 +95,7 @@ export function renderEnsMaintenancePhase({
95
95
  disabled: multiNeedsCustodySetup,
96
96
  })
97
97
  }
98
- options.push({ value: 'back', role: 'section', label: 'Navigation' })
99
- options.push({ value: 'back', label: 'Back', hint: 'Return to Identity Hub', role: 'utility' })
98
+ options.push({ value: 'back', label: 'Back', role: 'utility' })
100
99
  return (
101
100
  <Surface
102
101
  title="ENS Name"
@@ -134,7 +133,7 @@ export function renderEnsMaintenancePhase({
134
133
  subtitle={`Reading ethagent records from ${phase.fullName}`}
135
134
  footer={footerHint('esc back')}
136
135
  >
137
- <Spinner label="reading current ENS record values..." />
136
+ <Spinner label="reading ENS records..." />
138
137
  <EscCancel onCancel={() => setPhase({ kind: 'mode-select' })} />
139
138
  </Surface>
140
139
  )
@@ -167,7 +166,7 @@ export function renderEnsMaintenancePhase({
167
166
  subtitle={`Verifying the parent of ${phase.fullName} on Ethereum mainnet.`}
168
167
  footer={footerHint('esc back')}
169
168
  >
170
- <Spinner label="reading parent owner from ENS..." />
169
+ <Spinner label="reading ENS owner..." />
171
170
  <EscCancel onCancel={() => setPhase({ kind: 'mode-select' })} />
172
171
  </Surface>
173
172
  )
@@ -186,8 +185,7 @@ export function renderEnsMaintenancePhase({
186
185
  <Box>
187
186
  <Select<'back'>
188
187
  options={[
189
- { value: 'back', role: 'section', label: 'Navigation' },
190
- { value: 'back', label: 'Back to ENS', hint: 'Return to ENS menu', role: 'utility' },
188
+ { value: 'back', label: 'Back to ENS', role: 'utility' },
191
189
  ]}
192
190
  hintLayout="inline"
193
191
  onSubmit={() => setPhase({ kind: 'mode-select' })}
@@ -203,7 +201,7 @@ export function renderEnsMaintenancePhase({
203
201
  return (
204
202
  <Surface
205
203
  title="Delete ENS Subdomain"
206
- subtitle={`Clear the onchain entry for ${plan.fullName} at ${plan.parentName}.`}
204
+ subtitle={`Remove ${plan.fullName} from ${plan.parentName}.`}
207
205
  footer={footerHint('enter select · esc back')}
208
206
  >
209
207
  <Box flexDirection="column" marginBottom={1}>
@@ -214,18 +212,13 @@ export function renderEnsMaintenancePhase({
214
212
  label="Path"
215
213
  value={plan.parentWrapped ? 'NameWrapper.setSubnodeRecord' : 'Registry.setSubnodeRecord'}
216
214
  />
217
- <EnsSetupRow
218
- label="What changes"
219
- value="Onchain: subdomain owner and resolver set to 0. Locally: this token unlinks from the name."
220
- />
221
215
  </Box>
222
216
  <Box>
223
217
  <Select<'delete' | 'back'>
224
218
  options={[
225
219
  { value: 'delete', role: 'section', label: 'Action' },
226
- { value: 'delete', label: 'Delete Subdomain', hint: 'Sign with the owner wallet to clear the onchain entry' },
227
- { value: 'back', role: 'section', label: 'Navigation' },
228
- { value: 'back', label: 'Back', hint: 'Return to ENS menu', role: 'utility' },
220
+ { value: 'delete', label: 'Delete Subdomain', hint: 'Owner wallet signs' },
221
+ { value: 'back', label: 'Back', role: 'utility' },
229
222
  ]}
230
223
  hintLayout="inline"
231
224
  onSubmit={choice => {
@@ -265,14 +258,10 @@ export function renderEnsMaintenancePhase({
265
258
  subtitle={`${phase.fullName} is cleared onchain and unlinked from this token.`}
266
259
  footer={footerHint('enter select · esc back')}
267
260
  >
268
- <Box flexDirection="column" marginBottom={1}>
269
- <Text color={theme.text}>The label is freed for reuse on the parent name.</Text>
270
- </Box>
271
261
  <Box>
272
262
  <Select<'back'>
273
263
  options={[
274
- { value: 'back', role: 'section', label: 'Navigation' },
275
- { value: 'back', label: 'Back to ENS', hint: 'Return to ENS menu', role: 'utility' },
264
+ { value: 'back', label: 'Back to ENS', role: 'utility' },
276
265
  ]}
277
266
  hintLayout="inline"
278
267
  onSubmit={() => setPhase({ kind: 'mode-select' })}
@@ -72,11 +72,9 @@ export const SimpleEnsIssueScreen: React.FC<SimpleEnsIssueScreenProps> = ({
72
72
  <Box marginTop={1}>
73
73
  <Select<Action>
74
74
  options={[
75
- { value: 'create', role: 'section', label: 'Create Subdomain' },
76
- { value: 'create', label: 'Create This ENS Name', hint: 'Connected wallet creates it and sets the required records' },
77
- { value: 'change', label: 'Pick A Different Name', hint: 'Return to subdomain entry with this name still filled in' },
78
- { value: 'back', role: 'section', label: 'Navigation' },
79
- { value: 'back', label: 'Back', hint: 'Return to subdomain entry', role: 'utility' },
75
+ { value: 'create', label: 'Create This ENS Name' },
76
+ { value: 'change', label: 'Pick A Different Name' },
77
+ { value: 'back', label: 'Back', role: 'utility' },
80
78
  ]}
81
79
  hintLayout="inline"
82
80
  onSubmit={choice => {
@@ -121,9 +119,6 @@ export const EnsSetupReviewScreen: React.FC<EnsSetupReviewScreenProps> = ({
121
119
  : setup.registryAction === 'set-wrapped-resolver'
122
120
  ? 'Set Wrapped Resolver'
123
121
  : 'Subdomain Ready'
124
- const beginHint = setup.registryAction === 'none'
125
- ? 'Ethereum Mainnet: subdomain already ready'
126
- : 'Ethereum Mainnet: create or prepare subdomain'
127
122
  const reusingExistingSubdomain = setup.registryAction === 'none'
128
123
  return (
129
124
  <Surface
@@ -147,10 +142,8 @@ export const EnsSetupReviewScreen: React.FC<EnsSetupReviewScreenProps> = ({
147
142
  <Box marginTop={1}>
148
143
  <Select<Action>
149
144
  options={[
150
- { value: 'begin', role: 'section', label: 'Subdomain' },
151
- { value: 'begin', label: 'Continue Setup', hint: beginHint },
152
- { value: 'back', role: 'section', label: 'Navigation' },
153
- { value: 'back', label: 'Back', hint: isSimple ? 'Return to subdomain entry' : 'Return to operator wallet', role: 'utility' },
145
+ { value: 'begin', label: 'Continue Setup' },
146
+ { value: 'back', label: 'Back', role: 'utility' },
154
147
  ]}
155
148
  hintLayout="inline"
156
149
  onSubmit={choice => {
@@ -209,10 +202,8 @@ export const EnsSetupBlockedScreen: React.FC<EnsSetupBlockedScreenProps> = ({
209
202
  <Box marginTop={1}>
210
203
  <Select<Action>
211
204
  options={[
212
- { value: 'check', role: 'section', label: 'Automation' },
213
- { value: 'check', label: 'Check Again', hint: isSimple ? 'Re-run the simple ENS automation check' : 'Re-read ENS after the token is back with the owner wallet' },
214
- { value: 'back', role: 'section', label: 'Navigation' },
215
- { value: 'back', label: 'Back', hint: isSimple ? 'Return to subdomain entry' : 'Return to the previous setup step', role: 'utility' },
205
+ { value: 'check', label: 'Check Again' },
206
+ { value: 'back', label: 'Back', role: 'utility' },
216
207
  ]}
217
208
  hintLayout="inline"
218
209
  onSubmit={choice => {
@@ -254,13 +245,10 @@ export const UnlinkEnsReviewScreen: React.FC<UnlinkEnsReviewScreenProps> = ({
254
245
  <Box flexDirection="column">
255
246
  <EnsSetupRow label="ENS name" value={fullName} />
256
247
  <EnsSetupRow label="Custody" value={displayCustodyMode(currentMode)} />
257
- <Text color={theme.dim}>
258
- Records on Ethereum Mainnet, then token URI on {registryNetworkLabel}.
259
- </Text>
260
248
  {recordsAlreadyEmpty ? (
261
249
  <Box marginTop={1}>
262
250
  <Text color={theme.dim}>
263
- All ethagent ENS records are already empty. Unlink removes only the token URI link.
251
+ Removes the token URI link only.
264
252
  </Text>
265
253
  </Box>
266
254
  ) : (
@@ -278,16 +266,8 @@ export const UnlinkEnsReviewScreen: React.FC<UnlinkEnsReviewScreenProps> = ({
278
266
  <Box marginTop={1}>
279
267
  <Select<Action>
280
268
  options={[
281
- { value: 'unlink', role: 'section', label: 'Unlink' },
282
- {
283
- value: 'unlink',
284
- label: 'Unlink ENS',
285
- hint: recordsAlreadyEmpty
286
- ? 'Skip the records transaction and remove only the token URI link'
287
- : 'Clear ethagent-owned records, then remove the token URI link',
288
- },
289
- { value: 'back', role: 'section', label: 'Navigation' },
290
- { value: 'back', label: 'Back', hint: 'Return to ENS setup', role: 'utility' },
269
+ { value: 'unlink', label: 'Unlink ENS' },
270
+ { value: 'back', label: 'Back', role: 'utility' },
291
271
  ]}
292
272
  hintLayout="inline"
293
273
  onSubmit={choice => {
@@ -339,7 +319,7 @@ export const ReviewScreen: React.FC<ReviewScreenProps> = ({
339
319
  type ReviewAction = 'continue' | 'create' | 'check-again' | 'change' | 'back'
340
320
  const changedDiffs = recordsDiff.filter(d => d.changed)
341
321
  const hasRecordChanges = changedDiffs.length > 0
342
- const reviewSubtitle = 'Review Ethereum Mainnet ENS address and text records before saving this ENS link. No token approval is requested.'
322
+ const reviewSubtitle = 'Review ENS records before linking.'
343
323
 
344
324
  if (!validation.ok) {
345
325
  const reason = ensValidationReasonText(validation.reason)
@@ -368,14 +348,12 @@ export const ReviewScreen: React.FC<ReviewScreenProps> = ({
368
348
  options={[
369
349
  ...(onCreate
370
350
  ? [
371
- { value: 'create' as ReviewAction, role: 'section' as const, label: 'Create Subdomain' },
372
- { value: 'create' as ReviewAction, label: 'Create This ENS Name', hint: 'Use ethagent to create it and set the required records' },
351
+ { value: 'create' as ReviewAction, label: 'Create This ENS Name' },
373
352
  ]
374
353
  : []),
375
- { value: 'check-again', label: 'Check Again', hint: 'Re-verify on Ethereum mainnet' },
376
- { value: 'change', label: 'Pick A Different Name', hint: 'Return to the name picker' },
377
- { value: 'back', role: 'section', label: 'Navigation' },
378
- { value: 'back', label: 'Back', hint: 'Return to subdomain entry', role: 'utility' },
354
+ { value: 'check-again', label: 'Check Again' },
355
+ { value: 'change', label: 'Pick A Different Name' },
356
+ { value: 'back', label: 'Back', role: 'utility' },
379
357
  ]}
380
358
  hintLayout="inline"
381
359
  onSubmit={choice => {
@@ -392,11 +370,9 @@ export const ReviewScreen: React.FC<ReviewScreenProps> = ({
392
370
  }
393
371
 
394
372
  const options: Array<SelectOption<ReviewAction>> = [
395
- { value: 'continue', role: 'section', label: modeSwitchHeading(currentEnsName, currentMode, fullName, mode) },
396
- { value: 'continue', label: 'Continue Setup', hint: hasRecordChanges ? `Ethereum Mainnet: sign ${changedDiffs.length} record${changedDiffs.length === 1 ? '' : 's'}; ${registryNetworkLabel}: save token URI` : `Records match; ${registryNetworkLabel}: save token URI` },
397
- { value: 'change', label: 'Pick A Different Name', hint: 'Return to the name picker' },
398
- { value: 'back', role: 'section', label: 'Navigation' },
399
- { value: 'back', label: 'Back', hint: 'Return to Identity Hub', role: 'utility' },
373
+ { value: 'continue', label: 'Continue Setup' },
374
+ { value: 'change', label: 'Pick A Different Name' },
375
+ { value: 'back', label: 'Back', role: 'utility' },
400
376
  ]
401
377
 
402
378
  return (
@@ -55,7 +55,7 @@ export const DeleteSubdomainTxRunner: React.FC<{
55
55
  title="Delete ENS Subdomain"
56
56
  subtitle={`Clearing the subnode for ${plan.fullName} at ${plan.parentName} on Ethereum mainnet.`}
57
57
  walletSession={walletSession}
58
- label="waiting for owner wallet transaction..."
58
+ label="waiting for wallet to delete the subdomain..."
59
59
  onCancel={() => onError('Subdomain deletion cancelled.')}
60
60
  />
61
61
  )
@@ -133,8 +133,6 @@ export const SubdomainEntry: React.FC<SubdomainEntryProps> = ({ parent, ownerAdd
133
133
  title={`Subdomain of ${parent}`}
134
134
  footer={footerHint('enter continues · esc back')}
135
135
  >
136
- <Text color={theme.dim}>Type the subdomain name that goes before .{parent}.</Text>
137
- <Text color={theme.dim}>Use lowercase letters, digits, and hyphens.</Text>
138
136
  {error ? <Text color={theme.accentError}>{error}</Text> : null}
139
137
  <TextInput
140
138
  key={`edit-ens-subdomain-${parent}`}
@@ -106,7 +106,7 @@ export function renderSimpleEnsPhase({
106
106
  >
107
107
  <Box marginTop={1}>
108
108
  <Spinner
109
- label={`Looking up root ENS names for ${shortAddress(ownerAddress)}...`}
109
+ label="looking up ENS names..."
110
110
  startedAt={discoveryStartedAt}
111
111
  />
112
112
  </Box>
@@ -130,30 +130,25 @@ export function renderSimpleEnsPhase({
130
130
  ...ownedNames.map(name => ({
131
131
  value: `pick:${name}` as DomainAction,
132
132
  label: name,
133
- hint: `Next, choose the subdomain under ${name}`,
133
+ hint: 'Choose a subdomain',
134
134
  })),
135
135
  ]
136
136
  : []),
137
- { value: 'open-ens-domains' as DomainAction, role: 'section' as const, label: 'No Parent Name?' },
138
- { value: 'open-ens-domains' as DomainAction, label: 'Register .eth Name', hint: 'Opens ENS app; return once this wallet owns one' },
139
- ...(noOwnedNames || discovery.status === 'ok'
140
- ? [{ value: 'retry' as DomainAction, label: 'Scan Again', hint: 'Re-run root .eth name discovery for this wallet' }]
141
- : []),
137
+ { value: 'open-ens-domains' as DomainAction, label: 'Register .eth Name', hint: 'Opens the ENS app' },
142
138
  ...(discovery.status === 'error'
143
139
  ? [
144
- { value: 'retry' as DomainAction, label: errorMessage ? 'Try Again' : 'Retry Lookup', hint: 'Retry root ENS name search' },
145
- { value: 'manual' as DomainAction, label: 'Enter ENS Name Manually', hint: 'Lookup failed; type a root .eth name you own' },
140
+ { value: 'retry' as DomainAction, label: errorMessage ? 'Try Again' : 'Retry Lookup', hint: 'Retry lookup' },
141
+ { value: 'manual' as DomainAction, label: 'Enter ENS Name Manually' },
146
142
  ]
147
143
  : []),
148
- { value: 'back', role: 'section', label: 'Navigation' },
149
- { value: 'back', label: 'Back', hint: 'Return to setup type', role: 'utility' },
144
+ { value: 'back', label: 'Back', role: 'utility' },
150
145
  ]
151
146
 
152
147
  const advancedMode = phase.mode === 'advanced'
153
148
  return (
154
149
  <Surface
155
150
  title="Assign ENS Name"
156
- subtitle="Choose a root .eth name, then create a dedicated agent subdomain under it."
151
+ subtitle="Choose a root .eth name to create an agent subdomain under."
157
152
  footer={footerHint('enter select · esc back')}
158
153
  >
159
154
  <EnsStatusBanner identity={identity} noRootEnsName={noOwnedNames} />
@@ -168,7 +163,7 @@ export function renderSimpleEnsPhase({
168
163
  ? (
169
164
  <Box marginTop={1} flexDirection="column">
170
165
  <Text color={theme.dim}>This wallet does not own a parent <Text color={theme.text}>.eth</Text> name yet.</Text>
171
- <Text color={theme.dim}>Register one at <Text color={theme.text}>{ENS_DOMAINS_URL}</Text>, then come back and Scan Again.</Text>
166
+ <Text color={theme.dim}>Register one at <Text color={theme.text}>{ENS_DOMAINS_URL}</Text>, then come back here.</Text>
172
167
  </Box>
173
168
  )
174
169
  : null}
@@ -216,9 +211,6 @@ export function renderSimpleEnsPhase({
216
211
  {advancedMode
217
212
  ? <Text color={theme.dim}>Owner wallet: <Text color={theme.text}>{shortAddress(ownerAddress)}</Text></Text>
218
213
  : null}
219
- <Box marginTop={1}>
220
- <Text color={theme.dim}>Only root .eth names on Ethereum mainnet are supported.</Text>
221
- </Box>
222
214
  {phase.error ? <Text color={theme.accentError}>{phase.error}</Text> : null}
223
215
  <TextInput
224
216
  key={`edit-ens-parent-manual-${advancedMode ? 'advanced' : 'simple'}`}
@@ -266,7 +258,7 @@ export function renderSimpleEnsPhase({
266
258
  footer={footerHint('esc cancels')}
267
259
  >
268
260
  <Box marginTop={1}>
269
- <Spinner label="Looking up resolver and address record..." />
261
+ <Spinner label="checking ENS record..." />
270
262
  </Box>
271
263
  <EscCancel onCancel={() => backToSimpleSubdomain(phase.fullName)} />
272
264
  </Surface>
@@ -294,10 +286,9 @@ export function renderSimpleEnsPhase({
294
286
  >
295
287
  <Box flexDirection="column">
296
288
  <Text color={theme.dim}>Name: <Text color={theme.text}>{phase.fullName}</Text></Text>
297
- <Text color={theme.dim}>Connected wallet will create the subdomain and set the required records if checks pass.</Text>
298
289
  </Box>
299
290
  <Box marginTop={1}>
300
- <Spinner label="checking parent ownership and record changes..." />
291
+ <Spinner label="checking records..." />
301
292
  </Box>
302
293
  <EscCancel onCancel={() => backToSimpleSubdomain(phase.fullName)} />
303
294
  </Surface>
@@ -1,19 +1,23 @@
1
1
  import React from 'react'
2
+ import { Box, Text } from 'ink'
3
+ import { Surface } from '../../../ui/Surface.js'
4
+ import { Select } from '../../../ui/Select.js'
5
+ import { theme } from '../../../ui/theme.js'
2
6
  import { openImageFilePicker } from '../../profile/imagePicker.js'
3
7
  import { readOwnerAddressField } from '../../identityCompat.js'
4
8
  import type { BrowserWalletReady } from '../../wallet/browserWallet.js'
5
- import type { ProfileUpdates, Step } from '../identityHubReducer.js'
9
+ import type { ProfileUpdates, Step } from '../reducer.js'
6
10
  import { readCustodyMode } from '../custody/state.js'
7
11
  import { OperatorWalletsScreen } from './EnsOperatorWalletsScreen.js'
8
- import { EDIT_PROFILE_STEPS, EditProfileFlow } from '../profile/EditProfileFlow.js'
9
- import { FlowTimeline } from '../shared/components/FlowTimeline.js'
12
+ import { EditProfileFlow } from '../profile/EditProfileFlow.js'
10
13
  import { WalletApprovalScreen } from '../shared/components/WalletApprovalScreen.js'
11
14
  import type { AgentReconciliation } from '../shared/reconciliation/index.js'
12
15
 
13
16
  type StepOf<K extends Step['kind']> = Extract<Step, { kind: K }>
14
17
 
15
- type IdentityHubEnsStep = StepOf<
18
+ type IdentityManagerEnsStep = StepOf<
16
19
  | 'manage-ens-operators'
20
+ | 'edit-profile-menu'
17
21
  | 'edit-profile-name'
18
22
  | 'edit-profile-description'
19
23
  | 'edit-profile-image'
@@ -26,7 +30,7 @@ type IdentityHubEnsStep = StepOf<
26
30
  >
27
31
 
28
32
  type EnsFlowProps = {
29
- step: IdentityHubEnsStep
33
+ step: IdentityManagerEnsStep
30
34
  walletSession: BrowserWalletReady | null
31
35
  reconciliation: AgentReconciliation
32
36
  onSetStep: (step: Step) => void
@@ -34,10 +38,12 @@ type EnsFlowProps = {
34
38
  onWalletReady: (session: BrowserWalletReady | null) => void
35
39
  onTriggerRebackup: (backStep: Step, profileUpdates?: ProfileUpdates) => void
36
40
  onTriggerPublicProfileSave: (backStep: Step, profileUpdates: ProfileUpdates) => void
41
+ onWithdrawFromVault: (step: IdentityManagerEnsStep) => void
37
42
  }
38
43
 
39
- export function isEnsStep(step: Step): step is IdentityHubEnsStep {
44
+ export function isEnsStep(step: Step): step is IdentityManagerEnsStep {
40
45
  return step.kind === 'manage-ens-operators'
46
+ || step.kind === 'edit-profile-menu'
41
47
  || step.kind === 'edit-profile-name'
42
48
  || step.kind === 'edit-profile-description'
43
49
  || step.kind === 'edit-profile-image'
@@ -58,7 +64,13 @@ export const EnsFlow: React.FC<EnsFlowProps> = ({
58
64
  onWalletReady,
59
65
  onTriggerRebackup,
60
66
  onTriggerPublicProfileSave,
67
+ onWithdrawFromVault,
61
68
  }) => {
69
+ if (step.kind === 'edit-profile-ens'
70
+ && (reconciliation.custody === 'advanced' || reconciliation.custody === 'mid-flow-uri-pending')) {
71
+ return <EnsVaultGate onWithdraw={() => onWithdrawFromVault(step)} onBack={onBack} />
72
+ }
73
+
62
74
  if (step.kind === 'manage-ens-operators') {
63
75
  return (
64
76
  <OperatorWalletsScreen
@@ -75,61 +87,116 @@ export const EnsFlow: React.FC<EnsFlowProps> = ({
75
87
  }
76
88
 
77
89
  if (isEditProfileStep(step)) {
90
+ const editStep = step
91
+ const menuFromDrafts = (drafts: { name?: string; description?: string; imagePath?: string }) => {
92
+ const next: Step = {
93
+ kind: 'edit-profile-menu',
94
+ identity: editStep.identity,
95
+ registry: editStep.registry,
96
+ returnTo: 'returnTo' in editStep ? editStep.returnTo : undefined,
97
+ ...(drafts.name !== undefined ? { name: drafts.name } : {}),
98
+ ...(drafts.description !== undefined ? { description: drafts.description } : {}),
99
+ ...(drafts.imagePath !== undefined ? { imagePath: drafts.imagePath } : {}),
100
+ }
101
+ onSetStep(next)
102
+ }
103
+ const currentDrafts = () => ({
104
+ name: 'name' in editStep ? editStep.name : undefined,
105
+ description: 'description' in editStep ? editStep.description : undefined,
106
+ imagePath: 'imagePath' in editStep ? editStep.imagePath : undefined,
107
+ })
78
108
  return (
79
109
  <EditProfileFlow
80
110
  step={step}
81
111
  reconciliation={reconciliation}
82
- onNameSubmit={name => {
83
- if (step.kind !== 'edit-profile-name') return
112
+ onSelectField={field => {
113
+ if (editStep.kind !== 'edit-profile-menu') return
114
+ const carry = currentDrafts()
115
+ if (field === 'name') {
116
+ onSetStep({
117
+ kind: 'edit-profile-name',
118
+ identity: editStep.identity,
119
+ registry: editStep.registry,
120
+ ...(carry.name !== undefined ? { name: carry.name } : {}),
121
+ ...(carry.description !== undefined ? { description: carry.description } : {}),
122
+ ...(carry.imagePath !== undefined ? { imagePath: carry.imagePath } : {}),
123
+ returnTo: editStep.returnTo,
124
+ })
125
+ return
126
+ }
127
+ if (field === 'description') {
128
+ onSetStep({
129
+ kind: 'edit-profile-description',
130
+ identity: editStep.identity,
131
+ registry: editStep.registry,
132
+ ...(carry.name !== undefined ? { name: carry.name } : {}),
133
+ ...(carry.description !== undefined ? { description: carry.description } : {}),
134
+ ...(carry.imagePath !== undefined ? { imagePath: carry.imagePath } : {}),
135
+ returnTo: editStep.returnTo,
136
+ })
137
+ return
138
+ }
84
139
  onSetStep({
85
- kind: 'edit-profile-description',
86
- identity: step.identity,
87
- registry: step.registry,
88
- name,
89
- description: step.description,
90
- imagePath: step.imagePath,
91
- returnTo: step.returnTo,
140
+ kind: 'edit-profile-image',
141
+ identity: editStep.identity,
142
+ registry: editStep.registry,
143
+ ...(carry.name !== undefined ? { name: carry.name } : {}),
144
+ ...(carry.description !== undefined ? { description: carry.description } : {}),
145
+ ...(carry.imagePath !== undefined ? { imagePath: carry.imagePath } : {}),
146
+ returnTo: editStep.returnTo,
92
147
  })
93
148
  }}
149
+ onSaveProfile={() => {
150
+ if (editStep.kind !== 'edit-profile-menu') return
151
+ const carry = currentDrafts()
152
+ const savedName = (editStep.identity.state as Record<string, unknown> | undefined)?.['name'] as string | undefined
153
+ const savedDescription = (editStep.identity.state as Record<string, unknown> | undefined)?.['description'] as string | undefined
154
+ const updates: ProfileUpdates = {
155
+ name: carry.name ?? savedName ?? '',
156
+ description: carry.description ?? savedDescription ?? '',
157
+ ...(carry.imagePath !== undefined ? { imagePath: carry.imagePath } : {}),
158
+ }
159
+ onTriggerPublicProfileSave(editStep.returnTo ?? { kind: 'continuity-public' }, updates)
160
+ }}
161
+ onNameSubmit={name => {
162
+ if (editStep.kind !== 'edit-profile-name') return
163
+ menuFromDrafts({ ...currentDrafts(), name })
164
+ }}
94
165
  onDescriptionSubmit={description => {
95
- if (step.kind !== 'edit-profile-description') return
96
- onSetStep({ kind: 'edit-profile-image', identity: step.identity, registry: step.registry, name: step.name, description, imagePath: step.imagePath, returnTo: step.returnTo })
166
+ if (editStep.kind !== 'edit-profile-description') return
167
+ menuFromDrafts({ ...currentDrafts(), description })
97
168
  }}
98
169
  onIconSubmit={iconPath => {
99
- if (step.kind !== 'edit-profile-image') return
100
- onSetStep({ kind: 'edit-profile-review', identity: step.identity, registry: step.registry, name: step.name, description: step.description, ...(iconPath !== undefined ? { imagePath: iconPath } : {}), returnTo: step.returnTo })
170
+ if (editStep.kind !== 'edit-profile-image') return
171
+ if (iconPath === undefined) {
172
+ menuFromDrafts(currentDrafts())
173
+ return
174
+ }
175
+ menuFromDrafts({ ...currentDrafts(), imagePath: iconPath })
101
176
  }}
102
177
  onIconPick={() => {
103
- if (step.kind !== 'edit-profile-image') return
104
- const iconStep = step
178
+ if (editStep.kind !== 'edit-profile-image') return
179
+ const iconStep = editStep
105
180
  void openImageFilePicker()
106
181
  .then(result => {
107
182
  if (!result.ok) {
108
183
  onSetStep({ ...iconStep, error: result.cancelled ? 'icon selection cancelled.' : `${result.error}` })
109
184
  return
110
185
  }
111
- onSetStep({
112
- kind: 'edit-profile-review',
113
- identity: iconStep.identity,
114
- registry: iconStep.registry,
115
- name: iconStep.name,
116
- description: iconStep.description,
117
- imagePath: result.file,
118
- returnTo: iconStep.returnTo,
119
- })
186
+ menuFromDrafts({ ...currentDrafts(), imagePath: result.file })
120
187
  })
121
188
  .catch((err: unknown) => {
122
189
  onSetStep({ ...iconStep, error: `${(err as Error).message}` })
123
190
  })
124
191
  }}
125
192
  onReviewSave={() => {
126
- if (step.kind !== 'edit-profile-review') return
193
+ if (editStep.kind !== 'edit-profile-review') return
127
194
  const updates: ProfileUpdates = {
128
- name: step.name,
129
- description: step.description,
130
- ...(step.imagePath !== undefined ? { imagePath: step.imagePath } : {}),
195
+ name: editStep.name,
196
+ description: editStep.description,
197
+ ...(editStep.imagePath !== undefined ? { imagePath: editStep.imagePath } : {}),
131
198
  }
132
- onTriggerPublicProfileSave(step.returnTo ?? { kind: 'continuity-public' }, updates)
199
+ onTriggerPublicProfileSave(editStep.returnTo ?? { kind: 'continuity-public' }, updates)
133
200
  }}
134
201
  onEnsLink={(fullName, options) => {
135
202
  if (step.kind !== 'edit-profile-ens') return
@@ -194,6 +261,7 @@ export const EnsFlow: React.FC<EnsFlowProps> = ({
194
261
  }}
195
262
  onBack={onBack}
196
263
  onMenu={() => onSetStep(step.returnTo ?? { kind: 'continuity-public' })}
264
+ onBackToEditMenu={() => menuFromDrafts(currentDrafts())}
197
265
  />
198
266
  )
199
267
  }
@@ -206,7 +274,7 @@ export const EnsFlow: React.FC<EnsFlowProps> = ({
206
274
  ? `Ethereum Mainnet: sign one transaction to clear ethagent record values on ${step.fullName}. Requires gas.`
207
275
  : `Ethereum Mainnet: sign one transaction to set ENS records on ${step.fullName}. Requires gas.`}
208
276
  walletSession={walletSession}
209
- label="waiting for wallet transaction..."
277
+ label={step.clearRecords ? 'waiting for wallet to clear ENS records...' : 'waiting for wallet to update ENS records...'}
210
278
  onCancel={() => onSetStep({ kind: 'edit-profile-ens', identity: step.identity, registry: step.registry, returnTo: step.returnTo })}
211
279
  />
212
280
  )
@@ -219,7 +287,7 @@ export const EnsFlow: React.FC<EnsFlowProps> = ({
219
287
  title={step.setup.mode === 'simple' ? 'Use Connected Wallet' : 'Use Owner Wallet'}
220
288
  subtitle={`${signer} signs one Ethereum Mainnet ENS registry transaction for ${step.setup.fullName}.`}
221
289
  walletSession={walletSession}
222
- label={step.setup.mode === 'simple' ? 'waiting for connected wallet transaction...' : 'waiting for owner wallet transaction...'}
290
+ label="waiting for wallet to register the ENS name..."
223
291
  onCancel={() => onSetStep({
224
292
  kind: 'edit-profile-ens',
225
293
  identity: step.identity,
@@ -238,7 +306,7 @@ export const EnsFlow: React.FC<EnsFlowProps> = ({
238
306
  title={step.setup.mode === 'simple' ? 'Use Connected Wallet' : 'Use Owner Wallet'}
239
307
  subtitle={`${signer} signs one Ethereum Mainnet resolver transaction for ${step.setup.fullName}.`}
240
308
  walletSession={walletSession}
241
- label={step.setup.mode === 'simple' ? 'waiting for connected wallet transaction...' : 'waiting for owner wallet transaction...'}
309
+ label="waiting for wallet to set ENS records..."
242
310
  onCancel={() => onSetStep({
243
311
  kind: 'edit-profile-ens',
244
312
  identity: step.identity,
@@ -276,7 +344,7 @@ function publicProfileWalletApprovalView(step: StepOf<'public-profile-signing'>)
276
344
  }
277
345
  return {
278
346
  title: 'Use Wallet',
279
- subtitle: <FlowTimeline steps={EDIT_PROFILE_STEPS} current={5} />,
347
+ subtitle: 'Sign the public profile and ERC-8004 token URI update.',
280
348
  label: 'waiting for wallet signature...',
281
349
  }
282
350
  }
@@ -288,16 +356,40 @@ function usesAdvancedSetup(step: StepOf<'public-profile-signing'>): boolean {
288
356
  return custodyMode === 'advanced' && typeof ownerAddress === 'string' && ownerAddress.trim().length > 0
289
357
  }
290
358
 
291
- function isEditProfileStep(step: IdentityHubEnsStep): step is StepOf<
359
+ function isEditProfileStep(step: IdentityManagerEnsStep): step is StepOf<
360
+ | 'edit-profile-menu'
292
361
  | 'edit-profile-name'
293
362
  | 'edit-profile-description'
294
363
  | 'edit-profile-image'
295
364
  | 'edit-profile-review'
296
365
  | 'edit-profile-ens'
297
366
  > {
298
- return step.kind === 'edit-profile-name'
367
+ return step.kind === 'edit-profile-menu'
368
+ || step.kind === 'edit-profile-name'
299
369
  || step.kind === 'edit-profile-description'
300
370
  || step.kind === 'edit-profile-image'
301
371
  || step.kind === 'edit-profile-review'
302
372
  || step.kind === 'edit-profile-ens'
303
373
  }
374
+
375
+ const EnsVaultGate: React.FC<{ onWithdraw: () => void; onBack: () => void }> = ({ onWithdraw, onBack }) => (
376
+ <Surface
377
+ title="ENS Name"
378
+ footer={<Text color={theme.dim}>Token is in the Vault · enter select · esc back</Text>}
379
+ >
380
+ <Box marginBottom={1}>
381
+ <Text color={theme.textSubtle}>The owner wallet must hold the token directly to sign the ENS setup transaction.</Text>
382
+ </Box>
383
+ <Select<'withdraw' | 'back'>
384
+ options={[
385
+ { value: 'withdraw', role: 'section', label: 'Proceed' },
386
+ { value: 'withdraw', label: 'Withdraw Token from Vault' },
387
+ { value: 'back', role: 'section', label: 'Navigation' },
388
+ { value: 'back', label: 'Back', role: 'utility' },
389
+ ]}
390
+ hintLayout="inline"
391
+ onSubmit={choice => { if (choice === 'withdraw') onWithdraw(); else onBack() }}
392
+ onCancel={onBack}
393
+ />
394
+ </Surface>
395
+ )