ethagent 2.4.0 → 3.0.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 +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 +134 -9
- package/src/identity/continuity/publicSkills.ts +54 -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 +79 -5
- package/src/identity/hub/Routes.tsx +5 -3
- package/src/identity/hub/continuity/ContinuityDashboardScreen.tsx +7 -73
- package/src/identity/hub/continuity/RecoveryConfirmScreen.tsx +6 -6
- package/src/identity/hub/continuity/SavePromptScreen.tsx +1 -2
- 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/NewSkillScreen.tsx +57 -0
- package/src/identity/hub/continuity/skills/NewSkillVisibilityScreen.tsx +52 -0
- package/src/identity/hub/continuity/skills/SkillActionsScreen.tsx +151 -0
- package/src/identity/hub/continuity/skills/SkillsTreeScreen.tsx +181 -0
- package/src/identity/hub/continuity/snapshot.ts +3 -0
- package/src/identity/hub/continuity/state.ts +9 -8
- package/src/identity/hub/continuity/vault.ts +42 -10
- package/src/identity/hub/create/CreateFlow.tsx +1 -1
- package/src/identity/hub/custody/CustodyEditFlow.tsx +3 -3
- 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 +15 -0
- package/src/identity/hub/profile/EditProfileFlow.tsx +5 -5
- 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 +14 -4
- 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 +118 -54
- package/src/identity/hub/shared/components/MenuScreen.tsx +21 -18
- package/src/identity/hub/shared/components/UnlinkedIdentityScreen.tsx +4 -4
- 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/identity/wallet/page/copy.ts +43 -43
- 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 +45 -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,181 @@
|
|
|
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: 'open-folder' }
|
|
19
|
+
| { kind: 'noop' }
|
|
20
|
+
| { kind: 'back' }
|
|
21
|
+
|
|
22
|
+
interface SkillsTreeScreenProps {
|
|
23
|
+
identity?: EthagentIdentity
|
|
24
|
+
config?: EthagentConfig
|
|
25
|
+
workingStatus?: ContinuityWorkingTreeStatus | null
|
|
26
|
+
notice?: string
|
|
27
|
+
editorOpened?: boolean
|
|
28
|
+
footer: React.ReactNode
|
|
29
|
+
onOpenSkill: (relativePath: string) => void
|
|
30
|
+
onNewSkill: () => void
|
|
31
|
+
onOpenFolder: () => void
|
|
32
|
+
onBack: () => void
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const SkillsTreeScreen: React.FC<SkillsTreeScreenProps> = ({
|
|
36
|
+
identity,
|
|
37
|
+
config,
|
|
38
|
+
workingStatus,
|
|
39
|
+
notice,
|
|
40
|
+
editorOpened,
|
|
41
|
+
footer,
|
|
42
|
+
onOpenSkill,
|
|
43
|
+
onNewSkill,
|
|
44
|
+
onOpenFolder,
|
|
45
|
+
onBack,
|
|
46
|
+
}) => {
|
|
47
|
+
const [tree, setTree] = useState<SkillsTreeView | null>(null)
|
|
48
|
+
const [error, setError] = useState<string | null>(null)
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
let cancelled = false
|
|
52
|
+
if (!identity) {
|
|
53
|
+
setTree({ skills: [], supportingCounts: {} })
|
|
54
|
+
return () => { cancelled = true }
|
|
55
|
+
}
|
|
56
|
+
const refresh = (): Promise<void> => listSkillsTree(identity)
|
|
57
|
+
.then(view => {
|
|
58
|
+
if (cancelled) return
|
|
59
|
+
setTree(view)
|
|
60
|
+
setError(null)
|
|
61
|
+
})
|
|
62
|
+
.catch(err => {
|
|
63
|
+
if (cancelled) return
|
|
64
|
+
setTree({ skills: [], supportingCounts: {} })
|
|
65
|
+
setError(String((err as Error).message ?? err))
|
|
66
|
+
})
|
|
67
|
+
void refresh()
|
|
68
|
+
if (!editorOpened) return () => { cancelled = true }
|
|
69
|
+
const interval = setInterval(() => { void refresh() }, 1500)
|
|
70
|
+
return () => {
|
|
71
|
+
cancelled = true
|
|
72
|
+
clearInterval(interval)
|
|
73
|
+
}
|
|
74
|
+
}, [identity, editorOpened])
|
|
75
|
+
|
|
76
|
+
const subtitle = notice ?? 'Open a skill, create one, or remove one.'
|
|
77
|
+
const isLoading = tree === null
|
|
78
|
+
const skills = tree?.skills ?? []
|
|
79
|
+
const supportingCounts = tree?.supportingCounts ?? {}
|
|
80
|
+
const hasAny = skills.length > 0
|
|
81
|
+
|
|
82
|
+
const options = buildOptions(skills, supportingCounts, isLoading, hasAny)
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<Surface title="Skills" subtitle={subtitle} footer={footer}>
|
|
86
|
+
<IdentitySummary identity={identity} config={config} workingStatus={workingStatus} compact />
|
|
87
|
+
{error && (
|
|
88
|
+
<Box marginTop={1}>
|
|
89
|
+
<Text color={theme.accentError}>{error}</Text>
|
|
90
|
+
</Box>
|
|
91
|
+
)}
|
|
92
|
+
{editorOpened && (
|
|
93
|
+
<Box marginTop={1}>
|
|
94
|
+
<Text color={theme.accentPeriwinkle}>Save with ctrl+s in your editor</Text>
|
|
95
|
+
</Box>
|
|
96
|
+
)}
|
|
97
|
+
<Box marginTop={1}>
|
|
98
|
+
<Select<SkillsTreeAction>
|
|
99
|
+
options={options}
|
|
100
|
+
hintLayout="inline"
|
|
101
|
+
onSubmit={choice => {
|
|
102
|
+
if (choice.kind === 'skill') return onOpenSkill(choice.relativePath)
|
|
103
|
+
if (choice.kind === 'new') return onNewSkill()
|
|
104
|
+
if (choice.kind === 'open-folder') return onOpenFolder()
|
|
105
|
+
if (choice.kind === 'back') return onBack()
|
|
106
|
+
}}
|
|
107
|
+
onCancel={onBack}
|
|
108
|
+
/>
|
|
109
|
+
</Box>
|
|
110
|
+
</Surface>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function buildOptions(
|
|
115
|
+
entries: SkillIndexEntry[],
|
|
116
|
+
supportingCounts: Record<string, number>,
|
|
117
|
+
isLoading: boolean,
|
|
118
|
+
hasAny: boolean,
|
|
119
|
+
): Array<SelectOption<SkillsTreeAction>> {
|
|
120
|
+
const rows: Array<SelectOption<SkillsTreeAction>> = []
|
|
121
|
+
const noopValue: SkillsTreeAction = { kind: 'noop' }
|
|
122
|
+
|
|
123
|
+
if (isLoading) {
|
|
124
|
+
rows.push({
|
|
125
|
+
value: noopValue,
|
|
126
|
+
role: 'notice',
|
|
127
|
+
label: 'Loading...',
|
|
128
|
+
labelColor: theme.dim,
|
|
129
|
+
indent: 0,
|
|
130
|
+
})
|
|
131
|
+
} else if (!hasAny) {
|
|
132
|
+
rows.push({ value: noopValue, role: 'section', label: 'Catalog' })
|
|
133
|
+
rows.push({ value: noopValue, role: 'notice', label: 'skills/', labelColor: theme.dim, indent: 3 })
|
|
134
|
+
rows.push({ value: noopValue, role: 'notice', label: '└── <skill>/', labelColor: theme.dim, indent: 3 })
|
|
135
|
+
rows.push({ value: noopValue, role: 'notice', label: ' └── SKILL.md', labelColor: theme.dim, indent: 3 })
|
|
136
|
+
} else {
|
|
137
|
+
rows.push({ value: noopValue, role: 'section', label: 'Catalog' })
|
|
138
|
+
rows.push({ value: noopValue, role: 'notice', label: 'skills/', labelColor: theme.dim, indent: 3 })
|
|
139
|
+
const sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name))
|
|
140
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
141
|
+
const skill = sorted[i]
|
|
142
|
+
if (!skill) continue
|
|
143
|
+
const isLast = i === sorted.length - 1
|
|
144
|
+
const branch = isLast ? '└── ' : '├── '
|
|
145
|
+
const supportCount = supportingCounts[skill.name] ?? 0
|
|
146
|
+
const meta = [capitalize(skill.visibility)]
|
|
147
|
+
if (supportCount > 0) meta.push(`${supportCount + 1} files`)
|
|
148
|
+
rows.push({
|
|
149
|
+
value: { kind: 'skill', relativePath: skill.relativePath },
|
|
150
|
+
label: `${branch}${skill.name}/SKILL.md`,
|
|
151
|
+
hint: meta.join(' · '),
|
|
152
|
+
indent: 3,
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
rows.push({ value: noopValue, role: 'section', label: 'Manage' })
|
|
158
|
+
rows.push({
|
|
159
|
+
value: { kind: 'new' },
|
|
160
|
+
label: 'New Skill',
|
|
161
|
+
hint: 'Scaffold a new skill folder with SKILL.md',
|
|
162
|
+
})
|
|
163
|
+
rows.push({
|
|
164
|
+
value: { kind: 'open-folder' },
|
|
165
|
+
label: 'Open Skills Folder',
|
|
166
|
+
hint: 'Reveal skills/ in your file manager',
|
|
167
|
+
})
|
|
168
|
+
rows.push({
|
|
169
|
+
value: { kind: 'back' },
|
|
170
|
+
label: 'Back',
|
|
171
|
+
hint: 'Return to Identity Hub menu',
|
|
172
|
+
role: 'utility',
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
return rows
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function capitalize(value: string): string {
|
|
179
|
+
if (!value) return value
|
|
180
|
+
return value.charAt(0).toUpperCase() + value.slice(1)
|
|
181
|
+
}
|
|
@@ -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,14 +20,15 @@ export function changedContinuitySnapshotFiles(
|
|
|
20
20
|
workingStatus?: ContinuityWorkingTreeStatus | null,
|
|
21
21
|
): string[] {
|
|
22
22
|
if (!workingStatus?.localContentHashes || !workingStatus.publishedContentHashes) return []
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
const local = workingStatus.localContentHashes
|
|
24
|
+
const published = workingStatus.publishedContentHashes
|
|
25
|
+
const changed = (file: keyof typeof local): boolean =>
|
|
26
|
+
(local[file] ?? '') !== (published[file] ?? '')
|
|
27
|
+
const result: string[] = []
|
|
28
|
+
if (changed('SOUL.md')) result.push('SOUL.md')
|
|
29
|
+
if (changed('MEMORY.md')) result.push('MEMORY.md')
|
|
30
|
+
if (changed('skills.json') || changed('private-skills')) result.push('Skills')
|
|
31
|
+
return result
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
export function localChangeStatusView(
|
|
@@ -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
|
}
|
|
@@ -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
|
)
|
|
@@ -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"
|
|
@@ -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
|
)
|
|
@@ -59,6 +59,11 @@ 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-actions'; relativePath: string; notice?: string }
|
|
66
|
+
| { kind: 'continuity-skill-delete-confirm'; target: { kind: 'skill'; relativePath: string } }
|
|
62
67
|
| { kind: 'rebackup-confirm'; back: Step }
|
|
63
68
|
| { kind: 'recovery-refetch-confirm'; back: Step }
|
|
64
69
|
| { kind: 'recovery-refetching'; identity: EthagentIdentity; registry: Erc8004RegistryConfig; back: Step }
|
|
@@ -173,6 +178,16 @@ function backStep(from: Step): Step {
|
|
|
173
178
|
case 'continuity-private':
|
|
174
179
|
case 'continuity-public':
|
|
175
180
|
return { kind: 'menu' }
|
|
181
|
+
case 'continuity-skills-tree':
|
|
182
|
+
return { kind: 'menu' }
|
|
183
|
+
case 'continuity-skill-new':
|
|
184
|
+
return { kind: 'continuity-skills-tree' }
|
|
185
|
+
case 'continuity-skill-new-visibility':
|
|
186
|
+
return { kind: 'continuity-skill-new' }
|
|
187
|
+
case 'continuity-skill-actions':
|
|
188
|
+
return { kind: 'continuity-skills-tree' }
|
|
189
|
+
case 'continuity-skill-delete-confirm':
|
|
190
|
+
return { kind: 'continuity-skill-actions', relativePath: from.target.relativePath }
|
|
176
191
|
case 'rebackup-confirm':
|
|
177
192
|
case 'recovery-refetch-confirm':
|
|
178
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
|
>
|
|
@@ -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
|
})
|