ethagent 1.1.1 → 2.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 (271) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +127 -29
  3. package/package.json +16 -9
  4. package/src/app/FirstRun.tsx +192 -146
  5. package/src/app/FirstRunTimeline.tsx +47 -0
  6. package/src/app/input/AppInputProvider.tsx +1 -1
  7. package/src/app/keybindings/KeybindingProvider.tsx +1 -1
  8. package/src/chat/ChatBottomPane.tsx +0 -1
  9. package/src/chat/ChatInput.tsx +6 -6
  10. package/src/chat/ChatScreen.tsx +43 -18
  11. package/src/chat/ContextLimitView.tsx +4 -4
  12. package/src/chat/ContinuityEditReviewView.tsx +11 -17
  13. package/src/chat/ConversationStack.tsx +3 -0
  14. package/src/chat/CopyPicker.tsx +0 -1
  15. package/src/chat/MessageList.tsx +62 -45
  16. package/src/chat/PermissionPrompt.tsx +13 -9
  17. package/src/chat/PlanApprovalView.tsx +3 -3
  18. package/src/chat/ResumeView.tsx +1 -4
  19. package/src/chat/RewindView.tsx +2 -2
  20. package/src/chat/TranscriptView.tsx +6 -0
  21. package/src/chat/chatInputState.ts +1 -1
  22. package/src/chat/chatScreenUtils.ts +22 -11
  23. package/src/chat/chatSessionState.ts +2 -2
  24. package/src/chat/chatTurnOrchestrator.ts +16 -81
  25. package/src/chat/commands.ts +1 -1
  26. package/src/chat/textCursor.ts +1 -1
  27. package/src/chat/transcriptViewport.ts +2 -7
  28. package/src/cli/ResetConfirmView.tsx +1 -1
  29. package/src/cli/main.tsx +9 -3
  30. package/src/cli/preview.tsx +0 -5
  31. package/src/cli/updateNotice.ts +5 -3
  32. package/src/identity/continuity/editor.ts +7 -107
  33. package/src/identity/continuity/envelope.ts +1048 -40
  34. package/src/identity/continuity/history.ts +4 -4
  35. package/src/identity/continuity/localBackup.ts +249 -0
  36. package/src/identity/continuity/privateEdit/apply.ts +170 -0
  37. package/src/identity/continuity/privateEdit/diff.ts +82 -0
  38. package/src/identity/continuity/privateEdit/files.ts +23 -0
  39. package/src/identity/continuity/privateEdit/types.ts +28 -0
  40. package/src/identity/continuity/privateEdit.ts +10 -298
  41. package/src/identity/continuity/publicSkills.ts +8 -9
  42. package/src/identity/continuity/snapshots.ts +17 -6
  43. package/src/identity/continuity/storage/defaults.ts +111 -0
  44. package/src/identity/continuity/storage/files.ts +72 -0
  45. package/src/identity/continuity/storage/markdown.ts +81 -0
  46. package/src/identity/continuity/storage/paths.ts +24 -0
  47. package/src/identity/continuity/storage/scaffold.ts +124 -0
  48. package/src/identity/continuity/storage/status.ts +86 -0
  49. package/src/identity/continuity/storage/types.ts +27 -0
  50. package/src/identity/continuity/storage.ts +32 -507
  51. package/src/identity/continuity/zipWriter.ts +95 -0
  52. package/src/identity/crypto/backupEnvelope.ts +14 -247
  53. package/src/identity/crypto/eth.ts +7 -7
  54. package/src/identity/ens/agentRecords.ts +96 -0
  55. package/src/identity/ens/ensAutomation/contracts.ts +38 -0
  56. package/src/identity/ens/ensAutomation/delete.ts +80 -0
  57. package/src/identity/ens/ensAutomation/names.ts +14 -0
  58. package/src/identity/ens/ensAutomation/operators.ts +29 -0
  59. package/src/identity/ens/ensAutomation/read.ts +114 -0
  60. package/src/identity/ens/ensAutomation/root.ts +63 -0
  61. package/src/identity/ens/ensAutomation/setup.ts +284 -0
  62. package/src/identity/ens/ensAutomation/transactions.ts +107 -0
  63. package/src/identity/ens/ensAutomation/types.ts +126 -0
  64. package/src/identity/ens/ensAutomation.ts +29 -0
  65. package/src/identity/ens/ensLookup/client.ts +43 -0
  66. package/src/identity/ens/ensLookup/constants.ts +26 -0
  67. package/src/identity/ens/ensLookup/discovery.ts +70 -0
  68. package/src/identity/ens/ensLookup/names.ts +34 -0
  69. package/src/identity/ens/ensLookup/records.ts +45 -0
  70. package/src/identity/ens/ensLookup/resolve.ts +75 -0
  71. package/src/identity/ens/ensLookup/tokenReference.ts +17 -0
  72. package/src/identity/ens/ensLookup/types.ts +38 -0
  73. package/src/identity/ens/ensLookup/validation.ts +72 -0
  74. package/src/identity/ens/ensLookup.ts +19 -0
  75. package/src/identity/ens/ensRegistration.ts +199 -0
  76. package/src/identity/ens/resolverDelegation.ts +48 -0
  77. package/src/identity/hub/IdentityHub.tsx +13 -815
  78. package/src/identity/hub/OperationalRoutes.tsx +370 -0
  79. package/src/identity/hub/Routes.tsx +361 -0
  80. package/src/identity/hub/advancedEnsValidation.ts +45 -0
  81. package/src/identity/hub/{screens → components}/DetailsScreen.tsx +14 -8
  82. package/src/identity/hub/{screens → components}/ErrorScreen.tsx +15 -5
  83. package/src/identity/hub/components/FlowTimeline.tsx +27 -0
  84. package/src/identity/hub/components/IdentitySummary.tsx +190 -0
  85. package/src/identity/hub/components/MenuScreen.tsx +237 -0
  86. package/src/identity/hub/{screens → components}/NetworkScreen.tsx +3 -3
  87. package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx} +21 -18
  88. package/src/identity/hub/components/UnlinkedIdentityScreen.tsx +76 -0
  89. package/src/identity/hub/{screens → components}/WalletApprovalScreen.tsx +9 -8
  90. package/src/identity/hub/components/menuFlagsFromReconciliation.ts +68 -0
  91. package/src/identity/hub/effects/create.ts +310 -0
  92. package/src/identity/hub/effects/ens/flows.ts +218 -0
  93. package/src/identity/hub/effects/ens/index.ts +11 -0
  94. package/src/identity/hub/effects/ens/transactions.ts +239 -0
  95. package/src/identity/hub/effects/index.ts +74 -0
  96. package/src/identity/hub/effects/profile/profileState.ts +173 -0
  97. package/src/identity/hub/effects/publicProfile/index.ts +5 -0
  98. package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +646 -0
  99. package/src/identity/hub/effects/rebackup/index.ts +7 -0
  100. package/src/identity/hub/effects/rebackup/operatorVault.ts +378 -0
  101. package/src/identity/hub/effects/rebackup/runRebackup.ts +451 -0
  102. package/src/identity/hub/effects/receipts.ts +46 -0
  103. package/src/identity/hub/effects/restore/apply.ts +112 -0
  104. package/src/identity/hub/effects/restore/auth.ts +159 -0
  105. package/src/identity/hub/effects/restore/discover.ts +86 -0
  106. package/src/identity/hub/effects/restore/envelopes.ts +21 -0
  107. package/src/identity/hub/effects/restore/fetch.ts +25 -0
  108. package/src/identity/hub/effects/restore/index.ts +22 -0
  109. package/src/identity/hub/effects/restore/recovery.ts +135 -0
  110. package/src/identity/hub/effects/restore/resolve.ts +102 -0
  111. package/src/identity/hub/effects/restore/restoreEffects.ts +22 -0
  112. package/src/identity/hub/effects/restore/shared.ts +91 -0
  113. package/src/identity/hub/effects/restoreAdmin.ts +93 -0
  114. package/src/identity/hub/effects/shared/profilePrep.ts +139 -0
  115. package/src/identity/hub/effects/shared/snapshot.ts +336 -0
  116. package/src/identity/hub/effects/shared/sync.ts +190 -0
  117. package/src/identity/hub/effects/token-transfer/index.ts +6 -0
  118. package/src/identity/hub/effects/token-transfer/progress.ts +59 -0
  119. package/src/identity/hub/effects/token-transfer/runTokenTransfer.ts +299 -0
  120. package/src/identity/hub/effects/types.ts +53 -0
  121. package/src/identity/hub/effects/vault/preflight.ts +50 -0
  122. package/src/identity/hub/flows/continuity/ContinuityDashboardScreen.tsx +170 -0
  123. package/src/identity/hub/flows/continuity/RebackupStorageScreen.tsx +28 -0
  124. package/src/identity/hub/flows/continuity/RecoveryConfirmScreen.tsx +104 -0
  125. package/src/identity/hub/flows/continuity/SavePromptScreen.tsx +49 -0
  126. package/src/identity/hub/{screens → flows/create}/CreateFlow.tsx +61 -62
  127. package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +347 -0
  128. package/src/identity/hub/flows/custody/custodyEffects.ts +321 -0
  129. package/src/identity/hub/flows/custody/custodyFlowActions.ts +236 -0
  130. package/src/identity/hub/flows/custody/custodyFlowEffects.ts +163 -0
  131. package/src/identity/hub/flows/custody/custodyFlowHelpers.ts +25 -0
  132. package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +239 -0
  133. package/src/identity/hub/flows/custody/custodyFlowTypes.ts +45 -0
  134. package/src/identity/hub/flows/custody/useCustodyFlow.tsx +25 -0
  135. package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +336 -0
  136. package/src/identity/hub/flows/ens/EnsEditFlow.tsx +397 -0
  137. package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +332 -0
  138. package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +471 -0
  139. package/src/identity/hub/flows/ens/EnsEditRunners.tsx +198 -0
  140. package/src/identity/hub/flows/ens/EnsEditShared.tsx +162 -0
  141. package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +518 -0
  142. package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +299 -0
  143. package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +398 -0
  144. package/src/identity/hub/flows/ens/ensEditCopy.ts +117 -0
  145. package/src/identity/hub/flows/ens/ensEditTypes.ts +91 -0
  146. package/src/identity/hub/flows/profile/EditProfileFlow.tsx +271 -0
  147. package/src/identity/hub/flows/restore/RestoreFlow.tsx +324 -0
  148. package/src/identity/hub/flows/restore/useRestoreFlowEffects.ts +77 -0
  149. package/src/identity/hub/{screens → flows/settings}/StorageCredentialScreen.tsx +25 -43
  150. package/src/identity/hub/flows/token-transfer/IdentityHubTokenTransferFlow.tsx +162 -0
  151. package/src/identity/hub/flows/token-transfer/TokenTransferScreens.tsx +256 -0
  152. package/src/identity/hub/identityHubReducer.ts +166 -101
  153. package/src/identity/hub/model/continuity.ts +94 -0
  154. package/src/identity/hub/model/copy.ts +35 -0
  155. package/src/identity/hub/model/custody.ts +54 -0
  156. package/src/identity/hub/model/ens.ts +49 -0
  157. package/src/identity/hub/model/errors.ts +140 -0
  158. package/src/identity/hub/model/format.ts +15 -0
  159. package/src/identity/hub/model/identity.ts +94 -0
  160. package/src/identity/hub/model/network.ts +32 -0
  161. package/src/identity/hub/model/transfer.ts +57 -0
  162. package/src/identity/hub/operatorWallets.ts +131 -0
  163. package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +46 -0
  164. package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +129 -0
  165. package/src/identity/hub/reconciliation/agentReconciliation/run.ts +302 -0
  166. package/src/identity/hub/reconciliation/agentReconciliation/types.ts +17 -0
  167. package/src/identity/hub/reconciliation/index.ts +21 -0
  168. package/src/identity/hub/reconciliation/useAgentReconciliation.ts +10 -0
  169. package/src/identity/hub/reconciliation/walletSetup.ts +220 -0
  170. package/src/identity/hub/txGuard.ts +51 -0
  171. package/src/identity/hub/types.ts +17 -0
  172. package/src/identity/hub/useIdentityHubContinuity.ts +136 -0
  173. package/src/identity/hub/useIdentityHubController.ts +396 -0
  174. package/src/identity/hub/useIdentityHubSideEffects.ts +309 -0
  175. package/src/identity/hub/utils.ts +79 -0
  176. package/src/identity/identityCompat.ts +34 -0
  177. package/src/identity/profile/agentIcon.ts +61 -0
  178. package/src/identity/profile/imagePicker.ts +12 -12
  179. package/src/identity/registry/erc8004/abi.ts +14 -0
  180. package/src/identity/registry/erc8004/chains.ts +150 -0
  181. package/src/identity/registry/erc8004/client.ts +11 -0
  182. package/src/identity/registry/erc8004/discovery.ts +511 -0
  183. package/src/identity/registry/erc8004/metadata.ts +335 -0
  184. package/src/identity/registry/erc8004/ownership.ts +121 -0
  185. package/src/identity/registry/erc8004/preflight.ts +123 -0
  186. package/src/identity/registry/erc8004/transactions.ts +77 -0
  187. package/src/identity/registry/erc8004/types.ts +88 -0
  188. package/src/identity/registry/erc8004/uri.ts +59 -0
  189. package/src/identity/registry/erc8004/utils.ts +58 -0
  190. package/src/identity/registry/erc8004.ts +53 -1106
  191. package/src/identity/registry/fieldParsers.ts +28 -0
  192. package/src/identity/registry/operatorVault/bytecode.ts +98 -0
  193. package/src/identity/registry/operatorVault/constants.ts +38 -0
  194. package/src/identity/registry/operatorVault/read.ts +246 -0
  195. package/src/identity/registry/operatorVault/transactions.ts +81 -0
  196. package/src/identity/registry/operatorVault.ts +44 -0
  197. package/src/identity/storage/ipfs.ts +26 -24
  198. package/src/identity/wallet/browserWallet/gas.ts +41 -0
  199. package/src/identity/wallet/browserWallet/html.ts +106 -0
  200. package/src/identity/wallet/browserWallet/http.ts +28 -0
  201. package/src/identity/wallet/browserWallet/requestServer.ts +106 -0
  202. package/src/identity/wallet/browserWallet/requests.ts +191 -0
  203. package/src/identity/wallet/browserWallet/session.ts +325 -0
  204. package/src/identity/wallet/browserWallet/types.ts +192 -0
  205. package/src/identity/wallet/browserWallet/validation.ts +74 -0
  206. package/src/identity/wallet/browserWallet.ts +30 -393
  207. package/src/identity/wallet/page/constants.ts +5 -0
  208. package/src/identity/wallet/page/controller.ts +251 -0
  209. package/src/identity/wallet/page/copy.ts +340 -0
  210. package/src/identity/wallet/page/grainient.ts +278 -0
  211. package/src/identity/wallet/page/html.ts +28 -0
  212. package/src/identity/wallet/page/markup.ts +50 -0
  213. package/src/identity/wallet/page/state.ts +9 -0
  214. package/src/identity/wallet/page/styles/base.ts +259 -0
  215. package/src/identity/wallet/page/styles/components.ts +262 -0
  216. package/src/identity/wallet/page/styles/index.ts +5 -0
  217. package/src/identity/wallet/page/styles/responsive.ts +247 -0
  218. package/src/identity/wallet/page/types.ts +47 -0
  219. package/src/identity/wallet/page/view.ts +535 -0
  220. package/src/identity/wallet/page/walletProvider.ts +70 -0
  221. package/src/identity/wallet/page.tsx +38 -0
  222. package/src/identity/wallet/walletPurposeCompat.ts +27 -0
  223. package/src/mcp/manager.ts +0 -1
  224. package/src/models/ModelPicker.tsx +36 -30
  225. package/src/models/catalog.ts +5 -2
  226. package/src/models/huggingface.ts +9 -9
  227. package/src/models/llamacpp.ts +13 -13
  228. package/src/models/modelDisplay.ts +75 -0
  229. package/src/models/modelPickerOptions.ts +16 -3
  230. package/src/models/modelRecommendation.ts +0 -1
  231. package/src/providers/errors.ts +16 -0
  232. package/src/providers/gemini.ts +252 -39
  233. package/src/providers/registry.ts +2 -2
  234. package/src/providers/retry.ts +1 -1
  235. package/src/runtime/sessionMode.ts +1 -1
  236. package/src/runtime/systemPrompt.ts +2 -0
  237. package/src/runtime/toolExecution.ts +18 -22
  238. package/src/runtime/toolIntent.ts +0 -20
  239. package/src/runtime/turn.ts +0 -92
  240. package/src/storage/atomicWrite.ts +4 -1
  241. package/src/storage/config.ts +181 -5
  242. package/src/storage/identity.ts +9 -3
  243. package/src/storage/secrets.ts +2 -2
  244. package/src/tools/bashSafety.ts +8 -0
  245. package/src/tools/changeDirectoryTool.ts +1 -1
  246. package/src/tools/deleteFileTool.ts +4 -4
  247. package/src/tools/editTool.ts +4 -4
  248. package/src/tools/editUtils.ts +5 -5
  249. package/src/tools/privateContinuityEditTool.ts +4 -5
  250. package/src/tools/privateContinuityReadTool.ts +1 -2
  251. package/src/tools/registry.ts +30 -0
  252. package/src/tools/writeFileTool.ts +5 -5
  253. package/src/ui/BrandSplash.tsx +20 -85
  254. package/src/ui/ProgressBar.tsx +3 -5
  255. package/src/ui/Select.tsx +21 -9
  256. package/src/ui/Spinner.tsx +38 -3
  257. package/src/ui/Surface.tsx +3 -3
  258. package/src/ui/TextInput.tsx +191 -29
  259. package/src/ui/theme.ts +7 -34
  260. package/src/utils/openExternal.ts +21 -0
  261. package/src/utils/withRetry.ts +47 -3
  262. package/src/identity/hub/identityHubEffects.ts +0 -937
  263. package/src/identity/hub/identityHubModel.ts +0 -291
  264. package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -144
  265. package/src/identity/hub/screens/EditProfileFlow.tsx +0 -145
  266. package/src/identity/hub/screens/IdentitySummary.tsx +0 -90
  267. package/src/identity/hub/screens/MenuScreen.tsx +0 -117
  268. package/src/identity/hub/screens/RecoveryConfirmScreen.tsx +0 -87
  269. package/src/identity/hub/screens/RestoreFlow.tsx +0 -206
  270. package/src/identity/wallet/wallet-page/wallet.html +0 -1202
  271. /package/src/identity/hub/{screens → components}/BusyScreen.tsx +0 -0
@@ -0,0 +1,378 @@
1
+ import { getAddress, type Address, type Hex } from 'viem'
2
+ import type { EthagentIdentity } from '../../../../storage/config.js'
3
+ import {
4
+ prepareSyncedIdentityMarkdownScaffold,
5
+ readContinuityFiles,
6
+ readPublicSkillsFile,
7
+ writeIdentityMarkdownScaffold,
8
+ type IdentityMarkdownScaffold,
9
+ } from '../../../continuity/storage.js'
10
+ import {
11
+ createWalletRestoreAccessChallenge,
12
+ serializeContinuitySnapshotEnvelope,
13
+ type WalletChallengePurpose,
14
+ } from '../../../continuity/envelope.js'
15
+ import {
16
+ createAgentCard,
17
+ defaultPublicSkillsProfile,
18
+ serializeAgentCard,
19
+ } from '../../../continuity/publicSkills.js'
20
+ import { recordPublishedContinuitySnapshot } from '../../../continuity/snapshots.js'
21
+ import { addToIpfs, DEFAULT_IPFS_API_URL } from '../../../storage/ipfs.js'
22
+ import {
23
+ createErc8004PublicClient,
24
+ withEthagentPointers,
25
+ } from '../../../registry/erc8004.js'
26
+ import {
27
+ OPERATOR_VAULT_ABI,
28
+ encodeRotateAgentURI,
29
+ } from '../../../registry/operatorVault.js'
30
+ import {
31
+ requestBrowserWalletSignature,
32
+ requestBrowserWalletSignatureAndTransaction,
33
+ type WalletPurpose,
34
+ } from '../../../wallet/browserWallet.js'
35
+ import type { Step } from '../../identityHubReducer.js'
36
+ import type { EffectCallbacks } from '../types.js'
37
+ import { awaitConfirmedReceipt } from '../receipts.js'
38
+ import {
39
+ assertVerifiedPin,
40
+ prepareProfileStateForSave,
41
+ } from '../shared/profilePrep.js'
42
+ import {
43
+ assertSnapshotSaveSignerAuthorized,
44
+ createContinuityEnvelopeForSave,
45
+ expectedAccountForSnapshotSave,
46
+ operatorsPointerFromState,
47
+ operatorSignerFor,
48
+ ownerAddressForSnapshotSave,
49
+ type WalletRestoreAccessContext,
50
+ walletRestoreAccessContext,
51
+ } from '../shared/snapshot.js'
52
+ import { markCurrentContinuityFilesPublished } from '../shared/sync.js'
53
+
54
+ type BackupMetadata = NonNullable<EthagentIdentity['backup']>
55
+ type PublicSkillsMetadata = NonNullable<EthagentIdentity['publicSkills']>
56
+
57
+ type OperatorVaultPublishPrepared = {
58
+ nextIdentity: EthagentIdentity
59
+ markdownScaffold?: IdentityMarkdownScaffold
60
+ completionMessage: string
61
+ }
62
+
63
+ export async function runOperatorWalletRebackup(args: {
64
+ step: Extract<Step, { kind: 'rebackup-signing' }>
65
+ callbacks: EffectCallbacks
66
+ walletPurpose: WalletPurpose
67
+ deriveAgentName: (identity: EthagentIdentity) => string
68
+ }): Promise<void> {
69
+ const { step, callbacks } = args
70
+ if (!step.identity.agentId) throw new Error('Cannot back up: identity is missing an agent token ID')
71
+ const sourceAgentId = step.identity.agentId
72
+ const snapshotOwner = ownerAddressForSnapshotSave(step.identity, step.profileUpdates)
73
+ const purpose = args.walletPurpose
74
+ const challengePurpose: WalletChallengePurpose = 'restore-operator'
75
+ const walletAccess = walletRestoreAccessContext(step.identity, step.registry, step.profileUpdates, snapshotOwner)
76
+ if (!walletAccess) throw new Error('Cannot back up: missing wallet restore access context')
77
+ const expectedSigner = expectedAccountForSnapshotSave(step.identity, step.profileUpdates, walletAccess)
78
+
79
+ const effectiveSigner = expectedSigner ?? operatorSignerFor(step.identity)
80
+ if (step.vaultAddress && effectiveSigner) {
81
+ await runOperatorWalletVaultPublish({
82
+ step,
83
+ callbacks,
84
+ sourceAgentId,
85
+ snapshotOwner,
86
+ walletAccess,
87
+ challengePurpose,
88
+ expectedSigner: effectiveSigner,
89
+ vaultAddress: step.vaultAddress,
90
+ deriveAgentName: args.deriveAgentName,
91
+ })
92
+ return
93
+ }
94
+
95
+ const wallet = await requestBrowserWalletSignature({
96
+ chainId: step.registry.chainId,
97
+ messageForAccount: account => createWalletRestoreAccessChallenge({
98
+ token: walletAccess.token,
99
+ ownerAddress: snapshotOwner,
100
+ walletAddress: account,
101
+ accessEpoch: walletAccess.accessEpoch,
102
+ ...(challengePurpose ? { purpose: challengePurpose } : {}),
103
+ }),
104
+ onReady: callbacks.onWalletReady,
105
+ purpose,
106
+ ...(expectedSigner ? { expectedAccount: expectedSigner } : {}),
107
+ })
108
+ callbacks.onWalletReady(null)
109
+
110
+ assertSnapshotSaveSignerAuthorized(step.identity, step.profileUpdates, wallet.account, snapshotOwner, walletAccess)
111
+
112
+ const {
113
+ state,
114
+ nextEnsName,
115
+ uploadedImageUri,
116
+ } = await prepareProfileStateForSave({
117
+ identity: step.identity,
118
+ registry: step.registry,
119
+ profileUpdates: step.profileUpdates,
120
+ pinataJwt: step.pinataJwt,
121
+ ownerAddress: snapshotOwner,
122
+ walletAccount: getAddress(wallet.account),
123
+ includeLastBackedUpAt: true,
124
+ })
125
+ const nextIdentityForFiles: EthagentIdentity = { ...step.identity, state }
126
+ const markdownScaffold = step.profileUpdates
127
+ ? await prepareSyncedIdentityMarkdownScaffold(nextIdentityForFiles)
128
+ : undefined
129
+ const continuityFiles = markdownScaffold
130
+ ? { 'SOUL.md': markdownScaffold['SOUL.md'], 'MEMORY.md': markdownScaffold['MEMORY.md'] }
131
+ : await readContinuityFiles(nextIdentityForFiles)
132
+ const publicSkillsJson = markdownScaffold
133
+ ? markdownScaffold['skills.json']
134
+ : await readPublicSkillsFile(nextIdentityForFiles)
135
+ const publicSkillsPin = await addToIpfs(DEFAULT_IPFS_API_URL, publicSkillsJson, fetch, { pinataJwt: step.pinataJwt })
136
+ assertVerifiedPin(publicSkillsPin)
137
+ const agentCardPin = await addToIpfs(
138
+ DEFAULT_IPFS_API_URL,
139
+ serializeAgentCard(createAgentCard(defaultPublicSkillsProfile(nextIdentityForFiles))),
140
+ fetch,
141
+ { pinataJwt: step.pinataJwt },
142
+ )
143
+ assertVerifiedPin(agentCardPin)
144
+ const envelope = createContinuityEnvelopeForSave({
145
+ identity: nextIdentityForFiles,
146
+ registry: step.registry,
147
+ ownerAddress: snapshotOwner,
148
+ signerAddress: wallet.account,
149
+ walletSignature: wallet.signature,
150
+ state,
151
+ files: continuityFiles,
152
+ walletAccess,
153
+ ...(challengePurpose ? { challengePurpose } : {}),
154
+ })
155
+ const statePin = await addToIpfs(DEFAULT_IPFS_API_URL, serializeContinuitySnapshotEnvelope(envelope), fetch, { pinataJwt: step.pinataJwt })
156
+ assertVerifiedPin(statePin)
157
+
158
+ const backup: BackupMetadata = {
159
+ cid: statePin.cid,
160
+ createdAt: envelope.createdAt,
161
+ envelopeVersion: envelope.envelopeVersion,
162
+ ipfsApiUrl: DEFAULT_IPFS_API_URL,
163
+ status: 'pinned',
164
+ ownerAddress: snapshotOwner,
165
+ chainId: step.registry.chainId,
166
+ rpcUrl: step.registry.rpcUrl,
167
+ identityRegistryAddress: step.registry.identityRegistryAddress,
168
+ agentId: sourceAgentId,
169
+ }
170
+ const publicSkills: PublicSkillsMetadata = {
171
+ cid: publicSkillsPin.cid,
172
+ agentCardCid: agentCardPin.cid,
173
+ updatedAt: envelope.createdAt,
174
+ status: 'pinned',
175
+ }
176
+
177
+ const nextIdentity: EthagentIdentity = {
178
+ ...step.identity,
179
+ state,
180
+ backup,
181
+ publicSkills,
182
+ }
183
+
184
+ if (markdownScaffold) {
185
+ await writeIdentityMarkdownScaffold(nextIdentity, markdownScaffold)
186
+ }
187
+ await recordPublishedContinuitySnapshot({ identity: nextIdentity, label: 'local operator-wallet snapshot' }).catch(() => null)
188
+ await markCurrentContinuityFilesPublished(nextIdentity).catch(() => null)
189
+ const completionMessage = nextEnsName !== undefined && nextEnsName !== ((step.identity.state as Record<string, unknown> | undefined)?.ensName as string | undefined)
190
+ ? 'Snapshot saved locally. Owner wallet still needs to publish to make ENS changes discoverable.'
191
+ : uploadedImageUri !== undefined
192
+ ? 'Snapshot saved locally. Owner wallet still needs to publish to make profile changes discoverable.'
193
+ : 'Snapshot saved locally. Owner wallet still needs to publish to rotate the onchain pointer.'
194
+ await callbacks.onIdentityComplete(nextIdentity, completionMessage, 'update')
195
+ }
196
+
197
+ async function runOperatorWalletVaultPublish(args: {
198
+ step: Extract<Step, { kind: 'rebackup-signing' }>
199
+ callbacks: EffectCallbacks
200
+ sourceAgentId: string
201
+ snapshotOwner: Address
202
+ walletAccess: WalletRestoreAccessContext
203
+ challengePurpose: WalletChallengePurpose | undefined
204
+ expectedSigner: Address
205
+ vaultAddress: Address
206
+ deriveAgentName: (identity: EthagentIdentity) => string
207
+ }): Promise<void> {
208
+ const { step, callbacks, sourceAgentId, snapshotOwner, walletAccess, challengePurpose, expectedSigner, vaultAddress } = args
209
+
210
+ const probeClient = createErc8004PublicClient(step.registry)
211
+ const isOperator = await probeClient.readContract({
212
+ address: vaultAddress,
213
+ abi: OPERATOR_VAULT_ABI,
214
+ functionName: 'metadataOperators',
215
+ args: [
216
+ getAddress(step.registry.identityRegistryAddress),
217
+ BigInt(sourceAgentId),
218
+ expectedSigner,
219
+ ],
220
+ }) as boolean
221
+ if (!isOperator) {
222
+ throw new Error(
223
+ `Operator wallet ${expectedSigner} is not yet authorized on the operator delegation vault to rotate this agent's URI. Connect the owner wallet and run "Fix Records" or re-add this operator to grant the permission.`,
224
+ )
225
+ }
226
+
227
+ const result = await requestBrowserWalletSignatureAndTransaction<OperatorVaultPublishPrepared>({
228
+ chainId: step.registry.chainId,
229
+ messageForAccount: account => createWalletRestoreAccessChallenge({
230
+ token: walletAccess.token,
231
+ ownerAddress: snapshotOwner,
232
+ walletAddress: account,
233
+ accessEpoch: walletAccess.accessEpoch,
234
+ ...(challengePurpose ? { purpose: challengePurpose } : {}),
235
+ }),
236
+ onReady: callbacks.onWalletReady,
237
+ purpose: 'rotate-agent-uri-vault-operator',
238
+ ...(step.profileUpdates?.custodyPhase === 'switch-advanced' ? { flowId: 'advanced-custody' } : {}),
239
+ expectedAccount: expectedSigner,
240
+ prepareTransaction: async wallet => {
241
+ assertSnapshotSaveSignerAuthorized(step.identity, step.profileUpdates, wallet.account, snapshotOwner, walletAccess)
242
+
243
+ const {
244
+ state,
245
+ nextName,
246
+ nextDescription,
247
+ nextEnsName,
248
+ uploadedImageUri,
249
+ } = await prepareProfileStateForSave({
250
+ identity: step.identity,
251
+ registry: step.registry,
252
+ profileUpdates: step.profileUpdates,
253
+ pinataJwt: step.pinataJwt,
254
+ ownerAddress: snapshotOwner,
255
+ walletAccount: getAddress(wallet.account),
256
+ includeLastBackedUpAt: true,
257
+ })
258
+ const nextIdentityForFiles: EthagentIdentity = { ...step.identity, state }
259
+ const markdownScaffold = step.profileUpdates
260
+ ? await prepareSyncedIdentityMarkdownScaffold(nextIdentityForFiles)
261
+ : undefined
262
+ const continuityFiles = markdownScaffold
263
+ ? { 'SOUL.md': markdownScaffold['SOUL.md'], 'MEMORY.md': markdownScaffold['MEMORY.md'] }
264
+ : await readContinuityFiles(nextIdentityForFiles)
265
+ const publicSkillsJson = markdownScaffold
266
+ ? markdownScaffold['skills.json']
267
+ : await readPublicSkillsFile(nextIdentityForFiles)
268
+ const publicSkillsPin = await addToIpfs(DEFAULT_IPFS_API_URL, publicSkillsJson, fetch, { pinataJwt: step.pinataJwt })
269
+ assertVerifiedPin(publicSkillsPin)
270
+ const agentCardPin = await addToIpfs(
271
+ DEFAULT_IPFS_API_URL,
272
+ serializeAgentCard(createAgentCard(defaultPublicSkillsProfile(nextIdentityForFiles))),
273
+ fetch,
274
+ { pinataJwt: step.pinataJwt },
275
+ )
276
+ assertVerifiedPin(agentCardPin)
277
+ const envelope = createContinuityEnvelopeForSave({
278
+ identity: nextIdentityForFiles,
279
+ registry: step.registry,
280
+ ownerAddress: snapshotOwner,
281
+ signerAddress: wallet.account,
282
+ walletSignature: wallet.signature,
283
+ state,
284
+ files: continuityFiles,
285
+ walletAccess,
286
+ ...(challengePurpose ? { challengePurpose } : {}),
287
+ })
288
+ const statePin = await addToIpfs(DEFAULT_IPFS_API_URL, serializeContinuitySnapshotEnvelope(envelope), fetch, { pinataJwt: step.pinataJwt })
289
+ assertVerifiedPin(statePin)
290
+
291
+ const publicSkills: PublicSkillsMetadata = {
292
+ cid: publicSkillsPin.cid,
293
+ agentCardCid: agentCardPin.cid,
294
+ updatedAt: envelope.createdAt,
295
+ status: 'pinned',
296
+ }
297
+
298
+ const registration = withEthagentPointers({
299
+ type: 'https://eips.ethereum.org/EIPS/eip-8004#registration-v1',
300
+ name: nextName ?? args.deriveAgentName(step.identity),
301
+ ...(nextDescription ? { description: nextDescription } : {}),
302
+ ...(uploadedImageUri ? { image: uploadedImageUri } : {}),
303
+ }, {
304
+ backup: { cid: statePin.cid, envelopeVersion: envelope.envelopeVersion, createdAt: envelope.createdAt },
305
+ publicDiscovery: { skillsCid: publicSkills.cid, agentCardCid: publicSkills.agentCardCid, updatedAt: publicSkills.updatedAt },
306
+ registration: { chainId: step.registry.chainId, identityRegistryAddress: step.registry.identityRegistryAddress, agentId: sourceAgentId },
307
+ ensName: nextEnsName,
308
+ operators: operatorsPointerFromState(state, nextEnsName),
309
+ ownerAddress: snapshotOwner,
310
+ })
311
+ const metadataPin = await addToIpfs(DEFAULT_IPFS_API_URL, JSON.stringify(registration, null, 2), fetch, { pinataJwt: step.pinataJwt })
312
+ assertVerifiedPin(metadataPin)
313
+ const metadataCid = metadataPin.cid
314
+ const agentUri = `ipfs://${metadataCid}`
315
+
316
+ const vaultCall = encodeRotateAgentURI({
317
+ registry: getAddress(step.registry.identityRegistryAddress),
318
+ agentId: BigInt(sourceAgentId),
319
+ newURI: agentUri,
320
+ vaultAddress,
321
+ })
322
+
323
+ const backup: BackupMetadata = {
324
+ cid: statePin.cid,
325
+ createdAt: envelope.createdAt,
326
+ envelopeVersion: envelope.envelopeVersion,
327
+ ipfsApiUrl: DEFAULT_IPFS_API_URL,
328
+ status: 'pinned',
329
+ ownerAddress: snapshotOwner,
330
+ chainId: step.registry.chainId,
331
+ rpcUrl: step.registry.rpcUrl,
332
+ identityRegistryAddress: step.registry.identityRegistryAddress,
333
+ agentId: sourceAgentId,
334
+ metadataCid,
335
+ agentUri,
336
+ }
337
+ const nextIdentity: EthagentIdentity = {
338
+ ...step.identity,
339
+ state,
340
+ backup,
341
+ publicSkills,
342
+ agentUri,
343
+ metadataCid,
344
+ }
345
+
346
+ const completionMessage = nextEnsName !== undefined && nextEnsName !== ((step.identity.state as Record<string, unknown> | undefined)?.ensName as string | undefined)
347
+ ? 'Snapshot published onchain through the operator delegation vault. ENS records remain owner-signed, switch to the owner wallet to update them.'
348
+ : 'Snapshot published onchain through the operator delegation vault.'
349
+
350
+ return {
351
+ to: vaultCall.to,
352
+ data: vaultCall.data,
353
+ prepared: {
354
+ nextIdentity,
355
+ ...(markdownScaffold ? { markdownScaffold } : {}),
356
+ completionMessage,
357
+ },
358
+ }
359
+ },
360
+ })
361
+ callbacks.onWalletReady(null)
362
+
363
+ const client = createErc8004PublicClient(step.registry)
364
+ await awaitConfirmedReceipt(
365
+ client,
366
+ result.txHash as Hex,
367
+ 'Operator-driven URI rotation through vault',
368
+ { kind: 'rebackup-uri-vault', chainId: step.registry.chainId },
369
+ )
370
+
371
+ const nextIdentity = result.prepared.nextIdentity
372
+ if (result.prepared.markdownScaffold) {
373
+ await writeIdentityMarkdownScaffold(nextIdentity, result.prepared.markdownScaffold)
374
+ }
375
+ await recordPublishedContinuitySnapshot({ identity: nextIdentity, label: 'operator-published snapshot' }).catch(() => null)
376
+ await markCurrentContinuityFilesPublished(nextIdentity).catch(() => null)
377
+ await callbacks.onIdentityComplete(nextIdentity, result.prepared.completionMessage, 'update')
378
+ }