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
@@ -1,10 +1,7 @@
1
1
  import type { EthagentConfig, EthagentIdentity } from '../../../storage/config.js'
2
- import { supportedErc8004ChainForId } from '../../registry/erc8004.js'
3
2
  import { snapshotSaveRequiresOwnerSigner } from '../continuity/snapshot.js'
4
- import type { IdentityHubInitialAction } from '../types.js'
5
- import type { ProfileUpdates, Step } from '../identityHubReducer.js'
6
-
7
- const MIN_BUSY_ERROR_MS = 2000
3
+ import type { IdentityManagerInitialAction } from '../types.js'
4
+ import type { ProfileUpdates, Step } from '../reducer.js'
8
5
 
9
6
  export function isWalletCancelled(err: unknown): boolean {
10
7
  if (!err) return false
@@ -18,9 +15,7 @@ export function isStorageError(err: unknown): boolean {
18
15
  return /pinata|ipfs|pin|storage/i.test(message)
19
16
  }
20
17
 
21
- export function chainLabel(chainId: number): string {
22
- return supportedErc8004ChainForId(chainId)?.name ?? `chain ${chainId}`
23
- }
18
+ const MIN_BUSY_ERROR_MS = 2000
24
19
 
25
20
  export function waitForMinimumBusyTime(startedAt: number): Promise<void> {
26
21
  const remaining = MIN_BUSY_ERROR_MS - (Date.now() - startedAt)
@@ -51,11 +46,12 @@ export function rebackupWalletApprovalView(
51
46
  }
52
47
  }
53
48
 
54
- export function isCreateStep(step: Step): step is Extract<Step, { kind: 'replace-confirm' | 'create-name' | 'create-description' | 'create-custody' | 'create-preflight' | 'create-registry' | 'create-signing' | 'create-storage' }> {
49
+ export function isCreateStep(step: Step): step is Extract<Step, { kind: 'replace-confirm' | 'create-name' | 'create-description' | 'create-custody' | 'create-import' | 'create-preflight' | 'create-registry' | 'create-signing' | 'create-storage' }> {
55
50
  return step.kind === 'replace-confirm'
56
51
  || step.kind === 'create-name'
57
52
  || step.kind === 'create-description'
58
53
  || step.kind === 'create-custody'
54
+ || step.kind === 'create-import'
59
55
  || step.kind === 'create-preflight'
60
56
  || step.kind === 'create-registry'
61
57
  || step.kind === 'create-signing'
@@ -67,7 +63,7 @@ export function isRestoreStep(step: Step): step is Exclude<Extract<Step, { kind:
67
63
  }
68
64
 
69
65
  export function initialStepForAction(
70
- action: IdentityHubInitialAction | undefined,
66
+ action: IdentityManagerInitialAction | undefined,
71
67
  config: EthagentConfig | undefined,
72
68
  ): Step {
73
69
  if (action === 'create') return config?.identity ? { kind: 'replace-confirm', next: 'create' } : { kind: 'create-name' }
@@ -9,7 +9,7 @@ import type {
9
9
  EffectCallbacks,
10
10
  TokenTransferProgress,
11
11
  } from '../shared/effects/types.js'
12
- import type { Step } from '../identityHubReducer.js'
12
+ import type { Step } from '../reducer.js'
13
13
  import { BusyScreen } from '../shared/components/BusyScreen.js'
14
14
  import { RebackupStorageScreen } from '../continuity/RebackupStorageScreen.js'
15
15
  import {
@@ -87,7 +87,7 @@ export const TokenTransferFlow: React.FC<TokenTransferFlowProps> = ({
87
87
  return (
88
88
  <BusyScreen
89
89
  title="Resolve Receiver Wallet"
90
- subtitle={`Resolving ${step.targetHandle} before preparing the transfer snapshot.`}
90
+ subtitle={`Resolving ${step.targetHandle}.`}
91
91
  label="checking ens or address..."
92
92
  onCancel={() => {
93
93
  resolveAbortRef.current?.abort()
@@ -158,5 +158,5 @@ function networkLabelForChainId(chainId: number): string {
158
158
 
159
159
  function tokenTransferBackHint(returnTo: Step | undefined): string {
160
160
  if (returnTo?.kind === 'edit-profile-ens') return 'Return to ENS setup'
161
- return 'Return to Identity Hub menu'
161
+ return 'Return to menu'
162
162
  }
@@ -16,7 +16,6 @@ import { FlowTimeline } from '../shared/components/FlowTimeline.js'
16
16
  import { OPEN_BROWSER_HINT } from '../shared/components/WalletApprovalScreen.js'
17
17
 
18
18
  const TRANSFER_STEPS = ['Choose Receiver', 'Sender Signs', 'Receiver Signs', 'Sender Updates URI', 'Transfer Token']
19
- const APPROVAL_GUARDRAIL = 'No approve(), setApprovalForAll(), transferFrom(), or token approval is requested.'
20
19
 
21
20
  type TokenTransferTargetScreenProps = {
22
21
  identity: EthagentIdentity
@@ -49,12 +48,10 @@ export const TokenTransferTargetScreen: React.FC<TokenTransferTargetScreenProps>
49
48
  <StatusRow label="Network" value={tokenNetworkLabel} />
50
49
  <StatusRow label="Sender" value={senderValue} />
51
50
  <Box marginTop={1} flexDirection="column">
52
- <Text color={theme.textSubtle}>Use this before any ERC-8004 token transfer.</Text>
53
- <Text color={theme.textSubtle}>Both signed wallets can read this snapshot; after transfer, restore with the receiver wallet.</Text>
54
- <Text color={theme.textSubtle}>{APPROVAL_GUARDRAIL}</Text>
55
51
  {custodyMode === 'advanced' ? (
56
- <Text color={theme.textSubtle}>Advanced custody: connect the owner wallet ({senderValue}) to sign as sender.</Text>
52
+ <Text color={theme.textSubtle}>Advanced custody: sign as the owner wallet.</Text>
57
53
  ) : null}
54
+ <Text color={theme.textSubtle}>No token approval is requested.</Text>
58
55
  </Box>
59
56
  <Box marginTop={1} flexDirection="column">
60
57
  <TextInput
@@ -180,7 +177,6 @@ export const TokenTransferReadyScreen: React.FC<TokenTransferReadyScreenProps> =
180
177
  footer={footer}
181
178
  >
182
179
  <Box flexDirection="column">
183
- <Text color={theme.text}>Snapshot published. Transfer the ERC-8004 token externally on {tokenNetworkLabel} from sender to receiver.</Text>
184
180
  <Box marginTop={1} flexDirection="column">
185
181
  <StatusRow label="Token" value={identity.agentId ? `#${identity.agentId}` : 'not created'} tone={identity.agentId ? 'ok' : 'warn'} />
186
182
  <StatusRow label="Network" value={tokenNetworkLabel} tone="ok" />
@@ -190,10 +186,8 @@ export const TokenTransferReadyScreen: React.FC<TokenTransferReadyScreenProps> =
190
186
  <StatusRow label="Token URI tx" value={shortHash(txHash)} tone="ok" />
191
187
  </Box>
192
188
  <Box marginTop={1} flexDirection="column">
193
- <Text color={theme.textSubtle}>Use this process for every ERC-8004 token transfer.</Text>
194
- <Text color={theme.textSubtle}>Both sender and receiver signatures can decrypt this snapshot.</Text>
195
- <Text color={theme.textSubtle}>After transfer, use Switch Agent with the receiver wallet.</Text>
196
- <Text color={theme.textSubtle}>{APPROVAL_GUARDRAIL}</Text>
189
+ <Text color={theme.textSubtle}>Transfer the token externally, then restore with the receiver wallet.</Text>
190
+ <Text color={theme.textSubtle}>No token approval is requested.</Text>
197
191
  </Box>
198
192
  <Box marginTop={1}>
199
193
  <Select<'back'>
@@ -27,7 +27,7 @@ import {
27
27
  import { resolveValidatedPinataJwt, savePinataJwt } from '../../storage/pinataJwt.js'
28
28
  import { openBrowserWalletSession } from '../../wallet/browserWallet.js'
29
29
  import { resolveEnsAddress } from '../../ens/ensLookup.js'
30
- import type { Step } from '../identityHubReducer.js'
30
+ import type { Step } from '../reducer.js'
31
31
  import type { EffectCallbacks } from '../shared/effects/types.js'
32
32
  import { awaitConfirmedReceipt } from '../shared/effects/receipts.js'
33
33
  import {
@@ -1,17 +1,16 @@
1
1
  import type { EthagentConfig, EthagentIdentity } from '../../storage/config.js'
2
2
 
3
- export type IdentityHubResult =
3
+ export type IdentityManagerResult =
4
4
  | { kind: 'token'; identity: EthagentIdentity }
5
5
  | { kind: 'updated'; config: EthagentConfig; message: string }
6
- | { kind: 'skip' }
7
6
  | { kind: 'cancel' }
8
7
 
9
- export type IdentityHubInitialAction = 'create' | 'load' | 'settings' | 'save-snapshot' | 'save-prompt'
8
+ export type IdentityManagerInitialAction = 'create' | 'load' | 'settings' | 'save-snapshot' | 'save-prompt'
10
9
 
11
- export type IdentityHubProps = {
10
+ export type IdentityManagerProps = {
12
11
  mode: 'first-run' | 'manage'
13
12
  config?: EthagentConfig
14
- initialAction?: IdentityHubInitialAction
15
- onComplete: (result: IdentityHubResult) => void
13
+ initialAction?: IdentityManagerInitialAction
14
+ onComplete: (result: IdentityManagerResult) => void
16
15
  onConfigChange?: (config: EthagentConfig) => void
17
16
  }
@@ -1,4 +1,5 @@
1
- import { useEffect, useState } from 'react'
1
+ import { useCallback, useEffect, useState } from 'react'
2
+ import { watch, type FSWatcher } from 'node:fs'
2
3
  import type { EthagentIdentity } from '../../storage/config.js'
3
4
  import { catFromIpfs, DEFAULT_IPFS_API_URL } from '../storage/ipfs.js'
4
5
  import {
@@ -19,21 +20,37 @@ import {
19
20
  import type { SkillVisibility } from '../continuity/skills/types.js'
20
21
  import { syncAgentCardManifest } from '../continuity/skills/publicSkillsSync.js'
21
22
  import { continuityVaultRef } from '../continuity/storage.js'
22
- import type { Step } from './identityHubReducer.js'
23
+ import type { Step } from './reducer.js'
23
24
 
24
- type UseIdentityHubContinuityArgs = {
25
+ const WORKING_STATUS_STEPS = new Set<string>([
26
+ 'menu',
27
+ 'continuity-private',
28
+ 'continuity-public',
29
+ 'continuity-skills-tree',
30
+ 'save-prompt',
31
+ 'rebackup-confirm',
32
+ 'recovery-refetch-confirm',
33
+ 'continuity-overwrite-confirm',
34
+ 'restore-recovery-input',
35
+ 'restore-ens-input',
36
+ 'restore-token-id-input',
37
+ 'restore-select-token',
38
+ ])
39
+ const WATCHED_VAULT_FILES = new Set<string>(['SOUL.md', 'MEMORY.md', 'agent-card.json'])
40
+
41
+ type UseIdentityManagerContinuityArgs = {
25
42
  identity: EthagentIdentity | undefined
26
43
  step: Step
27
44
  setStep: (step: Step) => void
28
45
  handleStepError: (err: unknown, backStep: Step, softCancel?: Step) => void
29
46
  }
30
47
 
31
- export function useIdentityHubContinuity({
48
+ export function useIdentityManagerContinuity({
32
49
  identity,
33
50
  step,
34
51
  setStep,
35
52
  handleStepError,
36
- }: UseIdentityHubContinuityArgs): {
53
+ }: UseIdentityManagerContinuityArgs): {
37
54
  continuityReady: boolean
38
55
  setContinuityReady: (ready: boolean) => void
39
56
  workingStatus: ContinuityWorkingTreeStatus | null
@@ -60,36 +77,51 @@ export function useIdentityHubContinuity({
60
77
  return () => { cancelled = true }
61
78
  }, [identity, step.kind])
62
79
 
80
+ const computeStatus = useCallback(async (): Promise<ContinuityWorkingTreeStatus | null> => {
81
+ if (!identity) return null
82
+ try {
83
+ const [latest] = await listPublishedContinuitySnapshots(identity, 1)
84
+ return await continuityWorkingTreeStatus(identity, latest)
85
+ } catch {
86
+ return null
87
+ }
88
+ }, [identity])
89
+
90
+ const editorReturn = 'editorOpened' in step ? step.editorOpened : undefined
91
+
63
92
  useEffect(() => {
64
93
  let cancelled = false
65
94
  if (!identity) return
66
- if (
67
- step.kind !== 'menu'
68
- && step.kind !== 'continuity-private'
69
- && step.kind !== 'continuity-public'
70
- && step.kind !== 'continuity-skills-tree'
71
- && step.kind !== 'save-prompt'
72
- && step.kind !== 'rebackup-confirm'
73
- ) return
95
+ if (!WORKING_STATUS_STEPS.has(step.kind)) return
96
+ void computeStatus().then(status => { if (!cancelled) setWorkingStatus(status) })
97
+ return () => { cancelled = true }
98
+ }, [identity, step.kind, editorReturn, computeStatus])
74
99
 
75
- const checkStatus = async () => {
76
- try {
77
- const [latest] = await listPublishedContinuitySnapshots(identity, 1)
78
- const status = await continuityWorkingTreeStatus(identity, latest)
79
- if (cancelled) return
80
- setWorkingStatus(status)
81
- } catch {
82
- if (cancelled) return
83
- setWorkingStatus(null)
84
- }
100
+ useEffect(() => {
101
+ if (!identity) return
102
+ if (!WORKING_STATUS_STEPS.has(step.kind)) return
103
+ let cancelled = false
104
+ let timer: ReturnType<typeof setTimeout> | undefined
105
+ let watcher: FSWatcher | undefined
106
+ const schedule = (): void => {
107
+ if (timer) clearTimeout(timer)
108
+ timer = setTimeout(() => {
109
+ void computeStatus().then(status => { if (!cancelled) setWorkingStatus(status) })
110
+ }, 200)
85
111
  }
86
-
87
- void checkStatus()
88
-
112
+ try {
113
+ watcher = watch(continuityVaultRef(identity).dir, (_event, filename) => {
114
+ if (filename && !WATCHED_VAULT_FILES.has(String(filename))) return
115
+ schedule()
116
+ })
117
+ watcher.on('error', () => { try { watcher?.close() } catch {} })
118
+ } catch {}
89
119
  return () => {
90
120
  cancelled = true
121
+ if (timer) clearTimeout(timer)
122
+ try { watcher?.close() } catch {}
91
123
  }
92
- }, [identity, step.kind])
124
+ }, [identity, step.kind, computeStatus])
93
125
 
94
126
  const requireReadyVault = async (): Promise<EthagentIdentity> => {
95
127
  if (!identity) throw new Error('No active identity')
@@ -1,6 +1,5 @@
1
1
  import { useEffect, useReducer, useState } from 'react'
2
2
  import type { EthagentConfig, EthagentIdentity } from '../../storage/config.js'
3
- import { defaultModelFor } from '../../storage/config.js'
4
3
  import { setTokenIdentity } from '../../storage/identity.js'
5
4
  import type { BrowserWalletReady } from '../wallet/browserWallet.js'
6
5
  import { registryConfigFromConfig } from '../registry/registryConfig.js'
@@ -26,11 +25,11 @@ import type {
26
25
  } from './shared/effects/types.js'
27
26
  import { resolveVaultAddress } from './custody/transactions.js'
28
27
  import { useCustodyFlow } from './custody/useCustodyFlow.js'
29
- import { identityHubErrorView } from './shared/model/errors.js'
28
+ import { identityManagerErrorView } from './shared/model/errors.js'
30
29
  import { readCustodyMode } from './custody/state.js'
31
30
  import { selectEnsStatus } from './ens/state.js'
32
- import { identityHubReducer, type ProfileUpdates, type Step } from './identityHubReducer.js'
33
- import type { IdentityHubProps } from './types.js'
31
+ import { identityManagerReducer, type ProfileUpdates, type Step } from './reducer.js'
32
+ import type { IdentityManagerProps } from './types.js'
34
33
  import {
35
34
  capitalizeFeedbackMessage,
36
35
  initialStepForAction,
@@ -40,20 +39,20 @@ import {
40
39
  preflightTokenOwnership,
41
40
  useAgentReconciliation,
42
41
  } from './shared/reconciliation/index.js'
43
- import { useIdentityHubContinuity } from './useIdentityHubContinuity.js'
44
- import { useIdentityHubSideEffects } from './useIdentityHubSideEffects.js'
42
+ import { useIdentityManagerContinuity } from './useContinuity.js'
43
+ import { useIdentityManagerSideEffects } from './useSideEffects.js'
45
44
 
46
- export function useIdentityHubController({
45
+ export function useIdentityManagerController({
47
46
  mode,
48
47
  config,
49
48
  initialAction,
50
49
  onComplete,
51
50
  onConfigChange,
52
- }: IdentityHubProps) {
51
+ }: IdentityManagerProps) {
53
52
  const [firstRunIdentity, setFirstRunIdentity] = useState<EthagentIdentity | null>(null)
54
53
  const identity = config?.identity ?? firstRunIdentity ?? undefined
55
54
  const { reconciliation, refresh: refreshReconciliation } = useAgentReconciliation(config ?? null)
56
- const [step, dispatch] = useReducer(identityHubReducer, initialStepForAction(initialAction, config))
55
+ const [step, dispatch] = useReducer(identityManagerReducer, initialStepForAction(initialAction, config))
57
56
  const [walletSession, setWalletSession] = useState<BrowserWalletReady | null>(null)
58
57
  const [restoreProgress, setRestoreProgress] = useState<RestoreProgress | null>(null)
59
58
  const [tokenTransferProgress, setTokenTransferProgress] = useState<TokenTransferProgress | null>(null)
@@ -63,7 +62,7 @@ export function useIdentityHubController({
63
62
 
64
63
  const setStep = (s: Step) => dispatch({ type: 'setStep', step: s })
65
64
  const back = () => dispatch({ type: 'back', from: step })
66
- const closeHub = () => onComplete(mode === 'first-run' ? { kind: 'skip' } : { kind: 'cancel' })
65
+ const closeManager = () => onComplete({ kind: 'cancel' })
67
66
 
68
67
  useEffect(() => { setWalletSession(null) }, [step.kind])
69
68
  useEffect(() => {
@@ -114,21 +113,24 @@ export function useIdentityHubController({
114
113
  const registry = registryFromIdentity(nextIdentity)
115
114
  const custodyMode = readCustodyMode(nextIdentity.state)
116
115
 
116
+ const seedConfig: EthagentConfig = {
117
+ version: 2,
118
+ firstSeenAt: new Date().toISOString(),
119
+ identity: { ...nextIdentity, source: 'erc8004' },
120
+ ...(registry
121
+ ? {
122
+ erc8004: {
123
+ chainId: registry.chainId,
124
+ rpcUrl: registry.rpcUrl,
125
+ identityRegistryAddress: registry.identityRegistryAddress,
126
+ },
127
+ }
128
+ : {}),
129
+ }
130
+ const persisted = await setTokenIdentity(seedConfig, nextIdentity)
131
+ onConfigChange?.(persisted)
132
+
117
133
  if (source === 'create' && custodyMode === 'advanced' && registry) {
118
- const seedConfig: EthagentConfig = {
119
- version: 1,
120
- provider: 'llamacpp',
121
- model: defaultModelFor('llamacpp'),
122
- firstRunAt: new Date().toISOString(),
123
- identity: { ...nextIdentity, source: 'erc8004' },
124
- erc8004: {
125
- chainId: registry.chainId,
126
- rpcUrl: registry.rpcUrl,
127
- identityRegistryAddress: registry.identityRegistryAddress,
128
- },
129
- }
130
- const persisted = await setTokenIdentity(seedConfig, nextIdentity)
131
- onConfigChange?.(persisted)
132
134
  const ownerAddress = (nextIdentity.ownerAddress ?? nextIdentity.address) as `0x${string}`
133
135
  const profileUpdates: ProfileUpdates = {
134
136
  custodyMode: 'advanced',
@@ -148,10 +150,10 @@ export function useIdentityHubController({
148
150
 
149
151
  const ensStatus = selectEnsStatus(nextIdentity)
150
152
  if (registry && ensStatus.kind === 'none') {
151
- setStep({ kind: 'first-run-ens-prompt', identity: nextIdentity, registry })
153
+ setStep({ kind: 'first-run-ens-prompt', identity: nextIdentity, registry, origin: source === 'restore' ? 'restore' : 'create' })
152
154
  return
153
155
  }
154
- onComplete({ kind: 'token', identity: nextIdentity })
156
+ setStep({ kind: 'menu' })
155
157
  return
156
158
  }
157
159
  const nextConfig = await setTokenIdentity(config, nextIdentity)
@@ -184,7 +186,8 @@ export function useIdentityHubController({
184
186
  return
185
187
  }
186
188
  }
187
- onComplete({ kind: 'updated', config: nextConfig, message: capitalizeFeedbackMessage(message) })
189
+ onConfigChange?.(nextConfig)
190
+ setStep({ kind: 'operation-complete', message: capitalizeFeedbackMessage(message) })
188
191
  }
189
192
 
190
193
  const callbacks: EffectCallbacks = {
@@ -200,7 +203,7 @@ export function useIdentityHubController({
200
203
  setStep(softCancel)
201
204
  return
202
205
  }
203
- setStep({ kind: 'error', error: identityHubErrorView(err), back: backStep })
206
+ setStep({ kind: 'error', error: identityManagerErrorView(err), back: backStep })
204
207
  }
205
208
 
206
209
  const guardOwnership = async (
@@ -290,14 +293,14 @@ export function useIdentityHubController({
290
293
  ...(onConfigChange ? { onConfigChange } : {}),
291
294
  })
292
295
 
293
- const continuity = useIdentityHubContinuity({
296
+ const continuity = useIdentityManagerContinuity({
294
297
  identity,
295
298
  step,
296
299
  setStep,
297
300
  handleStepError,
298
301
  })
299
302
 
300
- useIdentityHubSideEffects({
303
+ useIdentityManagerSideEffects({
301
304
  step,
302
305
  config,
303
306
  callbacks,
@@ -310,10 +313,10 @@ export function useIdentityHubController({
310
313
 
311
314
  const finishFirstRunIdentity = (): void => {
312
315
  if (!firstRunIdentity) {
313
- onComplete({ kind: 'skip' })
316
+ onComplete({ kind: 'cancel' })
314
317
  return
315
318
  }
316
- onComplete({ kind: 'token', identity: firstRunIdentity })
319
+ setStep({ kind: 'menu' })
317
320
  }
318
321
 
319
322
  const openEnsEdit = (backStep: Step): void => {
@@ -337,7 +340,7 @@ export function useIdentityHubController({
337
340
  handleStepError(new Error('no agent registry configured for this identity'), backStep)
338
341
  return
339
342
  }
340
- setStep({ kind: 'edit-profile-name', identity, registry, returnTo: backStep })
343
+ setStep({ kind: 'edit-profile-menu', identity, registry, returnTo: backStep })
341
344
  }
342
345
 
343
346
  const openTokenTransferFlow = async (): Promise<void> => {
@@ -385,7 +388,7 @@ export function useIdentityHubController({
385
388
  workingStatus: continuity.workingStatus,
386
389
  setStep,
387
390
  back,
388
- closeHub,
391
+ closeManager,
389
392
  setWalletSession,
390
393
  setJwtSaved,
391
394
  setCopyNotice,
@@ -406,4 +409,4 @@ export function useIdentityHubController({
406
409
  }
407
410
  }
408
411
 
409
- export type IdentityHubController = ReturnType<typeof useIdentityHubController>
412
+ export type IdentityManagerController = ReturnType<typeof useIdentityManagerController>
@@ -2,11 +2,12 @@ import { useEffect } from 'react'
2
2
  import type { EthagentConfig } from '../../storage/config.js'
3
3
  import { setTokenIdentity } from '../../storage/identity.js'
4
4
  import { isRegistrationPreflightError, pinataErrorText } from './shared/model/errors.js'
5
- import type { ProfileUpdates, Step } from './identityHubReducer.js'
5
+ import type { ProfileUpdates, Step } from './reducer.js'
6
6
  import {
7
7
  runCreatePreflight,
8
8
  runCreateSigning,
9
9
  } from './create/effects.js'
10
+ import { scanImportCandidates } from './create/importScan.js'
10
11
  import {
11
12
  runRebackupSigning,
12
13
  } from './continuity/effects.js'
@@ -36,7 +37,7 @@ type TriggerRebackup = (
36
37
  options?: { vaultAddress?: `0x${string}`; useVault?: boolean },
37
38
  ) => void
38
39
 
39
- type UseIdentityHubSideEffectsArgs = {
40
+ type UseIdentityManagerSideEffectsArgs = {
40
41
  step: Step
41
42
  config: EthagentConfig | undefined
42
43
  callbacks: EffectCallbacks
@@ -47,7 +48,7 @@ type UseIdentityHubSideEffectsArgs = {
47
48
  onConfigChange?: (config: EthagentConfig) => void
48
49
  }
49
50
 
50
- export function useIdentityHubSideEffects({
51
+ export function useIdentityManagerSideEffects({
51
52
  step,
52
53
  config,
53
54
  callbacks,
@@ -56,12 +57,45 @@ export function useIdentityHubSideEffects({
56
57
  triggerRebackup,
57
58
  setContinuityReady,
58
59
  onConfigChange,
59
- }: UseIdentityHubSideEffectsArgs): void {
60
+ }: UseIdentityManagerSideEffectsArgs): void {
60
61
  useEffect(() => {
61
62
  if (step.kind !== 'rebackup-start') return
62
63
  triggerRebackup(step.back)
63
64
  }, [step])
64
65
 
66
+ useEffect(() => {
67
+ if (step.kind !== 'create-import') return
68
+ if (step.candidates !== undefined) return
69
+ let cancelled = false
70
+ const toPreflight: Step = {
71
+ kind: 'create-preflight',
72
+ name: step.name,
73
+ description: step.description,
74
+ ...(step.network ? { network: step.network } : {}),
75
+ custodyMode: step.custodyMode,
76
+ }
77
+ scanImportCandidates()
78
+ .then(candidates => {
79
+ if (cancelled) return
80
+ if (candidates.length === 0) {
81
+ setStep(toPreflight)
82
+ return
83
+ }
84
+ setStep({
85
+ kind: 'create-import',
86
+ name: step.name,
87
+ description: step.description,
88
+ ...(step.network ? { network: step.network } : {}),
89
+ custodyMode: step.custodyMode,
90
+ candidates,
91
+ })
92
+ })
93
+ .catch(() => {
94
+ if (!cancelled) setStep(toPreflight)
95
+ })
96
+ return () => { cancelled = true }
97
+ }, [step])
98
+
65
99
  useEffect(() => {
66
100
  if (step.kind !== 'create-preflight') return
67
101
  let cancelled = false
@@ -94,6 +128,7 @@ export function useIdentityHubSideEffects({
94
128
  custodyMode: step.custodyMode,
95
129
  error: pinataErrorText(err),
96
130
  pinataJwt: step.pinataJwt,
131
+ ...(step.importNotes ? { importNotes: step.importNotes } : {}),
97
132
  })
98
133
  return
99
134
  }
@@ -120,6 +155,7 @@ export function useIdentityHubSideEffects({
120
155
  profileUpdates: step.profileUpdates,
121
156
  returnTo: step.returnTo,
122
157
  walletPurpose: step.walletPurpose,
158
+ ...(step.vaultAddress ? { vaultAddress: step.vaultAddress } : {}),
123
159
  })
124
160
  return
125
161
  }
@@ -5,7 +5,10 @@ import { isAgentInVault } from '../vault.js'
5
5
  import { ERC8004_ABI, TRANSFER_EVENT } from './abi.js'
6
6
  import {
7
7
  SUPPORTED_ERC8004_CHAINS,
8
+ chainSortIndex,
8
9
  erc8004ConfigForSupportedChain,
10
+ logBlockRangeForChain,
11
+ minLogBlockRangeForChain,
9
12
  supportedErc8004ChainForId,
10
13
  } from './chains.js'
11
14
  import { createErc8004PublicClient } from './client.js'
@@ -492,20 +495,3 @@ function blockRangesBackwards(
492
495
  }
493
496
  return ranges
494
497
  }
495
-
496
- function logBlockRangeForChain(chainId: number): bigint {
497
- const chain = supportedErc8004ChainForId(chainId)
498
- if (!chain) return 10_000n
499
- return chain.logBlockRange
500
- }
501
-
502
- function minLogBlockRangeForChain(chainId: number): bigint {
503
- const chain = supportedErc8004ChainForId(chainId)
504
- if (!chain) return 2_000n
505
- return chain.kind === 'l2' ? chain.logBlockRange : chain.logBlockRange / 2n || 1n
506
- }
507
-
508
- function chainSortIndex(chainId: number): number {
509
- const index = SUPPORTED_ERC8004_CHAINS.findIndex(chain => chain.chainId === chainId)
510
- return index === -1 ? Number.MAX_SAFE_INTEGER : index
511
- }
@@ -12,7 +12,7 @@ export function uniqueStrings(values: string[]): string[] {
12
12
  export function cleanRpcError(err: unknown): string {
13
13
  const message = err instanceof Error ? err.message : String(err)
14
14
  return message
15
- .replace(/s+/g, ' ')
15
+ .replace(/\s+/g, ' ')
16
16
  .slice(0, 220)
17
17
  }
18
18
 
@@ -15,6 +15,26 @@ type IpfsOptions = {
15
15
  pinataJwt?: string
16
16
  }
17
17
 
18
+ export class PinataUploadError extends Error {
19
+ readonly status: number
20
+ readonly statusText: string
21
+ constructor(status: number, statusText: string) {
22
+ super(pinataUploadErrorMessage(status, statusText))
23
+ this.name = 'PinataUploadError'
24
+ this.status = status
25
+ this.statusText = statusText
26
+ }
27
+ }
28
+
29
+ function pinataUploadErrorMessage(status: number, statusText: string): string {
30
+ const code = statusText ? `${status} ${statusText}` : String(status)
31
+ if (status === 401) return `Pinata rejected the upload (${code}): the storage credential is invalid or expired.`
32
+ if (status === 403) return `Pinata refused the upload (${code}): your account is likely at its file or storage limit.`
33
+ if (status === 429) return `Pinata is rate-limiting uploads (${code}): too many requests in a short window.`
34
+ if (status === 413) return `Pinata rejected the upload (${code}): the snapshot is larger than your plan allows.`
35
+ return `IPFS upload failed: ${code}.`
36
+ }
37
+
18
38
  export function extractPinataJwt(input: string): string {
19
39
  const trimmed = input.trim()
20
40
  const matches = trimmed.match(/\b[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g) ?? []
@@ -142,7 +162,7 @@ async function addFileToPinata(
142
162
  },
143
163
  body,
144
164
  })
145
- if (!response.ok) throw new Error(`IPFS upload failed: ${response.status} ${response.statusText}`)
165
+ if (!response.ok) throw new PinataUploadError(response.status, response.statusText)
146
166
  const data = await response.json() as { data?: { cid?: string }; IpfsHash?: string; Hash?: string; Cid?: string }
147
167
  const cid = data.data?.cid ?? data.IpfsHash ?? data.Hash ?? data.Cid
148
168
  if (!cid) throw new Error('IPFS upload response did not include a CID')
@@ -4,14 +4,22 @@ import { loadWalletPageSource } from './walletPageSource.js'
4
4
 
5
5
  const WALLET_HTML = loadWalletHtml()
6
6
 
7
- export function walletPage(title: string, sessionToken: string, payload: Record<string, unknown>): string {
7
+ function injectWalletConfig(shell: string, title: string, sessionToken: string, payload: Record<string, unknown>): string {
8
8
  const config = JSON.stringify({ sessionToken, ...normalizeWalletPayloadPurpose(payload) }).replaceAll('<', '\\u003c')
9
9
  const injection = `<script>window.__WALLET_CONFIG__ = ${config};</script>`
10
- return WALLET_HTML
10
+ return shell
11
11
  .replace(/<title>.*?<\/title>/, `<title>${escapeHtml(title)}</title>`)
12
12
  .replace('<head>', `<head>\n ${injection}`)
13
13
  }
14
14
 
15
+ export function walletPage(title: string, sessionToken: string, payload: Record<string, unknown>): string {
16
+ return injectWalletConfig(WALLET_HTML, title, sessionToken, payload)
17
+ }
18
+
19
+ export function walletPageFresh(title: string, sessionToken: string, payload: Record<string, unknown>): string {
20
+ return injectWalletConfig(loadWalletHtml(), title, sessionToken, payload)
21
+ }
22
+
15
23
  export function __testWalletPage(title: string, sessionToken: string, payload: Record<string, unknown>): string {
16
24
  return walletPage(title, sessionToken, payload)
17
25
  }