ethagent 3.3.4 → 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 -260
  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
@@ -0,0 +1,220 @@
1
+ import React from 'react'
2
+ import { Box, Text } from 'ink'
3
+ import { theme } from '../../../../ui/theme.js'
4
+ import { isDemoMode } from '../../../../cli/demo.js'
5
+ import type { EthagentConfig, EthagentIdentity } from '../../../../storage/config.js'
6
+ import { identityPerspective } from '../../custody/state.js'
7
+ import { transferSnapshotView } from '../../transfer/state.js'
8
+ import type { AgentReconciliation } from '../reconciliation/index.js'
9
+ import { menuFlagsFromReconciliation } from './menuFlagsFromReconciliation.js'
10
+ import { localChangeStatusView } from '../../continuity/state.js'
11
+ import { LazyMenu, type LazyMenuRow } from './LazyMenu.js'
12
+
13
+ import type { ContinuityWorkingTreeStatus } from '../../../continuity/storage.js'
14
+
15
+ type MenuScreenProps = {
16
+ config?: EthagentConfig
17
+ identity?: EthagentIdentity
18
+ canRebackup: boolean
19
+ reconciliation?: AgentReconciliation
20
+ workingStatus?: ContinuityWorkingTreeStatus | null
21
+ onCreate: () => void
22
+ onLoad: () => void
23
+ onBackupNow: () => void
24
+ onRefetchLatest: () => void
25
+ onPublicProfile: () => void
26
+ onEnsName: () => void
27
+ onWalletSetup: () => void
28
+ onContinuity: () => void
29
+ onSkillsTree: () => void
30
+ onIdentityValues: () => void
31
+ onPrepareTransfer: () => void
32
+ onStorage: () => void
33
+ onCancel: () => void
34
+ }
35
+
36
+ type Action =
37
+ | 'public-profile'
38
+ | 'ens-name'
39
+ | 'wallet-setup'
40
+ | 'continuity'
41
+ | 'skills-tree'
42
+ | 'backup'
43
+ | 'refetch'
44
+ | 'identity-values'
45
+ | 'prepare-transfer'
46
+ | 'storage'
47
+ | 'create'
48
+ | 'load'
49
+ | 'cancel'
50
+
51
+ function shortAddress(addr: string | undefined): string {
52
+ if (!addr) return ''
53
+ return addr.slice(0, 6) + '…' + addr.slice(-4)
54
+ }
55
+
56
+ function networkLabel(config?: EthagentConfig, identity?: EthagentIdentity): string | null {
57
+ const chainId = identity?.chainId ?? config?.erc8004?.chainId
58
+ if (chainId === 1) return 'mainnet'
59
+ if (chainId === 8453) return 'base'
60
+ return config?.selectedNetwork ?? null
61
+ }
62
+
63
+ export const MenuScreen: React.FC<MenuScreenProps> = ({
64
+ config,
65
+ identity,
66
+ reconciliation,
67
+ workingStatus,
68
+ canRebackup,
69
+ onCreate,
70
+ onLoad,
71
+ onBackupNow,
72
+ onRefetchLatest,
73
+ onPublicProfile,
74
+ onEnsName,
75
+ onWalletSetup,
76
+ onContinuity,
77
+ onSkillsTree,
78
+ onIdentityValues,
79
+ onPrepareTransfer,
80
+ onStorage,
81
+ onCancel,
82
+ }) => {
83
+ const canRefetch = Boolean(canRebackup && identity?.backup?.cid)
84
+
85
+ const perspective = identityPerspective(identity)
86
+ const flags = reconciliation
87
+ ? menuFlagsFromReconciliation(reconciliation, perspective)
88
+ : (perspective === 'operator'
89
+ ? menuFlagsFromReconciliation({
90
+ token: 'unknown', custody: 'unknown', agentUri: 'unknown',
91
+ vault: 'unknown', workingTree: 'unknown', rpc: 'reachable', driftCount: 0, lastCheckedAt: '',
92
+ }, perspective)
93
+ : null)
94
+
95
+ const backupEnabled = canRebackup && !(flags?.saveSnapshotDisabled ?? false)
96
+ const localChangeStatus = localChangeStatusView(workingStatus)
97
+
98
+ const rows: Array<LazyMenuRow<Action>> = identity
99
+ ? [
100
+ { value: 'public-profile', label: 'Public Profile', shortcut: 'p' },
101
+ { value: 'continuity', label: 'Soul & Memory', shortcut: 'm' },
102
+ { value: 'skills-tree', label: 'Skills', shortcut: 's' },
103
+ { value: 'backup', label: 'Save Snapshot', shortcut: 'a', disabled: !canRebackup || (flags?.saveSnapshotDisabled ?? false), hint: flags?.saveSnapshotHint, ...(localChangeStatus.hasLocalChanges && backupEnabled ? { inlineNote: localChangeStatus.files.length > 0 ? localChangeStatus.files.join(', ') : 'unsaved changes', inlineNoteColor: theme.accentError } : {}) },
104
+ { value: 'refetch', label: 'Refetch Latest', shortcut: 'r', disabled: !canRefetch || (flags?.refetchLatestDisabled ?? false), hint: flags?.refetchHint },
105
+ { value: 'ens-name', label: 'ENS Name', shortcut: 'e', disabled: flags?.ensNameDisabled ?? false, hint: flags?.ensNameHint },
106
+ { value: 'identity-values', label: 'Token Values', shortcut: 'v', ...(flags?.tokenValuesUnlinkedNote ? { note: flags.tokenValuesUnlinkedNote } : {}) },
107
+ { value: 'wallet-setup', label: 'Custody Mode', shortcut: 'c', disabled: !identity.agentId || (flags?.custodyModeDisabled ?? false), hint: flags?.custodyModeHint ?? flags?.custodyHint },
108
+ ...(flags?.prepareTransferHidden ? [] : [{ value: 'prepare-transfer' as Action, label: 'Prepare Transfer', shortcut: 't', disabled: flags?.prepareTransferDisabled ?? false, hint: flags?.prepareTransferHint }]),
109
+ { value: 'load', label: 'Switch Agent', shortcut: 'w' },
110
+ { value: 'create', label: 'New Agent', shortcut: 'n' },
111
+ { value: 'storage', label: 'IPFS Storage', shortcut: 'i' },
112
+ { value: 'cancel', label: 'Quit', shortcut: 'q' },
113
+ ]
114
+ : [
115
+ { value: 'create', label: 'Create New Agent' },
116
+ { value: 'load', label: 'Load Existing' },
117
+ ]
118
+
119
+ const reconciliationBanner = identity && reconciliation
120
+ ? renderReconciliationBanner(reconciliation, identity)
121
+ : null
122
+
123
+ const network = networkLabel(config, identity)
124
+ const statusBits: string[] = []
125
+ if (identity?.agentId) statusBits.push(`#${identity.agentId}`)
126
+ const displayAddress = perspective === 'operator' ? identity?.connectedWallet : identity?.ownerAddress
127
+ if (displayAddress) statusBits.push(shortAddress(displayAddress))
128
+ if (network) statusBits.push(network)
129
+ if (perspective === 'operator') statusBits.push('operator')
130
+
131
+ return (
132
+ <Box flexDirection="column" alignItems="center" paddingY={1}>
133
+ <Box flexDirection="column" alignItems="center">
134
+ {reconciliationBanner ? (
135
+ <Box marginBottom={1} flexDirection="column" alignItems="center">
136
+ {reconciliationBanner}
137
+ </Box>
138
+ ) : null}
139
+ {!identity ? (
140
+ <Box marginBottom={1}>
141
+ <Text color={theme.textSubtle}>Create or link an agent identity to get started.</Text>
142
+ </Box>
143
+ ) : null}
144
+ <LazyMenu<Action>
145
+ rows={rows}
146
+ onSubmit={choice => {
147
+ if (choice === 'cancel') return onCancel()
148
+ if (choice === 'public-profile') return onPublicProfile()
149
+ if (choice === 'ens-name') return onEnsName()
150
+ if (choice === 'wallet-setup') return onWalletSetup()
151
+ if (choice === 'continuity') return onContinuity()
152
+ if (choice === 'skills-tree') return onSkillsTree()
153
+ if (choice === 'backup') return onBackupNow()
154
+ if (choice === 'refetch') return onRefetchLatest()
155
+ if (choice === 'identity-values') return onIdentityValues()
156
+ if (choice === 'prepare-transfer') return onPrepareTransfer()
157
+ if (choice === 'storage') return onStorage()
158
+ if (choice === 'load') return onLoad()
159
+ if (choice === 'create') return onCreate()
160
+ }}
161
+ onCancel={onCancel}
162
+ />
163
+ {!identity ? (
164
+ <Box marginTop={1}>
165
+ <Text color={theme.dim}>esc to quit</Text>
166
+ </Box>
167
+ ) : null}
168
+ </Box>
169
+ {statusBits.length > 0 ? (
170
+ <Box marginTop={2}>
171
+ {isDemoMode() ? (
172
+ <Text>
173
+ <Text color={theme.accentHighlight}>demo </Text>
174
+ <Text color={theme.menuStatus}>· {statusBits.join(' · ')}</Text>
175
+ </Text>
176
+ ) : (
177
+ <Text color={theme.menuStatus}>{statusBits.join(' · ')}</Text>
178
+ )}
179
+ </Box>
180
+ ) : null}
181
+ </Box>
182
+ )
183
+ }
184
+
185
+ function renderReconciliationBanner(r: AgentReconciliation, identity: EthagentIdentity): React.ReactNode {
186
+ if (r.token === 'no-agent') return null
187
+ if (r.token === 'unlinked') {
188
+ const tokenLabel = r.tokenAgentId ? `Token #${r.tokenAgentId}` : 'Token'
189
+ const transferSnapshot = transferSnapshotView(identity)
190
+ if (transferSnapshot) {
191
+ return (
192
+ <>
193
+ <Text color={theme.accentError} bold>Agent Unlinked</Text>
194
+ <Text color={theme.textSubtle}>{tokenLabel} was transferred. Local SOUL.md, MEMORY.md, and skills remain.</Text>
195
+ </>
196
+ )
197
+ }
198
+ return (
199
+ <>
200
+ <Text color={theme.accentError} bold>Agent Unlinked</Text>
201
+ <Text color={theme.textSubtle}>{tokenLabel} left without Prepare Transfer.</Text>
202
+ </>
203
+ )
204
+ }
205
+ if (r.token === 'unknown') {
206
+ return <Text color={theme.dim}>Ownership check failed (RPC?)</Text>
207
+ }
208
+ const lines: string[] = []
209
+ if (r.custody === 'mid-flow-uri-pending') lines.push('Advanced setup pending')
210
+ if (r.custody !== 'mid-flow-uri-pending' && r.agentUri === 'local-newer') lines.push('Local newer than onchain')
211
+ if (r.custody !== 'mid-flow-uri-pending' && r.agentUri === 'chain-newer') lines.push('Onchain newer than local')
212
+ if (r.vault === 'missing') lines.push('Vault contract missing')
213
+ if (lines.length === 0) return null
214
+ return (
215
+ <>
216
+ <Text color={theme.accentPeriwinkle} bold>{lines.length} item{lines.length === 1 ? '' : 's'} need attention</Text>
217
+ {lines.map((line, i) => <Text key={i} color={theme.textSubtle}>· {line}</Text>)}
218
+ </>
219
+ )
220
+ }
@@ -0,0 +1,28 @@
1
+ import React from 'react'
2
+ import { Box, Text } from 'ink'
3
+ import { Surface } from '../../../../ui/Surface.js'
4
+ import { Select } from '../../../../ui/Select.js'
5
+ import { theme } from '../../../../ui/theme.js'
6
+
7
+ type OperationCompleteScreenProps = {
8
+ message: string
9
+ onReturn: () => void
10
+ }
11
+
12
+ export const OperationCompleteScreen: React.FC<OperationCompleteScreenProps> = ({ message, onReturn }) => (
13
+ <Surface
14
+ title="Done"
15
+ footer={<Text color={theme.dim}>enter returns to menu</Text>}
16
+ >
17
+ <Box flexDirection="column">
18
+ <Text color={theme.text}>{message}</Text>
19
+ <Box marginTop={1}>
20
+ <Select<'menu'>
21
+ options={[{ value: 'menu', label: 'Return to Menu' }]}
22
+ onSubmit={onReturn}
23
+ onCancel={onReturn}
24
+ />
25
+ </Box>
26
+ </Box>
27
+ </Surface>
28
+ )
@@ -27,13 +27,13 @@ export const UnlinkedIdentityScreen: React.FC<UnlinkedIdentityScreenProps> = ({
27
27
  }) => {
28
28
  const options: Array<{ value: Action; label: string; hint?: string; role?: 'section' | 'utility' }> = [
29
29
  { value: 'load-agent', role: 'section', label: 'Switch Agent' },
30
- { value: 'load-agent', label: 'Switch Agent', hint: 'Reconnect this token by signing with the current owner wallet, or switch to a different one' },
31
- { value: 'open-menu', role: 'section', label: 'Identity Hub' },
32
- { value: 'open-menu', label: 'Open Identity Hub', hint: 'Browse local identity, continuity files, and settings without reconnecting' },
30
+ { value: 'load-agent', label: 'Switch Agent', hint: 'Reconnect or switch wallet' },
31
+ { value: 'open-menu', role: 'section', label: 'Identity' },
32
+ { value: 'open-menu', label: 'Open Identity', hint: 'Browse without reconnecting' },
33
33
  ]
34
34
  if (onRetry) {
35
35
  options.push({ value: 'retry', role: 'section', label: 'Recheck' })
36
- options.push({ value: 'retry', label: 'Retry Ownership Check', hint: 'Re-query onchain to confirm the current owner', role: 'utility' })
36
+ options.push({ value: 'retry', label: 'Retry Ownership Check', hint: 'Recheck owner onchain', role: 'utility' })
37
37
  }
38
38
 
39
39
  const tokenLabel = agentId ? `Token #${agentId}` : 'Token'
@@ -47,15 +47,14 @@ export const UnlinkedIdentityScreen: React.FC<UnlinkedIdentityScreenProps> = ({
47
47
  >
48
48
  <Box flexDirection="column">
49
49
  {transferSnapshot ? (
50
- <Text color={theme.textSubtle}>
51
- {tokenLabel} was transferred. Local SOUL.md, MEMORY.md, and skills remain. Back them up before this directory is reused.
52
- </Text>
50
+ <>
51
+ <Text color={theme.textSubtle}>{tokenLabel} was transferred.</Text>
52
+ <Text color={theme.textSubtle}>Local files remain. Back them up before reuse.</Text>
53
+ </>
53
54
  ) : (
54
55
  <>
55
56
  <Text color={theme.accentPeriwinkle}>{tokenLabel} left this wallet without Prepare Transfer, so the new holder has no continuity handoff.</Text>
56
- <Text color={theme.textSubtle}>
57
- Local SOUL.md, MEMORY.md, and skills remain. Back them up before this directory is reused.
58
- </Text>
57
+ <Text color={theme.textSubtle}>Local files remain. Back them up before reuse.</Text>
59
58
  </>
60
59
  )}
61
60
  </Box>
@@ -16,7 +16,6 @@ type WalletApprovalScreenProps = {
16
16
  }
17
17
 
18
18
  export const OPEN_BROWSER_HINT = 'Press enter to open in browser...'
19
- const PREPARING_WALLET_REQUEST_LABEL = 'preparing wallet request...'
20
19
 
21
20
  export const WalletApprovalScreen: React.FC<WalletApprovalScreenProps> = ({ title, subtitle, walletSession, label, onCancel }) => {
22
21
  useAppInput((_input, key) => {
@@ -37,7 +36,7 @@ export const WalletApprovalScreen: React.FC<WalletApprovalScreenProps> = ({ titl
37
36
  </Box>
38
37
  </Box>
39
38
  ) : (
40
- <Spinner label={PREPARING_WALLET_REQUEST_LABEL} />
39
+ <Spinner label={label} />
41
40
  )}
42
41
  </Surface>
43
42
  )
@@ -0,0 +1,54 @@
1
+ import React from 'react'
2
+ import { Box, Text } from 'ink'
3
+ import { theme, gradientColor } from '../../../../ui/theme.js'
4
+
5
+ export const LINES = [
6
+ '░░░░░░░╗░░░░░░░░╗░░╗ ░░╗ █████╗ ██████╗ ███████╗███╗ ██╗████████╗',
7
+ '░░╔════╝╚══░░╔══╝░░║ ░░║██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝',
8
+ '░░░░░╗ ░░║ ░░░░░░░║███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ',
9
+ '░░╔══╝ ░░║ ░░╔══░░║██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ',
10
+ '░░░░░░░╗ ░░║ ░░║ ░░║██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ',
11
+ '╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ',
12
+ ]
13
+
14
+ export const SPLIT = 25
15
+
16
+ export const LEFT_DECOR = [
17
+ ' ✦ ',
18
+ ' ⊹ ',
19
+ ' ',
20
+ ' . ',
21
+ ' ',
22
+ ' ฅ^•ﻌ•マ ',
23
+ ]
24
+
25
+ export const RIGHT_DECOR = [
26
+ ' ˖ 𐰁 ',
27
+ ' ',
28
+ ' 𝗓 ',
29
+ ' ⊹ ',
30
+ ' ᶻ ',
31
+ ' ',
32
+ ]
33
+
34
+ export const Wordmark: React.FC = () => (
35
+ <Box flexDirection="row">
36
+ <Text color={theme.wordmarkEth}>{LEFT_DECOR.join('\n')}</Text>
37
+ <Box flexDirection="column">
38
+ {LINES.map((line, i) => {
39
+ const eth = line.slice(0, SPLIT)
40
+ const agent = line.slice(SPLIT)
41
+ const maxAgent = Math.max(1, agent.length - 1)
42
+ return (
43
+ <Text key={i}>
44
+ <Text color={theme.wordmarkEth}>{eth}</Text>
45
+ {[...agent].map((ch, j) => (
46
+ <Text key={j} color={gradientColor(j / maxAgent)}>{ch}</Text>
47
+ ))}
48
+ </Text>
49
+ )
50
+ })}
51
+ </Box>
52
+ <Text color={theme.wordmarkEth}>{RIGHT_DECOR.join('\n')}</Text>
53
+ </Box>
54
+ )
@@ -3,19 +3,22 @@ import type { IdentityPerspective } from '../../custody/state.js'
3
3
 
4
4
  type MenuFlags = {
5
5
  prepareTransferDisabled: boolean
6
- prepareTransferReason?: string
6
+ prepareTransferHint?: string
7
+ prepareTransferHidden: boolean
7
8
  custodyModeDisabled: boolean
8
- custodyModeReason?: string
9
+ custodyModeHint?: string
9
10
  ensNameDisabled: boolean
10
- ensNameReason?: string
11
+ ensNameHint?: string
11
12
  saveSnapshotDisabled: boolean
12
13
  refetchLatestDisabled: boolean
14
+ refetchHint?: string
13
15
  tokenValuesUnlinkedNote?: string
14
16
 
15
17
  custodyAsterisk: boolean
16
18
  custodyHint?: string
17
19
  saveSnapshotAsterisk: boolean
18
20
  saveSnapshotHint?: string
21
+ workingTreeDirty: boolean
19
22
  }
20
23
 
21
24
  export function menuFlagsFromReconciliation(r: AgentReconciliation, perspective: IdentityPerspective = 'unknown'): MenuFlags {
@@ -23,11 +26,21 @@ export function menuFlagsFromReconciliation(r: AgentReconciliation, perspective:
23
26
  const inVault = r.custody === 'advanced' || r.custody === 'mid-flow-uri-pending'
24
27
  const isOperator = perspective === 'operator'
25
28
 
26
- let prepareTransferReason: string | undefined
27
- if (isOperator) {
28
- prepareTransferReason = 'Owner-only action'
29
- } else if (!unlinked && (r.custody === 'advanced' || r.custody === 'mid-flow-uri-pending')) {
30
- prepareTransferReason = 'Withdraw from vault first'
29
+ let prepareTransferHint: string | undefined
30
+ if (unlinked) {
31
+ prepareTransferHint = 'Token unlinked'
32
+ } else if (inVault) {
33
+ prepareTransferHint = 'Token is in the Vault'
34
+ }
35
+
36
+ let custodyModeHint: string | undefined
37
+ if (unlinked) {
38
+ custodyModeHint = 'Token unlinked'
39
+ }
40
+
41
+ let ensNameHint: string | undefined
42
+ if (unlinked) {
43
+ ensNameHint = 'Token unlinked'
31
44
  }
32
45
 
33
46
  const custodyAsterisk = r.custody === 'mid-flow-uri-pending' || r.vault === 'missing'
@@ -40,23 +53,34 @@ export function menuFlagsFromReconciliation(r: AgentReconciliation, perspective:
40
53
  custodyHint = 'Vault missing, open to redeploy'
41
54
  }
42
55
 
43
- const custodyModeReason = isOperator ? 'Owner-only action' : undefined
44
- const ensNameReason = isOperator ? 'Owner-only action' : undefined
56
+ const agentUriLocalNewer = r.agentUri === 'local-newer' && r.custody !== 'mid-flow-uri-pending'
57
+
58
+ let saveSnapshotHint: string | undefined
59
+ if (unlinked) {
60
+ saveSnapshotHint = 'Token unlinked'
61
+ } else if (agentUriLocalNewer) {
62
+ saveSnapshotHint = 'Local newer than onchain'
63
+ }
64
+
65
+ const refetchHint: string | undefined = unlinked ? 'Token unlinked' : undefined
45
66
 
46
67
  return {
47
68
  prepareTransferDisabled: unlinked || inVault || isOperator,
48
- ...(prepareTransferReason ? { prepareTransferReason } : {}),
69
+ ...(prepareTransferHint ? { prepareTransferHint } : {}),
70
+ prepareTransferHidden: inVault,
49
71
  custodyModeDisabled: unlinked || isOperator,
50
- ...(custodyModeReason ? { custodyModeReason } : {}),
72
+ ...(custodyModeHint ? { custodyModeHint } : {}),
51
73
  ensNameDisabled: unlinked || isOperator,
52
- ...(ensNameReason ? { ensNameReason } : {}),
74
+ ...(ensNameHint ? { ensNameHint } : {}),
53
75
  saveSnapshotDisabled: unlinked,
54
76
  refetchLatestDisabled: unlinked,
77
+ ...(refetchHint ? { refetchHint } : {}),
55
78
  ...(unlinked ? { tokenValuesUnlinkedNote: 'Unlinked, retained for reference' } : {}),
56
79
 
57
80
  custodyAsterisk: custodyAsterisk && !isOperator,
58
81
  ...(custodyHint ? { custodyHint } : {}),
59
- saveSnapshotAsterisk: r.agentUri === 'local-newer',
60
- ...(r.agentUri === 'local-newer' ? { saveSnapshotHint: 'Local newer than onchain' } : {}),
82
+ saveSnapshotAsterisk: agentUriLocalNewer || r.workingTree === 'dirty',
83
+ ...(saveSnapshotHint ? { saveSnapshotHint } : {}),
84
+ workingTreeDirty: r.workingTree === 'dirty',
61
85
  }
62
86
  }
@@ -3,7 +3,7 @@ import os from 'node:os'
3
3
  import path from 'node:path'
4
4
  import { getAddress, type Address } from 'viem'
5
5
  import type { EthagentIdentity } from '../../../../storage/config.js'
6
- import type { ProfileUpdates } from '../../identityHubReducer.js'
6
+ import type { ProfileUpdates } from '../../reducer.js'
7
7
  import type { Erc8004RegistryConfig } from '../../../registry/erc8004.js'
8
8
  import { addFileToIpfs, DEFAULT_IPFS_API_URL, type IpfsAddResult } from '../../../storage/ipfs.js'
9
9
  import { agentIconContentType, isAgentIconUrl, validateAgentIconReference } from '../../../profile/agentIcon.js'
@@ -0,0 +1,30 @@
1
+ import type { Address } from 'viem'
2
+ import type { EthagentIdentity } from '../../../../storage/config.js'
3
+ import type { BrowserWalletReady } from '../../../wallet/browserWallet.js'
4
+ import type { Step } from '../../reducer.js'
5
+
6
+ export type IdentityCompletionSource = 'create' | 'restore' | 'update'
7
+
8
+ export type EffectCallbacks = {
9
+ onStep: (step: Step) => void
10
+ onWalletReady: (session: BrowserWalletReady | null) => void
11
+ onIdentityComplete: (identity: EthagentIdentity, message: string, source?: IdentityCompletionSource) => Promise<void>
12
+ onRestoreProgress?: (progress: RestoreProgress | null) => void
13
+ onTokenTransferProgress?: (progress: TokenTransferProgress | null) => void
14
+ }
15
+
16
+ export type RestoreProgress = {
17
+ phase: 'decrypting' | 'writing' | 'finishing'
18
+ label: string
19
+ }
20
+
21
+ export type TokenTransferProgress = {
22
+ phase: 'sender-sign' | 'target-sign' | 'pinning' | 'sender-transaction' | 'confirming'
23
+ walletRole: 'sender' | 'receiver' | 'none'
24
+ title: string
25
+ detail: string
26
+ label: string
27
+ expectedAddress?: Address
28
+ walletAction?: string
29
+ }
30
+
@@ -27,7 +27,3 @@ export function copyableIdentityFields(identity?: EthagentIdentity, config?: Eth
27
27
  if (activeOperator) fields.push({ label: 'Operator Wallet', value: activeOperator })
28
28
  return fields
29
29
  }
30
-
31
- export function identityValuesCopyHint(_identity?: EthagentIdentity): string {
32
- return 'Copy token and pointers'
33
- }
@@ -10,16 +10,17 @@ import {
10
10
  formatVaultBytecodeMismatchDetail,
11
11
  } from '../../../registry/vault.js'
12
12
  import { BrowserWalletError } from '../../../wallet/browserWallet.js'
13
+ import { PinataUploadError } from '../../../storage/ipfs.js'
13
14
  import { TxGuardBusyError } from '../txGuard.js'
14
15
  import { shortAddress } from './format.js'
15
16
 
16
- export type IdentityHubErrorView = {
17
+ export type IdentityManagerErrorView = {
17
18
  title: string
18
19
  detail?: string
19
20
  hint?: string
20
21
  }
21
22
 
22
- export function identityHubErrorView(err: unknown): IdentityHubErrorView {
23
+ export function identityManagerErrorView(err: unknown): IdentityManagerErrorView {
23
24
  if (err instanceof ZodError) {
24
25
  const issue = err.issues[0]
25
26
  const path = issue?.path.join('.') ?? ''
@@ -88,6 +89,34 @@ export function identityHubErrorView(err: unknown): IdentityHubErrorView {
88
89
  hint: 'Switch to the expected wallet, confirm you have funds, then retry. If the wallet keeps reporting an internal RPC error, change the RPC endpoint in your wallet settings.',
89
90
  }
90
91
  }
92
+ if (err instanceof PinataUploadError) {
93
+ if (err.status === 403) {
94
+ return {
95
+ title: 'Pinata Storage Limit Reached',
96
+ detail: 'Pinata refused the upload (403). Your account is likely at its file or storage limit; the free plan caps at 500 files.',
97
+ hint: 'Check your usage at https://app.pinata.cloud, remove old pins or upgrade your plan, then try again.',
98
+ }
99
+ }
100
+ if (err.status === 401) {
101
+ return {
102
+ title: 'Pinata Rejected the Credential',
103
+ detail: 'Pinata rejected the upload (401). Your Pinata JWT is invalid or expired.',
104
+ hint: 'Update your Pinata JWT in Storage settings, then try again.',
105
+ }
106
+ }
107
+ if (err.status === 429) {
108
+ return {
109
+ title: 'Pinata Rate Limited',
110
+ detail: 'Pinata is throttling uploads (429): too many requests in a short window.',
111
+ hint: 'Wait a moment, then try again.',
112
+ }
113
+ }
114
+ return {
115
+ title: 'Pinata Upload Failed',
116
+ detail: err.message,
117
+ hint: 'Check your Pinata account status at https://app.pinata.cloud, then try again.',
118
+ }
119
+ }
91
120
  const message = err instanceof Error ? err.message : String(err)
92
121
  if (/^owner wallet required:/i.test(message)) {
93
122
  return {
@@ -124,7 +153,7 @@ export function identityHubErrorView(err: unknown): IdentityHubErrorView {
124
153
  }
125
154
 
126
155
  export function pinataErrorText(err: unknown): string {
127
- const view = identityHubErrorView(err)
156
+ const view = identityManagerErrorView(err)
128
157
  return view.detail ?? view.title
129
158
  }
130
159
 
@@ -12,8 +12,8 @@ export function networkLabel(network: SelectableNetwork): string {
12
12
  }
13
13
 
14
14
  const NETWORK_SUBTITLES: Record<SelectableNetwork, string> = {
15
- mainnet: 'Best for high-value identities and highest security',
16
- base: 'Best for lower-cost setup and routine updates',
15
+ mainnet: 'Best for security',
16
+ base: 'Best for lower-cost use',
17
17
  }
18
18
 
19
19
  export function networkSubtitle(network: SelectableNetwork): string {
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'
2
2
  import type { EthagentConfig } from '../../../../../storage/config.js'
3
3
  import { emptyReconciliation, runReconciliation } from './run.js'
4
4
  import type { AgentReconciliation } from './types.js'
5
+ import { isDemoMode, synthDemoReconciliation } from '../../../../../cli/demo.js'
5
6
 
6
7
  export function useAgentReconciliation(
7
8
  config: EthagentConfig | null,
@@ -19,6 +20,10 @@ export function useAgentReconciliation(
19
20
  const [refreshKey, setRefreshKey] = useState(0)
20
21
 
21
22
  useEffect(() => {
23
+ if (isDemoMode()) {
24
+ setReconciliation(synthDemoReconciliation())
25
+ return
26
+ }
22
27
  if (!config?.identity || !config.identity.agentId || !config.identity.chainId || !config.identity.identityRegistryAddress) {
23
28
  setReconciliation(emptyReconciliation())
24
29
  return
@@ -262,7 +262,7 @@ function computeDriftCount(r: AgentReconciliation): number {
262
262
  let n = 0
263
263
  if (r.token === 'unlinked') n++
264
264
  if (r.custody === 'mid-flow-uri-pending') n++
265
- if (r.agentUri === 'local-newer' || r.agentUri === 'chain-newer') n++
265
+ if ((r.agentUri === 'local-newer' || r.agentUri === 'chain-newer') && r.custody !== 'mid-flow-uri-pending') n++
266
266
  if (r.vault === 'missing') n++
267
267
  if (r.workingTree === 'dirty') n++
268
268
  return n
@@ -8,3 +8,9 @@ export type {
8
8
  OwnershipGuardResult,
9
9
  OwnershipRole,
10
10
  } from './agentReconciliation/ownership.js'
11
+ export {
12
+ computeApprovalDiff,
13
+ } from './walletSetup.js'
14
+ export type {
15
+ ApprovalDiff,
16
+ } from './walletSetup.js'