ethagent 3.0.0 → 3.0.2
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/package.json +1 -1
- package/src/chat/ChatScreen.tsx +17 -1
- package/src/cli/main.tsx +7 -0
- package/src/identity/continuity/envelope.ts +9 -9
- package/src/identity/continuity/publicSkills.ts +17 -0
- package/src/identity/hub/OperationalRoutes.tsx +11 -39
- package/src/identity/hub/continuity/ContinuityDashboardScreen.tsx +3 -23
- package/src/identity/hub/continuity/RecoveryConfirmScreen.tsx +5 -5
- package/src/identity/hub/continuity/SavePromptScreen.tsx +1 -3
- package/src/identity/hub/continuity/skills/SkillActionsScreen.tsx +151 -0
- package/src/identity/hub/continuity/skills/SkillsTreeScreen.tsx +1 -33
- package/src/identity/hub/continuity/state.ts +9 -9
- package/src/identity/hub/create/CreateFlow.tsx +1 -1
- package/src/identity/hub/custody/routes.tsx +1 -1
- package/src/identity/hub/ens/EnsEditAdvancedScreens.tsx +0 -1
- package/src/identity/hub/ens/EnsEditMaintenanceScreens.tsx +0 -1
- package/src/identity/hub/identityHubReducer.ts +3 -9
- package/src/identity/hub/profile/EditProfileFlow.tsx +5 -5
- package/src/identity/hub/restore/recovery.ts +3 -3
- package/src/identity/hub/shared/components/IdentitySummary.tsx +22 -2
- package/src/identity/hub/shared/components/MenuScreen.tsx +4 -4
- package/src/identity/hub/shared/components/UnlinkedIdentityScreen.tsx +3 -3
- package/src/identity/hub/shared/components/menuFlagsFromReconciliation.ts +1 -1
- package/src/identity/hub/useIdentityHubContinuity.ts +3 -3
- package/src/identity/registry/erc8004/metadata.ts +31 -23
- package/src/identity/wallet/page/copy.ts +43 -43
- package/src/models/modelPickerOptions.ts +2 -0
- package/src/ui/terminalTitle.ts +30 -0
- package/src/identity/hub/continuity/skills/DeleteSkillScreen.tsx +0 -123
- package/src/identity/hub/continuity/skills/SkillVisibilityScreen.tsx +0 -171
|
@@ -143,7 +143,7 @@ export const CreateFlow: React.FC<CreateFlowProps> = ({
|
|
|
143
143
|
<BusyScreen
|
|
144
144
|
title="Getting Ready"
|
|
145
145
|
subtitle={indicator}
|
|
146
|
-
label="checking IPFS storage and
|
|
146
|
+
label="checking IPFS storage and onchain..."
|
|
147
147
|
onCancel={onBack}
|
|
148
148
|
/>
|
|
149
149
|
)
|
|
@@ -54,7 +54,7 @@ export function renderCustodyStep({
|
|
|
54
54
|
footer={<Text color={theme.dim}>esc cancel</Text>}
|
|
55
55
|
>
|
|
56
56
|
<Box marginTop={1}>
|
|
57
|
-
<Text color={theme.textSubtle}>Reading vault state from
|
|
57
|
+
<Text color={theme.textSubtle}>Reading vault state from onchain...</Text>
|
|
58
58
|
</Box>
|
|
59
59
|
</Surface>
|
|
60
60
|
)
|
|
@@ -62,10 +62,8 @@ export type Step =
|
|
|
62
62
|
| { kind: 'continuity-skills-tree'; notice?: string; editorOpened?: boolean }
|
|
63
63
|
| { kind: 'continuity-skill-new'; error?: string }
|
|
64
64
|
| { kind: 'continuity-skill-new-visibility'; name: string; error?: string }
|
|
65
|
-
| { kind: 'continuity-skill-
|
|
65
|
+
| { kind: 'continuity-skill-actions'; relativePath: string; notice?: string }
|
|
66
66
|
| { kind: 'continuity-skill-delete-confirm'; target: { kind: 'skill'; relativePath: string } }
|
|
67
|
-
| { kind: 'continuity-skill-visibility'; notice?: string }
|
|
68
|
-
| { kind: 'continuity-skill-visibility-pick'; relativePath: string }
|
|
69
67
|
| { kind: 'rebackup-confirm'; back: Step }
|
|
70
68
|
| { kind: 'recovery-refetch-confirm'; back: Step }
|
|
71
69
|
| { kind: 'recovery-refetching'; identity: EthagentIdentity; registry: Erc8004RegistryConfig; back: Step }
|
|
@@ -186,14 +184,10 @@ function backStep(from: Step): Step {
|
|
|
186
184
|
return { kind: 'continuity-skills-tree' }
|
|
187
185
|
case 'continuity-skill-new-visibility':
|
|
188
186
|
return { kind: 'continuity-skill-new' }
|
|
189
|
-
case 'continuity-skill-
|
|
187
|
+
case 'continuity-skill-actions':
|
|
190
188
|
return { kind: 'continuity-skills-tree' }
|
|
191
189
|
case 'continuity-skill-delete-confirm':
|
|
192
|
-
return { kind: 'continuity-skill-
|
|
193
|
-
case 'continuity-skill-visibility':
|
|
194
|
-
return { kind: 'continuity-skills-tree' }
|
|
195
|
-
case 'continuity-skill-visibility-pick':
|
|
196
|
-
return { kind: 'continuity-skill-visibility' }
|
|
190
|
+
return { kind: 'continuity-skill-actions', relativePath: from.target.relativePath }
|
|
197
191
|
case 'rebackup-confirm':
|
|
198
192
|
case 'recovery-refetch-confirm':
|
|
199
193
|
case 'recovery-refetching':
|
|
@@ -57,7 +57,7 @@ export const EditProfileFlow: React.FC<EditProfileFlowProps> = ({
|
|
|
57
57
|
const currentName = step.name ?? readIdentityStateString(step.identity.state, 'name')
|
|
58
58
|
return (
|
|
59
59
|
<Surface
|
|
60
|
-
title="Edit
|
|
60
|
+
title="Edit Profile"
|
|
61
61
|
subtitle={<FlowTimeline steps={EDIT_PROFILE_STEPS} current={1} />}
|
|
62
62
|
footer={footerHint(EDIT_NEXT_FOOTER)}
|
|
63
63
|
>
|
|
@@ -112,7 +112,7 @@ export const EditProfileFlow: React.FC<EditProfileFlowProps> = ({
|
|
|
112
112
|
const draftDescription = step.description ?? currentDescription
|
|
113
113
|
return (
|
|
114
114
|
<Surface
|
|
115
|
-
title="Edit
|
|
115
|
+
title="Edit Profile"
|
|
116
116
|
subtitle={<FlowTimeline steps={EDIT_PROFILE_STEPS} current={2} />}
|
|
117
117
|
footer={footerHint(EDIT_DESCRIPTION_FOOTER)}
|
|
118
118
|
>
|
|
@@ -145,7 +145,7 @@ const AgentIconStep: React.FC<{
|
|
|
145
145
|
if (entryMode) {
|
|
146
146
|
return (
|
|
147
147
|
<Surface
|
|
148
|
-
title="Edit
|
|
148
|
+
title="Edit Profile"
|
|
149
149
|
subtitle={<FlowTimeline steps={EDIT_PROFILE_STEPS} current={3} />}
|
|
150
150
|
footer={footerHint(EDIT_NEXT_FOOTER)}
|
|
151
151
|
>
|
|
@@ -165,7 +165,7 @@ const AgentIconStep: React.FC<{
|
|
|
165
165
|
|
|
166
166
|
return (
|
|
167
167
|
<Surface
|
|
168
|
-
title="Edit
|
|
168
|
+
title="Edit Profile"
|
|
169
169
|
subtitle={<FlowTimeline steps={EDIT_PROFILE_STEPS} current={3} />}
|
|
170
170
|
footer={footerHint('enter select · esc back')}
|
|
171
171
|
>
|
|
@@ -208,7 +208,7 @@ const EditProfileReviewStep: React.FC<{
|
|
|
208
208
|
const currentIcon = readIdentityStateString(step.identity.state, 'imageUrl')
|
|
209
209
|
return (
|
|
210
210
|
<Surface
|
|
211
|
-
title="Edit
|
|
211
|
+
title="Edit Profile"
|
|
212
212
|
subtitle={<FlowTimeline steps={EDIT_PROFILE_STEPS} current={4} />}
|
|
213
213
|
footer={footerHint('enter save · esc back')}
|
|
214
214
|
>
|
|
@@ -78,7 +78,7 @@ export async function runRecoveryRefetch(
|
|
|
78
78
|
walletSignature: wallet.signature,
|
|
79
79
|
currentOwnerAddress: getAddress(wallet.account),
|
|
80
80
|
})
|
|
81
|
-
callbacks.onRestoreProgress?.({ phase: 'writing', label: 'restoring SOUL.md, MEMORY.md, and skills
|
|
81
|
+
callbacks.onRestoreProgress?.({ phase: 'writing', label: 'restoring SOUL.md, MEMORY.md, and skills...' })
|
|
82
82
|
const transferSnapshot = transferSnapshotMetadataFromEnvelope(envelope)
|
|
83
83
|
const refreshedBackup: BackupMetadata = {
|
|
84
84
|
cid: candidate.backup.cid,
|
|
@@ -136,10 +136,10 @@ export async function runRecoveryRefetch(
|
|
|
136
136
|
const publicSkillsRestored = await restorePublishedPublicSkills(nextIdentity, apiUrl, candidate.publicDiscovery?.skillsCid)
|
|
137
137
|
await ensureIdentityMarkdownScaffold(nextIdentity)
|
|
138
138
|
await syncPublicSkillsManifest(nextIdentity).catch(() => null)
|
|
139
|
-
await recordPublishedContinuitySnapshot({ identity: nextIdentity, label: 'Refetched Latest Snapshot From
|
|
139
|
+
await recordPublishedContinuitySnapshot({ identity: nextIdentity, label: 'Refetched Latest Snapshot From Onchain' }).catch(() => null)
|
|
140
140
|
if (publicSkillsRestored) {
|
|
141
141
|
const contentHashes = await localContinuitySnapshotContentHashes(nextIdentity)
|
|
142
142
|
await updatePublishedContinuitySnapshotContentHashes(nextIdentity, candidate.backup.cid, contentHashes).catch(() => null)
|
|
143
143
|
}
|
|
144
|
-
await callbacks.onIdentityComplete(nextIdentity, 'Latest Published Snapshot Restored From
|
|
144
|
+
await callbacks.onIdentityComplete(nextIdentity, 'Latest Published Snapshot Restored From Onchain', 'update')
|
|
145
145
|
}
|
|
@@ -28,9 +28,10 @@ interface IdentitySummaryProps {
|
|
|
28
28
|
hideHeader?: boolean
|
|
29
29
|
tokenLinked?: boolean
|
|
30
30
|
onchainOwner?: string
|
|
31
|
+
compact?: boolean
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, config, workingStatus, hideLocalChanges = false, hideHeader = false, tokenLinked = true, onchainOwner }) => {
|
|
34
|
+
export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, config, workingStatus, hideLocalChanges = false, hideHeader = false, tokenLinked = true, onchainOwner, compact = false }) => {
|
|
34
35
|
if (!identity) {
|
|
35
36
|
return (
|
|
36
37
|
<Text color={theme.dim}>No agent yet. Create or load one.</Text>
|
|
@@ -59,6 +60,25 @@ export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, conf
|
|
|
59
60
|
? `${tokenValue} · ${displayValue(networkValue)}`
|
|
60
61
|
: displayValue(tokenValue)
|
|
61
62
|
|
|
63
|
+
if (compact) {
|
|
64
|
+
const name = stateName || 'Active Agent'
|
|
65
|
+
const tokenSegment = identity.agentId ? `#${identity.agentId}` : null
|
|
66
|
+
const networkSegment = identity.agentId ? displayValue(networkValue) : null
|
|
67
|
+
const ensSegment = ensStatus.kind === 'linked'
|
|
68
|
+
? ensStatus.name
|
|
69
|
+
: ensStatus.kind === 'issue'
|
|
70
|
+
? ensStatus.name
|
|
71
|
+
: null
|
|
72
|
+
return (
|
|
73
|
+
<Text>
|
|
74
|
+
<Text color={theme.accentPeriwinkle} bold>{name}</Text>
|
|
75
|
+
{tokenSegment ? <><Text color={theme.dim}> · </Text><Text color={theme.text}>{tokenSegment}</Text></> : null}
|
|
76
|
+
{networkSegment ? <><Text color={theme.dim}> · </Text><Text color={theme.text}>{networkSegment}</Text></> : null}
|
|
77
|
+
{ensSegment ? <><Text color={theme.dim}> · </Text><Text color={ensStatus.kind === 'issue' ? theme.accentError : theme.accentPeriwinkle}>{ensSegment}</Text></> : null}
|
|
78
|
+
</Text>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
62
82
|
return (
|
|
63
83
|
<Box flexDirection="column">
|
|
64
84
|
{hideHeader ? null : (
|
|
@@ -100,7 +120,7 @@ export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, conf
|
|
|
100
120
|
}
|
|
101
121
|
const pendingCell = {
|
|
102
122
|
label: 'Pending',
|
|
103
|
-
value: <Text color={theme.dim}>local ahead of
|
|
123
|
+
value: <Text color={theme.dim}>local ahead of onchain, owner rotates pointer</Text>,
|
|
104
124
|
}
|
|
105
125
|
return (
|
|
106
126
|
<>
|
|
@@ -112,7 +112,7 @@ export const MenuScreen: React.FC<MenuScreenProps> = ({
|
|
|
112
112
|
const options: Array<SelectOption<Action>> = identity
|
|
113
113
|
? [
|
|
114
114
|
{ value: 'public-profile', role: 'section', label: 'Public Identity' },
|
|
115
|
-
{ value: 'public-profile', label: 'Public Profile', hint: '
|
|
115
|
+
{ value: 'public-profile', label: 'Public Profile', hint: 'Agent card and profile fields' },
|
|
116
116
|
{ value: 'ens-name', label: 'ENS Name', hint: ensNameHint, disabled: flags?.ensNameDisabled ?? false },
|
|
117
117
|
{ value: 'continuity', role: 'section', label: 'Continuity' },
|
|
118
118
|
{ value: 'continuity', label: 'Soul & Memory', hint: 'Edit SOUL.md and MEMORY.md' },
|
|
@@ -197,7 +197,7 @@ function renderReconciliationBanner(r: AgentReconciliation, identity: EthagentId
|
|
|
197
197
|
return (
|
|
198
198
|
<>
|
|
199
199
|
<Text color={theme.accentError} bold>Agent Unlinked</Text>
|
|
200
|
-
<Text color={theme.textSubtle}>{tokenLabel} was transferred. Local SOUL.md, MEMORY.md, skills
|
|
200
|
+
<Text color={theme.textSubtle}>{tokenLabel} was transferred. Local SOUL.md, MEMORY.md, and skills remain. Back them up before this directory is reused.</Text>
|
|
201
201
|
<Text color={theme.textSubtle}>Use Load Agent or New Agent to re-enable disabled actions.</Text>
|
|
202
202
|
</>
|
|
203
203
|
)
|
|
@@ -205,7 +205,7 @@ function renderReconciliationBanner(r: AgentReconciliation, identity: EthagentId
|
|
|
205
205
|
return (
|
|
206
206
|
<>
|
|
207
207
|
<Text color={theme.accentError} bold>Agent Unlinked</Text>
|
|
208
|
-
<Text color={theme.textSubtle}>{tokenLabel} left without Prepare Transfer. Back up local SOUL.md, MEMORY.md, skills
|
|
208
|
+
<Text color={theme.textSubtle}>{tokenLabel} left without Prepare Transfer. Back up local SOUL.md, MEMORY.md, and skills before loading another agent.</Text>
|
|
209
209
|
<Text color={theme.textSubtle}>For continuity handoff: ask the new holder to return the token, then run Prepare Transfer before re-sending.</Text>
|
|
210
210
|
<Text color={theme.textSubtle}>Use Load Agent or New Agent to re-enable disabled actions.</Text>
|
|
211
211
|
</>
|
|
@@ -224,7 +224,7 @@ function renderReconciliationBanner(r: AgentReconciliation, identity: EthagentId
|
|
|
224
224
|
}
|
|
225
225
|
const lines: string[] = []
|
|
226
226
|
if (r.custody === 'mid-flow-uri-pending') lines.push('Advanced setup pending. Open Custody Mode to finish.')
|
|
227
|
-
if (r.agentUri === 'local-newer') lines.push('Local state newer than
|
|
227
|
+
if (r.agentUri === 'local-newer') lines.push('Local state newer than onchain. Save Snapshot Now to publish.')
|
|
228
228
|
if (r.agentUri === 'chain-newer') lines.push('Onchain agentURI is newer than local. Refetch Latest.')
|
|
229
229
|
if (r.vault === 'missing') lines.push('Recorded vault address has no contract at it. Open Custody Mode to redeploy.')
|
|
230
230
|
if (r.workingTree === 'dirty') lines.push('Local edits pending. Save Snapshot Now to publish.')
|
|
@@ -33,7 +33,7 @@ export const UnlinkedIdentityScreen: React.FC<UnlinkedIdentityScreenProps> = ({
|
|
|
33
33
|
]
|
|
34
34
|
if (onRetry) {
|
|
35
35
|
options.push({ value: 'retry', role: 'section', label: 'Recheck' })
|
|
36
|
-
options.push({ value: 'retry', label: 'Retry Ownership Check', hint: 'Re-query
|
|
36
|
+
options.push({ value: 'retry', label: 'Retry Ownership Check', hint: 'Re-query onchain to confirm the current owner', role: 'utility' })
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const tokenLabel = agentId ? `Token #${agentId}` : 'Token'
|
|
@@ -48,13 +48,13 @@ export const UnlinkedIdentityScreen: React.FC<UnlinkedIdentityScreenProps> = ({
|
|
|
48
48
|
<Box flexDirection="column">
|
|
49
49
|
{transferSnapshot ? (
|
|
50
50
|
<Text color={theme.textSubtle}>
|
|
51
|
-
{tokenLabel} was transferred. Local SOUL.md, MEMORY.md, skills
|
|
51
|
+
{tokenLabel} was transferred. Local SOUL.md, MEMORY.md, and skills remain. Back them up before this directory is reused.
|
|
52
52
|
</Text>
|
|
53
53
|
) : (
|
|
54
54
|
<>
|
|
55
55
|
<Text color={theme.accentPeriwinkle}>{tokenLabel} left this wallet without Prepare Transfer, so the new holder has no continuity handoff.</Text>
|
|
56
56
|
<Text color={theme.textSubtle}>
|
|
57
|
-
Local SOUL.md, MEMORY.md, skills
|
|
57
|
+
Local SOUL.md, MEMORY.md, and skills remain. Back them up before this directory is reused.
|
|
58
58
|
</Text>
|
|
59
59
|
</>
|
|
60
60
|
)}
|
|
@@ -57,6 +57,6 @@ export function menuFlagsFromReconciliation(r: AgentReconciliation, perspective:
|
|
|
57
57
|
custodyAsterisk: custodyAsterisk && !isOperator,
|
|
58
58
|
...(custodyHint ? { custodyHint } : {}),
|
|
59
59
|
saveSnapshotAsterisk: r.agentUri === 'local-newer',
|
|
60
|
-
...(r.agentUri === 'local-newer' ? { saveSnapshotHint: 'Local newer than
|
|
60
|
+
...(r.agentUri === 'local-newer' ? { saveSnapshotHint: 'Local newer than onchain' } : {}),
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -208,7 +208,7 @@ export function useIdentityHubContinuity({
|
|
|
208
208
|
|
|
209
209
|
const deleteSkill = async (relativePath: string): Promise<void> => {
|
|
210
210
|
await mutateSkillsTree({
|
|
211
|
-
backStep: { kind: 'continuity-
|
|
211
|
+
backStep: { kind: 'continuity-skills-tree' },
|
|
212
212
|
run: async id => {
|
|
213
213
|
await deleteSkillEntry(id, relativePath)
|
|
214
214
|
return `deleted ${relativePath}`
|
|
@@ -221,8 +221,8 @@ export function useIdentityHubContinuity({
|
|
|
221
221
|
visibility: SkillVisibility,
|
|
222
222
|
): Promise<void> => {
|
|
223
223
|
await mutateSkillsTree({
|
|
224
|
-
backStep: { kind: 'continuity-skill-
|
|
225
|
-
successStep: notice => ({ kind: 'continuity-skill-
|
|
224
|
+
backStep: { kind: 'continuity-skill-actions', relativePath },
|
|
225
|
+
successStep: notice => ({ kind: 'continuity-skill-actions', relativePath, notice }),
|
|
226
226
|
run: async id => {
|
|
227
227
|
await setSkillVisibilityStorage(id, relativePath, visibility)
|
|
228
228
|
const display = relativePath.split('/')[0] ?? relativePath
|
|
@@ -181,16 +181,15 @@ function serializeOperatorsPointer(pointer: EthagentOperatorsPointer): Record<st
|
|
|
181
181
|
export function withEthagentBackupPointer(
|
|
182
182
|
registration: Record<string, unknown> | null,
|
|
183
183
|
backup: EthagentBackupPointer,
|
|
184
|
-
publicDiscovery
|
|
185
|
-
registrationPointer
|
|
186
|
-
ownerAddress
|
|
184
|
+
publicDiscovery: EthagentPublicDiscoveryPointer | undefined,
|
|
185
|
+
registrationPointer: EthagentRegistrationPointer | undefined,
|
|
186
|
+
ownerAddress: Address,
|
|
187
187
|
): Record<string, unknown> {
|
|
188
|
-
const inferredOwnerAddress = ownerAddress ?? backup.agentAddress
|
|
189
188
|
return withEthagentPointers(registration, {
|
|
190
189
|
backup,
|
|
191
190
|
publicDiscovery,
|
|
192
191
|
registration: registrationPointer,
|
|
193
|
-
|
|
192
|
+
ownerAddress,
|
|
194
193
|
})
|
|
195
194
|
}
|
|
196
195
|
|
|
@@ -202,29 +201,26 @@ export function withEthagentPointers(
|
|
|
202
201
|
registration?: EthagentRegistrationPointer
|
|
203
202
|
ensName?: string
|
|
204
203
|
operators?: EthagentOperatorsPointer
|
|
205
|
-
ownerAddress
|
|
204
|
+
ownerAddress: Address
|
|
206
205
|
},
|
|
207
206
|
): Record<string, unknown> {
|
|
208
207
|
const next: Record<string, unknown> = registration ? { ...registration } : {}
|
|
209
208
|
const prior = objectField(next, 'x-ethagent') ?? {}
|
|
210
209
|
const { backup, publicDiscovery, registration: registrationPointer, operators } = pointers
|
|
211
210
|
const updatedAt = publicDiscovery?.updatedAt ?? backup?.createdAt
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
: undefined
|
|
211
|
+
if (!pointers.ownerAddress) {
|
|
212
|
+
throw new Error('withEthagentPointers requires ownerAddress')
|
|
213
|
+
}
|
|
214
|
+
const ownerAddress = getAddress(pointers.ownerAddress)
|
|
217
215
|
const priorX402 = objectField(prior, 'x402') ?? {}
|
|
218
216
|
const ext: Record<string, unknown> = {
|
|
219
217
|
...prior,
|
|
220
218
|
version: 1,
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
},
|
|
227
|
-
} : {}),
|
|
219
|
+
agentAddress: ownerAddress,
|
|
220
|
+
x402: {
|
|
221
|
+
...priorX402,
|
|
222
|
+
walletAddress: ownerAddress,
|
|
223
|
+
},
|
|
228
224
|
...(backup ? {
|
|
229
225
|
backup: {
|
|
230
226
|
cid: backup.cid,
|
|
@@ -252,8 +248,11 @@ export function withEthagentPointers(
|
|
|
252
248
|
delete ext.transfer
|
|
253
249
|
delete ext.handoff
|
|
254
250
|
next['x-ethagent'] = ext
|
|
255
|
-
|
|
256
|
-
|
|
251
|
+
const agentWalletService = registrationPointer
|
|
252
|
+
? { name: 'agentWallet' as const, endpoint: `eip155:${registrationPointer.chainId}:${ownerAddress}` }
|
|
253
|
+
: undefined
|
|
254
|
+
if (publicDiscovery || agentWalletService) {
|
|
255
|
+
next.services = withEthagentServices(next.services, publicDiscovery, pointers.ensName, agentWalletService)
|
|
257
256
|
}
|
|
258
257
|
if (registrationPointer && registrationPointer.agentId !== undefined) {
|
|
259
258
|
next.registrations = withRegistrationsArray(next.registrations, registrationPointer)
|
|
@@ -272,10 +271,18 @@ function serializeTransferSnapshotMetadata(metadata: TransferSnapshotMetadata):
|
|
|
272
271
|
}
|
|
273
272
|
}
|
|
274
273
|
|
|
275
|
-
function
|
|
274
|
+
function withEthagentServices(
|
|
275
|
+
input: unknown,
|
|
276
|
+
publicDiscovery: EthagentPublicDiscoveryPointer | undefined,
|
|
277
|
+
ensName: string | undefined,
|
|
278
|
+
agentWallet: { name: 'agentWallet'; endpoint: string } | undefined,
|
|
279
|
+
): unknown[] {
|
|
276
280
|
const prior = Array.isArray(input) ? input.filter(item => item && typeof item === 'object') : []
|
|
277
281
|
const services = prior.filter(item => !isEthagentManagedService(item)) as unknown[]
|
|
278
|
-
if (
|
|
282
|
+
if (agentWallet) {
|
|
283
|
+
pushUniqueService(services, agentWallet)
|
|
284
|
+
}
|
|
285
|
+
if (publicDiscovery?.agentCardCid) {
|
|
279
286
|
const endpoint = `ipfs://${publicDiscovery.agentCardCid}`
|
|
280
287
|
pushUniqueService(services, {
|
|
281
288
|
type: 'a2a',
|
|
@@ -284,7 +291,7 @@ function withPublicDiscoveryServices(input: unknown, publicDiscovery: EthagentPu
|
|
|
284
291
|
url: endpoint,
|
|
285
292
|
})
|
|
286
293
|
}
|
|
287
|
-
if (publicDiscovery
|
|
294
|
+
if (publicDiscovery?.skillsCid) {
|
|
288
295
|
const endpoint = `ipfs://${publicDiscovery.skillsCid}`
|
|
289
296
|
pushUniqueService(services, {
|
|
290
297
|
type: 'A2A-skills',
|
|
@@ -313,6 +320,7 @@ function isEthagentManagedService(item: unknown): boolean {
|
|
|
313
320
|
const obj = item as Record<string, unknown>
|
|
314
321
|
const type = obj.type
|
|
315
322
|
const name = obj.name
|
|
323
|
+
if (name === 'agentWallet') return true
|
|
316
324
|
if (name === 'ENS') return true
|
|
317
325
|
if (type === 'a2a' && (name === undefined || name === 'agent-card')) return true
|
|
318
326
|
return (type === 'A2A-skills' || type === 'ipfs') && name === 'public-skills'
|