ethagent 3.2.0 → 3.3.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.
- package/README.md +15 -16
- package/package.json +1 -1
- package/src/identity/continuity/history.ts +8 -8
- package/src/identity/continuity/publicSkills.ts +2 -79
- package/src/identity/continuity/skills/publicSkillsSync.ts +8 -7
- package/src/identity/continuity/snapshots.ts +3 -8
- package/src/identity/continuity/storage/defaults.ts +3 -3
- package/src/identity/continuity/storage/paths.ts +1 -1
- package/src/identity/continuity/storage/scaffold.ts +37 -25
- package/src/identity/continuity/storage/status.ts +11 -11
- package/src/identity/continuity/storage/types.ts +4 -4
- package/src/identity/continuity/storage.ts +4 -4
- package/src/identity/ens/agentRecords.ts +61 -45
- package/src/identity/ens/ensAutomation/read.ts +7 -10
- package/src/identity/ens/ensAutomation/setup.ts +10 -16
- package/src/identity/ens/ensAutomation/types.ts +0 -1
- package/src/identity/ens/ensAutomation.ts +1 -0
- package/src/identity/ens/ensLookup/records.ts +1 -1
- package/src/identity/ens/ensLookup.ts +1 -1
- package/src/identity/ens/erc7930.ts +48 -0
- package/src/identity/hub/OperationalRoutes.tsx +4 -2
- package/src/identity/hub/Routes.tsx +1 -1
- package/src/identity/hub/continuity/effects.ts +17 -39
- package/src/identity/hub/continuity/skills/NewSkillVisibilityScreen.tsx +2 -2
- package/src/identity/hub/continuity/skills/SkillActionsScreen.tsx +2 -2
- package/src/identity/hub/continuity/state.ts +1 -1
- package/src/identity/hub/continuity/vault.ts +16 -50
- package/src/identity/hub/create/effects.ts +12 -16
- package/src/identity/hub/ens/EnsEditAdvancedScreens.tsx +0 -76
- package/src/identity/hub/ens/EnsEditFlow.tsx +28 -8
- package/src/identity/hub/ens/EnsEditMaintenanceScreens.tsx +3 -7
- package/src/identity/hub/ens/EnsEditReviewScreens.tsx +14 -18
- package/src/identity/hub/ens/EnsEditShared.tsx +4 -6
- package/src/identity/hub/ens/EnsEditSimpleScreens.tsx +2 -2
- package/src/identity/hub/ens/EnsFlow.tsx +0 -3
- package/src/identity/hub/ens/editCopy.ts +7 -15
- package/src/identity/hub/ens/transactions.ts +67 -18
- package/src/identity/hub/ens/types.ts +0 -2
- package/src/identity/hub/profile/EditProfileFlow.tsx +0 -3
- package/src/identity/hub/profile/effects.ts +15 -30
- package/src/identity/hub/profile/identity.ts +2 -4
- package/src/identity/hub/profile/operatorSave.ts +10 -30
- package/src/identity/hub/restore/RestoreFlow.tsx +9 -9
- package/src/identity/hub/restore/apply.ts +7 -8
- package/src/identity/hub/restore/helpers.ts +3 -3
- package/src/identity/hub/restore/recovery.ts +9 -10
- package/src/identity/hub/restore/resolve.ts +11 -9
- package/src/identity/hub/shared/components/MenuScreen.tsx +3 -3
- package/src/identity/hub/shared/components/UnlinkedIdentityScreen.tsx +2 -2
- package/src/identity/hub/shared/effects/sync.ts +1 -1
- package/src/identity/hub/transfer/TokenTransferScreens.tsx +1 -1
- package/src/identity/hub/transfer/effects.ts +10 -31
- package/src/identity/hub/useIdentityHubContinuity.ts +12 -12
- package/src/identity/hub/useIdentityHubController.ts +12 -3
- package/src/identity/registry/erc8004/metadata.ts +10 -27
- package/src/identity/registry/erc8004/types.ts +0 -1
- package/src/storage/config.ts +1 -2
- package/src/storage/identity.ts +3 -3
- package/src/storage/rewind.ts +1 -1
- package/src/tools/privateContinuityEditTool.ts +4 -4
- package/src/utils/withRetry.ts +2 -2
|
@@ -1,51 +1,79 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
} as const
|
|
1
|
+
import type { Address } from 'viem'
|
|
2
|
+
import { encodeInteroperableAddress } from './erc7930.js'
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
export const AGENT_TOKEN_RECORD_KEY = 'org.ethagent.token'
|
|
6
5
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
] as const
|
|
10
|
-
|
|
11
|
-
export const AGENT_RECORD_READ_KEY_LIST: readonly string[] = AGENT_RECORD_KEY_LIST
|
|
12
|
-
|
|
13
|
-
export type AgentEnsRecords = {
|
|
14
|
-
token?: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export type AgentEnsRecordState = AgentEnsRecords
|
|
6
|
+
export type AgentEnsRecords = Record<string, string>
|
|
7
|
+
export type AgentEnsRecordState = Record<string, string>
|
|
18
8
|
|
|
19
9
|
export type AgentRecordDiff = {
|
|
20
|
-
key:
|
|
21
|
-
field: keyof AgentEnsRecordState
|
|
10
|
+
key: string
|
|
22
11
|
current: string
|
|
23
12
|
next: string
|
|
24
13
|
changed: boolean
|
|
25
14
|
}
|
|
26
15
|
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
export type Ensip25KeyArgs = {
|
|
17
|
+
chainId: number
|
|
18
|
+
identityRegistryAddress: Address | string
|
|
19
|
+
agentId: string | bigint
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function buildEnsip25Key(args: Ensip25KeyArgs): string {
|
|
23
|
+
const agentIdStr = typeof args.agentId === 'bigint' ? args.agentId.toString() : args.agentId.trim()
|
|
24
|
+
if (!agentIdStr) throw new Error('agentId is required to build the ENSIP-25 record key')
|
|
25
|
+
if (agentIdStr.includes('[') || agentIdStr.includes(']')) {
|
|
26
|
+
throw new Error('agentId must not contain square brackets per ENSIP-25')
|
|
27
|
+
}
|
|
28
|
+
const registry = encodeInteroperableAddress({
|
|
29
|
+
chainId: args.chainId,
|
|
30
|
+
address: args.identityRegistryAddress as Address,
|
|
31
|
+
})
|
|
32
|
+
return `agent-registration[${registry}][${agentIdStr}]`
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
export function buildAgentTokenReferenceValue(args: Ensip25KeyArgs): string {
|
|
36
|
+
const agentIdStr = typeof args.agentId === 'bigint' ? args.agentId.toString() : args.agentId.trim()
|
|
37
|
+
return `eip155:${args.chainId}:${(args.identityRegistryAddress as string).toLowerCase()}:${agentIdStr}`
|
|
33
38
|
}
|
|
34
39
|
|
|
35
|
-
export function
|
|
40
|
+
export function buildAgentEnsRecords(args: {
|
|
41
|
+
chainId: number
|
|
42
|
+
identityRegistryAddress: Address | string
|
|
43
|
+
agentId: string | bigint | undefined
|
|
44
|
+
}): AgentEnsRecords {
|
|
45
|
+
if (args.agentId === undefined || args.agentId === '') return {}
|
|
46
|
+
const ensip25Key = buildEnsip25Key({
|
|
47
|
+
chainId: args.chainId,
|
|
48
|
+
identityRegistryAddress: args.identityRegistryAddress,
|
|
49
|
+
agentId: args.agentId,
|
|
50
|
+
})
|
|
51
|
+
const tokenValue = buildAgentTokenReferenceValue({
|
|
52
|
+
chainId: args.chainId,
|
|
53
|
+
identityRegistryAddress: args.identityRegistryAddress,
|
|
54
|
+
agentId: args.agentId,
|
|
55
|
+
})
|
|
36
56
|
return {
|
|
37
|
-
|
|
57
|
+
[ensip25Key]: '1',
|
|
58
|
+
[AGENT_TOKEN_RECORD_KEY]: tokenValue,
|
|
38
59
|
}
|
|
39
60
|
}
|
|
40
61
|
|
|
62
|
+
export function recordsFromTextMap(text: Record<string, string>): AgentEnsRecordState {
|
|
63
|
+
const out: AgentEnsRecordState = {}
|
|
64
|
+
for (const [key, value] of Object.entries(text)) {
|
|
65
|
+
if (value) out[key] = value
|
|
66
|
+
}
|
|
67
|
+
return out
|
|
68
|
+
}
|
|
69
|
+
|
|
41
70
|
export function diffRecords(current: AgentEnsRecordState, next: AgentEnsRecords): AgentRecordDiff[] {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const currentValue = (current[
|
|
45
|
-
const nextValue = (next[
|
|
71
|
+
const keys = new Set<string>([...Object.keys(current), ...Object.keys(next)])
|
|
72
|
+
return Array.from(keys).map(key => {
|
|
73
|
+
const currentValue = (current[key] ?? '').trim()
|
|
74
|
+
const nextValue = (next[key] ?? '').trim()
|
|
46
75
|
return {
|
|
47
76
|
key,
|
|
48
|
-
field,
|
|
49
77
|
current: currentValue,
|
|
50
78
|
next: nextValue,
|
|
51
79
|
changed: currentValue !== nextValue,
|
|
@@ -61,22 +89,10 @@ export function changedRecords(current: AgentEnsRecordState, next: AgentEnsRecor
|
|
|
61
89
|
return out
|
|
62
90
|
}
|
|
63
91
|
|
|
64
|
-
export function
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
export function formatRecordValue(_field: keyof AgentEnsRecordState, value: string): string {
|
|
69
|
-
return value
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function buildAgentEnsRecords(args: {
|
|
73
|
-
chainId: number
|
|
74
|
-
identityRegistryAddress: string
|
|
75
|
-
agentId: string | undefined
|
|
76
|
-
}): AgentEnsRecords {
|
|
77
|
-
const records: AgentEnsRecords = {}
|
|
78
|
-
if (args.agentId) {
|
|
79
|
-
records.token = `eip155:${args.chainId}:${args.identityRegistryAddress.toLowerCase()}:${args.agentId}`
|
|
92
|
+
export function clearedRecords(current: AgentEnsRecordState): AgentEnsRecords {
|
|
93
|
+
const out: AgentEnsRecords = {}
|
|
94
|
+
for (const key of Object.keys(current)) {
|
|
95
|
+
out[key] = ''
|
|
80
96
|
}
|
|
81
|
-
return
|
|
97
|
+
return out
|
|
82
98
|
}
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
} from 'viem'
|
|
10
10
|
import { mainnet } from 'viem/chains'
|
|
11
11
|
import {
|
|
12
|
-
AGENT_RECORD_READ_KEY_LIST,
|
|
13
12
|
recordsFromTextMap,
|
|
14
13
|
type AgentEnsRecordState,
|
|
15
14
|
} from '../agentRecords.js'
|
|
@@ -81,9 +80,14 @@ export async function readAddressRecord(client: EnsAutomationReadClient, resolve
|
|
|
81
80
|
}
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
export async function readTextRecords(
|
|
83
|
+
export async function readTextRecords(
|
|
84
|
+
client: EnsAutomationReadClient,
|
|
85
|
+
resolverAddress: Address,
|
|
86
|
+
node: Hex,
|
|
87
|
+
keys: readonly string[],
|
|
88
|
+
): Promise<AgentEnsRecordState> {
|
|
85
89
|
const text: Record<string, string> = {}
|
|
86
|
-
for (const key of
|
|
90
|
+
for (const key of keys) {
|
|
87
91
|
try {
|
|
88
92
|
const value = await client.readContract({
|
|
89
93
|
address: resolverAddress,
|
|
@@ -105,10 +109,3 @@ export function isZero(address: Address): boolean {
|
|
|
105
109
|
export function sameAddress(a: Address, b: Address): boolean {
|
|
106
110
|
return a.toLowerCase() === b.toLowerCase()
|
|
107
111
|
}
|
|
108
|
-
|
|
109
|
-
export function normalizeAgentRecords(records: AgentEnsRecordState): AgentEnsRecordState {
|
|
110
|
-
return {
|
|
111
|
-
...records,
|
|
112
|
-
...(records.token ? { token: records.token.toLowerCase() } : {}),
|
|
113
|
-
}
|
|
114
|
-
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { getAddress, namehash, type Address } from 'viem'
|
|
2
2
|
import {
|
|
3
|
+
AGENT_TOKEN_RECORD_KEY,
|
|
3
4
|
buildAgentEnsRecords,
|
|
5
|
+
buildEnsip25Key,
|
|
4
6
|
diffRecords,
|
|
5
7
|
} from '../agentRecords.js'
|
|
6
8
|
import { normalizeEthDomain, splitSubdomainName } from '../ensLookup.js'
|
|
@@ -12,7 +14,6 @@ import {
|
|
|
12
14
|
import {
|
|
13
15
|
createEnsAutomationClient,
|
|
14
16
|
isZero,
|
|
15
|
-
normalizeAgentRecords,
|
|
16
17
|
readAddressRecord,
|
|
17
18
|
readOwner,
|
|
18
19
|
readResolver,
|
|
@@ -225,26 +226,19 @@ export async function preflightEnsSetup(args: EnsSetupPreflightArgs): Promise<En
|
|
|
225
226
|
}
|
|
226
227
|
|
|
227
228
|
const currentAddress = isZero(childResolver) ? null : await readAddressRecord(client, resolverAddress, fullNode)
|
|
228
|
-
const
|
|
229
|
+
const ensip25Key = buildEnsip25Key({
|
|
230
|
+
chainId: args.registry.chainId,
|
|
231
|
+
identityRegistryAddress: args.registry.identityRegistryAddress,
|
|
232
|
+
agentId: String(args.agentId),
|
|
233
|
+
})
|
|
234
|
+
const currentRecords = isZero(childResolver)
|
|
235
|
+
? {}
|
|
236
|
+
: await readTextRecords(client, resolverAddress, fullNode, [ensip25Key, AGENT_TOKEN_RECORD_KEY])
|
|
229
237
|
const nextRecords = buildAgentEnsRecords({
|
|
230
238
|
chainId: args.registry.chainId,
|
|
231
239
|
identityRegistryAddress: args.registry.identityRegistryAddress,
|
|
232
240
|
agentId: String(args.agentId),
|
|
233
241
|
})
|
|
234
|
-
if (currentRecords.token && nextRecords.token && currentRecords.token !== nextRecords.token) {
|
|
235
|
-
return manual(args, {
|
|
236
|
-
rootName,
|
|
237
|
-
label,
|
|
238
|
-
fullName,
|
|
239
|
-
operatorAddress,
|
|
240
|
-
ownerAddress,
|
|
241
|
-
resolverAddress,
|
|
242
|
-
reason: 'token-record-collision',
|
|
243
|
-
detail: `${fullName} already points to another ERC-8004 token`,
|
|
244
|
-
currentRecords,
|
|
245
|
-
nextRecords,
|
|
246
|
-
})
|
|
247
|
-
}
|
|
248
242
|
|
|
249
243
|
const recordDiffs = diffRecords(currentRecords, nextRecords)
|
|
250
244
|
const addressChanged = !currentAddress || !sameAddress(currentAddress, ownerAddress)
|
|
@@ -24,6 +24,7 @@ export {
|
|
|
24
24
|
encodeEnsRecordsTransaction,
|
|
25
25
|
encodeEnsRegistryTransaction,
|
|
26
26
|
} from './ensAutomation/transactions.js'
|
|
27
|
+
export { readAddressRecord } from './ensAutomation/read.js'
|
|
27
28
|
export { isRootEthName } from './ensAutomation/names.js'
|
|
28
29
|
export { preflightDeleteSubdomain } from './ensAutomation/delete.js'
|
|
29
30
|
export { compareOperatorSets } from './ensAutomation/operators.js'
|
|
@@ -11,7 +11,7 @@ export type EncodedEnsRecordTransaction = {
|
|
|
11
11
|
calls: Hex[]
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export async function
|
|
14
|
+
export async function encodeSetEnsip25TextRecord(
|
|
15
15
|
fullName: string,
|
|
16
16
|
records: Record<string, string>,
|
|
17
17
|
opts: DiscoverOptions = {},
|
|
@@ -16,4 +16,4 @@ export { resolveEnsAddress, readEthagentTextRecords, readResolverAddress } from
|
|
|
16
16
|
export { parseAgentTokenReference } from './ensLookup/tokenReference.js'
|
|
17
17
|
export { discoverOwnedEnsNameDetails, discoverOwnedEnsNames } from './ensLookup/discovery.js'
|
|
18
18
|
export { validateAgentEnsLink } from './ensLookup/validation.js'
|
|
19
|
-
export {
|
|
19
|
+
export { encodeSetEnsip25TextRecord } from './ensLookup/records.js'
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Address } from 'viem'
|
|
2
|
+
|
|
3
|
+
const VERSION = 1
|
|
4
|
+
const CHAIN_TYPE_EIP155 = 0
|
|
5
|
+
const ADDRESS_BYTE_LENGTH = 20
|
|
6
|
+
|
|
7
|
+
export type InteroperableAddressArgs = {
|
|
8
|
+
chainId: number
|
|
9
|
+
address: Address
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function encodeInteroperableAddress(args: InteroperableAddressArgs): `0x${string}` {
|
|
13
|
+
if (!Number.isInteger(args.chainId) || args.chainId <= 0) {
|
|
14
|
+
throw new Error(`invalid chainId for ERC-7930: ${args.chainId}`)
|
|
15
|
+
}
|
|
16
|
+
const addressHex = args.address.toLowerCase().replace(/^0x/, '')
|
|
17
|
+
if (!/^[0-9a-f]{40}$/.test(addressHex)) {
|
|
18
|
+
throw new Error(`invalid address for ERC-7930: ${args.address}`)
|
|
19
|
+
}
|
|
20
|
+
const chainRefBytes = minimalBigEndianBytes(BigInt(args.chainId))
|
|
21
|
+
if (chainRefBytes.length > 0xff) {
|
|
22
|
+
throw new Error(`chainId too large for ERC-7930 single-byte length prefix: ${args.chainId}`)
|
|
23
|
+
}
|
|
24
|
+
return `0x${uint16Hex(VERSION)}${uint16Hex(CHAIN_TYPE_EIP155)}${uint8Hex(chainRefBytes.length)}${bytesHex(chainRefBytes)}${uint8Hex(ADDRESS_BYTE_LENGTH)}${addressHex}` as `0x${string}`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function uint16Hex(n: number): string {
|
|
28
|
+
return n.toString(16).padStart(4, '0')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function uint8Hex(n: number): string {
|
|
32
|
+
return n.toString(16).padStart(2, '0')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function minimalBigEndianBytes(value: bigint): Uint8Array {
|
|
36
|
+
if (value === 0n) return new Uint8Array([0])
|
|
37
|
+
const bytes: number[] = []
|
|
38
|
+
let remaining = value
|
|
39
|
+
while (remaining > 0n) {
|
|
40
|
+
bytes.unshift(Number(remaining & 0xffn))
|
|
41
|
+
remaining >>= 8n
|
|
42
|
+
}
|
|
43
|
+
return new Uint8Array(bytes)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function bytesHex(bytes: Uint8Array): string {
|
|
47
|
+
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('')
|
|
48
|
+
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
runPublicProfileStorageSubmit,
|
|
10
10
|
} from './profile/effects.js'
|
|
11
11
|
import { resolveVaultAddress } from './custody/transactions.js'
|
|
12
|
+
import { readCustodyMode } from './custody/state.js'
|
|
12
13
|
import { WalletApprovalScreen } from './shared/components/WalletApprovalScreen.js'
|
|
13
14
|
import { RebackupStorageScreen } from './continuity/RebackupStorageScreen.js'
|
|
14
15
|
import { BusyScreen } from './shared/components/BusyScreen.js'
|
|
@@ -280,7 +281,6 @@ export const IdentityHubOperationalRoutes: React.FC<IdentityHubOperationalRoutes
|
|
|
280
281
|
onWalletReady={setWalletSession}
|
|
281
282
|
onTriggerRebackup={triggerRebackup}
|
|
282
283
|
onTriggerPublicProfileSave={triggerPublicProfileSave}
|
|
283
|
-
onWithdrawTokenForEns={currentStep => custodyFlow.beginWithdrawToken(currentStep, currentStep, 'ens')}
|
|
284
284
|
/>
|
|
285
285
|
)
|
|
286
286
|
}
|
|
@@ -321,12 +321,14 @@ export const IdentityHubOperationalRoutes: React.FC<IdentityHubOperationalRoutes
|
|
|
321
321
|
|
|
322
322
|
if (step.kind === 'rebackup-signing') {
|
|
323
323
|
const approval = rebackupWalletApprovalView(step.identity, step.profileUpdates)
|
|
324
|
+
const vaultRouted = Boolean(step.vaultAddress)
|
|
325
|
+
&& readCustodyMode(step.identity.state as Record<string, unknown> | undefined) === 'advanced'
|
|
324
326
|
return (
|
|
325
327
|
<WalletApprovalScreen
|
|
326
328
|
title={approval.title}
|
|
327
329
|
subtitle={custodyFlow.renderRebackupSubtitle(
|
|
328
330
|
approval.subtitle,
|
|
329
|
-
|
|
331
|
+
vaultRouted,
|
|
330
332
|
)}
|
|
331
333
|
walletSession={walletSession}
|
|
332
334
|
label={approval.label}
|
|
@@ -83,7 +83,7 @@ export const IdentityHubRoutes: React.FC<{ controller: IdentityHubController }>
|
|
|
83
83
|
<Select<'ens' | 'skip'>
|
|
84
84
|
options={[
|
|
85
85
|
{ value: 'ens', role: 'section', label: 'Set Up Now' },
|
|
86
|
-
{ value: 'ens', label: 'Set Up ENS Name', hint: '
|
|
86
|
+
{ value: 'ens', label: 'Set Up ENS Name', hint: 'Root → Name → Review → Apply' },
|
|
87
87
|
{ value: 'skip', role: 'section', label: 'Skip' },
|
|
88
88
|
{ value: 'skip', label: 'Skip For Now', hint: 'Continue to model setup; add ENS later', role: 'utility' },
|
|
89
89
|
]}
|
|
@@ -6,17 +6,11 @@ import {
|
|
|
6
6
|
prepareSyncedIdentityMarkdownScaffold,
|
|
7
7
|
prepareSyncedSkillsTree,
|
|
8
8
|
readContinuityFiles,
|
|
9
|
-
readPublicSkillsFile,
|
|
10
9
|
writeIdentityMarkdownScaffold,
|
|
11
10
|
type IdentityMarkdownScaffold,
|
|
12
11
|
} from '../../continuity/storage.js'
|
|
13
12
|
import {
|
|
14
|
-
|
|
15
|
-
defaultPublicSkillsProfile as basePublicSkillsProfile,
|
|
16
|
-
} from '../../continuity/publicSkills.js'
|
|
17
|
-
import {
|
|
18
|
-
derivePublicSkillEntries,
|
|
19
|
-
syncPublicSkillsManifest,
|
|
13
|
+
syncAgentCardManifest,
|
|
20
14
|
} from '../../continuity/skills/publicSkillsSync.js'
|
|
21
15
|
import {
|
|
22
16
|
createWalletRestoreAccessChallenge,
|
|
@@ -25,12 +19,8 @@ import {
|
|
|
25
19
|
type ContinuitySkillsTree,
|
|
26
20
|
type WalletChallengePurpose,
|
|
27
21
|
} from '../../continuity/envelope.js'
|
|
28
|
-
import {
|
|
29
|
-
createAgentCard,
|
|
30
|
-
defaultPublicSkillsProfile,
|
|
31
|
-
serializeAgentCard,
|
|
32
|
-
} from '../../continuity/publicSkills.js'
|
|
33
22
|
import { recordPublishedContinuitySnapshot } from '../../continuity/snapshots.js'
|
|
23
|
+
import { readCustodyMode } from '../custody/state.js'
|
|
34
24
|
import { addToIpfs, DEFAULT_IPFS_API_URL, isPinataUploadUrl } from '../../storage/ipfs.js'
|
|
35
25
|
import {
|
|
36
26
|
createErc8004PublicClient,
|
|
@@ -83,19 +73,19 @@ import {
|
|
|
83
73
|
} from './completion.js'
|
|
84
74
|
|
|
85
75
|
type BackupMetadata = NonNullable<EthagentIdentity['backup']>
|
|
86
|
-
type
|
|
76
|
+
type AgentCardMetadata = NonNullable<EthagentIdentity['agentCard']>
|
|
87
77
|
|
|
88
78
|
type RebackupPreparedTransaction = {
|
|
89
79
|
ownerAddress: Address
|
|
90
80
|
agentUri: string
|
|
91
81
|
metadataCid: string
|
|
92
82
|
backup: BackupMetadata
|
|
93
|
-
|
|
83
|
+
agentCard: AgentCardMetadata
|
|
94
84
|
identity: EthagentIdentity
|
|
95
85
|
markdownScaffold?: IdentityMarkdownScaffold
|
|
96
86
|
publishedSources: {
|
|
97
87
|
privateFiles: ContinuityFiles
|
|
98
|
-
|
|
88
|
+
agentCard: string
|
|
99
89
|
skills: ContinuitySkillsTree
|
|
100
90
|
}
|
|
101
91
|
}
|
|
@@ -197,8 +187,9 @@ async function runRebackupSigningInner(
|
|
|
197
187
|
return
|
|
198
188
|
}
|
|
199
189
|
const snapshotOwner = ownerAddressForSnapshotSave(step.identity, step.profileUpdates)
|
|
190
|
+
const isAdvanced = readCustodyMode(step.identity.state as Record<string, unknown> | undefined) === 'advanced'
|
|
200
191
|
const purpose: WalletPurpose = step.walletPurpose
|
|
201
|
-
?? (step.vaultAddress ? 'rotate-agent-uri-vault-owner' : rebackupWalletPurpose(step.identity, step.profileUpdates))
|
|
192
|
+
?? (step.vaultAddress && isAdvanced ? 'rotate-agent-uri-vault-owner' : rebackupWalletPurpose(step.identity, step.profileUpdates))
|
|
202
193
|
const challengePurpose: WalletChallengePurpose = 'restore-owner'
|
|
203
194
|
const walletAccess = walletRestoreAccessContext(step.identity, step.registry, step.profileUpdates, snapshotOwner)
|
|
204
195
|
if (!walletAccess) throw new Error('Cannot back up: missing wallet restore access context')
|
|
@@ -244,20 +235,8 @@ async function runRebackupSigningInner(
|
|
|
244
235
|
const continuityFiles = markdownScaffold
|
|
245
236
|
? { 'SOUL.md': markdownScaffold['SOUL.md'], 'MEMORY.md': markdownScaffold['MEMORY.md'] }
|
|
246
237
|
: await readContinuityFiles(nextIdentityForFiles)
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
assertVerifiedPin(publicSkillsPin)
|
|
250
|
-
const publicSkillEntries = await derivePublicSkillEntries(nextIdentityForFiles)
|
|
251
|
-
const augmentedPublicProfile = appendPublicSkillEntries(
|
|
252
|
-
basePublicSkillsProfile(nextIdentityForFiles),
|
|
253
|
-
publicSkillEntries,
|
|
254
|
-
)
|
|
255
|
-
const agentCardPin = await addToIpfs(
|
|
256
|
-
DEFAULT_IPFS_API_URL,
|
|
257
|
-
serializeAgentCard(createAgentCard(augmentedPublicProfile)),
|
|
258
|
-
fetch,
|
|
259
|
-
{ pinataJwt: step.pinataJwt },
|
|
260
|
-
)
|
|
238
|
+
const agentCardJson = await syncAgentCardManifest(nextIdentityForFiles)
|
|
239
|
+
const agentCardPin = await addToIpfs(DEFAULT_IPFS_API_URL, agentCardJson, fetch, { pinataJwt: step.pinataJwt })
|
|
261
240
|
assertVerifiedPin(agentCardPin)
|
|
262
241
|
const skillsTree = await prepareSyncedSkillsTree(nextIdentityForFiles)
|
|
263
242
|
const envelope = createContinuityEnvelopeForSave({
|
|
@@ -287,9 +266,8 @@ async function runRebackupSigningInner(
|
|
|
287
266
|
identityRegistryAddress: step.registry.identityRegistryAddress,
|
|
288
267
|
agentId: sourceAgentId,
|
|
289
268
|
}
|
|
290
|
-
const
|
|
291
|
-
cid:
|
|
292
|
-
agentCardCid: agentCardPin.cid,
|
|
269
|
+
const agentCard: AgentCardMetadata = {
|
|
270
|
+
cid: agentCardPin.cid,
|
|
293
271
|
updatedAt: envelope.createdAt,
|
|
294
272
|
status: 'pinned',
|
|
295
273
|
}
|
|
@@ -300,7 +278,7 @@ async function runRebackupSigningInner(
|
|
|
300
278
|
...(uploadedImageUri ? { image: uploadedImageUri } : {}),
|
|
301
279
|
}, {
|
|
302
280
|
backup: { cid, envelopeVersion: envelope.envelopeVersion, createdAt: envelope.createdAt },
|
|
303
|
-
publicDiscovery: {
|
|
281
|
+
publicDiscovery: { agentCardCid: agentCard.cid, updatedAt: agentCard.updatedAt },
|
|
304
282
|
registration: { chainId: step.registry.chainId, identityRegistryAddress: step.registry.identityRegistryAddress, agentId: sourceAgentId },
|
|
305
283
|
ensName: nextEnsName,
|
|
306
284
|
operators: operatorsPointerFromState(state, nextEnsName),
|
|
@@ -332,12 +310,12 @@ async function runRebackupSigningInner(
|
|
|
332
310
|
agentUri,
|
|
333
311
|
metadataCid,
|
|
334
312
|
backup: { ...backup, metadataCid, agentUri },
|
|
335
|
-
|
|
313
|
+
agentCard,
|
|
336
314
|
identity: { ...step.identity, state },
|
|
337
315
|
...(markdownScaffold ? { markdownScaffold } : {}),
|
|
338
316
|
publishedSources: {
|
|
339
317
|
privateFiles: continuityFiles,
|
|
340
|
-
|
|
318
|
+
agentCard: agentCardJson,
|
|
341
319
|
skills: skillsTree,
|
|
342
320
|
},
|
|
343
321
|
},
|
|
@@ -357,12 +335,12 @@ async function runRebackupSigningInner(
|
|
|
357
335
|
agentUri,
|
|
358
336
|
metadataCid,
|
|
359
337
|
backup: { ...backup, metadataCid, agentUri },
|
|
360
|
-
|
|
338
|
+
agentCard,
|
|
361
339
|
identity: { ...step.identity, state },
|
|
362
340
|
...(markdownScaffold ? { markdownScaffold } : {}),
|
|
363
341
|
publishedSources: {
|
|
364
342
|
privateFiles: continuityFiles,
|
|
365
|
-
|
|
343
|
+
agentCard: agentCardJson,
|
|
366
344
|
skills: skillsTree,
|
|
367
345
|
},
|
|
368
346
|
},
|
|
@@ -388,7 +366,7 @@ async function runRebackupSigningInner(
|
|
|
388
366
|
agentUri: result.prepared.agentUri,
|
|
389
367
|
metadataCid: result.prepared.metadataCid,
|
|
390
368
|
backup: { ...result.prepared.backup, txHash: result.txHash },
|
|
391
|
-
|
|
369
|
+
agentCard: result.prepared.agentCard,
|
|
392
370
|
}
|
|
393
371
|
if (result.prepared.markdownScaffold) {
|
|
394
372
|
await writeIdentityMarkdownScaffold(nextIdentity, result.prepared.markdownScaffold)
|
|
@@ -33,8 +33,8 @@ export const NewSkillVisibilityScreen: React.FC<NewSkillVisibilityScreenProps> =
|
|
|
33
33
|
<Box marginTop={1}>
|
|
34
34
|
<Select<SkillVisibility | 'back'>
|
|
35
35
|
options={[
|
|
36
|
-
{ value: 'private', label: 'Private', hint: 'Local-only. Not in
|
|
37
|
-
{ value: 'public', label: 'Public', hint: 'Default.
|
|
36
|
+
{ value: 'private', label: 'Private', hint: 'Local-only. Not in the Agent Card.' },
|
|
37
|
+
{ value: 'public', label: 'Public', hint: 'Default. Listed in the Agent Card.' },
|
|
38
38
|
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
39
39
|
{ value: 'back', label: 'Back', hint: 'Return to the name step', role: 'utility' },
|
|
40
40
|
]}
|
|
@@ -140,8 +140,8 @@ function visibilityOption(level: SkillVisibility, current?: SkillVisibility): Se
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
function visibilityHint(level: SkillVisibility): string {
|
|
143
|
-
if (level === 'private') return 'Local-only. Not in
|
|
144
|
-
return 'Default.
|
|
143
|
+
if (level === 'private') return 'Local-only. Not in the Agent Card.'
|
|
144
|
+
return 'Default. Listed in the Agent Card.'
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
function capitalize(value: string): string {
|
|
@@ -27,7 +27,7 @@ export function changedContinuitySnapshotFiles(
|
|
|
27
27
|
const result: string[] = []
|
|
28
28
|
if (changed('SOUL.md')) result.push('SOUL.md')
|
|
29
29
|
if (changed('MEMORY.md')) result.push('MEMORY.md')
|
|
30
|
-
if (changed('
|
|
30
|
+
if (changed('agent-card.json') || changed('private-skills')) result.push('Skills')
|
|
31
31
|
return result
|
|
32
32
|
}
|
|
33
33
|
|