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
|
@@ -25,9 +25,9 @@ export function menuFlagsFromReconciliation(r: AgentReconciliation, perspective:
|
|
|
25
25
|
|
|
26
26
|
let prepareTransferReason: string | undefined
|
|
27
27
|
if (isOperator) {
|
|
28
|
-
prepareTransferReason = '
|
|
28
|
+
prepareTransferReason = 'Owner-only action'
|
|
29
29
|
} else if (!unlinked && (r.custody === 'advanced' || r.custody === 'mid-flow-uri-pending')) {
|
|
30
|
-
prepareTransferReason = '
|
|
30
|
+
prepareTransferReason = 'Withdraw from vault first'
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
const custodyAsterisk = r.custody === 'mid-flow-uri-pending' || r.vault === 'missing'
|
|
@@ -35,17 +35,13 @@ export function menuFlagsFromReconciliation(r: AgentReconciliation, perspective:
|
|
|
35
35
|
if (isOperator) {
|
|
36
36
|
custodyHint = undefined
|
|
37
37
|
} else if (r.custody === 'mid-flow-uri-pending') {
|
|
38
|
-
custodyHint = '
|
|
38
|
+
custodyHint = 'Setup pending, open to finish'
|
|
39
39
|
} else if (r.vault === 'missing') {
|
|
40
|
-
custodyHint = 'Vault
|
|
40
|
+
custodyHint = 'Vault missing, open to redeploy'
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const custodyModeReason = isOperator
|
|
44
|
-
|
|
45
|
-
: undefined
|
|
46
|
-
const ensNameReason = isOperator
|
|
47
|
-
? 'Operators cannot change the ENS subdomain or its records.'
|
|
48
|
-
: undefined
|
|
43
|
+
const custodyModeReason = isOperator ? 'Owner-only action' : undefined
|
|
44
|
+
const ensNameReason = isOperator ? 'Owner-only action' : undefined
|
|
49
45
|
|
|
50
46
|
return {
|
|
51
47
|
prepareTransferDisabled: unlinked || inVault || isOperator,
|
|
@@ -56,11 +52,11 @@ export function menuFlagsFromReconciliation(r: AgentReconciliation, perspective:
|
|
|
56
52
|
...(ensNameReason ? { ensNameReason } : {}),
|
|
57
53
|
saveSnapshotDisabled: unlinked,
|
|
58
54
|
refetchLatestDisabled: unlinked,
|
|
59
|
-
...(unlinked ? { tokenValuesUnlinkedNote: '
|
|
55
|
+
...(unlinked ? { tokenValuesUnlinkedNote: 'Unlinked, retained for reference' } : {}),
|
|
60
56
|
|
|
61
57
|
custodyAsterisk: custodyAsterisk && !isOperator,
|
|
62
58
|
...(custodyHint ? { custodyHint } : {}),
|
|
63
59
|
saveSnapshotAsterisk: r.agentUri === 'local-newer',
|
|
64
|
-
...(r.agentUri === 'local-newer' ? { saveSnapshotHint: 'Local
|
|
60
|
+
...(r.agentUri === 'local-newer' ? { saveSnapshotHint: 'Local newer than onchain' } : {}),
|
|
65
61
|
}
|
|
66
62
|
}
|
|
@@ -16,7 +16,11 @@ import {
|
|
|
16
16
|
} from '../reconciliation/index.js'
|
|
17
17
|
import { normalizeApprovedOperatorWallets } from '../operatorWallets.js'
|
|
18
18
|
import { readOwnerAddressField } from '../../../identityCompat.js'
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
continuitySnapshotContentHashesFromSources,
|
|
21
|
+
localContinuitySnapshotContentHashes,
|
|
22
|
+
} from '../../../continuity/storage.js'
|
|
23
|
+
import type { ContinuityFiles, ContinuitySkillsTree } from '../../../continuity/envelope.js'
|
|
20
24
|
import { updatePublishedContinuitySnapshotContentHashes } from '../../../continuity/snapshots.js'
|
|
21
25
|
import type { EffectCallbacks } from './types.js'
|
|
22
26
|
import { awaitConfirmedReceipt } from './receipts.js'
|
|
@@ -141,9 +145,18 @@ export async function syncVaultMetadataOperatorsAfterOwnerSave(args: {
|
|
|
141
145
|
}
|
|
142
146
|
}
|
|
143
147
|
|
|
144
|
-
export async function markCurrentContinuityFilesPublished(
|
|
148
|
+
export async function markCurrentContinuityFilesPublished(
|
|
149
|
+
identity: EthagentIdentity,
|
|
150
|
+
publishedSources?: {
|
|
151
|
+
privateFiles: ContinuityFiles
|
|
152
|
+
publicSkills: string
|
|
153
|
+
skills: ContinuitySkillsTree
|
|
154
|
+
},
|
|
155
|
+
): Promise<void> {
|
|
145
156
|
const cid = identity.backup?.cid
|
|
146
157
|
if (!cid) return
|
|
147
|
-
const contentHashes =
|
|
158
|
+
const contentHashes = publishedSources
|
|
159
|
+
? continuitySnapshotContentHashesFromSources(publishedSources)
|
|
160
|
+
: await localContinuitySnapshotContentHashes(identity)
|
|
148
161
|
await updatePublishedContinuitySnapshotContentHashes(identity, cid, contentHashes).catch(() => null)
|
|
149
162
|
}
|
|
@@ -28,8 +28,6 @@ export function copyableIdentityFields(identity?: EthagentIdentity, config?: Eth
|
|
|
28
28
|
return fields
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export function identityValuesCopyHint(
|
|
32
|
-
return
|
|
33
|
-
? 'Copy token, ENS, and token URI pointers'
|
|
34
|
-
: 'Copy token and token URI pointers'
|
|
31
|
+
export function identityValuesCopyHint(_identity?: EthagentIdentity): string {
|
|
32
|
+
return 'Copy token and pointers'
|
|
35
33
|
}
|
|
@@ -9,15 +9,21 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
continuityAgentSnapshot,
|
|
11
11
|
continuityVaultStatus,
|
|
12
|
+
prepareSyncedSkillsTree,
|
|
12
13
|
readContinuityFiles,
|
|
13
14
|
readPublicSkillsFile,
|
|
14
15
|
writePublicSkillsFile,
|
|
15
16
|
} from '../../continuity/storage.js'
|
|
16
17
|
import {
|
|
18
|
+
appendPublicSkillEntries,
|
|
17
19
|
createAgentCard,
|
|
18
20
|
defaultPublicSkillsProfile,
|
|
19
21
|
serializeAgentCard,
|
|
20
22
|
} from '../../continuity/publicSkills.js'
|
|
23
|
+
import {
|
|
24
|
+
derivePublicSkillEntries,
|
|
25
|
+
syncPublicSkillsManifest,
|
|
26
|
+
} from '../../continuity/skills/publicSkillsSync.js'
|
|
21
27
|
import { recordPublishedContinuitySnapshot } from '../../continuity/snapshots.js'
|
|
22
28
|
import { addToIpfs, DEFAULT_IPFS_API_URL, isPinataUploadUrl } from '../../storage/ipfs.js'
|
|
23
29
|
import {
|
|
@@ -161,16 +167,22 @@ export async function runTokenTransferSigning(
|
|
|
161
167
|
}
|
|
162
168
|
const nextIdentityForFiles: EthagentIdentity = { ...step.identity, state }
|
|
163
169
|
const continuityFiles = await readContinuityFiles(nextIdentityForFiles)
|
|
164
|
-
const publicSkillsJson = await
|
|
170
|
+
const publicSkillsJson = await syncPublicSkillsManifest(nextIdentityForFiles)
|
|
165
171
|
const publicSkillsPin = await addToIpfs(DEFAULT_IPFS_API_URL, publicSkillsJson, fetch, { pinataJwt: step.pinataJwt })
|
|
166
172
|
assertVerifiedPin(publicSkillsPin)
|
|
173
|
+
const publicSkillEntries = await derivePublicSkillEntries(nextIdentityForFiles)
|
|
174
|
+
const augmentedPublicProfile = appendPublicSkillEntries(
|
|
175
|
+
defaultPublicSkillsProfile(nextIdentityForFiles),
|
|
176
|
+
publicSkillEntries,
|
|
177
|
+
)
|
|
167
178
|
const agentCardPin = await addToIpfs(
|
|
168
179
|
DEFAULT_IPFS_API_URL,
|
|
169
|
-
serializeAgentCard(createAgentCard(
|
|
180
|
+
serializeAgentCard(createAgentCard(augmentedPublicProfile)),
|
|
170
181
|
fetch,
|
|
171
182
|
{ pinataJwt: step.pinataJwt },
|
|
172
183
|
)
|
|
173
184
|
assertVerifiedPin(agentCardPin)
|
|
185
|
+
const skillsTree = await prepareSyncedSkillsTree(nextIdentityForFiles)
|
|
174
186
|
const envelope = createTransferContinuitySnapshotEnvelope({
|
|
175
187
|
ownerAddress,
|
|
176
188
|
ownerWalletSignature: senderSignature.signature,
|
|
@@ -181,6 +193,7 @@ export async function runTokenTransferSigning(
|
|
|
181
193
|
payload: {
|
|
182
194
|
agent: continuityAgentSnapshot(nextIdentityForFiles),
|
|
183
195
|
files: continuityFiles,
|
|
196
|
+
...(Object.keys(skillsTree).length > 0 ? { skills: skillsTree } : {}),
|
|
184
197
|
transcript: [],
|
|
185
198
|
state,
|
|
186
199
|
},
|
|
@@ -2,15 +2,23 @@ import { useEffect, useState } from 'react'
|
|
|
2
2
|
import type { EthagentIdentity } from '../../storage/config.js'
|
|
3
3
|
import { catFromIpfs, DEFAULT_IPFS_API_URL } from '../storage/ipfs.js'
|
|
4
4
|
import {
|
|
5
|
-
continuityVaultRef,
|
|
6
5
|
continuityVaultStatus,
|
|
7
6
|
continuityWorkingTreeStatus,
|
|
8
7
|
ensurePublicSkillsFile,
|
|
9
8
|
type ContinuityWorkingTreeStatus,
|
|
10
9
|
} from '../continuity/storage.js'
|
|
11
|
-
import { openFileInEditor } from '../continuity/editor.js'
|
|
12
|
-
import { exportLocalBackup } from '../continuity/localBackup.js'
|
|
10
|
+
import { openFileInEditor, openInFileManager } from '../continuity/editor.js'
|
|
13
11
|
import { listPublishedContinuitySnapshots } from '../continuity/snapshots.js'
|
|
12
|
+
import {
|
|
13
|
+
createSkillFile,
|
|
14
|
+
deleteSkillEntry,
|
|
15
|
+
invalidateSkillsCache,
|
|
16
|
+
readSkillByRelativePath,
|
|
17
|
+
setSkillVisibility as setSkillVisibilityStorage,
|
|
18
|
+
} from '../continuity/skills/loadSkills.js'
|
|
19
|
+
import type { SkillVisibility } from '../continuity/skills/types.js'
|
|
20
|
+
import { syncPublicSkillsManifest } from '../continuity/skills/publicSkillsSync.js'
|
|
21
|
+
import { continuityVaultRef } from '../continuity/storage.js'
|
|
14
22
|
import type { Step } from './identityHubReducer.js'
|
|
15
23
|
|
|
16
24
|
type UseIdentityHubContinuityArgs = {
|
|
@@ -30,7 +38,11 @@ export function useIdentityHubContinuity({
|
|
|
30
38
|
setContinuityReady: (ready: boolean) => void
|
|
31
39
|
workingStatus: ContinuityWorkingTreeStatus | null
|
|
32
40
|
openContinuityFile: (kind: 'soul' | 'memory' | 'skills') => Promise<void>
|
|
33
|
-
|
|
41
|
+
openSkillFile: (relativePath: string) => Promise<void>
|
|
42
|
+
openSkillsFolder: () => Promise<void>
|
|
43
|
+
createSkill: (name: string, visibility: SkillVisibility) => Promise<void>
|
|
44
|
+
deleteSkill: (relativePath: string) => Promise<void>
|
|
45
|
+
setSkillVisibility: (relativePath: string, visibility: SkillVisibility) => Promise<void>
|
|
34
46
|
} {
|
|
35
47
|
const [continuityReady, setContinuityReady] = useState<boolean>(false)
|
|
36
48
|
const [workingStatus, setWorkingStatus] = useState<ContinuityWorkingTreeStatus | null>(null)
|
|
@@ -55,6 +67,7 @@ export function useIdentityHubContinuity({
|
|
|
55
67
|
step.kind !== 'menu'
|
|
56
68
|
&& step.kind !== 'continuity-private'
|
|
57
69
|
&& step.kind !== 'continuity-public'
|
|
70
|
+
&& step.kind !== 'continuity-skills-tree'
|
|
58
71
|
&& step.kind !== 'save-prompt'
|
|
59
72
|
&& step.kind !== 'rebackup-confirm'
|
|
60
73
|
) return
|
|
@@ -78,8 +91,39 @@ export function useIdentityHubContinuity({
|
|
|
78
91
|
}
|
|
79
92
|
}, [identity, step.kind])
|
|
80
93
|
|
|
94
|
+
const requireReadyVault = async (): Promise<EthagentIdentity> => {
|
|
95
|
+
if (!identity) throw new Error('No active identity')
|
|
96
|
+
const status = await continuityVaultStatus(identity)
|
|
97
|
+
if (!status.ready) {
|
|
98
|
+
throw new Error('Restore local continuity files before editing the skills tree')
|
|
99
|
+
}
|
|
100
|
+
return identity
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const mutateSkillsTree = async (args: {
|
|
104
|
+
backStep: Step
|
|
105
|
+
run: (id: EthagentIdentity) => Promise<string>
|
|
106
|
+
successStep?: (notice: string) => Step
|
|
107
|
+
}): Promise<void> => {
|
|
108
|
+
try {
|
|
109
|
+
const id = await requireReadyVault()
|
|
110
|
+
const notice = await args.run(id)
|
|
111
|
+
invalidateSkillsCache(id)
|
|
112
|
+
await syncPublicSkillsManifest(id)
|
|
113
|
+
const next = args.successStep
|
|
114
|
+
? args.successStep(notice)
|
|
115
|
+
: { kind: 'continuity-skills-tree' as const, notice }
|
|
116
|
+
setStep(next)
|
|
117
|
+
} catch (err: unknown) {
|
|
118
|
+
handleStepError(err, args.backStep)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
81
122
|
const openContinuityFile = async (kind: 'soul' | 'memory' | 'skills'): Promise<void> => {
|
|
82
123
|
if (!identity) return
|
|
124
|
+
const returnKind: 'continuity-private' | 'continuity-skills-tree' = kind === 'skills'
|
|
125
|
+
? 'continuity-skills-tree'
|
|
126
|
+
: 'continuity-private'
|
|
83
127
|
try {
|
|
84
128
|
if (kind === 'skills') {
|
|
85
129
|
await ensurePublicSkillsFile(identity, {
|
|
@@ -89,43 +133,121 @@ export function useIdentityHubContinuity({
|
|
|
89
133
|
const ref = continuityVaultRef(identity)
|
|
90
134
|
const file = kind === 'soul' ? ref.soulPath : kind === 'memory' ? ref.memoryPath : ref.publicSkillsPath
|
|
91
135
|
const result = await openFileInEditor(file)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
: `open failed: ${result.error}
|
|
96
|
-
|
|
136
|
+
if (result.ok) {
|
|
137
|
+
setStep({ kind: returnKind, editorOpened: true })
|
|
138
|
+
} else {
|
|
139
|
+
setStep({ kind: returnKind, notice: `open failed: ${result.error}`, editorOpened: false })
|
|
140
|
+
}
|
|
97
141
|
} catch (err: unknown) {
|
|
98
|
-
handleStepError(err, { kind:
|
|
142
|
+
handleStepError(err, { kind: returnKind })
|
|
99
143
|
}
|
|
100
144
|
}
|
|
101
145
|
|
|
102
|
-
const
|
|
146
|
+
const openSkillFile = async (relativePath: string): Promise<void> => {
|
|
103
147
|
if (!identity) return
|
|
104
148
|
try {
|
|
105
|
-
await
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
: result.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
149
|
+
const skill = await readSkillByRelativePath(identity, relativePath)
|
|
150
|
+
const result = await openFileInEditor(skill.absolutePath)
|
|
151
|
+
invalidateSkillsCache(identity)
|
|
152
|
+
try {
|
|
153
|
+
await syncPublicSkillsManifest(identity)
|
|
154
|
+
} catch (syncErr: unknown) {
|
|
155
|
+
const failPrefix = result.ok ? '' : `open failed: ${result.error}; `
|
|
156
|
+
setStep({ kind: 'continuity-skills-tree', notice: `${failPrefix}public manifest sync failed: ${(syncErr as Error).message}`, editorOpened: result.ok })
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
if (result.ok) {
|
|
160
|
+
setStep({ kind: 'continuity-skills-tree', editorOpened: true })
|
|
161
|
+
} else {
|
|
162
|
+
setStep({ kind: 'continuity-skills-tree', notice: `open failed: ${result.error}`, editorOpened: false })
|
|
163
|
+
}
|
|
115
164
|
} catch (err: unknown) {
|
|
116
|
-
handleStepError(err, { kind: 'continuity-
|
|
165
|
+
handleStepError(err, { kind: 'continuity-skills-tree' })
|
|
117
166
|
}
|
|
118
167
|
}
|
|
119
168
|
|
|
169
|
+
const openSkillsFolder = async (): Promise<void> => {
|
|
170
|
+
if (!identity) return
|
|
171
|
+
try {
|
|
172
|
+
const ref = continuityVaultRef(identity)
|
|
173
|
+
const result = await openInFileManager(ref.skillsDir)
|
|
174
|
+
if (result.ok) {
|
|
175
|
+
setStep({ kind: 'continuity-skills-tree', editorOpened: true })
|
|
176
|
+
} else {
|
|
177
|
+
setStep({ kind: 'continuity-skills-tree', notice: `open failed: ${result.error}`, editorOpened: false })
|
|
178
|
+
}
|
|
179
|
+
} catch (err: unknown) {
|
|
180
|
+
handleStepError(err, { kind: 'continuity-skills-tree' })
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const createSkill = async (name: string, visibility: SkillVisibility): Promise<void> => {
|
|
185
|
+
const normalizedName = sanitizeSkillSegment(name)
|
|
186
|
+
if (!normalizedName) {
|
|
187
|
+
handleStepError(
|
|
188
|
+
new Error('Folder name must contain only letters, numbers, dashes, underscores, or dots'),
|
|
189
|
+
{ kind: 'continuity-skill-new' },
|
|
190
|
+
)
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
const id = await requireReadyVault()
|
|
195
|
+
const created = await createSkillFile(id, { name: normalizedName, visibility })
|
|
196
|
+
invalidateSkillsCache(id)
|
|
197
|
+
await syncPublicSkillsManifest(id)
|
|
198
|
+
const result = await openFileInEditor(created.absolutePath)
|
|
199
|
+
if (result.ok) {
|
|
200
|
+
setStep({ kind: 'continuity-skills-tree', editorOpened: true })
|
|
201
|
+
} else {
|
|
202
|
+
setStep({ kind: 'continuity-skills-tree', notice: `created ${created.relativePath}; open failed: ${result.error}`, editorOpened: false })
|
|
203
|
+
}
|
|
204
|
+
} catch (err: unknown) {
|
|
205
|
+
handleStepError(err, { kind: 'continuity-skill-new' })
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const deleteSkill = async (relativePath: string): Promise<void> => {
|
|
210
|
+
await mutateSkillsTree({
|
|
211
|
+
backStep: { kind: 'continuity-skills-tree' },
|
|
212
|
+
run: async id => {
|
|
213
|
+
await deleteSkillEntry(id, relativePath)
|
|
214
|
+
return `deleted ${relativePath}`
|
|
215
|
+
},
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const setSkillVisibility = async (
|
|
220
|
+
relativePath: string,
|
|
221
|
+
visibility: SkillVisibility,
|
|
222
|
+
): Promise<void> => {
|
|
223
|
+
await mutateSkillsTree({
|
|
224
|
+
backStep: { kind: 'continuity-skill-actions', relativePath },
|
|
225
|
+
successStep: notice => ({ kind: 'continuity-skill-actions', relativePath, notice }),
|
|
226
|
+
run: async id => {
|
|
227
|
+
await setSkillVisibilityStorage(id, relativePath, visibility)
|
|
228
|
+
const display = relativePath.split('/')[0] ?? relativePath
|
|
229
|
+
return `${display} now ${visibility}`
|
|
230
|
+
},
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
|
|
120
234
|
return {
|
|
121
235
|
continuityReady,
|
|
122
236
|
setContinuityReady,
|
|
123
237
|
workingStatus,
|
|
124
238
|
openContinuityFile,
|
|
125
|
-
|
|
239
|
+
openSkillFile,
|
|
240
|
+
openSkillsFolder,
|
|
241
|
+
createSkill,
|
|
242
|
+
deleteSkill,
|
|
243
|
+
setSkillVisibility,
|
|
126
244
|
}
|
|
127
245
|
}
|
|
128
246
|
|
|
247
|
+
export function sanitizeSkillSegment(value: string): string {
|
|
248
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 60)
|
|
249
|
+
}
|
|
250
|
+
|
|
129
251
|
async function readPublishedPublicSkills(identity: EthagentIdentity): Promise<string> {
|
|
130
252
|
const cid = identity.publicSkills?.cid
|
|
131
253
|
if (!cid) throw new Error('No saved public skills CID')
|
|
@@ -389,7 +389,11 @@ export function useIdentityHubController({
|
|
|
389
389
|
openTokenTransferFlow,
|
|
390
390
|
openPublicProfileEdit,
|
|
391
391
|
openContinuityFile: continuity.openContinuityFile,
|
|
392
|
-
|
|
392
|
+
openSkillFile: continuity.openSkillFile,
|
|
393
|
+
openSkillsFolder: continuity.openSkillsFolder,
|
|
394
|
+
createSkill: continuity.createSkill,
|
|
395
|
+
deleteSkill: continuity.deleteSkill,
|
|
396
|
+
setSkillVisibility: continuity.setSkillVisibility,
|
|
393
397
|
}
|
|
394
398
|
}
|
|
395
399
|
|
|
@@ -20,10 +20,8 @@ import {
|
|
|
20
20
|
runEnsSetupRecordsTransaction,
|
|
21
21
|
runEnsSetupRegistryTransaction,
|
|
22
22
|
runUpdateEnsRecords,
|
|
23
|
-
} from './ens/
|
|
24
|
-
import {
|
|
25
|
-
runRecoveryRefetch,
|
|
26
|
-
} from './restore/index.js'
|
|
23
|
+
} from './ens/transactions.js'
|
|
24
|
+
import { runRecoveryRefetch } from './restore/recovery.js'
|
|
27
25
|
import type { EffectCallbacks } from './shared/effects/types.js'
|
|
28
26
|
import { useRestoreEffects } from './restore/useRestoreEffects.js'
|
|
29
27
|
import {
|