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
@@ -36,6 +36,8 @@ import {
36
36
  type WalletContinuitySnapshotSlot,
37
37
  } from './envelopeTypes.js'
38
38
 
39
+ const MAX_WALLET_RESTORE_SLOTS = 256
40
+
39
41
  export function restoreContinuitySnapshotEnvelope(args: {
40
42
  envelope: ContinuitySnapshotEnvelope
41
43
  walletSignature: string
@@ -232,7 +234,7 @@ function restoreTransferContinuitySnapshotEnvelope(args: {
232
234
  }): ContinuitySnapshotPayload {
233
235
  const currentAddress = args.currentOwnerAddress
234
236
  ? toChecksumAddress(args.currentOwnerAddress)
235
- : toChecksumAddress(recoverAddressFromSignature(args.envelope.challenge, args.walletSignature))
237
+ : recoverTransferSlotAddress(args.envelope, args.walletSignature)
236
238
  const slot = transferSlotForCurrentOwner(args.envelope, currentAddress)
237
239
  assertSignatureForAddress(slot.challenge, args.walletSignature, slot.address)
238
240
 
@@ -348,6 +350,20 @@ function walletSlotForRestore(
348
350
  throw new ContinuitySnapshotRestoreSlotMissingError('unknown')
349
351
  }
350
352
 
353
+ function recoverTransferSlotAddress(
354
+ envelope: TransferContinuitySnapshotEnvelope,
355
+ walletSignature: string,
356
+ ): string {
357
+ for (const slot of [envelope.slots.owner, envelope.slots.target]) {
358
+ try {
359
+ const recovered = recoverAddressFromSignature(slot.challenge, walletSignature)
360
+ if (recovered.toLowerCase() === slot.address.toLowerCase()) return toChecksumAddress(slot.address)
361
+ } catch {
362
+ }
363
+ }
364
+ throw new ContinuityTransferSnapshotTargetMismatchError(envelope.ownerAddress, envelope.targetAddress, 'unknown')
365
+ }
366
+
351
367
  function transferSlotForCurrentOwner(
352
368
  envelope: TransferContinuitySnapshotEnvelope,
353
369
  currentOwner: string,
@@ -368,6 +384,9 @@ export function normalizeContinuitySnapshotEnvelope(input: unknown): ContinuityS
368
384
  }
369
385
  const ownerAddress = toChecksumAddress(input.ownerAddress)
370
386
  const token = normalizeContinuitySnapshotToken(input.token)
387
+ if (input.slots.length > MAX_WALLET_RESTORE_SLOTS) {
388
+ throw new Error('Continuity wallet snapshot has too many restore slots')
389
+ }
371
390
  const slots = input.slots.map(normalizeWalletSlot)
372
391
  if (slots.length === 0) throw new Error('Continuity wallet snapshot needs at least one slot')
373
392
  return {
@@ -1,7 +1,8 @@
1
1
  import type { EthagentIdentity } from '../../storage/config.js'
2
2
  import type { SkillIndexEntry } from './skills/types.js'
3
- import { identityOwnerAddress } from '../hub/custody/state.js'
3
+ import { identityOwnerAddress } from '../manager/custody/state.js'
4
4
  import { toChecksumAddress } from '../crypto/eth.js'
5
+ import { isDraftScaffold } from './skills/scaffold.js'
5
6
 
6
7
  type PublicSkill = {
7
8
  id: string
@@ -82,6 +83,7 @@ export function appendPublicSkillEntries(
82
83
  const usedIds = new Set(baselineIds)
83
84
  for (const entry of entries) {
84
85
  if (entry.visibility !== 'public') continue
86
+ if (isDraftScaffold(entry)) continue
85
87
  const id = uniqueSkillId(entry.name, usedIds)
86
88
  usedIds.add(id)
87
89
  appended.push({
@@ -8,11 +8,12 @@ import {
8
8
  } from '../publicSkills.js'
9
9
  import { ensureContinuityVault, ensureTrailingNewline, readOrDefault } from '../storage/files.js'
10
10
  import { listSkills } from './loadSkills.js'
11
+ import { isDraftScaffold } from './scaffold.js'
11
12
  import type { SkillIndexEntry } from './types.js'
12
13
 
13
14
  export async function derivePublicSkillEntries(identity: EthagentIdentity): Promise<SkillIndexEntry[]> {
14
15
  const entries = await listSkills(identity)
15
- return entries.filter(entry => entry.visibility === 'public')
16
+ return entries.filter(entry => entry.visibility === 'public' && !isDraftScaffold(entry))
16
17
  }
17
18
 
18
19
  export async function renderAgentCardJsonForIdentity(identity: EthagentIdentity): Promise<string> {
@@ -9,13 +9,13 @@ export function defaultSkillScaffold({ name, visibility = 'public' }: SkillScaff
9
9
  return [
10
10
  '---',
11
11
  `name: ${name}`,
12
- 'description:',
12
+ 'description: <one-line public description before publishing>',
13
13
  `visibility: ${visibility}`,
14
14
  '---',
15
15
  '',
16
16
  '# Overview',
17
17
  '',
18
- 'Describe in one or two sentences what this skill does and the problem it solves.',
18
+ 'Replace this draft with one or two sentences describing what this skill does and the problem it solves.',
19
19
  '',
20
20
  '# When to use',
21
21
  '',
@@ -48,5 +48,8 @@ export function isDraftScaffold(entry: { description: string; name: string }): b
48
48
  if (desc.length === 0) return true
49
49
  if (/^<.*>$/.test(desc)) return true
50
50
  if (desc === entry.name) return true
51
+ if (/^overview$/i.test(desc)) return true
52
+ if (/^describe in one or two sentences/i.test(desc)) return true
53
+ if (/^replace this draft/i.test(desc)) return true
51
54
  return false
52
55
  }
@@ -82,7 +82,7 @@ export async function updatePublishedContinuitySnapshotContentHashes(
82
82
  ): Promise<void> {
83
83
  await ensureContinuityVault(identity)
84
84
  const current = currentPublishedSnapshot(identity)
85
- const snapshots = await readPublishedContinuitySnapshotFile(identity)
85
+ const { snapshots, unparsable } = await readPublishedContinuitySnapshotLines(identity)
86
86
  const index = snapshots.findIndex(item => item.cid === cid)
87
87
  if (index === -1) {
88
88
  const base = current.find(item => item.cid === cid)
@@ -93,7 +93,7 @@ export async function updatePublishedContinuitySnapshotContentHashes(
93
93
  }
94
94
  await atomicWriteText(
95
95
  publishedContinuitySnapshotsPath(identity),
96
- snapshots.map(snapshot => JSON.stringify(snapshot)).join('\n') + '\n',
96
+ [...snapshots.map(snapshot => JSON.stringify(snapshot)), ...unparsable].join('\n') + '\n',
97
97
  { mode: 0o600 },
98
98
  )
99
99
  }
@@ -143,25 +143,32 @@ function refreshPublishedSnapshotSidecars(
143
143
  }
144
144
 
145
145
  async function readPublishedContinuitySnapshotFile(identity: EthagentIdentity): Promise<PublishedContinuitySnapshot[]> {
146
+ return (await readPublishedContinuitySnapshotLines(identity)).snapshots
147
+ }
148
+
149
+ async function readPublishedContinuitySnapshotLines(
150
+ identity: EthagentIdentity,
151
+ ): Promise<{ snapshots: PublishedContinuitySnapshot[]; unparsable: string[] }> {
146
152
  let raw: string
147
153
  try {
148
154
  raw = await fs.readFile(publishedContinuitySnapshotsPath(identity), 'utf8')
149
155
  } catch (error: unknown) {
150
- if ((error as NodeJS.ErrnoException).code === 'ENOENT') return []
156
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { snapshots: [], unparsable: [] }
151
157
  throw error
152
158
  }
153
159
 
154
160
  const snapshots: PublishedContinuitySnapshot[] = []
161
+ const unparsable: string[] = []
155
162
  for (const line of raw.split('\n')) {
156
163
  const trimmed = line.trim()
157
164
  if (!trimmed) continue
158
165
  try {
159
166
  snapshots.push(JSON.parse(trimmed) as PublishedContinuitySnapshot)
160
167
  } catch {
161
- continue
168
+ unparsable.push(trimmed)
162
169
  }
163
170
  }
164
- return snapshots
171
+ return { snapshots, unparsable }
165
172
  }
166
173
 
167
174
  function currentPublishedSnapshot(identity: EthagentIdentity): PublishedContinuitySnapshot[] {
@@ -32,27 +32,27 @@ export function defaultContinuityFiles(identity: EthagentIdentity, _now = new Da
32
32
  '',
33
33
  '## Persona',
34
34
  '',
35
- 'The standing voice, collaboration style, and behavior this agent keeps across sessions and models.',
35
+ 'Stable identity, voice, and collaboration defaults this agent carries across sessions and models. Replace these prompts with durable instructions.',
36
36
  '',
37
- '- Voice: <e.g., direct, concise, no filler>',
38
- '- Tone: <e.g., warm but professional>',
39
- '- Communication style: <e.g., bullet lists for technical work, prose for narrative>',
37
+ '- Role: what this agent is and the kind of work it should be trusted with.',
38
+ '- Voice: how it should sound when helping you.',
39
+ '- Collaboration style: how it should plan, ask questions, and present tradeoffs.',
40
40
  '',
41
41
  '## Principles',
42
42
  '',
43
- 'Owner-approved working principles. Decisions that should be made the same way every time.',
43
+ 'Owner-approved defaults for repeated decisions. Keep these durable, specific, and easy to apply.',
44
44
  '',
45
- '- <principle: e.g., always confirm before destructive operations>',
46
- '- <principle: e.g., bias toward editing existing code over creating new files>',
47
- '- <principle: e.g., never hide errors, always surface them clearly>',
45
+ '- Decision rule: how the agent should choose when there are tradeoffs.',
46
+ '- Engineering standard: what quality bar it should preserve.',
47
+ '- Escalation rule: when it should stop and ask.',
48
48
  '',
49
49
  '## Boundaries',
50
50
  '',
51
- 'What this agent must never do, regardless of how it is asked.',
51
+ 'Private limits and hard constraints. These override task-level convenience.',
52
52
  '',
53
- '- <boundary: e.g., never bypass authentication or authorization checks>',
54
- '- <boundary: e.g., never commit secrets to source control>',
55
53
  '- Never store seed phrases, private keys, raw wallet signatures, or API keys.',
54
+ '- Ask for explicit approval before destructive operations.',
55
+ '- Public capabilities belong in skill folders; keep private persona and limits here.',
56
56
  '',
57
57
  ].join('\n'),
58
58
  'MEMORY.md': [
@@ -62,11 +62,11 @@ export function defaultContinuityFiles(identity: EthagentIdentity, _now = new Da
62
62
  '',
63
63
  '## Durable User Preferences',
64
64
  '',
65
- 'Long-lived owner preferences that survive sessions and model switches. Capture how the user works.',
65
+ 'Long-lived owner preferences that survive sessions and model switches. Replace these prompts with stable facts.',
66
66
  '',
67
- '- Communication: <e.g., prefers concise summaries over walls of text>',
68
- '- Tooling: <e.g., VS Code, zsh, TypeScript over JavaScript>',
69
- '- Workflow: <e.g., reviews PRs in the morning, deploys on Fridays>',
67
+ '- Name and aliases:',
68
+ '- Communication preferences:',
69
+ '- Tooling and workflow:',
70
70
  '',
71
71
  '## Project Context',
72
72
  '',
@@ -74,14 +74,15 @@ export function defaultContinuityFiles(identity: EthagentIdentity, _now = new Da
74
74
  '',
75
75
  '### <project-name>',
76
76
  '',
77
- '- Repository: <url or local path>',
78
- '- Stack: <languages, frameworks, key libraries>',
79
- '- Conventions: <e.g., conventional commits, semver, branch naming>',
80
- '- Active workstream: <YYYY-MM-DD what you are focused on>',
77
+ '- Repository:',
78
+ '- Stack:',
79
+ '- Conventions:',
80
+ '- Active workstream: <YYYY-MM-DD summary>',
81
81
  '',
82
82
  '## Boundaries',
83
83
  '',
84
84
  'Never store seed phrases, private keys, raw wallet signatures, or API keys.',
85
+ 'Public capabilities belong in skill folders; keep private user and project facts here.',
85
86
  '',
86
87
  ].join('\n'),
87
88
  }
@@ -109,7 +109,7 @@ function hashContinuitySnapshotContent(value: string): string {
109
109
  return createHash('sha256').update(normalizeSnapshotContent(value), 'utf8').digest('hex')
110
110
  }
111
111
 
112
- function normalizeSnapshotContent(value: string): string {
112
+ export function normalizeSnapshotContent(value: string): string {
113
113
  const normalized = value.replace(/\r\n?/g, '\n')
114
114
  return normalized.endsWith('\n') ? normalized : `${normalized}\n`
115
115
  }
@@ -11,7 +11,7 @@ export const ENS_RPC_URLS = [
11
11
  'https://rpc.ankr.com/eth',
12
12
  ] as const
13
13
 
14
- export const ETH_NAME_PATTERN = /^([a-z0-9-]+.)+eth$/i
14
+ export const ETH_NAME_PATTERN = /^([a-z0-9-]+\.)+eth$/i
15
15
 
16
16
  export const ENS_REGISTRY_ABI = parseAbi([
17
17
  'function owner(bytes32 node) view returns (address)',
@@ -0,0 +1,33 @@
1
+ import React, { useEffect, useState } from 'react'
2
+ import { Box, useStdout } from 'ink'
3
+ import { IdentityManagerRoutes } from './Routes.js'
4
+ import type { IdentityManagerProps } from './types.js'
5
+ import { useIdentityManagerController } from './useController.js'
6
+ import { Wordmark } from './shared/components/Wordmark.js'
7
+
8
+ export type {
9
+ IdentityManagerInitialAction,
10
+ IdentityManagerResult,
11
+ } from './types.js'
12
+
13
+ export const IdentityManager: React.FC<IdentityManagerProps> = props => {
14
+ const controller = useIdentityManagerController(props)
15
+ const { stdout } = useStdout()
16
+ const [rows, setRows] = useState<number>(stdout?.rows ?? 24)
17
+
18
+ useEffect(() => {
19
+ if (!stdout) return
20
+ const onResize = () => setRows(stdout.rows ?? 24)
21
+ stdout.on('resize', onResize)
22
+ return () => { stdout.off('resize', onResize) }
23
+ }, [stdout])
24
+
25
+ return (
26
+ <Box flexDirection="column" alignItems="center" justifyContent="center" width="100%" minHeight={rows}>
27
+ <Wordmark />
28
+ <Box flexDirection="column" marginTop={1} width="100%">
29
+ <IdentityManagerRoutes controller={controller} />
30
+ </Box>
31
+ </Box>
32
+ )
33
+ }
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
  import { hasPendingPublish } from './continuity/state.js'
3
- import type { ProfileUpdates } from './identityHubReducer.js'
3
+ import type { ProfileUpdates } from './reducer.js'
4
4
  import { clearPinataJwt, savePinataJwt } from '../storage/pinataJwt.js'
5
5
  import {
6
6
  runRebackupStorageSubmit,
@@ -26,6 +26,7 @@ import { DeleteSkillConfirmScreen } from './continuity/skills/DeleteSkillConfirm
26
26
  import { RecoveryConfirmScreen } from './continuity/RecoveryConfirmScreen.js'
27
27
  import { SavePromptScreen } from './continuity/SavePromptScreen.js'
28
28
  import { ErrorScreen } from './shared/components/ErrorScreen.js'
29
+ import { OperationCompleteScreen } from './shared/components/OperationCompleteScreen.js'
29
30
  import { UnlinkedIdentityScreen } from './shared/components/UnlinkedIdentityScreen.js'
30
31
  import { invalidateOwnershipCache } from './shared/reconciliation/index.js'
31
32
  import {
@@ -34,19 +35,18 @@ import {
34
35
  } from './ens/EnsFlow.js'
35
36
  import { CustodyEditFlow, isCustodyEditStep } from './custody/CustodyEditFlow.js'
36
37
  import { rebackupWalletApprovalView } from './shared/utils.js'
37
- import type { IdentityHubController } from './useIdentityHubController.js'
38
+ import type { IdentityManagerController } from './useController.js'
38
39
 
39
- type IdentityHubOperationalRoutesProps = {
40
- controller: IdentityHubController
40
+ type IdentityManagerOperationalRoutesProps = {
41
+ controller: IdentityManagerController
41
42
  footer: React.ReactNode
42
43
  }
43
44
 
44
- export const IdentityHubOperationalRoutes: React.FC<IdentityHubOperationalRoutesProps> = ({
45
+ export const IdentityManagerOperationalRoutes: React.FC<IdentityManagerOperationalRoutesProps> = ({
45
46
  controller,
46
47
  footer,
47
48
  }) => {
48
49
  const {
49
- mode,
50
50
  config,
51
51
  onComplete,
52
52
  identity,
@@ -61,7 +61,7 @@ export const IdentityHubOperationalRoutes: React.FC<IdentityHubOperationalRoutes
61
61
  workingStatus,
62
62
  setStep,
63
63
  back,
64
- closeHub,
64
+ closeManager,
65
65
  setWalletSession,
66
66
  setJwtSaved,
67
67
  setCopyNotice,
@@ -133,14 +133,35 @@ export const IdentityHubOperationalRoutes: React.FC<IdentityHubOperationalRoutes
133
133
  return (
134
134
  <WalletApprovalScreen
135
135
  title="Refetch Latest Snapshot"
136
- subtitle="Wallet signature decrypts the latest saved snapshot and restores SOUL.md, MEMORY.md, and skills."
136
+ subtitle="Decrypts and restores SOUL.md, MEMORY.md, and skills."
137
137
  walletSession={walletSession}
138
- label={restoreProgress?.label ?? 'fetching latest snapshot from onchain...'}
138
+ label={restoreProgress?.label ?? (walletSession ? 'waiting for your signature...' : 'fetching snapshot...')}
139
139
  onCancel={() => setStep(step.back)}
140
140
  />
141
141
  )
142
142
  }
143
143
 
144
+ if (step.kind === 'continuity-overwrite-confirm') {
145
+ return (
146
+ <RecoveryConfirmScreen
147
+ mode="restore"
148
+ workingStatus={workingStatus}
149
+ footer={footer}
150
+ onConfirm={() => setStep(step.next)}
151
+ onBack={() => setStep(step.back)}
152
+ />
153
+ )
154
+ }
155
+
156
+ if (step.kind === 'operation-complete') {
157
+ return (
158
+ <OperationCompleteScreen
159
+ message={step.message}
160
+ onReturn={() => setStep({ kind: 'menu' })}
161
+ />
162
+ )
163
+ }
164
+
144
165
  if (step.kind === 'continuity-private') {
145
166
  return (
146
167
  <PrivateContinuityScreen
@@ -281,6 +302,7 @@ export const IdentityHubOperationalRoutes: React.FC<IdentityHubOperationalRoutes
281
302
  onWalletReady={setWalletSession}
282
303
  onTriggerRebackup={triggerRebackup}
283
304
  onTriggerPublicProfileSave={triggerPublicProfileSave}
305
+ onWithdrawFromVault={s => custodyFlow.beginWithdrawToken(s, s, 'ens')}
284
306
  />
285
307
  )
286
308
  }
@@ -299,8 +321,6 @@ export const IdentityHubOperationalRoutes: React.FC<IdentityHubOperationalRoutes
299
321
  onSetStep={setStep}
300
322
  onSwitchToAdvanced={(returnTo, updates) => custodyFlow.beginVaultDeposit(step, returnTo, updates)}
301
323
  onSwitchToSimple={(returnTo, updates) => custodyFlow.beginVaultUnwrap(step, returnTo, updates)}
302
- onWithdrawToken={returnTo => custodyFlow.beginWithdrawToken(step, returnTo)}
303
- onReturnToVault={(returnTo, vaultAddress) => custodyFlow.beginReturnToVault(step, returnTo, vaultAddress)}
304
324
  onResumeAdvanced={returnTo => {
305
325
  const vaultAddress = resolveVaultAddress(step.identity, config?.erc8004?.operatorVaults)
306
326
  const updates: ProfileUpdates = {
@@ -340,8 +360,8 @@ export const IdentityHubOperationalRoutes: React.FC<IdentityHubOperationalRoutes
340
360
  if (step.kind === 'rebackup-start') {
341
361
  return (
342
362
  <BusyScreen
343
- title="Identity Hub"
344
- label="preparing encrypted snapshot..."
363
+ title="Identity"
364
+ label="preparing snapshot..."
345
365
  onCancel={back}
346
366
  />
347
367
  )
@@ -385,7 +405,7 @@ export const IdentityHubOperationalRoutes: React.FC<IdentityHubOperationalRoutes
385
405
  return (
386
406
  <WalletApprovalScreen
387
407
  title="Connect Wallet"
388
- subtitle="Find agents this wallet owns, whether held directly or linked to it."
408
+ subtitle="Find agents this wallet owns or is linked to."
389
409
  walletSession={walletSession}
390
410
  label="waiting for wallet connection..."
391
411
  onCancel={() => setStep({ kind: 'menu' })}
@@ -396,7 +416,7 @@ export const IdentityHubOperationalRoutes: React.FC<IdentityHubOperationalRoutes
396
416
  if (step.kind === 'busy') {
397
417
  return (
398
418
  <BusyScreen
399
- title="Identity Hub"
419
+ title="Identity"
400
420
  label={step.label}
401
421
  onCancel={back}
402
422
  />
@@ -409,10 +429,9 @@ export const IdentityHubOperationalRoutes: React.FC<IdentityHubOperationalRoutes
409
429
  error={step.error}
410
430
  back={step.back}
411
431
  footer={footer}
412
- closeLabel={mode === 'first-run' ? 'Skip Identity For Now' : 'Close Identity Hub'}
413
- closeHint={mode === 'first-run' ? 'Continue First-Run Setup without an agent identity' : 'Return to chat without retrying'}
432
+ closeLabel="Close"
414
433
  onBack={backStep => setStep(backStep)}
415
- onClose={closeHub}
434
+ onClose={closeManager}
416
435
  />
417
436
  )
418
437
  }
@@ -4,10 +4,10 @@ import { getAddress, isAddress } from 'viem'
4
4
  import { Surface } from '../../ui/Surface.js'
5
5
  import { Select } from '../../ui/Select.js'
6
6
  import { theme } from '../../ui/theme.js'
7
- import type { SelectableNetwork } from '../../storage/config.js'
7
+ import type { EthagentIdentity, SelectableNetwork } from '../../storage/config.js'
8
8
  import { copyToClipboard } from '../../utils/clipboard.js'
9
9
  import { DEFAULT_IPFS_API_URL } from '../storage/ipfs.js'
10
- import { chainIdForNetwork, erc8004ConfigForSupportedChain } from '../registry/erc8004.js'
10
+ import { chainIdForNetwork, erc8004ConfigForSupportedChain, type Erc8004AgentCandidate } from '../registry/erc8004.js'
11
11
  import { shortAddress } from './shared/model/format.js'
12
12
  import { canRestoreCandidate } from './restore/discover.js'
13
13
  import {
@@ -31,16 +31,16 @@ import {
31
31
  isTokenTransferStep,
32
32
  } from './transfer/TokenTransferFlow.js'
33
33
  import {
34
- chainLabel,
35
34
  isCreateStep,
36
35
  isRestoreStep,
37
36
  } from './shared/utils.js'
38
- import { IdentityHubOperationalRoutes } from './OperationalRoutes.js'
39
- import type { IdentityHubController } from './useIdentityHubController.js'
37
+ import { localChangeStatusView } from './continuity/state.js'
38
+ import { IdentityManagerOperationalRoutes } from './OperationalRoutes.js'
39
+ import type { IdentityManagerController } from './useController.js'
40
+ import type { Step } from './reducer.js'
40
41
 
41
- export const IdentityHubRoutes: React.FC<{ controller: IdentityHubController }> = ({ controller }) => {
42
+ export const IdentityManagerRoutes: React.FC<{ controller: IdentityManagerController }> = ({ controller }) => {
42
43
  const {
43
- mode,
44
44
  config,
45
45
  onComplete,
46
46
  onConfigChange,
@@ -56,7 +56,7 @@ export const IdentityHubRoutes: React.FC<{ controller: IdentityHubController }>
56
56
  workingStatus,
57
57
  setStep,
58
58
  back,
59
- closeHub,
59
+ closeManager,
60
60
  setCopyNotice,
61
61
  handleStepError,
62
62
  resolveRegistryForIdentity,
@@ -66,30 +66,40 @@ export const IdentityHubRoutes: React.FC<{ controller: IdentityHubController }>
66
66
  } = controller
67
67
 
68
68
  const footer = <Text color={theme.dim}>enter select · esc back</Text>
69
+ const setRestoreFetchingStep = (next: Extract<Step, { kind: 'restore-fetching' }>, backStep: Step): void => {
70
+ if (
71
+ isSameLoadedAgent(identity, next.candidate)
72
+ && localChangeStatusView(workingStatus).hasLocalChanges
73
+ ) {
74
+ setStep({ kind: 'continuity-overwrite-confirm', action: 'restore', next, back: backStep })
75
+ return
76
+ }
77
+ setStep(next)
78
+ }
69
79
 
70
80
  if (step.kind === 'first-run-ens-prompt') {
71
81
  const tokenLabel = step.identity.agentId ? `#${step.identity.agentId}` : ''
72
82
  return (
73
83
  <Surface
74
- title="Token Minted"
75
- subtitle={`Agent token ${tokenLabel} is live on ${chainLabel(step.registry.chainId)}. Optional next step: link an ENS subdomain so others find this agent by name.`}
84
+ title={step.origin === 'restore' ? 'Agent Restored' : 'Token Minted'}
85
+ subtitle={`${tokenLabel} is live. Optional: link an ENS name.`}
76
86
  footer={footer}
77
87
  >
78
88
  <Box flexDirection="column">
79
- <Text color={theme.textSubtle}>An ENS subdomain like agent.example.eth makes the agent discoverable without sharing a token ID. Recommended, skippable.</Text>
80
- <Text color={theme.textSubtle}>The token ID + network already make the agent restorable; ENS only adds a public name.</Text>
89
+ <Text color={theme.textSubtle}>An ENS name like agent.you.eth gives your agent a public handle.</Text>
90
+ <Text color={theme.textSubtle}>Optional. The token id and network already make it restorable.</Text>
81
91
  </Box>
82
92
  <Box marginTop={1}>
83
- <Select<'ens' | 'skip'>
93
+ <Select<'ens' | 'later'>
84
94
  options={[
85
95
  { value: 'ens', role: 'section', label: 'Set Up Now' },
86
- { value: 'ens', label: 'Set Up ENS Name', hint: 'Root → Name → Review → Apply' },
87
- { value: 'skip', role: 'section', label: 'Skip' },
88
- { value: 'skip', label: 'Skip For Now', hint: 'Continue to model setup; add ENS later', role: 'utility' },
96
+ { value: 'ens', label: 'Set Up ENS Name', hint: 'Root → Name → Apply' },
97
+ { value: 'later', role: 'section', label: 'Later' },
98
+ { value: 'later', label: 'Add ENS Later', hint: 'Name it anytime from the menu', role: 'utility' },
89
99
  ]}
90
- hintLayout="inline"
100
+ hintLayout="below"
91
101
  onSubmit={choice => {
92
- if (choice === 'skip') {
102
+ if (choice === 'later') {
93
103
  finishFirstRunIdentity()
94
104
  return
95
105
  }
@@ -97,7 +107,7 @@ export const IdentityHubRoutes: React.FC<{ controller: IdentityHubController }>
97
107
  kind: 'edit-profile-ens',
98
108
  identity: step.identity,
99
109
  registry: step.registry,
100
- returnTo: { kind: 'first-run-ens-prompt', identity: step.identity, registry: step.registry },
110
+ returnTo: { kind: 'first-run-ens-prompt', identity: step.identity, registry: step.registry, origin: step.origin },
101
111
  })
102
112
  }}
103
113
  onCancel={finishFirstRunIdentity}
@@ -110,13 +120,11 @@ export const IdentityHubRoutes: React.FC<{ controller: IdentityHubController }>
110
120
  if (step.kind === 'menu') {
111
121
  return (
112
122
  <MenuScreen
113
- mode={mode}
114
123
  config={config}
115
124
  identity={identity}
116
- workingStatus={workingStatus}
117
125
  canRebackup={canRebackup}
118
126
  reconciliation={reconciliation}
119
- footer={footer}
127
+ workingStatus={workingStatus}
120
128
  onCreate={() => {
121
129
  if (identity) setStep({ kind: 'replace-confirm', next: 'create' })
122
130
  else setStep({ kind: 'create-name' })
@@ -146,8 +154,7 @@ export const IdentityHubRoutes: React.FC<{ controller: IdentityHubController }>
146
154
  onIdentityValues={() => setStep({ kind: 'details' })}
147
155
  onPrepareTransfer={openTokenTransferFlow}
148
156
  onStorage={() => setStep({ kind: 'storage-credential' })}
149
- onSkip={() => onComplete({ kind: 'skip' })}
150
- onCancel={closeHub}
157
+ onCancel={closeManager}
151
158
  />
152
159
  )
153
160
  }
@@ -162,7 +169,7 @@ export const IdentityHubRoutes: React.FC<{ controller: IdentityHubController }>
162
169
  onDescriptionSubmit={(name, description) => setStep({ kind: 'create-network', name, description })}
163
170
  onCustodySubmit={(custodyMode) => {
164
171
  if (step.kind !== 'create-custody') return
165
- setStep({ kind: 'create-preflight', name: step.name, description: step.description, ...(step.network ? { network: step.network } : {}), custodyMode })
172
+ setStep({ kind: 'create-import', name: step.name, description: step.description, ...(step.network ? { network: step.network } : {}), custodyMode })
166
173
  }}
167
174
  onRegistrySubmit={async value => {
168
175
  if (step.kind !== 'create-registry') return
@@ -253,14 +260,14 @@ export const IdentityHubRoutes: React.FC<{ controller: IdentityHubController }>
253
260
  if (step.kind !== 'restore-select-token') return
254
261
  const candidate = step.candidates.find(item => item.agentId.toString() === value)
255
262
  if (!candidate?.backup?.cid) return
256
- setStep({
263
+ setRestoreFetchingStep({
257
264
  kind: 'restore-fetching',
258
265
  cid: candidate.backup.cid,
259
266
  apiUrl: DEFAULT_IPFS_API_URL,
260
267
  candidate,
261
268
  requesterAddress: step.requesterAddress,
262
269
  purpose: step.purpose,
263
- })
270
+ }, step)
264
271
  }}
265
272
  onEnsSubmit={async value => {
266
273
  if (step.kind !== 'restore-ens-input') return
@@ -275,17 +282,17 @@ export const IdentityHubRoutes: React.FC<{ controller: IdentityHubController }>
275
282
  return
276
283
  }
277
284
  if (!isAddress(step.ownerHandle, { strict: false }) || !canRestoreCandidate(resolution.candidate, getAddress(step.ownerHandle))) {
278
- setStep({ ...step, busy: false, error: `${shortAddress(step.ownerHandle)} is not a operator wallet for this agent. Sign in with an approved operator wallet, or with the owner wallet that holds the token.` })
285
+ setStep({ ...step, busy: false, error: `${shortAddress(step.ownerHandle)} is not an operator wallet for this agent. Sign in with an approved operator wallet, or with the owner wallet that holds the token.` })
279
286
  return
280
287
  }
281
- setStep({
288
+ setRestoreFetchingStep({
282
289
  kind: 'restore-fetching',
283
290
  cid: resolution.candidate.backup.cid,
284
291
  apiUrl: DEFAULT_IPFS_API_URL,
285
292
  candidate: resolution.candidate,
286
293
  requesterAddress: step.ownerHandle,
287
294
  purpose: step.purpose,
288
- })
295
+ }, { ...step, busy: false, error: undefined })
289
296
  }}
290
297
  onTokenIdSubmit={async value => {
291
298
  if (step.kind !== 'restore-token-id-input') return
@@ -300,17 +307,17 @@ export const IdentityHubRoutes: React.FC<{ controller: IdentityHubController }>
300
307
  return
301
308
  }
302
309
  if (!isAddress(step.ownerHandle, { strict: false }) || !canRestoreCandidate(resolution.candidate, getAddress(step.ownerHandle))) {
303
- setStep({ ...step, busy: false, error: `${shortAddress(step.ownerHandle)} is not a operator wallet for this agent. Sign in with an approved operator wallet, or with the owner wallet that holds the token.` })
310
+ setStep({ ...step, busy: false, error: `${shortAddress(step.ownerHandle)} is not an operator wallet for this agent. Sign in with an approved operator wallet, or with the owner wallet that holds the token.` })
304
311
  return
305
312
  }
306
- setStep({
313
+ setRestoreFetchingStep({
307
314
  kind: 'restore-fetching',
308
315
  cid: resolution.candidate.backup.cid,
309
316
  apiUrl: DEFAULT_IPFS_API_URL,
310
317
  candidate: resolution.candidate,
311
318
  requesterAddress: step.ownerHandle,
312
319
  purpose: step.purpose,
313
- })
320
+ }, { ...step, busy: false, error: undefined })
314
321
  }}
315
322
  onPickRecoveryMethod={choice => {
316
323
  if (step.kind !== 'restore-recovery-input' && step.kind !== 'restore-select-token') return
@@ -359,5 +366,12 @@ export const IdentityHubRoutes: React.FC<{ controller: IdentityHubController }>
359
366
  )
360
367
  }
361
368
 
362
- return <IdentityHubOperationalRoutes controller={controller} footer={footer} />
369
+ return <IdentityManagerOperationalRoutes controller={controller} footer={footer} />
370
+ }
371
+
372
+ function isSameLoadedAgent(identity: EthagentIdentity | undefined, candidate: Erc8004AgentCandidate): boolean {
373
+ if (!identity?.agentId || !identity.chainId || !identity.identityRegistryAddress) return false
374
+ if (identity.chainId !== candidate.chainId) return false
375
+ if (identity.agentId !== candidate.agentId.toString()) return false
376
+ return identity.identityRegistryAddress.toLowerCase() === candidate.identityRegistryAddress.toLowerCase()
363
377
  }