ethagent 2.4.0 → 3.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/README.md +7 -4
- package/package.json +2 -1
- package/src/app/FirstRun.tsx +155 -15
- package/src/app/FirstRunTimeline.tsx +4 -0
- package/src/app/input/AppInputProvider.tsx +19 -0
- package/src/app/input/appInputParser.ts +19 -4
- package/src/chat/ChatBottomPane.tsx +3 -1
- package/src/chat/ChatScreen.tsx +7 -1
- package/src/chat/ConversationStack.tsx +25 -19
- package/src/chat/MessageList.tsx +194 -53
- package/src/chat/chatSessionState.ts +1 -1
- package/src/chat/chatTurnOrchestrator.ts +59 -0
- package/src/chat/input/ChatInput.tsx +3 -0
- package/src/chat/input/textCursor.ts +13 -3
- package/src/chat/transcript/TranscriptView.tsx +7 -5
- package/src/chat/transcript/transcriptViewport.ts +88 -17
- package/src/chat/views/PermissionPrompt.tsx +26 -26
- package/src/chat/views/PermissionsView.tsx +18 -12
- package/src/chat/views/RewindView.tsx +3 -1
- package/src/cli/ResetConfirmView.tsx +24 -9
- package/src/identity/continuity/editor.ts +27 -2
- package/src/identity/continuity/envelope.ts +125 -0
- package/src/identity/continuity/publicSkills.ts +37 -1
- package/src/identity/continuity/skills/frontmatter.ts +183 -0
- package/src/identity/continuity/skills/loadSkills.ts +609 -0
- package/src/identity/continuity/skills/publicSkillsSync.ts +32 -0
- package/src/identity/continuity/skills/scaffold.ts +52 -0
- package/src/identity/continuity/skills/types.ts +30 -0
- package/src/identity/continuity/storage/defaults.ts +28 -47
- package/src/identity/continuity/storage/files.ts +1 -0
- package/src/identity/continuity/storage/paths.ts +1 -0
- package/src/identity/continuity/storage/scaffold.ts +25 -23
- package/src/identity/continuity/storage/status.ts +34 -5
- package/src/identity/continuity/storage/types.ts +3 -2
- package/src/identity/continuity/storage.ts +3 -0
- package/src/identity/hub/OperationalRoutes.tsx +105 -3
- package/src/identity/hub/Routes.tsx +5 -3
- package/src/identity/hub/continuity/ContinuityDashboardScreen.tsx +5 -51
- package/src/identity/hub/continuity/RecoveryConfirmScreen.tsx +1 -1
- package/src/identity/hub/continuity/SavePromptScreen.tsx +1 -0
- package/src/identity/hub/continuity/effects.ts +36 -5
- package/src/identity/hub/continuity/skills/DeleteSkillConfirmScreen.tsx +112 -0
- package/src/identity/hub/continuity/skills/DeleteSkillScreen.tsx +123 -0
- package/src/identity/hub/continuity/skills/NewSkillScreen.tsx +57 -0
- package/src/identity/hub/continuity/skills/NewSkillVisibilityScreen.tsx +52 -0
- package/src/identity/hub/continuity/skills/SkillVisibilityScreen.tsx +171 -0
- package/src/identity/hub/continuity/skills/SkillsTreeScreen.tsx +213 -0
- package/src/identity/hub/continuity/snapshot.ts +3 -0
- package/src/identity/hub/continuity/state.ts +3 -2
- package/src/identity/hub/continuity/vault.ts +42 -10
- package/src/identity/hub/custody/CustodyEditFlow.tsx +3 -3
- package/src/identity/hub/identityHubReducer.ts +21 -0
- package/src/identity/hub/profile/effects.ts +16 -3
- package/src/identity/hub/restore/RestoreFlow.tsx +43 -6
- package/src/identity/hub/restore/apply.ts +12 -1
- package/src/identity/hub/restore/recovery.ts +11 -1
- package/src/identity/hub/restore/resolve.ts +1 -1
- package/src/identity/hub/restore/useRestoreEffects.ts +4 -6
- package/src/identity/hub/shared/components/DetailsScreen.tsx +4 -1
- package/src/identity/hub/shared/components/IdentitySummary.tsx +97 -53
- package/src/identity/hub/shared/components/MenuScreen.tsx +18 -15
- package/src/identity/hub/shared/components/UnlinkedIdentityScreen.tsx +1 -1
- package/src/identity/hub/shared/components/menuFlagsFromReconciliation.ts +8 -12
- package/src/identity/hub/shared/effects/sync.ts +16 -3
- package/src/identity/hub/shared/model/copy.ts +2 -4
- package/src/identity/hub/transfer/effects.ts +15 -2
- package/src/identity/hub/useIdentityHubContinuity.ts +145 -23
- package/src/identity/hub/useIdentityHubController.ts +5 -1
- package/src/identity/hub/useIdentityHubSideEffects.ts +2 -4
- package/src/mcp/manager.ts +1 -1
- package/src/models/ModelPicker.tsx +89 -84
- package/src/models/llamacpp.ts +160 -11
- package/src/models/llamacppPreflight.ts +1 -16
- package/src/models/modelPickerOptions.ts +43 -37
- package/src/providers/contracts.ts +1 -0
- package/src/providers/openai-chat.ts +50 -9
- package/src/providers/openai-responses.ts +19 -4
- package/src/runtime/toolExecution.ts +4 -3
- package/src/runtime/turn.ts +61 -30
- package/src/tools/changeDirectoryTool.ts +1 -1
- package/src/tools/contracts.ts +10 -0
- package/src/tools/deleteFileTool.ts +1 -1
- package/src/tools/editTool.ts +1 -1
- package/src/tools/listDirectoryTool.ts +1 -1
- package/src/tools/listSkillFilesTool.ts +77 -0
- package/src/tools/listSkillsTool.ts +68 -0
- package/src/tools/mcpResourceTools.ts +2 -2
- package/src/tools/privateContinuityReadTool.ts +1 -1
- package/src/tools/readSkillTool.ts +107 -0
- package/src/tools/readTool.ts +1 -1
- package/src/tools/registry.ts +6 -0
- package/src/tools/writeFileTool.ts +22 -2
- package/src/ui/Spinner.tsx +1 -1
- package/src/identity/continuity/localBackup.ts +0 -249
- package/src/identity/continuity/zipWriter.ts +0 -95
- package/src/identity/hub/continuity/index.ts +0 -7
- package/src/identity/hub/ens/index.ts +0 -11
- package/src/identity/hub/restore/index.ts +0 -22
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
|
+
import { Box, Text } from 'ink'
|
|
3
|
+
import { Surface } from '../../../../ui/Surface.js'
|
|
4
|
+
import { Select, type SelectOption } from '../../../../ui/Select.js'
|
|
5
|
+
import { theme } from '../../../../ui/theme.js'
|
|
6
|
+
import type { EthagentConfig, EthagentIdentity } from '../../../../storage/config.js'
|
|
7
|
+
import {
|
|
8
|
+
listSkillsTree,
|
|
9
|
+
type SkillsTreeView,
|
|
10
|
+
} from '../../../continuity/skills/loadSkills.js'
|
|
11
|
+
import type { SkillIndexEntry } from '../../../continuity/skills/types.js'
|
|
12
|
+
import { IdentitySummary } from '../../shared/components/IdentitySummary.js'
|
|
13
|
+
import type { ContinuityWorkingTreeStatus } from '../../../continuity/storage.js'
|
|
14
|
+
|
|
15
|
+
type SkillsTreeAction =
|
|
16
|
+
| { kind: 'skill'; relativePath: string }
|
|
17
|
+
| { kind: 'new' }
|
|
18
|
+
| { kind: 'delete' }
|
|
19
|
+
| { kind: 'visibility' }
|
|
20
|
+
| { kind: 'view-manifest' }
|
|
21
|
+
| { kind: 'open-folder' }
|
|
22
|
+
| { kind: 'noop' }
|
|
23
|
+
| { kind: 'back' }
|
|
24
|
+
|
|
25
|
+
interface SkillsTreeScreenProps {
|
|
26
|
+
identity?: EthagentIdentity
|
|
27
|
+
config?: EthagentConfig
|
|
28
|
+
workingStatus?: ContinuityWorkingTreeStatus | null
|
|
29
|
+
notice?: string
|
|
30
|
+
editorOpened?: boolean
|
|
31
|
+
footer: React.ReactNode
|
|
32
|
+
onOpenSkill: (relativePath: string) => void
|
|
33
|
+
onNewSkill: () => void
|
|
34
|
+
onDelete: () => void
|
|
35
|
+
onVisibility: () => void
|
|
36
|
+
onViewPublicManifest: () => void
|
|
37
|
+
onOpenFolder: () => void
|
|
38
|
+
onBack: () => void
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const SkillsTreeScreen: React.FC<SkillsTreeScreenProps> = ({
|
|
42
|
+
identity,
|
|
43
|
+
config,
|
|
44
|
+
workingStatus,
|
|
45
|
+
notice,
|
|
46
|
+
editorOpened,
|
|
47
|
+
footer,
|
|
48
|
+
onOpenSkill,
|
|
49
|
+
onNewSkill,
|
|
50
|
+
onDelete,
|
|
51
|
+
onVisibility,
|
|
52
|
+
onViewPublicManifest,
|
|
53
|
+
onOpenFolder,
|
|
54
|
+
onBack,
|
|
55
|
+
}) => {
|
|
56
|
+
const [tree, setTree] = useState<SkillsTreeView | null>(null)
|
|
57
|
+
const [error, setError] = useState<string | null>(null)
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
let cancelled = false
|
|
61
|
+
if (!identity) {
|
|
62
|
+
setTree({ skills: [], supportingCounts: {} })
|
|
63
|
+
return () => { cancelled = true }
|
|
64
|
+
}
|
|
65
|
+
const refresh = (): Promise<void> => listSkillsTree(identity)
|
|
66
|
+
.then(view => {
|
|
67
|
+
if (cancelled) return
|
|
68
|
+
setTree(view)
|
|
69
|
+
setError(null)
|
|
70
|
+
})
|
|
71
|
+
.catch(err => {
|
|
72
|
+
if (cancelled) return
|
|
73
|
+
setTree({ skills: [], supportingCounts: {} })
|
|
74
|
+
setError(String((err as Error).message ?? err))
|
|
75
|
+
})
|
|
76
|
+
void refresh()
|
|
77
|
+
if (!editorOpened) return () => { cancelled = true }
|
|
78
|
+
const interval = setInterval(() => { void refresh() }, 1500)
|
|
79
|
+
return () => {
|
|
80
|
+
cancelled = true
|
|
81
|
+
clearInterval(interval)
|
|
82
|
+
}
|
|
83
|
+
}, [identity, editorOpened])
|
|
84
|
+
|
|
85
|
+
const subtitle = notice ?? 'Open a skill, create one, or remove one.'
|
|
86
|
+
const isLoading = tree === null
|
|
87
|
+
const skills = tree?.skills ?? []
|
|
88
|
+
const supportingCounts = tree?.supportingCounts ?? {}
|
|
89
|
+
const hasAny = skills.length > 0
|
|
90
|
+
|
|
91
|
+
const options = buildOptions(skills, supportingCounts, isLoading, hasAny)
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<Surface title="Skills" subtitle={subtitle} footer={footer}>
|
|
95
|
+
<IdentitySummary identity={identity} config={config} workingStatus={workingStatus} />
|
|
96
|
+
{error && (
|
|
97
|
+
<Box marginTop={1}>
|
|
98
|
+
<Text color={theme.accentError}>{error}</Text>
|
|
99
|
+
</Box>
|
|
100
|
+
)}
|
|
101
|
+
{editorOpened && (
|
|
102
|
+
<Box marginTop={1}>
|
|
103
|
+
<Text color={theme.accentPeriwinkle}>Save with ctrl+s in your editor</Text>
|
|
104
|
+
</Box>
|
|
105
|
+
)}
|
|
106
|
+
<Box marginTop={1}>
|
|
107
|
+
<Select<SkillsTreeAction>
|
|
108
|
+
options={options}
|
|
109
|
+
hintLayout="inline"
|
|
110
|
+
onSubmit={choice => {
|
|
111
|
+
if (choice.kind === 'skill') return onOpenSkill(choice.relativePath)
|
|
112
|
+
if (choice.kind === 'new') return onNewSkill()
|
|
113
|
+
if (choice.kind === 'delete') return onDelete()
|
|
114
|
+
if (choice.kind === 'visibility') return onVisibility()
|
|
115
|
+
if (choice.kind === 'view-manifest') return onViewPublicManifest()
|
|
116
|
+
if (choice.kind === 'open-folder') return onOpenFolder()
|
|
117
|
+
if (choice.kind === 'back') return onBack()
|
|
118
|
+
}}
|
|
119
|
+
onCancel={onBack}
|
|
120
|
+
/>
|
|
121
|
+
</Box>
|
|
122
|
+
</Surface>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function buildOptions(
|
|
127
|
+
entries: SkillIndexEntry[],
|
|
128
|
+
supportingCounts: Record<string, number>,
|
|
129
|
+
isLoading: boolean,
|
|
130
|
+
hasAny: boolean,
|
|
131
|
+
): Array<SelectOption<SkillsTreeAction>> {
|
|
132
|
+
const rows: Array<SelectOption<SkillsTreeAction>> = []
|
|
133
|
+
const noopValue: SkillsTreeAction = { kind: 'noop' }
|
|
134
|
+
|
|
135
|
+
if (isLoading) {
|
|
136
|
+
rows.push({
|
|
137
|
+
value: noopValue,
|
|
138
|
+
role: 'notice',
|
|
139
|
+
label: 'Loading...',
|
|
140
|
+
labelColor: theme.dim,
|
|
141
|
+
indent: 0,
|
|
142
|
+
})
|
|
143
|
+
} else if (!hasAny) {
|
|
144
|
+
rows.push({ value: noopValue, role: 'section', label: 'Catalog' })
|
|
145
|
+
rows.push({ value: noopValue, role: 'notice', label: 'skills/', labelColor: theme.dim, indent: 3 })
|
|
146
|
+
rows.push({ value: noopValue, role: 'notice', label: '└── <skill>/', labelColor: theme.dim, indent: 3 })
|
|
147
|
+
rows.push({ value: noopValue, role: 'notice', label: ' └── SKILL.md', labelColor: theme.dim, indent: 3 })
|
|
148
|
+
} else {
|
|
149
|
+
rows.push({ value: noopValue, role: 'section', label: 'Catalog' })
|
|
150
|
+
rows.push({ value: noopValue, role: 'notice', label: 'skills/', labelColor: theme.dim, indent: 3 })
|
|
151
|
+
const sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name))
|
|
152
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
153
|
+
const skill = sorted[i]
|
|
154
|
+
if (!skill) continue
|
|
155
|
+
const isLast = i === sorted.length - 1
|
|
156
|
+
const branch = isLast ? '└── ' : '├── '
|
|
157
|
+
const supportCount = supportingCounts[skill.name] ?? 0
|
|
158
|
+
const meta = [capitalize(skill.visibility)]
|
|
159
|
+
if (supportCount > 0) meta.push(`${supportCount + 1} files`)
|
|
160
|
+
rows.push({
|
|
161
|
+
value: { kind: 'skill', relativePath: skill.relativePath },
|
|
162
|
+
label: `${branch}${skill.name}/SKILL.md`,
|
|
163
|
+
hint: meta.join(' · '),
|
|
164
|
+
indent: 3,
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
rows.push({ value: noopValue, role: 'notice', label: '' })
|
|
170
|
+
rows.push({ value: noopValue, role: 'section', label: 'Manage' })
|
|
171
|
+
rows.push({
|
|
172
|
+
value: { kind: 'new' },
|
|
173
|
+
label: 'New Skill',
|
|
174
|
+
hint: 'Scaffold a new skill folder with SKILL.md',
|
|
175
|
+
})
|
|
176
|
+
if (hasAny) {
|
|
177
|
+
rows.push({
|
|
178
|
+
value: { kind: 'delete' },
|
|
179
|
+
label: 'Delete Skill',
|
|
180
|
+
hint: 'Remove a skill folder and all its supporting files',
|
|
181
|
+
})
|
|
182
|
+
rows.push({
|
|
183
|
+
value: { kind: 'visibility' },
|
|
184
|
+
label: 'Change Visibility',
|
|
185
|
+
hint: 'Toggle public, discoverable, or private',
|
|
186
|
+
})
|
|
187
|
+
}
|
|
188
|
+
rows.push({ value: noopValue, role: 'section', label: 'Inspect' })
|
|
189
|
+
rows.push({
|
|
190
|
+
value: { kind: 'view-manifest' },
|
|
191
|
+
label: 'Edit skills.json',
|
|
192
|
+
hint: 'Open skills.json in your editor',
|
|
193
|
+
})
|
|
194
|
+
rows.push({
|
|
195
|
+
value: { kind: 'open-folder' },
|
|
196
|
+
label: 'Open Skills Folder',
|
|
197
|
+
hint: 'Reveal skills/ in your file manager',
|
|
198
|
+
})
|
|
199
|
+
rows.push({ value: noopValue, role: 'section', label: 'Navigation' })
|
|
200
|
+
rows.push({
|
|
201
|
+
value: { kind: 'back' },
|
|
202
|
+
label: 'Back',
|
|
203
|
+
hint: 'Return to Identity Hub menu',
|
|
204
|
+
role: 'utility',
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
return rows
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function capitalize(value: string): string {
|
|
211
|
+
if (!value) return value
|
|
212
|
+
return value.charAt(0).toUpperCase() + value.slice(1)
|
|
213
|
+
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
continuityAgentSnapshot,
|
|
13
13
|
defaultContinuityFiles,
|
|
14
14
|
} from '../../continuity/storage.js'
|
|
15
|
+
import type { ContinuitySkillsTree } from '../../continuity/envelope.js'
|
|
15
16
|
import type { Erc8004RegistryConfig, EthagentOperatorsPointer } from '../../registry/erc8004.js'
|
|
16
17
|
import { readOwnerAddressField } from '../../identityCompat.js'
|
|
17
18
|
import { readCustodyMode } from '../custody/state.js'
|
|
@@ -206,6 +207,7 @@ export function createContinuityEnvelopeForSave(args: {
|
|
|
206
207
|
walletSignature: string
|
|
207
208
|
state: Record<string, unknown>
|
|
208
209
|
files: ReturnType<typeof defaultContinuityFiles>
|
|
210
|
+
skills?: ContinuitySkillsTree
|
|
209
211
|
walletAccess: WalletRestoreAccessContext
|
|
210
212
|
challengePurpose?: WalletChallengePurpose
|
|
211
213
|
}): ContinuitySnapshotEnvelope {
|
|
@@ -259,6 +261,7 @@ export function createContinuityEnvelopeForSave(args: {
|
|
|
259
261
|
payload: {
|
|
260
262
|
agent: continuityAgentSnapshot(args.identity),
|
|
261
263
|
files: args.files,
|
|
264
|
+
...(args.skills && Object.keys(args.skills).length > 0 ? { skills: args.skills } : {}),
|
|
262
265
|
transcript: [],
|
|
263
266
|
state: args.state,
|
|
264
267
|
},
|
|
@@ -20,13 +20,14 @@ export function changedContinuitySnapshotFiles(
|
|
|
20
20
|
workingStatus?: ContinuityWorkingTreeStatus | null,
|
|
21
21
|
): string[] {
|
|
22
22
|
if (!workingStatus?.localContentHashes || !workingStatus.publishedContentHashes) return []
|
|
23
|
-
const files: Array<keyof typeof workingStatus.localContentHashes> = ['SOUL.md', 'MEMORY.md', 'skills.json']
|
|
23
|
+
const files: Array<keyof typeof workingStatus.localContentHashes> = ['SOUL.md', 'MEMORY.md', 'skills.json', 'private-skills']
|
|
24
24
|
return files
|
|
25
|
-
.filter(file => workingStatus.localContentHashes?.[file] !== workingStatus.publishedContentHashes?.[file])
|
|
25
|
+
.filter(file => (workingStatus.localContentHashes?.[file] ?? '') !== (workingStatus.publishedContentHashes?.[file] ?? ''))
|
|
26
26
|
.map(displayContinuitySnapshotFile)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function displayContinuitySnapshotFile(file: keyof NonNullable<ContinuityWorkingTreeStatus['localContentHashes']>): string {
|
|
30
|
+
if (file === 'private-skills') return 'skills/'
|
|
30
31
|
return file
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -2,6 +2,7 @@ import { getAddress, type Address, type Hex } from 'viem'
|
|
|
2
2
|
import type { EthagentIdentity } from '../../../storage/config.js'
|
|
3
3
|
import {
|
|
4
4
|
prepareSyncedIdentityMarkdownScaffold,
|
|
5
|
+
prepareSyncedSkillsTree,
|
|
5
6
|
readContinuityFiles,
|
|
6
7
|
readPublicSkillsFile,
|
|
7
8
|
writeIdentityMarkdownScaffold,
|
|
@@ -10,13 +11,20 @@ import {
|
|
|
10
11
|
import {
|
|
11
12
|
createWalletRestoreAccessChallenge,
|
|
12
13
|
serializeContinuitySnapshotEnvelope,
|
|
14
|
+
type ContinuityFiles,
|
|
15
|
+
type ContinuitySkillsTree,
|
|
13
16
|
type WalletChallengePurpose,
|
|
14
17
|
} from '../../continuity/envelope.js'
|
|
15
18
|
import {
|
|
19
|
+
appendPublicSkillEntries,
|
|
16
20
|
createAgentCard,
|
|
17
21
|
defaultPublicSkillsProfile,
|
|
18
22
|
serializeAgentCard,
|
|
19
23
|
} from '../../continuity/publicSkills.js'
|
|
24
|
+
import {
|
|
25
|
+
derivePublicSkillEntries,
|
|
26
|
+
syncPublicSkillsManifest,
|
|
27
|
+
} from '../../continuity/skills/publicSkillsSync.js'
|
|
20
28
|
import { recordPublishedContinuitySnapshot } from '../../continuity/snapshots.js'
|
|
21
29
|
import { addToIpfs, DEFAULT_IPFS_API_URL } from '../../storage/ipfs.js'
|
|
22
30
|
import {
|
|
@@ -58,6 +66,11 @@ type VaultPublishPrepared = {
|
|
|
58
66
|
nextIdentity: EthagentIdentity
|
|
59
67
|
markdownScaffold?: IdentityMarkdownScaffold
|
|
60
68
|
completionMessage: string
|
|
69
|
+
publishedSources: {
|
|
70
|
+
privateFiles: ContinuityFiles
|
|
71
|
+
publicSkills: string
|
|
72
|
+
skills: ContinuitySkillsTree
|
|
73
|
+
}
|
|
61
74
|
}
|
|
62
75
|
|
|
63
76
|
export async function runOperatorWalletRebackup(args: {
|
|
@@ -129,18 +142,22 @@ export async function runOperatorWalletRebackup(args: {
|
|
|
129
142
|
const continuityFiles = markdownScaffold
|
|
130
143
|
? { 'SOUL.md': markdownScaffold['SOUL.md'], 'MEMORY.md': markdownScaffold['MEMORY.md'] }
|
|
131
144
|
: await readContinuityFiles(nextIdentityForFiles)
|
|
132
|
-
const publicSkillsJson =
|
|
133
|
-
? markdownScaffold['skills.json']
|
|
134
|
-
: await readPublicSkillsFile(nextIdentityForFiles)
|
|
145
|
+
const publicSkillsJson = await syncPublicSkillsManifest(nextIdentityForFiles)
|
|
135
146
|
const publicSkillsPin = await addToIpfs(DEFAULT_IPFS_API_URL, publicSkillsJson, fetch, { pinataJwt: step.pinataJwt })
|
|
136
147
|
assertVerifiedPin(publicSkillsPin)
|
|
148
|
+
const publicSkillEntries = await derivePublicSkillEntries(nextIdentityForFiles)
|
|
149
|
+
const augmentedPublicProfile = appendPublicSkillEntries(
|
|
150
|
+
defaultPublicSkillsProfile(nextIdentityForFiles),
|
|
151
|
+
publicSkillEntries,
|
|
152
|
+
)
|
|
137
153
|
const agentCardPin = await addToIpfs(
|
|
138
154
|
DEFAULT_IPFS_API_URL,
|
|
139
|
-
serializeAgentCard(createAgentCard(
|
|
155
|
+
serializeAgentCard(createAgentCard(augmentedPublicProfile)),
|
|
140
156
|
fetch,
|
|
141
157
|
{ pinataJwt: step.pinataJwt },
|
|
142
158
|
)
|
|
143
159
|
assertVerifiedPin(agentCardPin)
|
|
160
|
+
const skillsTree = await prepareSyncedSkillsTree(nextIdentityForFiles)
|
|
144
161
|
const envelope = createContinuityEnvelopeForSave({
|
|
145
162
|
identity: nextIdentityForFiles,
|
|
146
163
|
registry: step.registry,
|
|
@@ -149,6 +166,7 @@ export async function runOperatorWalletRebackup(args: {
|
|
|
149
166
|
walletSignature: wallet.signature,
|
|
150
167
|
state,
|
|
151
168
|
files: continuityFiles,
|
|
169
|
+
skills: skillsTree,
|
|
152
170
|
walletAccess,
|
|
153
171
|
...(challengePurpose ? { challengePurpose } : {}),
|
|
154
172
|
})
|
|
@@ -185,7 +203,11 @@ export async function runOperatorWalletRebackup(args: {
|
|
|
185
203
|
await writeIdentityMarkdownScaffold(nextIdentity, markdownScaffold)
|
|
186
204
|
}
|
|
187
205
|
await recordPublishedContinuitySnapshot({ identity: nextIdentity, label: 'local operator-wallet snapshot' }).catch(() => null)
|
|
188
|
-
await markCurrentContinuityFilesPublished(nextIdentity
|
|
206
|
+
await markCurrentContinuityFilesPublished(nextIdentity, {
|
|
207
|
+
privateFiles: continuityFiles,
|
|
208
|
+
publicSkills: publicSkillsJson,
|
|
209
|
+
skills: skillsTree,
|
|
210
|
+
}).catch(() => null)
|
|
189
211
|
const completionMessage = nextEnsName !== undefined && nextEnsName !== ((step.identity.state as Record<string, unknown> | undefined)?.ensName as string | undefined)
|
|
190
212
|
? 'Snapshot saved locally. Owner wallet still needs to publish to make ENS changes discoverable.'
|
|
191
213
|
: uploadedImageUri !== undefined
|
|
@@ -262,18 +284,22 @@ async function runOperatorWalletVaultPublish(args: {
|
|
|
262
284
|
const continuityFiles = markdownScaffold
|
|
263
285
|
? { 'SOUL.md': markdownScaffold['SOUL.md'], 'MEMORY.md': markdownScaffold['MEMORY.md'] }
|
|
264
286
|
: await readContinuityFiles(nextIdentityForFiles)
|
|
265
|
-
const publicSkillsJson =
|
|
266
|
-
? markdownScaffold['skills.json']
|
|
267
|
-
: await readPublicSkillsFile(nextIdentityForFiles)
|
|
287
|
+
const publicSkillsJson = await syncPublicSkillsManifest(nextIdentityForFiles)
|
|
268
288
|
const publicSkillsPin = await addToIpfs(DEFAULT_IPFS_API_URL, publicSkillsJson, fetch, { pinataJwt: step.pinataJwt })
|
|
269
289
|
assertVerifiedPin(publicSkillsPin)
|
|
290
|
+
const publicSkillEntries = await derivePublicSkillEntries(nextIdentityForFiles)
|
|
291
|
+
const augmentedPublicProfile = appendPublicSkillEntries(
|
|
292
|
+
defaultPublicSkillsProfile(nextIdentityForFiles),
|
|
293
|
+
publicSkillEntries,
|
|
294
|
+
)
|
|
270
295
|
const agentCardPin = await addToIpfs(
|
|
271
296
|
DEFAULT_IPFS_API_URL,
|
|
272
|
-
serializeAgentCard(createAgentCard(
|
|
297
|
+
serializeAgentCard(createAgentCard(augmentedPublicProfile)),
|
|
273
298
|
fetch,
|
|
274
299
|
{ pinataJwt: step.pinataJwt },
|
|
275
300
|
)
|
|
276
301
|
assertVerifiedPin(agentCardPin)
|
|
302
|
+
const skillsTree = await prepareSyncedSkillsTree(nextIdentityForFiles)
|
|
277
303
|
const envelope = createContinuityEnvelopeForSave({
|
|
278
304
|
identity: nextIdentityForFiles,
|
|
279
305
|
registry: step.registry,
|
|
@@ -282,6 +308,7 @@ async function runOperatorWalletVaultPublish(args: {
|
|
|
282
308
|
walletSignature: wallet.signature,
|
|
283
309
|
state,
|
|
284
310
|
files: continuityFiles,
|
|
311
|
+
skills: skillsTree,
|
|
285
312
|
walletAccess,
|
|
286
313
|
...(challengePurpose ? { challengePurpose } : {}),
|
|
287
314
|
})
|
|
@@ -354,6 +381,11 @@ async function runOperatorWalletVaultPublish(args: {
|
|
|
354
381
|
nextIdentity,
|
|
355
382
|
...(markdownScaffold ? { markdownScaffold } : {}),
|
|
356
383
|
completionMessage,
|
|
384
|
+
publishedSources: {
|
|
385
|
+
privateFiles: continuityFiles,
|
|
386
|
+
publicSkills: publicSkillsJson,
|
|
387
|
+
skills: skillsTree,
|
|
388
|
+
},
|
|
357
389
|
},
|
|
358
390
|
}
|
|
359
391
|
},
|
|
@@ -373,6 +405,6 @@ async function runOperatorWalletVaultPublish(args: {
|
|
|
373
405
|
await writeIdentityMarkdownScaffold(nextIdentity, result.prepared.markdownScaffold)
|
|
374
406
|
}
|
|
375
407
|
await recordPublishedContinuitySnapshot({ identity: nextIdentity, label: 'operator-published snapshot' }).catch(() => null)
|
|
376
|
-
await markCurrentContinuityFilesPublished(nextIdentity).catch(() => null)
|
|
408
|
+
await markCurrentContinuityFilesPublished(nextIdentity, result.prepared.publishedSources).catch(() => null)
|
|
377
409
|
await callbacks.onIdentityComplete(nextIdentity, result.prepared.completionMessage, 'update')
|
|
378
410
|
}
|
|
@@ -214,7 +214,7 @@ export const CustodyEditFlow: React.FC<CustodyEditFlowProps> = ({
|
|
|
214
214
|
<Surface
|
|
215
215
|
title="Switch to Advanced"
|
|
216
216
|
subtitle="Move this token into its own Vault so authorized operator wallets can update this agent onchain without your signature each time."
|
|
217
|
-
footer={footerHint('enter confirm
|
|
217
|
+
footer={footerHint('enter confirm · esc back')}
|
|
218
218
|
>
|
|
219
219
|
<Box flexDirection="column">
|
|
220
220
|
<Row label="Token" value={tokenLabel} />
|
|
@@ -236,7 +236,7 @@ export const CustodyEditFlow: React.FC<CustodyEditFlowProps> = ({
|
|
|
236
236
|
{ value: 'confirm', label: 'Yes, Switch to Advanced', hint: `Sign with ${shortAddress(ownerAddress || tokenOwner)} to deposit this token into its Vault` },
|
|
237
237
|
{ value: 'transfer', role: 'section', label: 'Move Token First' },
|
|
238
238
|
{ value: 'transfer', label: 'Prepare Token Transfer', hint: 'Move the token to a different wallet first, with snapshot handoff' },
|
|
239
|
-
{ value: 'back', role: 'section', label: '
|
|
239
|
+
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
240
240
|
{ value: 'back', label: 'No, Go Back', hint: 'Return without changing custody', role: 'utility' },
|
|
241
241
|
]}
|
|
242
242
|
hintLayout="inline"
|
|
@@ -278,7 +278,7 @@ export const CustodyEditFlow: React.FC<CustodyEditFlowProps> = ({
|
|
|
278
278
|
options={[
|
|
279
279
|
{ value: 'confirm', role: 'section', label: 'Confirm' },
|
|
280
280
|
{ value: 'confirm', label: 'Yes, Switch to Simple', hint: `Sign with the owner wallet to unwrap ${tokenLabel} from its Vault` },
|
|
281
|
-
{ value: 'back', role: 'section', label: '
|
|
281
|
+
{ value: 'back', role: 'section', label: 'Navigation' },
|
|
282
282
|
{ value: 'back', label: 'No, Go Back', hint: 'Return without changing custody', role: 'utility' },
|
|
283
283
|
]}
|
|
284
284
|
hintLayout="inline"
|
|
@@ -59,6 +59,13 @@ export type Step =
|
|
|
59
59
|
| { kind: 'public-profile-storage'; identity: EthagentIdentity; registry: Erc8004RegistryConfig; error?: string; pinataJwt?: string; profileUpdates?: ProfileUpdates; returnTo?: Step; vaultAddress?: `0x${string}` }
|
|
60
60
|
| { kind: 'continuity-private'; notice?: string; editorOpened?: boolean }
|
|
61
61
|
| { kind: 'continuity-public'; notice?: string; editorOpened?: boolean }
|
|
62
|
+
| { kind: 'continuity-skills-tree'; notice?: string; editorOpened?: boolean }
|
|
63
|
+
| { kind: 'continuity-skill-new'; error?: string }
|
|
64
|
+
| { kind: 'continuity-skill-new-visibility'; name: string; error?: string }
|
|
65
|
+
| { kind: 'continuity-skill-delete'; notice?: string }
|
|
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 }
|
|
62
69
|
| { kind: 'rebackup-confirm'; back: Step }
|
|
63
70
|
| { kind: 'recovery-refetch-confirm'; back: Step }
|
|
64
71
|
| { kind: 'recovery-refetching'; identity: EthagentIdentity; registry: Erc8004RegistryConfig; back: Step }
|
|
@@ -173,6 +180,20 @@ function backStep(from: Step): Step {
|
|
|
173
180
|
case 'continuity-private':
|
|
174
181
|
case 'continuity-public':
|
|
175
182
|
return { kind: 'menu' }
|
|
183
|
+
case 'continuity-skills-tree':
|
|
184
|
+
return { kind: 'menu' }
|
|
185
|
+
case 'continuity-skill-new':
|
|
186
|
+
return { kind: 'continuity-skills-tree' }
|
|
187
|
+
case 'continuity-skill-new-visibility':
|
|
188
|
+
return { kind: 'continuity-skill-new' }
|
|
189
|
+
case 'continuity-skill-delete':
|
|
190
|
+
return { kind: 'continuity-skills-tree' }
|
|
191
|
+
case 'continuity-skill-delete-confirm':
|
|
192
|
+
return { kind: 'continuity-skill-delete' }
|
|
193
|
+
case 'continuity-skill-visibility':
|
|
194
|
+
return { kind: 'continuity-skills-tree' }
|
|
195
|
+
case 'continuity-skill-visibility-pick':
|
|
196
|
+
return { kind: 'continuity-skill-visibility' }
|
|
176
197
|
case 'rebackup-confirm':
|
|
177
198
|
case 'recovery-refetch-confirm':
|
|
178
199
|
case 'recovery-refetching':
|
|
@@ -6,15 +6,21 @@ import {
|
|
|
6
6
|
type WalletChallengePurpose,
|
|
7
7
|
} from '../../continuity/envelope.js'
|
|
8
8
|
import {
|
|
9
|
+
prepareSyncedSkillsTree,
|
|
9
10
|
prepareSyncedPublicSkillsJson,
|
|
10
11
|
readContinuityFiles,
|
|
11
12
|
writePublicSkillsFile,
|
|
12
13
|
} from '../../continuity/storage.js'
|
|
13
14
|
import {
|
|
15
|
+
appendPublicSkillEntries,
|
|
14
16
|
createAgentCard,
|
|
15
17
|
defaultPublicSkillsProfile,
|
|
16
18
|
serializeAgentCard,
|
|
17
19
|
} from '../../continuity/publicSkills.js'
|
|
20
|
+
import {
|
|
21
|
+
derivePublicSkillEntries,
|
|
22
|
+
syncPublicSkillsManifest,
|
|
23
|
+
} from '../../continuity/skills/publicSkillsSync.js'
|
|
18
24
|
import { recordPublishedContinuitySnapshot } from '../../continuity/snapshots.js'
|
|
19
25
|
import { addToIpfs, DEFAULT_IPFS_API_URL, isPinataUploadUrl } from '../../storage/ipfs.js'
|
|
20
26
|
import {
|
|
@@ -151,7 +157,7 @@ async function runPublicProfileSigningInner(
|
|
|
151
157
|
includeLastBackedUpAt: false,
|
|
152
158
|
})
|
|
153
159
|
const nextIdentityForFiles: EthagentIdentity = { ...step.identity, state }
|
|
154
|
-
const publicSkillsJson = await
|
|
160
|
+
const publicSkillsJson = await syncPublicSkillsManifest(nextIdentityForFiles)
|
|
155
161
|
const publicSkillsPin = await addToIpfs(DEFAULT_IPFS_API_URL, publicSkillsJson, fetch, { pinataJwt: step.pinataJwt })
|
|
156
162
|
assertVerifiedPin(publicSkillsPin)
|
|
157
163
|
const agentCardPin = await addToIpfs(
|
|
@@ -395,18 +401,24 @@ async function prepareOperatorProfileArtifacts(args: {
|
|
|
395
401
|
})
|
|
396
402
|
const nextIdentityForFiles: EthagentIdentity = { ...step.identity, state }
|
|
397
403
|
|
|
398
|
-
const publicSkillsJson = await
|
|
404
|
+
const publicSkillsJson = await syncPublicSkillsManifest(nextIdentityForFiles)
|
|
399
405
|
const publicSkillsPin = await addToIpfs(DEFAULT_IPFS_API_URL, publicSkillsJson, fetch, { pinataJwt: step.pinataJwt })
|
|
400
406
|
assertVerifiedPin(publicSkillsPin)
|
|
407
|
+
const publicSkillEntries = await derivePublicSkillEntries(nextIdentityForFiles)
|
|
408
|
+
const augmentedPublicProfile = appendPublicSkillEntries(
|
|
409
|
+
defaultPublicSkillsProfile(nextIdentityForFiles),
|
|
410
|
+
publicSkillEntries,
|
|
411
|
+
)
|
|
401
412
|
const agentCardPin = await addToIpfs(
|
|
402
413
|
DEFAULT_IPFS_API_URL,
|
|
403
|
-
serializeAgentCard(createAgentCard(
|
|
414
|
+
serializeAgentCard(createAgentCard(augmentedPublicProfile)),
|
|
404
415
|
fetch,
|
|
405
416
|
{ pinataJwt: step.pinataJwt },
|
|
406
417
|
)
|
|
407
418
|
assertVerifiedPin(agentCardPin)
|
|
408
419
|
|
|
409
420
|
const continuityFiles = await readContinuityFiles(nextIdentityForFiles)
|
|
421
|
+
const skillsTree = await prepareSyncedSkillsTree(nextIdentityForFiles)
|
|
410
422
|
const envelope = createContinuityEnvelopeForSave({
|
|
411
423
|
identity: nextIdentityForFiles,
|
|
412
424
|
registry: step.registry,
|
|
@@ -415,6 +427,7 @@ async function prepareOperatorProfileArtifacts(args: {
|
|
|
415
427
|
walletSignature: wallet.signature,
|
|
416
428
|
state,
|
|
417
429
|
files: continuityFiles,
|
|
430
|
+
skills: skillsTree,
|
|
418
431
|
walletAccess,
|
|
419
432
|
challengePurpose,
|
|
420
433
|
})
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { Text } from 'ink'
|
|
2
|
+
import { Box, Text } from 'ink'
|
|
3
3
|
import { Surface } from '../../../ui/Surface.js'
|
|
4
4
|
import { Select } from '../../../ui/Select.js'
|
|
5
5
|
import { TextInput } from '../../../ui/TextInput.js'
|
|
6
|
+
import { Spinner } from '../../../ui/Spinner.js'
|
|
6
7
|
import { theme } from '../../../ui/theme.js'
|
|
7
8
|
import { normalizeErc8004RegistryConfig } from '../../registry/erc8004.js'
|
|
8
9
|
import {
|
|
@@ -18,7 +19,7 @@ import { WalletApprovalScreen } from '../shared/components/WalletApprovalScreen.
|
|
|
18
19
|
import { BusyScreen } from '../shared/components/BusyScreen.js'
|
|
19
20
|
import type { BrowserWalletReady } from '../../wallet/browserWallet.js'
|
|
20
21
|
import type { EthagentConfig } from '../../../storage/config.js'
|
|
21
|
-
import { restoreSignatureRequestForStep } from './
|
|
22
|
+
import { restoreSignatureRequestForStep } from './auth.js'
|
|
22
23
|
import type { RestoreProgress } from '../shared/effects/types.js'
|
|
23
24
|
|
|
24
25
|
type RestoreStep = Exclude<Extract<Step, { kind: `restore-${string}` }>, { kind: 'restore-wallet' | 'restore-network' }>
|
|
@@ -122,11 +123,24 @@ export const RestoreFlow: React.FC<RestoreFlowProps> = ({
|
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
if (step.kind === 'restore-ens-input') {
|
|
126
|
+
if (step.busy) {
|
|
127
|
+
return (
|
|
128
|
+
<Surface
|
|
129
|
+
title={isSwitch ? 'Load Agent' : 'Restore Agent'}
|
|
130
|
+
subtitle="Looking up the agent onchain."
|
|
131
|
+
footer={footerHint('esc cancels')}
|
|
132
|
+
>
|
|
133
|
+
<Box marginTop={1}>
|
|
134
|
+
<Spinner label="looking up ENS name onchain..." />
|
|
135
|
+
</Box>
|
|
136
|
+
</Surface>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
125
139
|
return (
|
|
126
140
|
<Surface
|
|
127
141
|
title={isSwitch ? 'Load Agent' : 'Restore Agent'}
|
|
128
142
|
subtitle="Enter the agent's ENS name to decrypt with an authorized operator wallet."
|
|
129
|
-
footer={footerHint(
|
|
143
|
+
footer={footerHint('enter continue · esc back')}
|
|
130
144
|
>
|
|
131
145
|
<Text color={theme.dim}>The full agent subdomain, e.g. agent.example.eth.</Text>
|
|
132
146
|
<TextInput
|
|
@@ -134,17 +148,35 @@ export const RestoreFlow: React.FC<RestoreFlowProps> = ({
|
|
|
134
148
|
onSubmit={value => onEnsSubmit(value.trim())}
|
|
135
149
|
onCancel={onBack}
|
|
136
150
|
/>
|
|
137
|
-
{step.error ?
|
|
151
|
+
{step.error ? (
|
|
152
|
+
<Box marginTop={1} flexDirection="column">
|
|
153
|
+
<Text color={theme.accentError}>Could not resolve ENS name</Text>
|
|
154
|
+
<Text color={theme.textSubtle}>{step.error}</Text>
|
|
155
|
+
</Box>
|
|
156
|
+
) : null}
|
|
138
157
|
</Surface>
|
|
139
158
|
)
|
|
140
159
|
}
|
|
141
160
|
|
|
142
161
|
if (step.kind === 'restore-token-id-input') {
|
|
162
|
+
if (step.busy) {
|
|
163
|
+
return (
|
|
164
|
+
<Surface
|
|
165
|
+
title={isSwitch ? 'Load Agent' : 'Restore Agent'}
|
|
166
|
+
subtitle="Looking up the agent onchain."
|
|
167
|
+
footer={footerHint('esc cancels')}
|
|
168
|
+
>
|
|
169
|
+
<Box marginTop={1}>
|
|
170
|
+
<Spinner label="looking up token onchain..." />
|
|
171
|
+
</Box>
|
|
172
|
+
</Surface>
|
|
173
|
+
)
|
|
174
|
+
}
|
|
143
175
|
return (
|
|
144
176
|
<Surface
|
|
145
177
|
title={isSwitch ? 'Load Agent' : 'Restore Agent'}
|
|
146
178
|
subtitle={`Enter the ERC-8004 token ID on ${networkLabelForRegistry(step.registry)}.`}
|
|
147
|
-
footer={footerHint(
|
|
179
|
+
footer={footerHint('enter continue · esc back')}
|
|
148
180
|
>
|
|
149
181
|
<Text color={theme.dim}>The integer token ID assigned at mint.</Text>
|
|
150
182
|
<TextInput
|
|
@@ -152,7 +184,12 @@ export const RestoreFlow: React.FC<RestoreFlowProps> = ({
|
|
|
152
184
|
onSubmit={value => onTokenIdSubmit(value.trim())}
|
|
153
185
|
onCancel={onBack}
|
|
154
186
|
/>
|
|
155
|
-
{step.error ?
|
|
187
|
+
{step.error ? (
|
|
188
|
+
<Box marginTop={1} flexDirection="column">
|
|
189
|
+
<Text color={theme.accentError}>Could not resolve token</Text>
|
|
190
|
+
<Text color={theme.textSubtle}>{step.error}</Text>
|
|
191
|
+
</Box>
|
|
192
|
+
) : null}
|
|
156
193
|
</Surface>
|
|
157
194
|
)
|
|
158
195
|
}
|
|
@@ -5,7 +5,12 @@ import {
|
|
|
5
5
|
restoreContinuitySnapshotEnvelope,
|
|
6
6
|
transferSnapshotMetadataFromEnvelope,
|
|
7
7
|
} from '../../continuity/envelope.js'
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
ensureIdentityMarkdownScaffold,
|
|
10
|
+
restoreSkillsTree,
|
|
11
|
+
writeContinuityFiles,
|
|
12
|
+
} from '../../continuity/storage.js'
|
|
13
|
+
import { syncPublicSkillsManifest } from '../../continuity/skills/publicSkillsSync.js'
|
|
9
14
|
import { recordPublishedContinuitySnapshot } from '../../continuity/snapshots.js'
|
|
10
15
|
import { requestBrowserWalletSignature } from '../../wallet/browserWallet.js'
|
|
11
16
|
import { setVaultAddressField } from '../../identityCompat.js'
|
|
@@ -31,6 +36,7 @@ export async function runRestoreAuthorize(
|
|
|
31
36
|
callbacks.onRestoreProgress?.({ phase: 'decrypting', label: 'signature received · decrypting encrypted snapshot...' })
|
|
32
37
|
let restored: ReturnType<typeof restoreAgentStateBackupEnvelope> | ReturnType<typeof restoreContinuitySnapshotEnvelope>
|
|
33
38
|
let continuityFiles: ReturnType<typeof restoreContinuitySnapshotEnvelope>['files'] | undefined
|
|
39
|
+
let continuitySkills: ReturnType<typeof restoreContinuitySnapshotEnvelope>['skills']
|
|
34
40
|
if (isContinuitySnapshotEnvelope(step.envelope)) {
|
|
35
41
|
const payload = restoreContinuitySnapshotEnvelope({
|
|
36
42
|
envelope: step.envelope,
|
|
@@ -39,6 +45,7 @@ export async function runRestoreAuthorize(
|
|
|
39
45
|
})
|
|
40
46
|
restored = payload
|
|
41
47
|
continuityFiles = payload.files
|
|
48
|
+
continuitySkills = payload.skills
|
|
42
49
|
} else {
|
|
43
50
|
restored = restoreAgentStateBackupEnvelope({
|
|
44
51
|
envelope: step.envelope,
|
|
@@ -104,9 +111,13 @@ export async function runRestoreAuthorize(
|
|
|
104
111
|
if (continuityFiles) {
|
|
105
112
|
await writeContinuityFiles(nextIdentity, continuityFiles)
|
|
106
113
|
}
|
|
114
|
+
if (continuitySkills) {
|
|
115
|
+
await restoreSkillsTree(nextIdentity, continuitySkills)
|
|
116
|
+
}
|
|
107
117
|
callbacks.onRestoreProgress?.({ phase: 'finishing', label: 'finalizing restored identity...' })
|
|
108
118
|
await restorePublishedPublicSkills(nextIdentity, step.apiUrl, step.candidate.publicDiscovery?.skillsCid)
|
|
109
119
|
await ensureIdentityMarkdownScaffold(nextIdentity)
|
|
120
|
+
await syncPublicSkillsManifest(nextIdentity).catch(() => null)
|
|
110
121
|
await recordPublishedContinuitySnapshot({ identity: nextIdentity, label: 'restored from agent backup' }).catch(() => null)
|
|
111
122
|
await callbacks.onIdentityComplete(nextIdentity, `ERC-8004 agent restored · #${step.candidate.agentId.toString()}`, 'restore')
|
|
112
123
|
}
|