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.
- package/LICENSE +21 -21
- package/README.md +127 -29
- package/package.json +16 -9
- package/src/app/FirstRun.tsx +192 -146
- package/src/app/FirstRunTimeline.tsx +47 -0
- package/src/app/input/AppInputProvider.tsx +1 -1
- package/src/app/keybindings/KeybindingProvider.tsx +1 -1
- package/src/chat/ChatBottomPane.tsx +0 -1
- package/src/chat/ChatInput.tsx +6 -6
- package/src/chat/ChatScreen.tsx +43 -18
- package/src/chat/ContextLimitView.tsx +4 -4
- package/src/chat/ContinuityEditReviewView.tsx +11 -17
- package/src/chat/ConversationStack.tsx +3 -0
- package/src/chat/CopyPicker.tsx +0 -1
- package/src/chat/MessageList.tsx +62 -45
- package/src/chat/PermissionPrompt.tsx +13 -9
- package/src/chat/PlanApprovalView.tsx +3 -3
- package/src/chat/ResumeView.tsx +1 -4
- package/src/chat/RewindView.tsx +2 -2
- package/src/chat/TranscriptView.tsx +6 -0
- package/src/chat/chatInputState.ts +1 -1
- package/src/chat/chatScreenUtils.ts +22 -11
- package/src/chat/chatSessionState.ts +2 -2
- package/src/chat/chatTurnOrchestrator.ts +16 -81
- package/src/chat/commands.ts +1 -1
- package/src/chat/textCursor.ts +1 -1
- package/src/chat/transcriptViewport.ts +2 -7
- package/src/cli/ResetConfirmView.tsx +1 -1
- package/src/cli/main.tsx +9 -3
- package/src/cli/preview.tsx +0 -5
- package/src/cli/updateNotice.ts +5 -3
- package/src/identity/continuity/editor.ts +7 -107
- package/src/identity/continuity/envelope.ts +1048 -40
- package/src/identity/continuity/history.ts +4 -4
- package/src/identity/continuity/localBackup.ts +249 -0
- package/src/identity/continuity/privateEdit/apply.ts +170 -0
- package/src/identity/continuity/privateEdit/diff.ts +82 -0
- package/src/identity/continuity/privateEdit/files.ts +23 -0
- package/src/identity/continuity/privateEdit/types.ts +28 -0
- package/src/identity/continuity/privateEdit.ts +10 -298
- package/src/identity/continuity/publicSkills.ts +8 -9
- package/src/identity/continuity/snapshots.ts +17 -6
- package/src/identity/continuity/storage/defaults.ts +111 -0
- package/src/identity/continuity/storage/files.ts +72 -0
- package/src/identity/continuity/storage/markdown.ts +81 -0
- package/src/identity/continuity/storage/paths.ts +24 -0
- package/src/identity/continuity/storage/scaffold.ts +124 -0
- package/src/identity/continuity/storage/status.ts +86 -0
- package/src/identity/continuity/storage/types.ts +27 -0
- package/src/identity/continuity/storage.ts +32 -507
- package/src/identity/continuity/zipWriter.ts +95 -0
- package/src/identity/crypto/backupEnvelope.ts +14 -247
- package/src/identity/crypto/eth.ts +7 -7
- package/src/identity/ens/agentRecords.ts +96 -0
- package/src/identity/ens/ensAutomation/contracts.ts +38 -0
- package/src/identity/ens/ensAutomation/delete.ts +80 -0
- package/src/identity/ens/ensAutomation/names.ts +14 -0
- package/src/identity/ens/ensAutomation/operators.ts +29 -0
- package/src/identity/ens/ensAutomation/read.ts +114 -0
- package/src/identity/ens/ensAutomation/root.ts +63 -0
- package/src/identity/ens/ensAutomation/setup.ts +284 -0
- package/src/identity/ens/ensAutomation/transactions.ts +107 -0
- package/src/identity/ens/ensAutomation/types.ts +126 -0
- package/src/identity/ens/ensAutomation.ts +29 -0
- package/src/identity/ens/ensLookup/client.ts +43 -0
- package/src/identity/ens/ensLookup/constants.ts +26 -0
- package/src/identity/ens/ensLookup/discovery.ts +70 -0
- package/src/identity/ens/ensLookup/names.ts +34 -0
- package/src/identity/ens/ensLookup/records.ts +45 -0
- package/src/identity/ens/ensLookup/resolve.ts +75 -0
- package/src/identity/ens/ensLookup/tokenReference.ts +17 -0
- package/src/identity/ens/ensLookup/types.ts +38 -0
- package/src/identity/ens/ensLookup/validation.ts +72 -0
- package/src/identity/ens/ensLookup.ts +19 -0
- package/src/identity/ens/ensRegistration.ts +199 -0
- package/src/identity/ens/resolverDelegation.ts +48 -0
- package/src/identity/hub/IdentityHub.tsx +13 -815
- package/src/identity/hub/OperationalRoutes.tsx +370 -0
- package/src/identity/hub/Routes.tsx +361 -0
- package/src/identity/hub/advancedEnsValidation.ts +45 -0
- package/src/identity/hub/{screens → components}/DetailsScreen.tsx +14 -8
- package/src/identity/hub/{screens → components}/ErrorScreen.tsx +15 -5
- package/src/identity/hub/components/FlowTimeline.tsx +27 -0
- package/src/identity/hub/components/IdentitySummary.tsx +190 -0
- package/src/identity/hub/components/MenuScreen.tsx +237 -0
- package/src/identity/hub/{screens → components}/NetworkScreen.tsx +3 -3
- package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx} +21 -18
- package/src/identity/hub/components/UnlinkedIdentityScreen.tsx +76 -0
- package/src/identity/hub/{screens → components}/WalletApprovalScreen.tsx +9 -8
- package/src/identity/hub/components/menuFlagsFromReconciliation.ts +68 -0
- package/src/identity/hub/effects/create.ts +310 -0
- package/src/identity/hub/effects/ens/flows.ts +218 -0
- package/src/identity/hub/effects/ens/index.ts +11 -0
- package/src/identity/hub/effects/ens/transactions.ts +239 -0
- package/src/identity/hub/effects/index.ts +74 -0
- package/src/identity/hub/effects/profile/profileState.ts +173 -0
- package/src/identity/hub/effects/publicProfile/index.ts +5 -0
- package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +646 -0
- package/src/identity/hub/effects/rebackup/index.ts +7 -0
- package/src/identity/hub/effects/rebackup/operatorVault.ts +378 -0
- package/src/identity/hub/effects/rebackup/runRebackup.ts +451 -0
- package/src/identity/hub/effects/receipts.ts +46 -0
- package/src/identity/hub/effects/restore/apply.ts +112 -0
- package/src/identity/hub/effects/restore/auth.ts +159 -0
- package/src/identity/hub/effects/restore/discover.ts +86 -0
- package/src/identity/hub/effects/restore/envelopes.ts +21 -0
- package/src/identity/hub/effects/restore/fetch.ts +25 -0
- package/src/identity/hub/effects/restore/index.ts +22 -0
- package/src/identity/hub/effects/restore/recovery.ts +135 -0
- package/src/identity/hub/effects/restore/resolve.ts +102 -0
- package/src/identity/hub/effects/restore/restoreEffects.ts +22 -0
- package/src/identity/hub/effects/restore/shared.ts +91 -0
- package/src/identity/hub/effects/restoreAdmin.ts +93 -0
- package/src/identity/hub/effects/shared/profilePrep.ts +139 -0
- package/src/identity/hub/effects/shared/snapshot.ts +336 -0
- package/src/identity/hub/effects/shared/sync.ts +190 -0
- package/src/identity/hub/effects/token-transfer/index.ts +6 -0
- package/src/identity/hub/effects/token-transfer/progress.ts +59 -0
- package/src/identity/hub/effects/token-transfer/runTokenTransfer.ts +299 -0
- package/src/identity/hub/effects/types.ts +53 -0
- package/src/identity/hub/effects/vault/preflight.ts +50 -0
- package/src/identity/hub/flows/continuity/ContinuityDashboardScreen.tsx +170 -0
- package/src/identity/hub/flows/continuity/RebackupStorageScreen.tsx +28 -0
- package/src/identity/hub/flows/continuity/RecoveryConfirmScreen.tsx +104 -0
- package/src/identity/hub/flows/continuity/SavePromptScreen.tsx +49 -0
- package/src/identity/hub/{screens → flows/create}/CreateFlow.tsx +61 -62
- package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +347 -0
- package/src/identity/hub/flows/custody/custodyEffects.ts +321 -0
- package/src/identity/hub/flows/custody/custodyFlowActions.ts +236 -0
- package/src/identity/hub/flows/custody/custodyFlowEffects.ts +163 -0
- package/src/identity/hub/flows/custody/custodyFlowHelpers.ts +25 -0
- package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +239 -0
- package/src/identity/hub/flows/custody/custodyFlowTypes.ts +45 -0
- package/src/identity/hub/flows/custody/useCustodyFlow.tsx +25 -0
- package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +336 -0
- package/src/identity/hub/flows/ens/EnsEditFlow.tsx +397 -0
- package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +332 -0
- package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +471 -0
- package/src/identity/hub/flows/ens/EnsEditRunners.tsx +198 -0
- package/src/identity/hub/flows/ens/EnsEditShared.tsx +162 -0
- package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +518 -0
- package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +299 -0
- package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +398 -0
- package/src/identity/hub/flows/ens/ensEditCopy.ts +117 -0
- package/src/identity/hub/flows/ens/ensEditTypes.ts +91 -0
- package/src/identity/hub/flows/profile/EditProfileFlow.tsx +271 -0
- package/src/identity/hub/flows/restore/RestoreFlow.tsx +324 -0
- package/src/identity/hub/flows/restore/useRestoreFlowEffects.ts +77 -0
- package/src/identity/hub/{screens → flows/settings}/StorageCredentialScreen.tsx +25 -43
- package/src/identity/hub/flows/token-transfer/IdentityHubTokenTransferFlow.tsx +162 -0
- package/src/identity/hub/flows/token-transfer/TokenTransferScreens.tsx +256 -0
- package/src/identity/hub/identityHubReducer.ts +166 -101
- package/src/identity/hub/model/continuity.ts +94 -0
- package/src/identity/hub/model/copy.ts +35 -0
- package/src/identity/hub/model/custody.ts +54 -0
- package/src/identity/hub/model/ens.ts +49 -0
- package/src/identity/hub/model/errors.ts +140 -0
- package/src/identity/hub/model/format.ts +15 -0
- package/src/identity/hub/model/identity.ts +94 -0
- package/src/identity/hub/model/network.ts +32 -0
- package/src/identity/hub/model/transfer.ts +57 -0
- package/src/identity/hub/operatorWallets.ts +131 -0
- package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +46 -0
- package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +129 -0
- package/src/identity/hub/reconciliation/agentReconciliation/run.ts +302 -0
- package/src/identity/hub/reconciliation/agentReconciliation/types.ts +17 -0
- package/src/identity/hub/reconciliation/index.ts +21 -0
- package/src/identity/hub/reconciliation/useAgentReconciliation.ts +10 -0
- package/src/identity/hub/reconciliation/walletSetup.ts +220 -0
- package/src/identity/hub/txGuard.ts +51 -0
- package/src/identity/hub/types.ts +17 -0
- package/src/identity/hub/useIdentityHubContinuity.ts +136 -0
- package/src/identity/hub/useIdentityHubController.ts +396 -0
- package/src/identity/hub/useIdentityHubSideEffects.ts +309 -0
- package/src/identity/hub/utils.ts +79 -0
- package/src/identity/identityCompat.ts +34 -0
- package/src/identity/profile/agentIcon.ts +61 -0
- package/src/identity/profile/imagePicker.ts +12 -12
- package/src/identity/registry/erc8004/abi.ts +14 -0
- package/src/identity/registry/erc8004/chains.ts +150 -0
- package/src/identity/registry/erc8004/client.ts +11 -0
- package/src/identity/registry/erc8004/discovery.ts +511 -0
- package/src/identity/registry/erc8004/metadata.ts +335 -0
- package/src/identity/registry/erc8004/ownership.ts +121 -0
- package/src/identity/registry/erc8004/preflight.ts +123 -0
- package/src/identity/registry/erc8004/transactions.ts +77 -0
- package/src/identity/registry/erc8004/types.ts +88 -0
- package/src/identity/registry/erc8004/uri.ts +59 -0
- package/src/identity/registry/erc8004/utils.ts +58 -0
- package/src/identity/registry/erc8004.ts +53 -1106
- package/src/identity/registry/fieldParsers.ts +28 -0
- package/src/identity/registry/operatorVault/bytecode.ts +98 -0
- package/src/identity/registry/operatorVault/constants.ts +38 -0
- package/src/identity/registry/operatorVault/read.ts +246 -0
- package/src/identity/registry/operatorVault/transactions.ts +81 -0
- package/src/identity/registry/operatorVault.ts +44 -0
- package/src/identity/storage/ipfs.ts +26 -24
- package/src/identity/wallet/browserWallet/gas.ts +41 -0
- package/src/identity/wallet/browserWallet/html.ts +106 -0
- package/src/identity/wallet/browserWallet/http.ts +28 -0
- package/src/identity/wallet/browserWallet/requestServer.ts +106 -0
- package/src/identity/wallet/browserWallet/requests.ts +191 -0
- package/src/identity/wallet/browserWallet/session.ts +325 -0
- package/src/identity/wallet/browserWallet/types.ts +192 -0
- package/src/identity/wallet/browserWallet/validation.ts +74 -0
- package/src/identity/wallet/browserWallet.ts +30 -393
- package/src/identity/wallet/page/constants.ts +5 -0
- package/src/identity/wallet/page/controller.ts +251 -0
- package/src/identity/wallet/page/copy.ts +340 -0
- package/src/identity/wallet/page/grainient.ts +278 -0
- package/src/identity/wallet/page/html.ts +28 -0
- package/src/identity/wallet/page/markup.ts +50 -0
- package/src/identity/wallet/page/state.ts +9 -0
- package/src/identity/wallet/page/styles/base.ts +259 -0
- package/src/identity/wallet/page/styles/components.ts +262 -0
- package/src/identity/wallet/page/styles/index.ts +5 -0
- package/src/identity/wallet/page/styles/responsive.ts +247 -0
- package/src/identity/wallet/page/types.ts +47 -0
- package/src/identity/wallet/page/view.ts +535 -0
- package/src/identity/wallet/page/walletProvider.ts +70 -0
- package/src/identity/wallet/page.tsx +38 -0
- package/src/identity/wallet/walletPurposeCompat.ts +27 -0
- package/src/mcp/manager.ts +0 -1
- package/src/models/ModelPicker.tsx +36 -30
- package/src/models/catalog.ts +5 -2
- package/src/models/huggingface.ts +9 -9
- package/src/models/llamacpp.ts +13 -13
- package/src/models/modelDisplay.ts +75 -0
- package/src/models/modelPickerOptions.ts +16 -3
- package/src/models/modelRecommendation.ts +0 -1
- package/src/providers/errors.ts +16 -0
- package/src/providers/gemini.ts +252 -39
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +1 -1
- package/src/runtime/sessionMode.ts +1 -1
- package/src/runtime/systemPrompt.ts +2 -0
- package/src/runtime/toolExecution.ts +18 -22
- package/src/runtime/toolIntent.ts +0 -20
- package/src/runtime/turn.ts +0 -92
- package/src/storage/atomicWrite.ts +4 -1
- package/src/storage/config.ts +181 -5
- package/src/storage/identity.ts +9 -3
- package/src/storage/secrets.ts +2 -2
- package/src/tools/bashSafety.ts +8 -0
- package/src/tools/changeDirectoryTool.ts +1 -1
- package/src/tools/deleteFileTool.ts +4 -4
- package/src/tools/editTool.ts +4 -4
- package/src/tools/editUtils.ts +5 -5
- package/src/tools/privateContinuityEditTool.ts +4 -5
- package/src/tools/privateContinuityReadTool.ts +1 -2
- package/src/tools/registry.ts +30 -0
- package/src/tools/writeFileTool.ts +5 -5
- package/src/ui/BrandSplash.tsx +20 -85
- package/src/ui/ProgressBar.tsx +3 -5
- package/src/ui/Select.tsx +21 -9
- package/src/ui/Spinner.tsx +38 -3
- package/src/ui/Surface.tsx +3 -3
- package/src/ui/TextInput.tsx +191 -29
- package/src/ui/theme.ts +7 -34
- package/src/utils/openExternal.ts +21 -0
- package/src/utils/withRetry.ts +47 -3
- package/src/identity/hub/identityHubEffects.ts +0 -937
- package/src/identity/hub/identityHubModel.ts +0 -291
- package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -144
- package/src/identity/hub/screens/EditProfileFlow.tsx +0 -145
- package/src/identity/hub/screens/IdentitySummary.tsx +0 -90
- package/src/identity/hub/screens/MenuScreen.tsx +0 -117
- package/src/identity/hub/screens/RecoveryConfirmScreen.tsx +0 -87
- package/src/identity/hub/screens/RestoreFlow.tsx +0 -206
- package/src/identity/wallet/wallet-page/wallet.html +0 -1202
- /package/src/identity/hub/{screens → components}/BusyScreen.tsx +0 -0
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
import { getAddress, type Address, type Hex } from 'viem'
|
|
2
|
+
import type { EthagentIdentity } from '../../../../storage/config.js'
|
|
3
|
+
import {
|
|
4
|
+
createWalletRestoreAccessChallenge,
|
|
5
|
+
serializeContinuitySnapshotEnvelope,
|
|
6
|
+
type WalletChallengePurpose,
|
|
7
|
+
} from '../../../continuity/envelope.js'
|
|
8
|
+
import {
|
|
9
|
+
prepareSyncedPublicSkillsJson,
|
|
10
|
+
readContinuityFiles,
|
|
11
|
+
writePublicSkillsFile,
|
|
12
|
+
} from '../../../continuity/storage.js'
|
|
13
|
+
import {
|
|
14
|
+
createAgentCard,
|
|
15
|
+
defaultPublicSkillsProfile,
|
|
16
|
+
serializeAgentCard,
|
|
17
|
+
} from '../../../continuity/publicSkills.js'
|
|
18
|
+
import { recordPublishedContinuitySnapshot } from '../../../continuity/snapshots.js'
|
|
19
|
+
import { addToIpfs, DEFAULT_IPFS_API_URL, isPinataUploadUrl } from '../../../storage/ipfs.js'
|
|
20
|
+
import {
|
|
21
|
+
createErc8004PublicClient,
|
|
22
|
+
encodeSetAgentUri,
|
|
23
|
+
preflightSetAgentUri,
|
|
24
|
+
withEthagentPointers,
|
|
25
|
+
type Erc8004RegistryConfig,
|
|
26
|
+
} from '../../../registry/erc8004.js'
|
|
27
|
+
import {
|
|
28
|
+
OPERATOR_VAULT_ABI,
|
|
29
|
+
encodeRotateAgentURI,
|
|
30
|
+
} from '../../../registry/operatorVault.js'
|
|
31
|
+
import { resolveValidatedPinataJwt, savePinataJwt } from '../../../storage/pinataJwt.js'
|
|
32
|
+
import {
|
|
33
|
+
openBrowserWalletSession,
|
|
34
|
+
requestBrowserWalletSignatureAndTransaction,
|
|
35
|
+
type BrowserWalletSession,
|
|
36
|
+
type BrowserWalletSignature,
|
|
37
|
+
type WalletPurpose,
|
|
38
|
+
} from '../../../wallet/browserWallet.js'
|
|
39
|
+
import type { ProfileUpdates, Step } from '../../identityHubReducer.js'
|
|
40
|
+
import { reconcileWalletSetup } from '../../reconciliation/index.js'
|
|
41
|
+
import { acquireTxGuard, releaseTxGuard } from '../../txGuard.js'
|
|
42
|
+
import type { EffectCallbacks } from '../types.js'
|
|
43
|
+
import { awaitConfirmedReceipt } from '../receipts.js'
|
|
44
|
+
import {
|
|
45
|
+
createMainnetEnsPublicClient,
|
|
46
|
+
runUpdateEnsRecords,
|
|
47
|
+
} from '../ens/transactions.js'
|
|
48
|
+
import {
|
|
49
|
+
assertVerifiedPin,
|
|
50
|
+
deriveAgentName,
|
|
51
|
+
prepareProfileStateForSave,
|
|
52
|
+
readEnsOkFromState,
|
|
53
|
+
} from '../shared/profilePrep.js'
|
|
54
|
+
import {
|
|
55
|
+
appendResolverSyncWarning,
|
|
56
|
+
markCurrentContinuityFilesPublished,
|
|
57
|
+
resolverSyncWarningMessage,
|
|
58
|
+
syncResolverApprovalsAfterOwnerSave,
|
|
59
|
+
} from '../shared/sync.js'
|
|
60
|
+
import {
|
|
61
|
+
assertSnapshotSaveSignerAuthorized,
|
|
62
|
+
createContinuityEnvelopeForSave,
|
|
63
|
+
expectedAccountForSnapshotSave,
|
|
64
|
+
operatorsPointerFromState,
|
|
65
|
+
ownerAddressForSnapshotSave,
|
|
66
|
+
snapshotSaveWalletRole,
|
|
67
|
+
walletRestoreAccessContext,
|
|
68
|
+
} from '../shared/snapshot.js'
|
|
69
|
+
import { rebackupCompletionMessage } from '../rebackup/runRebackup.js'
|
|
70
|
+
|
|
71
|
+
type BackupMetadata = NonNullable<EthagentIdentity['backup']>
|
|
72
|
+
type PublicSkillsMetadata = NonNullable<EthagentIdentity['publicSkills']>
|
|
73
|
+
|
|
74
|
+
type PublicProfilePreparedTransaction = {
|
|
75
|
+
ownerAddress: Address
|
|
76
|
+
agentUri: string
|
|
77
|
+
metadataCid: string
|
|
78
|
+
publicSkills: PublicSkillsMetadata
|
|
79
|
+
identity: EthagentIdentity
|
|
80
|
+
publicSkillsJson: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function runPublicProfilePreflight(
|
|
84
|
+
identity: EthagentIdentity,
|
|
85
|
+
registry: Erc8004RegistryConfig,
|
|
86
|
+
callbacks: EffectCallbacks,
|
|
87
|
+
profileUpdates?: ProfileUpdates,
|
|
88
|
+
returnTo: Step = { kind: 'continuity-public' },
|
|
89
|
+
vaultAddress?: Address,
|
|
90
|
+
): Promise<void> {
|
|
91
|
+
const apiUrl = DEFAULT_IPFS_API_URL
|
|
92
|
+
let jwt: string | undefined
|
|
93
|
+
try {
|
|
94
|
+
jwt = isPinataUploadUrl(apiUrl) ? await resolveValidatedPinataJwt() : undefined
|
|
95
|
+
} catch (err: unknown) {
|
|
96
|
+
callbacks.onStep({ kind: 'public-profile-storage', identity, registry, error: (err as Error).message, profileUpdates, returnTo, ...(vaultAddress ? { vaultAddress } : {}) })
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
if (isPinataUploadUrl(apiUrl) && !jwt) {
|
|
100
|
+
callbacks.onStep({ kind: 'public-profile-storage', identity, registry, profileUpdates, returnTo, ...(vaultAddress ? { vaultAddress } : {}) })
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
callbacks.onStep({ kind: 'public-profile-signing', identity, registry, pinataJwt: jwt, profileUpdates, returnTo, ...(vaultAddress ? { vaultAddress } : {}) })
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function runPublicProfileSigning(
|
|
107
|
+
step: Extract<Step, { kind: 'public-profile-signing' }>,
|
|
108
|
+
callbacks: EffectCallbacks,
|
|
109
|
+
): Promise<void> {
|
|
110
|
+
acquireTxGuard('public-profile')
|
|
111
|
+
try {
|
|
112
|
+
return await runPublicProfileSigningInner(step, callbacks)
|
|
113
|
+
} finally {
|
|
114
|
+
releaseTxGuard('public-profile')
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function runPublicProfileSigningInner(
|
|
119
|
+
step: Extract<Step, { kind: 'public-profile-signing' }>,
|
|
120
|
+
callbacks: EffectCallbacks,
|
|
121
|
+
): Promise<void> {
|
|
122
|
+
if (snapshotSaveWalletRole(step.identity, step.profileUpdates) === 'operator') {
|
|
123
|
+
await runOperatorWalletPublicProfileSave(step, callbacks)
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
const snapshotOwner = ownerAddressForSnapshotSave(step.identity, step.profileUpdates)
|
|
127
|
+
const walletAccess = walletRestoreAccessContext(step.identity, step.registry, step.profileUpdates, snapshotOwner)
|
|
128
|
+
const expectedSigner = expectedAccountForSnapshotSave(step.identity, step.profileUpdates, walletAccess)
|
|
129
|
+
const profileRole = snapshotSaveWalletRole(step.identity, step.profileUpdates)
|
|
130
|
+
const profilePurpose: WalletPurpose = profileRole === 'operator'
|
|
131
|
+
? 'update-profile-operator'
|
|
132
|
+
: profileRole === 'owner'
|
|
133
|
+
? 'update-profile-owner'
|
|
134
|
+
: 'update-profile-connected'
|
|
135
|
+
const result = await requestBrowserWalletSignatureAndTransaction<PublicProfilePreparedTransaction>({
|
|
136
|
+
chainId: step.registry.chainId,
|
|
137
|
+
messageForAccount: account => `Authorize public profile update for agent #${step.identity.agentId ?? 'unknown'} (${account})`,
|
|
138
|
+
onReady: callbacks.onWalletReady,
|
|
139
|
+
purpose: profilePurpose,
|
|
140
|
+
...(expectedSigner ? { expectedAccount: expectedSigner } : {}),
|
|
141
|
+
prepareTransaction: async wallet => {
|
|
142
|
+
if (!step.identity.agentId) throw new Error('Cannot update public profile: identity is missing an agent token ID')
|
|
143
|
+
assertSnapshotSaveSignerAuthorized(step.identity, step.profileUpdates, wallet.account, snapshotOwner, walletAccess)
|
|
144
|
+
const {
|
|
145
|
+
state,
|
|
146
|
+
nextName,
|
|
147
|
+
nextDescription,
|
|
148
|
+
nextEnsName,
|
|
149
|
+
uploadedImageUri,
|
|
150
|
+
} = await prepareProfileStateForSave({
|
|
151
|
+
identity: step.identity,
|
|
152
|
+
registry: step.registry,
|
|
153
|
+
profileUpdates: step.profileUpdates,
|
|
154
|
+
pinataJwt: step.pinataJwt,
|
|
155
|
+
ownerAddress: snapshotOwner,
|
|
156
|
+
walletAccount: getAddress(wallet.account),
|
|
157
|
+
includeLastBackedUpAt: false,
|
|
158
|
+
})
|
|
159
|
+
const nextIdentityForFiles: EthagentIdentity = { ...step.identity, state }
|
|
160
|
+
const publicSkillsJson = await prepareSyncedPublicSkillsJson(nextIdentityForFiles)
|
|
161
|
+
const publicSkillsPin = await addToIpfs(DEFAULT_IPFS_API_URL, publicSkillsJson, fetch, { pinataJwt: step.pinataJwt })
|
|
162
|
+
assertVerifiedPin(publicSkillsPin)
|
|
163
|
+
const agentCardPin = await addToIpfs(
|
|
164
|
+
DEFAULT_IPFS_API_URL,
|
|
165
|
+
serializeAgentCard(createAgentCard(defaultPublicSkillsProfile(nextIdentityForFiles))),
|
|
166
|
+
fetch,
|
|
167
|
+
{ pinataJwt: step.pinataJwt },
|
|
168
|
+
)
|
|
169
|
+
assertVerifiedPin(agentCardPin)
|
|
170
|
+
const updatedAt = new Date().toISOString()
|
|
171
|
+
const publicSkills: PublicSkillsMetadata = {
|
|
172
|
+
cid: publicSkillsPin.cid,
|
|
173
|
+
agentCardCid: agentCardPin.cid,
|
|
174
|
+
updatedAt,
|
|
175
|
+
status: 'pinned',
|
|
176
|
+
}
|
|
177
|
+
const existingBackup = step.identity.backup ? {
|
|
178
|
+
cid: step.identity.backup.cid,
|
|
179
|
+
...(step.identity.backup.envelopeVersion ? { envelopeVersion: step.identity.backup.envelopeVersion } : {}),
|
|
180
|
+
...(step.identity.backup.createdAt ? { createdAt: step.identity.backup.createdAt } : {}),
|
|
181
|
+
} : undefined
|
|
182
|
+
const registration = withEthagentPointers({
|
|
183
|
+
type: 'https://eips.ethereum.org/EIPS/eip-8004#registration-v1',
|
|
184
|
+
name: nextName ?? deriveAgentName(step.identity),
|
|
185
|
+
...(nextDescription ? { description: nextDescription } : {}),
|
|
186
|
+
...(uploadedImageUri ? { image: uploadedImageUri } : {}),
|
|
187
|
+
}, {
|
|
188
|
+
backup: existingBackup,
|
|
189
|
+
publicDiscovery: {
|
|
190
|
+
skillsCid: publicSkills.cid,
|
|
191
|
+
agentCardCid: publicSkills.agentCardCid,
|
|
192
|
+
updatedAt,
|
|
193
|
+
},
|
|
194
|
+
registration: {
|
|
195
|
+
chainId: step.registry.chainId,
|
|
196
|
+
identityRegistryAddress: step.registry.identityRegistryAddress,
|
|
197
|
+
agentId: step.identity.agentId,
|
|
198
|
+
},
|
|
199
|
+
ensName: nextEnsName,
|
|
200
|
+
operators: operatorsPointerFromState(state, nextEnsName),
|
|
201
|
+
ownerAddress: snapshotOwner,
|
|
202
|
+
})
|
|
203
|
+
const metadataPin = await addToIpfs(DEFAULT_IPFS_API_URL, JSON.stringify(registration, null, 2), fetch, { pinataJwt: step.pinataJwt })
|
|
204
|
+
assertVerifiedPin(metadataPin)
|
|
205
|
+
const metadataCid = metadataPin.cid
|
|
206
|
+
const agentUri = `ipfs://${metadataCid}`
|
|
207
|
+
const agentId = BigInt(step.identity.agentId)
|
|
208
|
+
await preflightSetAgentUri({
|
|
209
|
+
...step.registry,
|
|
210
|
+
account: wallet.account,
|
|
211
|
+
agentId,
|
|
212
|
+
newUri: agentUri,
|
|
213
|
+
})
|
|
214
|
+
return {
|
|
215
|
+
to: step.registry.identityRegistryAddress,
|
|
216
|
+
data: encodeSetAgentUri({ agentId, newUri: agentUri }),
|
|
217
|
+
prepared: {
|
|
218
|
+
ownerAddress: snapshotOwner,
|
|
219
|
+
agentUri,
|
|
220
|
+
metadataCid,
|
|
221
|
+
publicSkills,
|
|
222
|
+
identity: { ...step.identity, state },
|
|
223
|
+
publicSkillsJson,
|
|
224
|
+
},
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
})
|
|
228
|
+
const client = createErc8004PublicClient(step.registry)
|
|
229
|
+
await awaitConfirmedReceipt(client, result.txHash, 'Owner-wallet profile save', { kind: 'public-profile', chainId: step.registry.chainId })
|
|
230
|
+
const nextIdentity: EthagentIdentity = {
|
|
231
|
+
...result.prepared.identity,
|
|
232
|
+
source: 'erc8004',
|
|
233
|
+
address: getAddress(result.prepared.ownerAddress),
|
|
234
|
+
ownerAddress: getAddress(result.prepared.ownerAddress),
|
|
235
|
+
chainId: step.registry.chainId,
|
|
236
|
+
rpcUrl: step.registry.rpcUrl,
|
|
237
|
+
identityRegistryAddress: step.registry.identityRegistryAddress,
|
|
238
|
+
agentUri: result.prepared.agentUri,
|
|
239
|
+
metadataCid: result.prepared.metadataCid,
|
|
240
|
+
publicSkills: result.prepared.publicSkills,
|
|
241
|
+
}
|
|
242
|
+
await writePublicSkillsFile(nextIdentity, result.prepared.publicSkillsJson)
|
|
243
|
+
await markCurrentContinuityFilesPublished(nextIdentity)
|
|
244
|
+
const resolverSyncWarning = await syncResolverApprovalsAfterOwnerSave({
|
|
245
|
+
beforeIdentity: step.identity,
|
|
246
|
+
afterIdentity: nextIdentity,
|
|
247
|
+
registry: step.registry,
|
|
248
|
+
callbacks,
|
|
249
|
+
}).then(() => null).catch(err => resolverSyncWarningMessage(err))
|
|
250
|
+
const completionMessage = appendResolverSyncWarning(
|
|
251
|
+
rebackupCompletionMessage(
|
|
252
|
+
step.profileUpdates,
|
|
253
|
+
step.identity,
|
|
254
|
+
readEnsOkFromState(nextIdentity.state),
|
|
255
|
+
),
|
|
256
|
+
resolverSyncWarning,
|
|
257
|
+
)
|
|
258
|
+
await callbacks.onIdentityComplete(nextIdentity, completionMessage, 'update')
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function runOperatorWalletPublicProfileSave(
|
|
262
|
+
step: Extract<Step, { kind: 'public-profile-signing' }>,
|
|
263
|
+
callbacks: EffectCallbacks,
|
|
264
|
+
): Promise<void> {
|
|
265
|
+
if (!step.identity.agentId) throw new Error('Cannot update public profile: identity is missing an agent token ID')
|
|
266
|
+
const baseState = (step.identity.state ?? {}) as Record<string, unknown>
|
|
267
|
+
const ensName = typeof baseState.ensName === 'string' ? baseState.ensName.trim() : ''
|
|
268
|
+
if (!ensName) {
|
|
269
|
+
throw new Error('Operator wallet profile updates require an ENS subdomain, connect the owner wallet to set one up first')
|
|
270
|
+
}
|
|
271
|
+
const snapshotOwner = ownerAddressForSnapshotSave(step.identity, step.profileUpdates)
|
|
272
|
+
const walletAccess = walletRestoreAccessContext(step.identity, step.registry, step.profileUpdates, snapshotOwner)
|
|
273
|
+
if (!walletAccess) throw new Error('Operator-wallet profile updates require wallet restore access context')
|
|
274
|
+
const challengePurpose: WalletChallengePurpose = 'restore-operator'
|
|
275
|
+
if (step.vaultAddress) {
|
|
276
|
+
await runOperatorWalletVaultPublicProfileSave({
|
|
277
|
+
step,
|
|
278
|
+
callbacks,
|
|
279
|
+
snapshotOwner,
|
|
280
|
+
walletAccess,
|
|
281
|
+
challengePurpose,
|
|
282
|
+
vaultAddress: step.vaultAddress,
|
|
283
|
+
})
|
|
284
|
+
return
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const reconcileBaseState = (step.identity.state ?? {}) as Record<string, unknown>
|
|
288
|
+
const activeOperator = typeof reconcileBaseState.activeOperatorAddress === 'string'
|
|
289
|
+
? reconcileBaseState.activeOperatorAddress.trim()
|
|
290
|
+
: ''
|
|
291
|
+
if (activeOperator) {
|
|
292
|
+
const fixPlan = await reconcileWalletSetup({ identity: step.identity, registry: step.registry })
|
|
293
|
+
const stale = fixPlan.items.find(
|
|
294
|
+
(item): item is Extract<typeof item, { kind: 'missing-approval' }> =>
|
|
295
|
+
item.kind === 'missing-approval'
|
|
296
|
+
&& item.address.toLowerCase() === activeOperator.toLowerCase(),
|
|
297
|
+
)
|
|
298
|
+
if (stale) {
|
|
299
|
+
throw new Error(
|
|
300
|
+
`Operator wallet ${stale.address} is no longer approved as a resolver delegate for this agent's ENS subdomain. Connect the owner wallet and run "Fix Records" to restore the approval before retrying.`,
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const session = await openBrowserWalletSession({ onReady: callbacks.onWalletReady })
|
|
306
|
+
try {
|
|
307
|
+
const wallet = await session.requestSignature({
|
|
308
|
+
chainId: step.registry.chainId,
|
|
309
|
+
messageForAccount: account => createWalletRestoreAccessChallenge({
|
|
310
|
+
token: walletAccess.token,
|
|
311
|
+
ownerAddress: snapshotOwner,
|
|
312
|
+
walletAddress: account,
|
|
313
|
+
accessEpoch: walletAccess.accessEpoch,
|
|
314
|
+
purpose: challengePurpose,
|
|
315
|
+
}),
|
|
316
|
+
purpose: 'update-profile-operator',
|
|
317
|
+
})
|
|
318
|
+
assertSnapshotSaveSignerAuthorized(step.identity, step.profileUpdates, wallet.account, snapshotOwner, walletAccess)
|
|
319
|
+
const prepared = await prepareOperatorProfileArtifacts({
|
|
320
|
+
step,
|
|
321
|
+
wallet,
|
|
322
|
+
snapshotOwner,
|
|
323
|
+
walletAccess,
|
|
324
|
+
challengePurpose,
|
|
325
|
+
includeMetadata: false,
|
|
326
|
+
})
|
|
327
|
+
await publishOperatorProfileEnsRecord({
|
|
328
|
+
ensName,
|
|
329
|
+
signer: wallet.account,
|
|
330
|
+
registry: step.registry,
|
|
331
|
+
agentCardUri: prepared.agentCardUri,
|
|
332
|
+
callbacks,
|
|
333
|
+
session,
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
await writePublicSkillsFile(prepared.nextIdentity, prepared.publicSkillsJson)
|
|
337
|
+
await markCurrentContinuityFilesPublished(prepared.nextIdentity).catch(() => null)
|
|
338
|
+
await recordPublishedContinuitySnapshot({
|
|
339
|
+
identity: prepared.nextIdentity,
|
|
340
|
+
label: 'operator-wallet ENS profile update',
|
|
341
|
+
}).catch(() => null)
|
|
342
|
+
|
|
343
|
+
await callbacks.onIdentityComplete(
|
|
344
|
+
prepared.nextIdentity,
|
|
345
|
+
'Profile pointer published to ENS records (org.ethagent.profile). ERC-8004 metadata stays put until owner wallet next signs.',
|
|
346
|
+
'update',
|
|
347
|
+
)
|
|
348
|
+
} finally {
|
|
349
|
+
await session.close().catch(() => null)
|
|
350
|
+
callbacks.onWalletReady(null)
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
type WalletAccessContext = NonNullable<ReturnType<typeof walletRestoreAccessContext>>
|
|
355
|
+
|
|
356
|
+
async function runOperatorWalletVaultPublicProfileSave(args: {
|
|
357
|
+
step: Extract<Step, { kind: 'public-profile-signing' }>
|
|
358
|
+
callbacks: EffectCallbacks
|
|
359
|
+
snapshotOwner: Address
|
|
360
|
+
walletAccess: WalletAccessContext
|
|
361
|
+
challengePurpose: WalletChallengePurpose
|
|
362
|
+
vaultAddress: Address
|
|
363
|
+
}): Promise<void> {
|
|
364
|
+
const { step, callbacks, snapshotOwner, walletAccess, challengePurpose, vaultAddress } = args
|
|
365
|
+
const session = await openBrowserWalletSession({ onReady: callbacks.onWalletReady })
|
|
366
|
+
try {
|
|
367
|
+
const wallet = await session.requestSignature({
|
|
368
|
+
chainId: step.registry.chainId,
|
|
369
|
+
messageForAccount: account => createWalletRestoreAccessChallenge({
|
|
370
|
+
token: walletAccess.token,
|
|
371
|
+
ownerAddress: snapshotOwner,
|
|
372
|
+
walletAddress: account,
|
|
373
|
+
accessEpoch: walletAccess.accessEpoch,
|
|
374
|
+
purpose: challengePurpose,
|
|
375
|
+
}),
|
|
376
|
+
purpose: 'update-profile-operator',
|
|
377
|
+
flowId: 'public-profile-vault',
|
|
378
|
+
flowStep: 1,
|
|
379
|
+
})
|
|
380
|
+
assertSnapshotSaveSignerAuthorized(step.identity, step.profileUpdates, wallet.account, snapshotOwner, walletAccess)
|
|
381
|
+
await assertVaultSignerCanRotateAgentUri({
|
|
382
|
+
registry: step.registry,
|
|
383
|
+
vaultAddress,
|
|
384
|
+
agentId: BigInt(step.identity.agentId!),
|
|
385
|
+
signer: wallet.account,
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
const prepared = await prepareOperatorProfileArtifacts({
|
|
389
|
+
step,
|
|
390
|
+
wallet,
|
|
391
|
+
snapshotOwner,
|
|
392
|
+
walletAccess,
|
|
393
|
+
challengePurpose,
|
|
394
|
+
includeMetadata: true,
|
|
395
|
+
})
|
|
396
|
+
if (!prepared.agentUri || !prepared.metadataCid) {
|
|
397
|
+
throw new Error('Vault profile update did not prepare ERC-8004 metadata')
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const vaultCall = encodeRotateAgentURI({
|
|
401
|
+
registry: getAddress(step.registry.identityRegistryAddress),
|
|
402
|
+
agentId: BigInt(step.identity.agentId!),
|
|
403
|
+
newURI: prepared.agentUri,
|
|
404
|
+
vaultAddress,
|
|
405
|
+
})
|
|
406
|
+
const vaultTx = await session.sendTransaction({
|
|
407
|
+
chainId: step.registry.chainId,
|
|
408
|
+
expectedAccount: wallet.account,
|
|
409
|
+
to: vaultCall.to,
|
|
410
|
+
data: vaultCall.data,
|
|
411
|
+
purpose: 'rotate-agent-uri-vault-operator',
|
|
412
|
+
flowId: 'public-profile-vault',
|
|
413
|
+
flowStep: 2,
|
|
414
|
+
})
|
|
415
|
+
const client = createErc8004PublicClient(step.registry)
|
|
416
|
+
await awaitConfirmedReceipt(
|
|
417
|
+
client,
|
|
418
|
+
vaultTx.txHash,
|
|
419
|
+
'Operator public profile URI rotation through vault',
|
|
420
|
+
{ kind: 'public-profile', chainId: step.registry.chainId },
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
const nextIdentity: EthagentIdentity = {
|
|
424
|
+
...prepared.nextIdentity,
|
|
425
|
+
backup: prepared.nextIdentity.backup
|
|
426
|
+
? { ...prepared.nextIdentity.backup, txHash: vaultTx.txHash }
|
|
427
|
+
: prepared.nextIdentity.backup,
|
|
428
|
+
}
|
|
429
|
+
await writePublicSkillsFile(nextIdentity, prepared.publicSkillsJson)
|
|
430
|
+
await markCurrentContinuityFilesPublished(nextIdentity).catch(() => null)
|
|
431
|
+
await recordPublishedContinuitySnapshot({
|
|
432
|
+
identity: nextIdentity,
|
|
433
|
+
label: 'operator-wallet vaulted profile update',
|
|
434
|
+
}).catch(() => null)
|
|
435
|
+
|
|
436
|
+
await callbacks.onIdentityComplete(
|
|
437
|
+
nextIdentity,
|
|
438
|
+
'Profile updated. ERC-8004 metadata published through the operator delegation vault.',
|
|
439
|
+
'update',
|
|
440
|
+
)
|
|
441
|
+
} finally {
|
|
442
|
+
await session.close().catch(() => null)
|
|
443
|
+
callbacks.onWalletReady(null)
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
type OperatorProfileArtifacts = {
|
|
448
|
+
nextIdentity: EthagentIdentity
|
|
449
|
+
publicSkillsJson: string
|
|
450
|
+
agentCardUri: string
|
|
451
|
+
agentUri?: string
|
|
452
|
+
metadataCid?: string
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
async function prepareOperatorProfileArtifacts(args: {
|
|
456
|
+
step: Extract<Step, { kind: 'public-profile-signing' }>
|
|
457
|
+
wallet: BrowserWalletSignature
|
|
458
|
+
snapshotOwner: Address
|
|
459
|
+
walletAccess: WalletAccessContext
|
|
460
|
+
challengePurpose: WalletChallengePurpose
|
|
461
|
+
includeMetadata: boolean
|
|
462
|
+
}): Promise<OperatorProfileArtifacts> {
|
|
463
|
+
const { step, wallet, snapshotOwner, walletAccess, challengePurpose } = args
|
|
464
|
+
const {
|
|
465
|
+
state,
|
|
466
|
+
nextName,
|
|
467
|
+
nextDescription,
|
|
468
|
+
nextEnsName,
|
|
469
|
+
uploadedImageUri,
|
|
470
|
+
} = await prepareProfileStateForSave({
|
|
471
|
+
identity: step.identity,
|
|
472
|
+
registry: step.registry,
|
|
473
|
+
profileUpdates: step.profileUpdates,
|
|
474
|
+
pinataJwt: step.pinataJwt,
|
|
475
|
+
ownerAddress: snapshotOwner,
|
|
476
|
+
walletAccount: getAddress(wallet.account),
|
|
477
|
+
includeLastBackedUpAt: true,
|
|
478
|
+
})
|
|
479
|
+
const nextIdentityForFiles: EthagentIdentity = { ...step.identity, state }
|
|
480
|
+
|
|
481
|
+
const publicSkillsJson = await prepareSyncedPublicSkillsJson(nextIdentityForFiles)
|
|
482
|
+
const publicSkillsPin = await addToIpfs(DEFAULT_IPFS_API_URL, publicSkillsJson, fetch, { pinataJwt: step.pinataJwt })
|
|
483
|
+
assertVerifiedPin(publicSkillsPin)
|
|
484
|
+
const agentCardPin = await addToIpfs(
|
|
485
|
+
DEFAULT_IPFS_API_URL,
|
|
486
|
+
serializeAgentCard(createAgentCard(defaultPublicSkillsProfile(nextIdentityForFiles))),
|
|
487
|
+
fetch,
|
|
488
|
+
{ pinataJwt: step.pinataJwt },
|
|
489
|
+
)
|
|
490
|
+
assertVerifiedPin(agentCardPin)
|
|
491
|
+
|
|
492
|
+
const continuityFiles = await readContinuityFiles(nextIdentityForFiles)
|
|
493
|
+
const envelope = createContinuityEnvelopeForSave({
|
|
494
|
+
identity: nextIdentityForFiles,
|
|
495
|
+
registry: step.registry,
|
|
496
|
+
ownerAddress: snapshotOwner,
|
|
497
|
+
signerAddress: wallet.account,
|
|
498
|
+
walletSignature: wallet.signature,
|
|
499
|
+
state,
|
|
500
|
+
files: continuityFiles,
|
|
501
|
+
walletAccess,
|
|
502
|
+
challengePurpose,
|
|
503
|
+
})
|
|
504
|
+
const statePin = await addToIpfs(DEFAULT_IPFS_API_URL, serializeContinuitySnapshotEnvelope(envelope), fetch, { pinataJwt: step.pinataJwt })
|
|
505
|
+
assertVerifiedPin(statePin)
|
|
506
|
+
|
|
507
|
+
const publicSkills: PublicSkillsMetadata = {
|
|
508
|
+
cid: publicSkillsPin.cid,
|
|
509
|
+
agentCardCid: agentCardPin.cid,
|
|
510
|
+
updatedAt: envelope.createdAt,
|
|
511
|
+
status: 'pinned',
|
|
512
|
+
}
|
|
513
|
+
const backup: BackupMetadata = {
|
|
514
|
+
cid: statePin.cid,
|
|
515
|
+
createdAt: envelope.createdAt,
|
|
516
|
+
envelopeVersion: envelope.envelopeVersion,
|
|
517
|
+
ipfsApiUrl: DEFAULT_IPFS_API_URL,
|
|
518
|
+
status: 'pinned',
|
|
519
|
+
ownerAddress: snapshotOwner,
|
|
520
|
+
chainId: step.registry.chainId,
|
|
521
|
+
rpcUrl: step.registry.rpcUrl,
|
|
522
|
+
identityRegistryAddress: step.registry.identityRegistryAddress,
|
|
523
|
+
agentId: step.identity.agentId!,
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
let metadataCid: string | undefined
|
|
527
|
+
let agentUri: string | undefined
|
|
528
|
+
if (args.includeMetadata) {
|
|
529
|
+
const registration = withEthagentPointers({
|
|
530
|
+
type: 'https://eips.ethereum.org/EIPS/eip-8004#registration-v1',
|
|
531
|
+
name: nextName ?? deriveAgentName(step.identity),
|
|
532
|
+
...(nextDescription ? { description: nextDescription } : {}),
|
|
533
|
+
...(uploadedImageUri ? { image: uploadedImageUri } : {}),
|
|
534
|
+
}, {
|
|
535
|
+
backup: { cid: statePin.cid, envelopeVersion: envelope.envelopeVersion, createdAt: envelope.createdAt },
|
|
536
|
+
publicDiscovery: { skillsCid: publicSkills.cid, agentCardCid: publicSkills.agentCardCid, updatedAt: publicSkills.updatedAt },
|
|
537
|
+
registration: { chainId: step.registry.chainId, identityRegistryAddress: step.registry.identityRegistryAddress, agentId: step.identity.agentId },
|
|
538
|
+
ensName: nextEnsName,
|
|
539
|
+
operators: operatorsPointerFromState(state, nextEnsName),
|
|
540
|
+
ownerAddress: snapshotOwner,
|
|
541
|
+
})
|
|
542
|
+
const metadataPin = await addToIpfs(DEFAULT_IPFS_API_URL, JSON.stringify(registration, null, 2), fetch, { pinataJwt: step.pinataJwt })
|
|
543
|
+
assertVerifiedPin(metadataPin)
|
|
544
|
+
metadataCid = metadataPin.cid
|
|
545
|
+
agentUri = `ipfs://${metadataCid}`
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const nextBackup = agentUri && metadataCid
|
|
549
|
+
? { ...backup, metadataCid, agentUri }
|
|
550
|
+
: backup
|
|
551
|
+
const nextIdentity: EthagentIdentity = {
|
|
552
|
+
...step.identity,
|
|
553
|
+
state,
|
|
554
|
+
backup: nextBackup,
|
|
555
|
+
publicSkills,
|
|
556
|
+
...(agentUri ? { agentUri } : {}),
|
|
557
|
+
...(metadataCid ? { metadataCid } : {}),
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return {
|
|
561
|
+
nextIdentity,
|
|
562
|
+
publicSkillsJson,
|
|
563
|
+
agentCardUri: `ipfs://${agentCardPin.cid}`,
|
|
564
|
+
...(agentUri ? { agentUri } : {}),
|
|
565
|
+
...(metadataCid ? { metadataCid } : {}),
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async function publishOperatorProfileEnsRecord(args: {
|
|
570
|
+
ensName: string
|
|
571
|
+
signer: Address
|
|
572
|
+
registry: Erc8004RegistryConfig
|
|
573
|
+
agentCardUri: string
|
|
574
|
+
callbacks: EffectCallbacks
|
|
575
|
+
session: BrowserWalletSession
|
|
576
|
+
flowId?: string
|
|
577
|
+
flowStep?: number
|
|
578
|
+
}): Promise<void> {
|
|
579
|
+
const ensClient = createMainnetEnsPublicClient()
|
|
580
|
+
const tx = await runUpdateEnsRecords({
|
|
581
|
+
fullName: args.ensName,
|
|
582
|
+
ownerAddress: args.signer,
|
|
583
|
+
records: { profile: args.agentCardUri },
|
|
584
|
+
callbacks: args.callbacks,
|
|
585
|
+
purpose: 'update-profile-operator',
|
|
586
|
+
tokenChainId: args.registry.chainId,
|
|
587
|
+
session: args.session,
|
|
588
|
+
publicClient: ensClient,
|
|
589
|
+
...(args.flowId ? { flowId: args.flowId } : {}),
|
|
590
|
+
...(typeof args.flowStep === 'number' ? { flowStep: args.flowStep } : {}),
|
|
591
|
+
})
|
|
592
|
+
await awaitConfirmedReceipt(
|
|
593
|
+
ensClient,
|
|
594
|
+
tx.txHash as Hex,
|
|
595
|
+
'Public profile ENS record update',
|
|
596
|
+
{ kind: 'public-profile', chainId: 1 },
|
|
597
|
+
)
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
async function assertVaultSignerCanRotateAgentUri(args: {
|
|
601
|
+
registry: Erc8004RegistryConfig
|
|
602
|
+
vaultAddress: Address
|
|
603
|
+
agentId: bigint
|
|
604
|
+
signer: Address
|
|
605
|
+
}): Promise<void> {
|
|
606
|
+
const client = createErc8004PublicClient(args.registry)
|
|
607
|
+
const registryAddress = getAddress(args.registry.identityRegistryAddress)
|
|
608
|
+
const vaultAddress = getAddress(args.vaultAddress)
|
|
609
|
+
const signer = getAddress(args.signer)
|
|
610
|
+
let vaultOwner: Address
|
|
611
|
+
try {
|
|
612
|
+
vaultOwner = getAddress(await client.readContract({
|
|
613
|
+
address: vaultAddress,
|
|
614
|
+
abi: OPERATOR_VAULT_ABI,
|
|
615
|
+
functionName: 'agentOwner',
|
|
616
|
+
args: [registryAddress, args.agentId],
|
|
617
|
+
}) as Address)
|
|
618
|
+
} catch (err: unknown) {
|
|
619
|
+
throw new Error(`Could not verify operator delegation vault custody for agent #${args.agentId.toString()}: ${err instanceof Error ? err.message : String(err)}`)
|
|
620
|
+
}
|
|
621
|
+
if (vaultOwner === '0x0000000000000000000000000000000000000000') {
|
|
622
|
+
throw new Error(`Operator delegation vault ${vaultAddress} does not currently hold agent token #${args.agentId.toString()}. Connect the owner wallet and run "Fix Records" or return the token to the vault before retrying.`)
|
|
623
|
+
}
|
|
624
|
+
if (vaultOwner.toLowerCase() === signer.toLowerCase()) return
|
|
625
|
+
|
|
626
|
+
const isOperator = await client.readContract({
|
|
627
|
+
address: vaultAddress,
|
|
628
|
+
abi: OPERATOR_VAULT_ABI,
|
|
629
|
+
functionName: 'metadataOperators',
|
|
630
|
+
args: [registryAddress, args.agentId, signer],
|
|
631
|
+
}) as boolean
|
|
632
|
+
if (isOperator) return
|
|
633
|
+
|
|
634
|
+
throw new Error(
|
|
635
|
+
`Operator wallet ${signer} 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.`,
|
|
636
|
+
)
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
export async function runPublicProfileStorageSubmit(
|
|
640
|
+
input: string,
|
|
641
|
+
step: Extract<Step, { kind: 'public-profile-storage' }>,
|
|
642
|
+
callbacks: EffectCallbacks,
|
|
643
|
+
): Promise<void> {
|
|
644
|
+
const { jwt: pinataJwt } = await savePinataJwt(input)
|
|
645
|
+
callbacks.onStep({ kind: 'public-profile-signing', identity: step.identity, registry: step.registry, pinataJwt, profileUpdates: step.profileUpdates, returnTo: step.returnTo, ...(step.vaultAddress ? { vaultAddress: step.vaultAddress } : {}) })
|
|
646
|
+
}
|