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.
Files changed (61) hide show
  1. package/README.md +15 -16
  2. package/package.json +1 -1
  3. package/src/identity/continuity/history.ts +8 -8
  4. package/src/identity/continuity/publicSkills.ts +2 -79
  5. package/src/identity/continuity/skills/publicSkillsSync.ts +8 -7
  6. package/src/identity/continuity/snapshots.ts +3 -8
  7. package/src/identity/continuity/storage/defaults.ts +3 -3
  8. package/src/identity/continuity/storage/paths.ts +1 -1
  9. package/src/identity/continuity/storage/scaffold.ts +37 -25
  10. package/src/identity/continuity/storage/status.ts +11 -11
  11. package/src/identity/continuity/storage/types.ts +4 -4
  12. package/src/identity/continuity/storage.ts +4 -4
  13. package/src/identity/ens/agentRecords.ts +61 -45
  14. package/src/identity/ens/ensAutomation/read.ts +7 -10
  15. package/src/identity/ens/ensAutomation/setup.ts +10 -16
  16. package/src/identity/ens/ensAutomation/types.ts +0 -1
  17. package/src/identity/ens/ensAutomation.ts +1 -0
  18. package/src/identity/ens/ensLookup/records.ts +1 -1
  19. package/src/identity/ens/ensLookup.ts +1 -1
  20. package/src/identity/ens/erc7930.ts +48 -0
  21. package/src/identity/hub/OperationalRoutes.tsx +4 -2
  22. package/src/identity/hub/Routes.tsx +1 -1
  23. package/src/identity/hub/continuity/effects.ts +17 -39
  24. package/src/identity/hub/continuity/skills/NewSkillVisibilityScreen.tsx +2 -2
  25. package/src/identity/hub/continuity/skills/SkillActionsScreen.tsx +2 -2
  26. package/src/identity/hub/continuity/state.ts +1 -1
  27. package/src/identity/hub/continuity/vault.ts +16 -50
  28. package/src/identity/hub/create/effects.ts +12 -16
  29. package/src/identity/hub/ens/EnsEditAdvancedScreens.tsx +0 -76
  30. package/src/identity/hub/ens/EnsEditFlow.tsx +28 -8
  31. package/src/identity/hub/ens/EnsEditMaintenanceScreens.tsx +3 -7
  32. package/src/identity/hub/ens/EnsEditReviewScreens.tsx +14 -18
  33. package/src/identity/hub/ens/EnsEditShared.tsx +4 -6
  34. package/src/identity/hub/ens/EnsEditSimpleScreens.tsx +2 -2
  35. package/src/identity/hub/ens/EnsFlow.tsx +0 -3
  36. package/src/identity/hub/ens/editCopy.ts +7 -15
  37. package/src/identity/hub/ens/transactions.ts +67 -18
  38. package/src/identity/hub/ens/types.ts +0 -2
  39. package/src/identity/hub/profile/EditProfileFlow.tsx +0 -3
  40. package/src/identity/hub/profile/effects.ts +15 -30
  41. package/src/identity/hub/profile/identity.ts +2 -4
  42. package/src/identity/hub/profile/operatorSave.ts +10 -30
  43. package/src/identity/hub/restore/RestoreFlow.tsx +9 -9
  44. package/src/identity/hub/restore/apply.ts +7 -8
  45. package/src/identity/hub/restore/helpers.ts +3 -3
  46. package/src/identity/hub/restore/recovery.ts +9 -10
  47. package/src/identity/hub/restore/resolve.ts +11 -9
  48. package/src/identity/hub/shared/components/MenuScreen.tsx +3 -3
  49. package/src/identity/hub/shared/components/UnlinkedIdentityScreen.tsx +2 -2
  50. package/src/identity/hub/shared/effects/sync.ts +1 -1
  51. package/src/identity/hub/transfer/TokenTransferScreens.tsx +1 -1
  52. package/src/identity/hub/transfer/effects.ts +10 -31
  53. package/src/identity/hub/useIdentityHubContinuity.ts +12 -12
  54. package/src/identity/hub/useIdentityHubController.ts +12 -3
  55. package/src/identity/registry/erc8004/metadata.ts +10 -27
  56. package/src/identity/registry/erc8004/types.ts +0 -1
  57. package/src/storage/config.ts +1 -2
  58. package/src/storage/identity.ts +3 -3
  59. package/src/storage/rewind.ts +1 -1
  60. package/src/tools/privateContinuityEditTool.ts +4 -4
  61. package/src/utils/withRetry.ts +2 -2
@@ -1,51 +1,79 @@
1
- export const AGENT_RECORD_KEYS = {
2
- token: 'org.ethagent.token',
3
- } as const
1
+ import type { Address } from 'viem'
2
+ import { encodeInteroperableAddress } from './erc7930.js'
4
3
 
5
- type AgentRecordKey = typeof AGENT_RECORD_KEYS[keyof typeof AGENT_RECORD_KEYS]
4
+ export const AGENT_TOKEN_RECORD_KEY = 'org.ethagent.token'
6
5
 
7
- export const AGENT_RECORD_KEY_LIST: readonly AgentRecordKey[] = [
8
- AGENT_RECORD_KEYS.token,
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: AgentRecordKey
21
- field: keyof AgentEnsRecordState
10
+ key: string
22
11
  current: string
23
12
  next: string
24
13
  changed: boolean
25
14
  }
26
15
 
27
- const FIELD_FOR_KEY: Record<AgentRecordKey, keyof AgentEnsRecords> = {
28
- [AGENT_RECORD_KEYS.token]: 'token',
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
- const LABEL_FOR_FIELD: Record<keyof AgentEnsRecordState, string> = {
32
- token: 'Agent token',
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 recordsFromTextMap(text: Record<string, string>): AgentEnsRecordState {
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
- token: text[AGENT_RECORD_KEYS.token] ?? '',
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
- return AGENT_RECORD_KEY_LIST.map(key => {
43
- const field = FIELD_FOR_KEY[key]
44
- const currentValue = (current[field] ?? '').trim()
45
- const nextValue = (next[field] ?? '').trim()
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 recordLabel(field: keyof AgentEnsRecordState): string {
65
- return LABEL_FOR_FIELD[field]
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 records
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(client: EnsAutomationReadClient, resolverAddress: Address, node: Hex): Promise<AgentEnsRecordState> {
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 AGENT_RECORD_READ_KEY_LIST) {
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 currentRecords = normalizeAgentRecords(isZero(childResolver) ? {} : await readTextRecords(client, resolverAddress, fullNode))
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)
@@ -56,7 +56,6 @@ export type EnsSetupBlockedPlan = {
56
56
  | 'operator-matches-owner'
57
57
  | 'token-owner-mismatch'
58
58
  | 'token-owner-lookup-failed'
59
- | 'token-record-collision'
60
59
  | 'lookup-failed'
61
60
  detail: string
62
61
  currentRecords?: AgentEnsRecordState
@@ -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 encodeSetEthagentTextRecords(
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 { encodeSetEthagentTextRecords } from './ensLookup/records.js'
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
- Boolean(step.vaultAddress),
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: 'Walks you through Root, Name, Review, and Apply' },
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
- appendPublicSkillEntries,
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 PublicSkillsMetadata = NonNullable<EthagentIdentity['publicSkills']>
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
- publicSkills: PublicSkillsMetadata
83
+ agentCard: AgentCardMetadata
94
84
  identity: EthagentIdentity
95
85
  markdownScaffold?: IdentityMarkdownScaffold
96
86
  publishedSources: {
97
87
  privateFiles: ContinuityFiles
98
- publicSkills: string
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 publicSkillsJson = await syncPublicSkillsManifest(nextIdentityForFiles)
248
- const publicSkillsPin = await addToIpfs(DEFAULT_IPFS_API_URL, publicSkillsJson, fetch, { pinataJwt: step.pinataJwt })
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 publicSkills: PublicSkillsMetadata = {
291
- cid: publicSkillsPin.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: { skillsCid: publicSkills.cid, agentCardCid: publicSkills.agentCardCid, updatedAt: publicSkills.updatedAt },
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
- publicSkills,
313
+ agentCard,
336
314
  identity: { ...step.identity, state },
337
315
  ...(markdownScaffold ? { markdownScaffold } : {}),
338
316
  publishedSources: {
339
317
  privateFiles: continuityFiles,
340
- publicSkills: publicSkillsJson,
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
- publicSkills,
338
+ agentCard,
361
339
  identity: { ...step.identity, state },
362
340
  ...(markdownScaffold ? { markdownScaffold } : {}),
363
341
  publishedSources: {
364
342
  privateFiles: continuityFiles,
365
- publicSkills: publicSkillsJson,
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
- publicSkills: result.prepared.publicSkills,
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 skills.json.' },
37
- { value: 'public', label: 'Public', hint: 'Default. Indexed in skills.json and Agent Card.' },
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 skills.json.'
144
- return 'Default. Indexed with description and Agent Card link.'
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('skills.json') || changed('private-skills')) result.push('Skills')
30
+ if (changed('agent-card.json') || changed('private-skills')) result.push('Skills')
31
31
  return result
32
32
  }
33
33