ethagent 1.1.2 → 2.0.1

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 (268) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +124 -32
  3. package/package.json +8 -3
  4. package/src/app/FirstRun.tsx +190 -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 +35 -15
  11. package/src/chat/ContextLimitView.tsx +4 -4
  12. package/src/chat/ContinuityEditReviewView.tsx +10 -22
  13. package/src/chat/CopyPicker.tsx +0 -1
  14. package/src/chat/MessageList.tsx +62 -45
  15. package/src/chat/PermissionPrompt.tsx +13 -9
  16. package/src/chat/PlanApprovalView.tsx +3 -3
  17. package/src/chat/ResumeView.tsx +1 -4
  18. package/src/chat/RewindView.tsx +2 -2
  19. package/src/chat/chatInputState.ts +1 -1
  20. package/src/chat/chatScreenUtils.ts +22 -11
  21. package/src/chat/chatSessionState.ts +2 -2
  22. package/src/chat/chatTurnOrchestrator.ts +16 -81
  23. package/src/chat/commands.ts +1 -1
  24. package/src/chat/textCursor.ts +1 -1
  25. package/src/chat/transcriptViewport.ts +2 -7
  26. package/src/cli/ResetConfirmView.tsx +1 -1
  27. package/src/cli/main.tsx +9 -3
  28. package/src/cli/preview.tsx +0 -5
  29. package/src/cli/updateNotice.ts +4 -2
  30. package/src/identity/continuity/editor.ts +7 -107
  31. package/src/identity/continuity/envelope.ts +1048 -40
  32. package/src/identity/continuity/history.ts +4 -4
  33. package/src/identity/continuity/localBackup.ts +249 -0
  34. package/src/identity/continuity/privateEdit/apply.ts +170 -0
  35. package/src/identity/continuity/privateEdit/diff.ts +82 -0
  36. package/src/identity/continuity/privateEdit/files.ts +23 -0
  37. package/src/identity/continuity/privateEdit/types.ts +28 -0
  38. package/src/identity/continuity/privateEdit.ts +10 -298
  39. package/src/identity/continuity/publicSkills.ts +8 -9
  40. package/src/identity/continuity/snapshots.ts +17 -6
  41. package/src/identity/continuity/storage/defaults.ts +111 -0
  42. package/src/identity/continuity/storage/files.ts +72 -0
  43. package/src/identity/continuity/storage/markdown.ts +81 -0
  44. package/src/identity/continuity/storage/paths.ts +24 -0
  45. package/src/identity/continuity/storage/scaffold.ts +124 -0
  46. package/src/identity/continuity/storage/status.ts +86 -0
  47. package/src/identity/continuity/storage/types.ts +27 -0
  48. package/src/identity/continuity/storage.ts +32 -507
  49. package/src/identity/continuity/zipWriter.ts +95 -0
  50. package/src/identity/crypto/backupEnvelope.ts +14 -247
  51. package/src/identity/crypto/eth.ts +7 -7
  52. package/src/identity/ens/agentRecords.ts +96 -0
  53. package/src/identity/ens/ensAutomation/contracts.ts +38 -0
  54. package/src/identity/ens/ensAutomation/delete.ts +80 -0
  55. package/src/identity/ens/ensAutomation/names.ts +14 -0
  56. package/src/identity/ens/ensAutomation/operators.ts +29 -0
  57. package/src/identity/ens/ensAutomation/read.ts +114 -0
  58. package/src/identity/ens/ensAutomation/root.ts +63 -0
  59. package/src/identity/ens/ensAutomation/setup.ts +284 -0
  60. package/src/identity/ens/ensAutomation/transactions.ts +107 -0
  61. package/src/identity/ens/ensAutomation/types.ts +126 -0
  62. package/src/identity/ens/ensAutomation.ts +29 -0
  63. package/src/identity/ens/ensLookup/client.ts +43 -0
  64. package/src/identity/ens/ensLookup/constants.ts +26 -0
  65. package/src/identity/ens/ensLookup/discovery.ts +70 -0
  66. package/src/identity/ens/ensLookup/names.ts +34 -0
  67. package/src/identity/ens/ensLookup/records.ts +45 -0
  68. package/src/identity/ens/ensLookup/resolve.ts +75 -0
  69. package/src/identity/ens/ensLookup/tokenReference.ts +17 -0
  70. package/src/identity/ens/ensLookup/types.ts +38 -0
  71. package/src/identity/ens/ensLookup/validation.ts +72 -0
  72. package/src/identity/ens/ensLookup.ts +19 -0
  73. package/src/identity/ens/ensRegistration.ts +199 -0
  74. package/src/identity/ens/resolverDelegation.ts +48 -0
  75. package/src/identity/hub/IdentityHub.tsx +13 -817
  76. package/src/identity/hub/OperationalRoutes.tsx +370 -0
  77. package/src/identity/hub/Routes.tsx +361 -0
  78. package/src/identity/hub/advancedEnsValidation.ts +45 -0
  79. package/src/identity/hub/{screens → components}/DetailsScreen.tsx +14 -8
  80. package/src/identity/hub/{screens → components}/ErrorScreen.tsx +15 -5
  81. package/src/identity/hub/components/FlowTimeline.tsx +27 -0
  82. package/src/identity/hub/components/IdentitySummary.tsx +190 -0
  83. package/src/identity/hub/components/MenuScreen.tsx +237 -0
  84. package/src/identity/hub/{screens → components}/NetworkScreen.tsx +3 -3
  85. package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx} +21 -18
  86. package/src/identity/hub/components/UnlinkedIdentityScreen.tsx +76 -0
  87. package/src/identity/hub/{screens → components}/WalletApprovalScreen.tsx +9 -8
  88. package/src/identity/hub/components/menuFlagsFromReconciliation.ts +68 -0
  89. package/src/identity/hub/effects/create.ts +310 -0
  90. package/src/identity/hub/effects/ens/flows.ts +218 -0
  91. package/src/identity/hub/effects/ens/index.ts +11 -0
  92. package/src/identity/hub/effects/ens/transactions.ts +239 -0
  93. package/src/identity/hub/effects/index.ts +74 -0
  94. package/src/identity/hub/effects/profile/profileState.ts +173 -0
  95. package/src/identity/hub/effects/publicProfile/index.ts +5 -0
  96. package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +646 -0
  97. package/src/identity/hub/effects/rebackup/index.ts +7 -0
  98. package/src/identity/hub/effects/rebackup/operatorVault.ts +378 -0
  99. package/src/identity/hub/effects/rebackup/runRebackup.ts +451 -0
  100. package/src/identity/hub/effects/receipts.ts +46 -0
  101. package/src/identity/hub/effects/restore/apply.ts +112 -0
  102. package/src/identity/hub/effects/restore/auth.ts +159 -0
  103. package/src/identity/hub/effects/restore/discover.ts +86 -0
  104. package/src/identity/hub/effects/restore/envelopes.ts +21 -0
  105. package/src/identity/hub/effects/restore/fetch.ts +25 -0
  106. package/src/identity/hub/effects/restore/index.ts +22 -0
  107. package/src/identity/hub/effects/restore/recovery.ts +135 -0
  108. package/src/identity/hub/effects/restore/resolve.ts +102 -0
  109. package/src/identity/hub/effects/restore/restoreEffects.ts +22 -0
  110. package/src/identity/hub/effects/restore/shared.ts +91 -0
  111. package/src/identity/hub/effects/restoreAdmin.ts +93 -0
  112. package/src/identity/hub/effects/shared/profilePrep.ts +139 -0
  113. package/src/identity/hub/effects/shared/snapshot.ts +336 -0
  114. package/src/identity/hub/effects/shared/sync.ts +190 -0
  115. package/src/identity/hub/effects/token-transfer/index.ts +6 -0
  116. package/src/identity/hub/effects/token-transfer/progress.ts +59 -0
  117. package/src/identity/hub/effects/token-transfer/runTokenTransfer.ts +299 -0
  118. package/src/identity/hub/effects/types.ts +53 -0
  119. package/src/identity/hub/effects/vault/preflight.ts +50 -0
  120. package/src/identity/hub/flows/continuity/ContinuityDashboardScreen.tsx +170 -0
  121. package/src/identity/hub/flows/continuity/RebackupStorageScreen.tsx +28 -0
  122. package/src/identity/hub/{screens → flows/continuity}/RecoveryConfirmScreen.tsx +28 -19
  123. package/src/identity/hub/flows/continuity/SavePromptScreen.tsx +49 -0
  124. package/src/identity/hub/{screens → flows/create}/CreateFlow.tsx +61 -62
  125. package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +347 -0
  126. package/src/identity/hub/flows/custody/custodyEffects.ts +321 -0
  127. package/src/identity/hub/flows/custody/custodyFlowActions.ts +236 -0
  128. package/src/identity/hub/flows/custody/custodyFlowEffects.ts +163 -0
  129. package/src/identity/hub/flows/custody/custodyFlowHelpers.ts +25 -0
  130. package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +239 -0
  131. package/src/identity/hub/flows/custody/custodyFlowTypes.ts +45 -0
  132. package/src/identity/hub/flows/custody/useCustodyFlow.tsx +25 -0
  133. package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +336 -0
  134. package/src/identity/hub/flows/ens/EnsEditFlow.tsx +397 -0
  135. package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +332 -0
  136. package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +471 -0
  137. package/src/identity/hub/flows/ens/EnsEditRunners.tsx +198 -0
  138. package/src/identity/hub/flows/ens/EnsEditShared.tsx +162 -0
  139. package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +518 -0
  140. package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +299 -0
  141. package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +398 -0
  142. package/src/identity/hub/flows/ens/ensEditCopy.ts +117 -0
  143. package/src/identity/hub/flows/ens/ensEditTypes.ts +91 -0
  144. package/src/identity/hub/flows/profile/EditProfileFlow.tsx +271 -0
  145. package/src/identity/hub/flows/restore/RestoreFlow.tsx +324 -0
  146. package/src/identity/hub/flows/restore/useRestoreFlowEffects.ts +77 -0
  147. package/src/identity/hub/{screens → flows/settings}/StorageCredentialScreen.tsx +23 -44
  148. package/src/identity/hub/flows/token-transfer/IdentityHubTokenTransferFlow.tsx +162 -0
  149. package/src/identity/hub/flows/token-transfer/TokenTransferScreens.tsx +256 -0
  150. package/src/identity/hub/identityHubReducer.ts +164 -99
  151. package/src/identity/hub/model/continuity.ts +94 -0
  152. package/src/identity/hub/model/copy.ts +35 -0
  153. package/src/identity/hub/model/custody.ts +54 -0
  154. package/src/identity/hub/model/ens.ts +49 -0
  155. package/src/identity/hub/model/errors.ts +140 -0
  156. package/src/identity/hub/model/format.ts +15 -0
  157. package/src/identity/hub/model/identity.ts +94 -0
  158. package/src/identity/hub/model/network.ts +32 -0
  159. package/src/identity/hub/model/transfer.ts +57 -0
  160. package/src/identity/hub/operatorWallets.ts +131 -0
  161. package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +46 -0
  162. package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +129 -0
  163. package/src/identity/hub/reconciliation/agentReconciliation/run.ts +302 -0
  164. package/src/identity/hub/reconciliation/agentReconciliation/types.ts +17 -0
  165. package/src/identity/hub/reconciliation/index.ts +21 -0
  166. package/src/identity/hub/reconciliation/useAgentReconciliation.ts +10 -0
  167. package/src/identity/hub/reconciliation/walletSetup.ts +220 -0
  168. package/src/identity/hub/txGuard.ts +51 -0
  169. package/src/identity/hub/types.ts +17 -0
  170. package/src/identity/hub/useIdentityHubContinuity.ts +136 -0
  171. package/src/identity/hub/useIdentityHubController.ts +396 -0
  172. package/src/identity/hub/useIdentityHubSideEffects.ts +309 -0
  173. package/src/identity/hub/utils.ts +79 -0
  174. package/src/identity/identityCompat.ts +34 -0
  175. package/src/identity/profile/agentIcon.ts +61 -0
  176. package/src/identity/profile/imagePicker.ts +12 -12
  177. package/src/identity/registry/erc8004/abi.ts +14 -0
  178. package/src/identity/registry/erc8004/chains.ts +150 -0
  179. package/src/identity/registry/erc8004/client.ts +11 -0
  180. package/src/identity/registry/erc8004/discovery.ts +511 -0
  181. package/src/identity/registry/erc8004/metadata.ts +335 -0
  182. package/src/identity/registry/erc8004/ownership.ts +121 -0
  183. package/src/identity/registry/erc8004/preflight.ts +123 -0
  184. package/src/identity/registry/erc8004/transactions.ts +77 -0
  185. package/src/identity/registry/erc8004/types.ts +88 -0
  186. package/src/identity/registry/erc8004/uri.ts +59 -0
  187. package/src/identity/registry/erc8004/utils.ts +58 -0
  188. package/src/identity/registry/erc8004.ts +53 -1106
  189. package/src/identity/registry/fieldParsers.ts +28 -0
  190. package/src/identity/registry/operatorVault/bytecode.ts +98 -0
  191. package/src/identity/registry/operatorVault/constants.ts +38 -0
  192. package/src/identity/registry/operatorVault/read.ts +246 -0
  193. package/src/identity/registry/operatorVault/transactions.ts +81 -0
  194. package/src/identity/registry/operatorVault.ts +44 -0
  195. package/src/identity/storage/ipfs.ts +26 -24
  196. package/src/identity/wallet/browserWallet/gas.ts +41 -0
  197. package/src/identity/wallet/browserWallet/html.ts +106 -0
  198. package/src/identity/wallet/browserWallet/http.ts +28 -0
  199. package/src/identity/wallet/browserWallet/requestServer.ts +106 -0
  200. package/src/identity/wallet/browserWallet/requests.ts +191 -0
  201. package/src/identity/wallet/browserWallet/session.ts +325 -0
  202. package/src/identity/wallet/browserWallet/types.ts +192 -0
  203. package/src/identity/wallet/browserWallet/validation.ts +74 -0
  204. package/src/identity/wallet/browserWallet.ts +30 -393
  205. package/src/identity/wallet/page/constants.ts +5 -0
  206. package/src/identity/wallet/page/controller.ts +251 -0
  207. package/src/identity/wallet/page/copy.ts +340 -0
  208. package/src/identity/wallet/page/grainient.ts +278 -0
  209. package/src/identity/wallet/page/html.ts +28 -0
  210. package/src/identity/wallet/page/markup.ts +50 -0
  211. package/src/identity/wallet/page/state.ts +9 -0
  212. package/src/identity/wallet/page/styles/base.ts +259 -0
  213. package/src/identity/wallet/page/styles/components.ts +262 -0
  214. package/src/identity/wallet/page/styles/index.ts +5 -0
  215. package/src/identity/wallet/page/styles/responsive.ts +247 -0
  216. package/src/identity/wallet/page/types.ts +47 -0
  217. package/src/identity/wallet/page/view.ts +535 -0
  218. package/src/identity/wallet/page/walletProvider.ts +70 -0
  219. package/src/identity/wallet/page.tsx +38 -0
  220. package/src/identity/wallet/walletPurposeCompat.ts +27 -0
  221. package/src/mcp/manager.ts +0 -1
  222. package/src/models/ModelPicker.tsx +36 -30
  223. package/src/models/catalog.ts +5 -2
  224. package/src/models/huggingface.ts +9 -9
  225. package/src/models/llamacpp.ts +13 -13
  226. package/src/models/modelDisplay.ts +75 -0
  227. package/src/models/modelPickerOptions.ts +16 -3
  228. package/src/models/modelRecommendation.ts +0 -1
  229. package/src/providers/errors.ts +16 -0
  230. package/src/providers/gemini.ts +252 -39
  231. package/src/providers/registry.ts +2 -2
  232. package/src/providers/retry.ts +1 -1
  233. package/src/runtime/sessionMode.ts +1 -1
  234. package/src/runtime/systemPrompt.ts +2 -0
  235. package/src/runtime/toolExecution.ts +18 -22
  236. package/src/runtime/toolIntent.ts +0 -20
  237. package/src/runtime/turn.ts +0 -92
  238. package/src/storage/atomicWrite.ts +4 -1
  239. package/src/storage/config.ts +181 -5
  240. package/src/storage/identity.ts +9 -3
  241. package/src/storage/secrets.ts +2 -2
  242. package/src/tools/bashSafety.ts +8 -0
  243. package/src/tools/changeDirectoryTool.ts +1 -1
  244. package/src/tools/deleteFileTool.ts +4 -4
  245. package/src/tools/editTool.ts +4 -4
  246. package/src/tools/editUtils.ts +5 -5
  247. package/src/tools/privateContinuityEditTool.ts +4 -5
  248. package/src/tools/privateContinuityReadTool.ts +1 -2
  249. package/src/tools/registry.ts +30 -0
  250. package/src/tools/writeFileTool.ts +5 -5
  251. package/src/ui/BrandSplash.tsx +20 -85
  252. package/src/ui/ProgressBar.tsx +3 -5
  253. package/src/ui/Select.tsx +20 -8
  254. package/src/ui/Spinner.tsx +38 -3
  255. package/src/ui/Surface.tsx +2 -2
  256. package/src/ui/TextInput.tsx +63 -20
  257. package/src/ui/theme.ts +7 -34
  258. package/src/utils/openExternal.ts +21 -0
  259. package/src/utils/withRetry.ts +47 -3
  260. package/src/identity/hub/identityHubEffects.ts +0 -937
  261. package/src/identity/hub/identityHubModel.ts +0 -371
  262. package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -156
  263. package/src/identity/hub/screens/EditProfileFlow.tsx +0 -146
  264. package/src/identity/hub/screens/IdentitySummary.tsx +0 -106
  265. package/src/identity/hub/screens/MenuScreen.tsx +0 -117
  266. package/src/identity/hub/screens/RestoreFlow.tsx +0 -206
  267. package/src/identity/wallet/wallet-page/wallet.html +0 -1202
  268. /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
+ }