ethagent 1.1.1 → 2.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/LICENSE +21 -21
- package/README.md +127 -29
- package/package.json +16 -9
- package/src/app/FirstRun.tsx +192 -146
- package/src/app/FirstRunTimeline.tsx +47 -0
- package/src/app/input/AppInputProvider.tsx +1 -1
- package/src/app/keybindings/KeybindingProvider.tsx +1 -1
- package/src/chat/ChatBottomPane.tsx +0 -1
- package/src/chat/ChatInput.tsx +6 -6
- package/src/chat/ChatScreen.tsx +43 -18
- package/src/chat/ContextLimitView.tsx +4 -4
- package/src/chat/ContinuityEditReviewView.tsx +11 -17
- package/src/chat/ConversationStack.tsx +3 -0
- package/src/chat/CopyPicker.tsx +0 -1
- package/src/chat/MessageList.tsx +62 -45
- package/src/chat/PermissionPrompt.tsx +13 -9
- package/src/chat/PlanApprovalView.tsx +3 -3
- package/src/chat/ResumeView.tsx +1 -4
- package/src/chat/RewindView.tsx +2 -2
- package/src/chat/TranscriptView.tsx +6 -0
- package/src/chat/chatInputState.ts +1 -1
- package/src/chat/chatScreenUtils.ts +22 -11
- package/src/chat/chatSessionState.ts +2 -2
- package/src/chat/chatTurnOrchestrator.ts +16 -81
- package/src/chat/commands.ts +1 -1
- package/src/chat/textCursor.ts +1 -1
- package/src/chat/transcriptViewport.ts +2 -7
- package/src/cli/ResetConfirmView.tsx +1 -1
- package/src/cli/main.tsx +9 -3
- package/src/cli/preview.tsx +0 -5
- package/src/cli/updateNotice.ts +5 -3
- package/src/identity/continuity/editor.ts +7 -107
- package/src/identity/continuity/envelope.ts +1048 -40
- package/src/identity/continuity/history.ts +4 -4
- package/src/identity/continuity/localBackup.ts +249 -0
- package/src/identity/continuity/privateEdit/apply.ts +170 -0
- package/src/identity/continuity/privateEdit/diff.ts +82 -0
- package/src/identity/continuity/privateEdit/files.ts +23 -0
- package/src/identity/continuity/privateEdit/types.ts +28 -0
- package/src/identity/continuity/privateEdit.ts +10 -298
- package/src/identity/continuity/publicSkills.ts +8 -9
- package/src/identity/continuity/snapshots.ts +17 -6
- package/src/identity/continuity/storage/defaults.ts +111 -0
- package/src/identity/continuity/storage/files.ts +72 -0
- package/src/identity/continuity/storage/markdown.ts +81 -0
- package/src/identity/continuity/storage/paths.ts +24 -0
- package/src/identity/continuity/storage/scaffold.ts +124 -0
- package/src/identity/continuity/storage/status.ts +86 -0
- package/src/identity/continuity/storage/types.ts +27 -0
- package/src/identity/continuity/storage.ts +32 -507
- package/src/identity/continuity/zipWriter.ts +95 -0
- package/src/identity/crypto/backupEnvelope.ts +14 -247
- package/src/identity/crypto/eth.ts +7 -7
- package/src/identity/ens/agentRecords.ts +96 -0
- package/src/identity/ens/ensAutomation/contracts.ts +38 -0
- package/src/identity/ens/ensAutomation/delete.ts +80 -0
- package/src/identity/ens/ensAutomation/names.ts +14 -0
- package/src/identity/ens/ensAutomation/operators.ts +29 -0
- package/src/identity/ens/ensAutomation/read.ts +114 -0
- package/src/identity/ens/ensAutomation/root.ts +63 -0
- package/src/identity/ens/ensAutomation/setup.ts +284 -0
- package/src/identity/ens/ensAutomation/transactions.ts +107 -0
- package/src/identity/ens/ensAutomation/types.ts +126 -0
- package/src/identity/ens/ensAutomation.ts +29 -0
- package/src/identity/ens/ensLookup/client.ts +43 -0
- package/src/identity/ens/ensLookup/constants.ts +26 -0
- package/src/identity/ens/ensLookup/discovery.ts +70 -0
- package/src/identity/ens/ensLookup/names.ts +34 -0
- package/src/identity/ens/ensLookup/records.ts +45 -0
- package/src/identity/ens/ensLookup/resolve.ts +75 -0
- package/src/identity/ens/ensLookup/tokenReference.ts +17 -0
- package/src/identity/ens/ensLookup/types.ts +38 -0
- package/src/identity/ens/ensLookup/validation.ts +72 -0
- package/src/identity/ens/ensLookup.ts +19 -0
- package/src/identity/ens/ensRegistration.ts +199 -0
- package/src/identity/ens/resolverDelegation.ts +48 -0
- package/src/identity/hub/IdentityHub.tsx +13 -815
- package/src/identity/hub/OperationalRoutes.tsx +370 -0
- package/src/identity/hub/Routes.tsx +361 -0
- package/src/identity/hub/advancedEnsValidation.ts +45 -0
- package/src/identity/hub/{screens → components}/DetailsScreen.tsx +14 -8
- package/src/identity/hub/{screens → components}/ErrorScreen.tsx +15 -5
- package/src/identity/hub/components/FlowTimeline.tsx +27 -0
- package/src/identity/hub/components/IdentitySummary.tsx +190 -0
- package/src/identity/hub/components/MenuScreen.tsx +237 -0
- package/src/identity/hub/{screens → components}/NetworkScreen.tsx +3 -3
- package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx} +21 -18
- package/src/identity/hub/components/UnlinkedIdentityScreen.tsx +76 -0
- package/src/identity/hub/{screens → components}/WalletApprovalScreen.tsx +9 -8
- package/src/identity/hub/components/menuFlagsFromReconciliation.ts +68 -0
- package/src/identity/hub/effects/create.ts +310 -0
- package/src/identity/hub/effects/ens/flows.ts +218 -0
- package/src/identity/hub/effects/ens/index.ts +11 -0
- package/src/identity/hub/effects/ens/transactions.ts +239 -0
- package/src/identity/hub/effects/index.ts +74 -0
- package/src/identity/hub/effects/profile/profileState.ts +173 -0
- package/src/identity/hub/effects/publicProfile/index.ts +5 -0
- package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +646 -0
- package/src/identity/hub/effects/rebackup/index.ts +7 -0
- package/src/identity/hub/effects/rebackup/operatorVault.ts +378 -0
- package/src/identity/hub/effects/rebackup/runRebackup.ts +451 -0
- package/src/identity/hub/effects/receipts.ts +46 -0
- package/src/identity/hub/effects/restore/apply.ts +112 -0
- package/src/identity/hub/effects/restore/auth.ts +159 -0
- package/src/identity/hub/effects/restore/discover.ts +86 -0
- package/src/identity/hub/effects/restore/envelopes.ts +21 -0
- package/src/identity/hub/effects/restore/fetch.ts +25 -0
- package/src/identity/hub/effects/restore/index.ts +22 -0
- package/src/identity/hub/effects/restore/recovery.ts +135 -0
- package/src/identity/hub/effects/restore/resolve.ts +102 -0
- package/src/identity/hub/effects/restore/restoreEffects.ts +22 -0
- package/src/identity/hub/effects/restore/shared.ts +91 -0
- package/src/identity/hub/effects/restoreAdmin.ts +93 -0
- package/src/identity/hub/effects/shared/profilePrep.ts +139 -0
- package/src/identity/hub/effects/shared/snapshot.ts +336 -0
- package/src/identity/hub/effects/shared/sync.ts +190 -0
- package/src/identity/hub/effects/token-transfer/index.ts +6 -0
- package/src/identity/hub/effects/token-transfer/progress.ts +59 -0
- package/src/identity/hub/effects/token-transfer/runTokenTransfer.ts +299 -0
- package/src/identity/hub/effects/types.ts +53 -0
- package/src/identity/hub/effects/vault/preflight.ts +50 -0
- package/src/identity/hub/flows/continuity/ContinuityDashboardScreen.tsx +170 -0
- package/src/identity/hub/flows/continuity/RebackupStorageScreen.tsx +28 -0
- package/src/identity/hub/flows/continuity/RecoveryConfirmScreen.tsx +104 -0
- package/src/identity/hub/flows/continuity/SavePromptScreen.tsx +49 -0
- package/src/identity/hub/{screens → flows/create}/CreateFlow.tsx +61 -62
- package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +347 -0
- package/src/identity/hub/flows/custody/custodyEffects.ts +321 -0
- package/src/identity/hub/flows/custody/custodyFlowActions.ts +236 -0
- package/src/identity/hub/flows/custody/custodyFlowEffects.ts +163 -0
- package/src/identity/hub/flows/custody/custodyFlowHelpers.ts +25 -0
- package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +239 -0
- package/src/identity/hub/flows/custody/custodyFlowTypes.ts +45 -0
- package/src/identity/hub/flows/custody/useCustodyFlow.tsx +25 -0
- package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +336 -0
- package/src/identity/hub/flows/ens/EnsEditFlow.tsx +397 -0
- package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +332 -0
- package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +471 -0
- package/src/identity/hub/flows/ens/EnsEditRunners.tsx +198 -0
- package/src/identity/hub/flows/ens/EnsEditShared.tsx +162 -0
- package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +518 -0
- package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +299 -0
- package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +398 -0
- package/src/identity/hub/flows/ens/ensEditCopy.ts +117 -0
- package/src/identity/hub/flows/ens/ensEditTypes.ts +91 -0
- package/src/identity/hub/flows/profile/EditProfileFlow.tsx +271 -0
- package/src/identity/hub/flows/restore/RestoreFlow.tsx +324 -0
- package/src/identity/hub/flows/restore/useRestoreFlowEffects.ts +77 -0
- package/src/identity/hub/{screens → flows/settings}/StorageCredentialScreen.tsx +25 -43
- package/src/identity/hub/flows/token-transfer/IdentityHubTokenTransferFlow.tsx +162 -0
- package/src/identity/hub/flows/token-transfer/TokenTransferScreens.tsx +256 -0
- package/src/identity/hub/identityHubReducer.ts +166 -101
- package/src/identity/hub/model/continuity.ts +94 -0
- package/src/identity/hub/model/copy.ts +35 -0
- package/src/identity/hub/model/custody.ts +54 -0
- package/src/identity/hub/model/ens.ts +49 -0
- package/src/identity/hub/model/errors.ts +140 -0
- package/src/identity/hub/model/format.ts +15 -0
- package/src/identity/hub/model/identity.ts +94 -0
- package/src/identity/hub/model/network.ts +32 -0
- package/src/identity/hub/model/transfer.ts +57 -0
- package/src/identity/hub/operatorWallets.ts +131 -0
- package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +46 -0
- package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +129 -0
- package/src/identity/hub/reconciliation/agentReconciliation/run.ts +302 -0
- package/src/identity/hub/reconciliation/agentReconciliation/types.ts +17 -0
- package/src/identity/hub/reconciliation/index.ts +21 -0
- package/src/identity/hub/reconciliation/useAgentReconciliation.ts +10 -0
- package/src/identity/hub/reconciliation/walletSetup.ts +220 -0
- package/src/identity/hub/txGuard.ts +51 -0
- package/src/identity/hub/types.ts +17 -0
- package/src/identity/hub/useIdentityHubContinuity.ts +136 -0
- package/src/identity/hub/useIdentityHubController.ts +396 -0
- package/src/identity/hub/useIdentityHubSideEffects.ts +309 -0
- package/src/identity/hub/utils.ts +79 -0
- package/src/identity/identityCompat.ts +34 -0
- package/src/identity/profile/agentIcon.ts +61 -0
- package/src/identity/profile/imagePicker.ts +12 -12
- package/src/identity/registry/erc8004/abi.ts +14 -0
- package/src/identity/registry/erc8004/chains.ts +150 -0
- package/src/identity/registry/erc8004/client.ts +11 -0
- package/src/identity/registry/erc8004/discovery.ts +511 -0
- package/src/identity/registry/erc8004/metadata.ts +335 -0
- package/src/identity/registry/erc8004/ownership.ts +121 -0
- package/src/identity/registry/erc8004/preflight.ts +123 -0
- package/src/identity/registry/erc8004/transactions.ts +77 -0
- package/src/identity/registry/erc8004/types.ts +88 -0
- package/src/identity/registry/erc8004/uri.ts +59 -0
- package/src/identity/registry/erc8004/utils.ts +58 -0
- package/src/identity/registry/erc8004.ts +53 -1106
- package/src/identity/registry/fieldParsers.ts +28 -0
- package/src/identity/registry/operatorVault/bytecode.ts +98 -0
- package/src/identity/registry/operatorVault/constants.ts +38 -0
- package/src/identity/registry/operatorVault/read.ts +246 -0
- package/src/identity/registry/operatorVault/transactions.ts +81 -0
- package/src/identity/registry/operatorVault.ts +44 -0
- package/src/identity/storage/ipfs.ts +26 -24
- package/src/identity/wallet/browserWallet/gas.ts +41 -0
- package/src/identity/wallet/browserWallet/html.ts +106 -0
- package/src/identity/wallet/browserWallet/http.ts +28 -0
- package/src/identity/wallet/browserWallet/requestServer.ts +106 -0
- package/src/identity/wallet/browserWallet/requests.ts +191 -0
- package/src/identity/wallet/browserWallet/session.ts +325 -0
- package/src/identity/wallet/browserWallet/types.ts +192 -0
- package/src/identity/wallet/browserWallet/validation.ts +74 -0
- package/src/identity/wallet/browserWallet.ts +30 -393
- package/src/identity/wallet/page/constants.ts +5 -0
- package/src/identity/wallet/page/controller.ts +251 -0
- package/src/identity/wallet/page/copy.ts +340 -0
- package/src/identity/wallet/page/grainient.ts +278 -0
- package/src/identity/wallet/page/html.ts +28 -0
- package/src/identity/wallet/page/markup.ts +50 -0
- package/src/identity/wallet/page/state.ts +9 -0
- package/src/identity/wallet/page/styles/base.ts +259 -0
- package/src/identity/wallet/page/styles/components.ts +262 -0
- package/src/identity/wallet/page/styles/index.ts +5 -0
- package/src/identity/wallet/page/styles/responsive.ts +247 -0
- package/src/identity/wallet/page/types.ts +47 -0
- package/src/identity/wallet/page/view.ts +535 -0
- package/src/identity/wallet/page/walletProvider.ts +70 -0
- package/src/identity/wallet/page.tsx +38 -0
- package/src/identity/wallet/walletPurposeCompat.ts +27 -0
- package/src/mcp/manager.ts +0 -1
- package/src/models/ModelPicker.tsx +36 -30
- package/src/models/catalog.ts +5 -2
- package/src/models/huggingface.ts +9 -9
- package/src/models/llamacpp.ts +13 -13
- package/src/models/modelDisplay.ts +75 -0
- package/src/models/modelPickerOptions.ts +16 -3
- package/src/models/modelRecommendation.ts +0 -1
- package/src/providers/errors.ts +16 -0
- package/src/providers/gemini.ts +252 -39
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +1 -1
- package/src/runtime/sessionMode.ts +1 -1
- package/src/runtime/systemPrompt.ts +2 -0
- package/src/runtime/toolExecution.ts +18 -22
- package/src/runtime/toolIntent.ts +0 -20
- package/src/runtime/turn.ts +0 -92
- package/src/storage/atomicWrite.ts +4 -1
- package/src/storage/config.ts +181 -5
- package/src/storage/identity.ts +9 -3
- package/src/storage/secrets.ts +2 -2
- package/src/tools/bashSafety.ts +8 -0
- package/src/tools/changeDirectoryTool.ts +1 -1
- package/src/tools/deleteFileTool.ts +4 -4
- package/src/tools/editTool.ts +4 -4
- package/src/tools/editUtils.ts +5 -5
- package/src/tools/privateContinuityEditTool.ts +4 -5
- package/src/tools/privateContinuityReadTool.ts +1 -2
- package/src/tools/registry.ts +30 -0
- package/src/tools/writeFileTool.ts +5 -5
- package/src/ui/BrandSplash.tsx +20 -85
- package/src/ui/ProgressBar.tsx +3 -5
- package/src/ui/Select.tsx +21 -9
- package/src/ui/Spinner.tsx +38 -3
- package/src/ui/Surface.tsx +3 -3
- package/src/ui/TextInput.tsx +191 -29
- package/src/ui/theme.ts +7 -34
- package/src/utils/openExternal.ts +21 -0
- package/src/utils/withRetry.ts +47 -3
- package/src/identity/hub/identityHubEffects.ts +0 -937
- package/src/identity/hub/identityHubModel.ts +0 -291
- package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -144
- package/src/identity/hub/screens/EditProfileFlow.tsx +0 -145
- package/src/identity/hub/screens/IdentitySummary.tsx +0 -90
- package/src/identity/hub/screens/MenuScreen.tsx +0 -117
- package/src/identity/hub/screens/RecoveryConfirmScreen.tsx +0 -87
- package/src/identity/hub/screens/RestoreFlow.tsx +0 -206
- package/src/identity/wallet/wallet-page/wallet.html +0 -1202
- /package/src/identity/hub/{screens → components}/BusyScreen.tsx +0 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
type SyncBlock = {
|
|
2
|
+
marker: string
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function renderPrivateIdentityBlock(args: {
|
|
6
|
+
owner: string
|
|
7
|
+
token: string
|
|
8
|
+
chainId: string
|
|
9
|
+
registry: string
|
|
10
|
+
}): string {
|
|
11
|
+
return [
|
|
12
|
+
'<!-- ethagent:identity:start -->',
|
|
13
|
+
'## Agent Identity',
|
|
14
|
+
`- Owner wallet: ${args.owner}`,
|
|
15
|
+
`- ERC-8004 token: ${args.token}`,
|
|
16
|
+
`- Chain ID: ${args.chainId}`,
|
|
17
|
+
`- Registry: ${args.registry}`,
|
|
18
|
+
'- Visibility: private local working file; encrypted before IPFS backup.',
|
|
19
|
+
'<!-- ethagent:identity:end -->',
|
|
20
|
+
].join('\n')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function syncGeneratedMarkdown(existing: string, fresh: string, blocks: SyncBlock[]): string {
|
|
24
|
+
let next = replaceFirstHeading(existing, firstHeading(fresh))
|
|
25
|
+
for (const block of blocks) {
|
|
26
|
+
next = replaceOrInsertMarkedBlock(next, fresh, block)
|
|
27
|
+
}
|
|
28
|
+
return ensureTrailingNewline(next)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function firstHeading(markdown: string): string {
|
|
32
|
+
return markdown.split(/\r?\n/).find(line => line.startsWith('# ')) ?? ''
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function replaceFirstHeading(markdown: string, heading: string): string {
|
|
36
|
+
if (!heading) return markdown
|
|
37
|
+
const lines = markdown.split(/\r?\n/)
|
|
38
|
+
const index = lines.findIndex(line => line.startsWith('# '))
|
|
39
|
+
if (index === -1) return `${heading}\n\n${markdown.trimStart()}`
|
|
40
|
+
lines[index] = heading
|
|
41
|
+
return lines.join('\n')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function replaceOrInsertMarkedBlock(markdown: string, fresh: string, block: SyncBlock): string {
|
|
45
|
+
const freshBlock = extractMarkedBlock(fresh, block.marker)
|
|
46
|
+
if (!freshBlock) return markdown
|
|
47
|
+
const replaced = replaceMarkedBlock(markdown, block.marker, freshBlock)
|
|
48
|
+
if (replaced) return replaced
|
|
49
|
+
return insertAfterFirstHeading(markdown, freshBlock)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function extractMarkedBlock(markdown: string, marker: string): string | null {
|
|
53
|
+
const start = `<!-- ethagent:${marker}:start -->`
|
|
54
|
+
const end = `<!-- ethagent:${marker}:end -->`
|
|
55
|
+
const startIndex = markdown.indexOf(start)
|
|
56
|
+
const endIndex = markdown.indexOf(end, startIndex + start.length)
|
|
57
|
+
if (startIndex === -1 || endIndex === -1) return null
|
|
58
|
+
return markdown.slice(startIndex, endIndex + end.length).trim()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function replaceMarkedBlock(markdown: string, marker: string, replacement: string): string | null {
|
|
62
|
+
const start = `<!-- ethagent:${marker}:start -->`
|
|
63
|
+
const end = `<!-- ethagent:${marker}:end -->`
|
|
64
|
+
const startIndex = markdown.indexOf(start)
|
|
65
|
+
const endIndex = markdown.indexOf(end, startIndex + start.length)
|
|
66
|
+
if (startIndex === -1 || endIndex === -1) return null
|
|
67
|
+
return `${markdown.slice(0, startIndex)}${replacement}${markdown.slice(endIndex + end.length)}`
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function insertAfterFirstHeading(markdown: string, block: string): string {
|
|
71
|
+
const lines = markdown.split(/\r?\n/)
|
|
72
|
+
const headingIndex = lines.findIndex(line => line.startsWith('# '))
|
|
73
|
+
if (headingIndex === -1) return `${block}\n\n${markdown.trimStart()}`
|
|
74
|
+
const before = lines.slice(0, headingIndex + 1)
|
|
75
|
+
const after = lines.slice(headingIndex + 1)
|
|
76
|
+
return [...before, '', block, '', ...after].join('\n')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function ensureTrailingNewline(value: string): string {
|
|
80
|
+
return value.endsWith('\n') ? value : `${value}\n`
|
|
81
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { getConfigDir, type EthagentIdentity } from '../../../storage/config.js'
|
|
3
|
+
import type { ContinuityVaultRef } from './types.js'
|
|
4
|
+
|
|
5
|
+
export function continuityVaultRef(identity: Pick<EthagentIdentity, 'chainId' | 'identityRegistryAddress' | 'agentId' | 'address'>): ContinuityVaultRef {
|
|
6
|
+
const dir = path.join(getConfigDir(), 'continuity', continuityVaultId(identity))
|
|
7
|
+
return {
|
|
8
|
+
dir,
|
|
9
|
+
soulPath: path.join(dir, 'SOUL.md'),
|
|
10
|
+
memoryPath: path.join(dir, 'MEMORY.md'),
|
|
11
|
+
publicSkillsPath: path.join(dir, 'skills.json'),
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function continuityVaultId(identity: Pick<EthagentIdentity, 'chainId' | 'identityRegistryAddress' | 'agentId' | 'address'>): string {
|
|
16
|
+
const chain = identity.chainId?.toString() ?? 'unknown-chain'
|
|
17
|
+
const registry = sanitizePathPart(identity.identityRegistryAddress ?? 'unknown-registry')
|
|
18
|
+
const token = sanitizePathPart(identity.agentId ?? identity.address)
|
|
19
|
+
return `${chain}-${registry}-${token}`
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function sanitizePathPart(value: string): string {
|
|
23
|
+
return value.trim().toLowerCase().replace(/^0x/, '').replace(/[^a-z0-9._-]+/g, '-').slice(0, 120) || 'unknown'
|
|
24
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { atomicWriteText } from '../../../storage/atomicWrite.js'
|
|
2
|
+
import type { EthagentIdentity } from '../../../storage/config.js'
|
|
3
|
+
import type { ContinuityFiles } from '../envelope.js'
|
|
4
|
+
import { defaultContinuityFiles, defaultPublicSkillsJson } from './defaults.js'
|
|
5
|
+
import {
|
|
6
|
+
ensureContinuityFiles,
|
|
7
|
+
ensureContinuityVault,
|
|
8
|
+
ensureTrailingNewline,
|
|
9
|
+
exists,
|
|
10
|
+
readContinuityFiles,
|
|
11
|
+
readOrDefault,
|
|
12
|
+
writeContinuityFiles,
|
|
13
|
+
} from './files.js'
|
|
14
|
+
import { syncGeneratedMarkdown } from './markdown.js'
|
|
15
|
+
import type { ContinuityVaultRef, IdentityMarkdownScaffold } from './types.js'
|
|
16
|
+
|
|
17
|
+
export async function ensureIdentityMarkdownScaffold(
|
|
18
|
+
identity: EthagentIdentity,
|
|
19
|
+
options: { publicSkillsFallback?: string | (() => Promise<string>) } = {},
|
|
20
|
+
): Promise<IdentityMarkdownScaffold> {
|
|
21
|
+
const privateFiles = await ensureContinuityFiles(identity)
|
|
22
|
+
const publicSkills = await ensurePublicSkillsFile(identity, { fallback: options.publicSkillsFallback })
|
|
23
|
+
return {
|
|
24
|
+
...privateFiles,
|
|
25
|
+
'skills.json': publicSkills,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function writeIdentityMarkdownScaffold(
|
|
30
|
+
identity: EthagentIdentity,
|
|
31
|
+
files: IdentityMarkdownScaffold,
|
|
32
|
+
): Promise<ContinuityVaultRef> {
|
|
33
|
+
const ref = await writeContinuityFiles(identity, {
|
|
34
|
+
'SOUL.md': files['SOUL.md'],
|
|
35
|
+
'MEMORY.md': files['MEMORY.md'],
|
|
36
|
+
})
|
|
37
|
+
await writePublicSkillsFile(identity, files['skills.json'])
|
|
38
|
+
return ref
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function syncIdentityMarkdownScaffold(identity: EthagentIdentity): Promise<IdentityMarkdownScaffold> {
|
|
42
|
+
const next = await prepareSyncedIdentityMarkdownScaffold(identity)
|
|
43
|
+
await writeIdentityMarkdownScaffold(identity, next)
|
|
44
|
+
return next
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function prepareSyncedIdentityMarkdownScaffold(identity: EthagentIdentity): Promise<IdentityMarkdownScaffold> {
|
|
48
|
+
await ensureIdentityMarkdownScaffold(identity)
|
|
49
|
+
const privateFiles = await readContinuityFiles(identity)
|
|
50
|
+
const publicSkills = await readPublicSkillsFile(identity)
|
|
51
|
+
const privateDefaults = defaultContinuityFiles(identity)
|
|
52
|
+
const publicDefault = defaultPublicSkillsJson(identity)
|
|
53
|
+
return {
|
|
54
|
+
'SOUL.md': syncGeneratedMarkdown(privateFiles['SOUL.md'], privateDefaults['SOUL.md'], [
|
|
55
|
+
{ marker: 'identity' },
|
|
56
|
+
]),
|
|
57
|
+
'MEMORY.md': syncGeneratedMarkdown(privateFiles['MEMORY.md'], privateDefaults['MEMORY.md'], [
|
|
58
|
+
{ marker: 'identity' },
|
|
59
|
+
]),
|
|
60
|
+
'skills.json': syncSkillsJson(publicSkills, publicDefault),
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function prepareSyncedPublicSkillsJson(identity: EthagentIdentity): Promise<string> {
|
|
65
|
+
await ensurePublicSkillsFile(identity)
|
|
66
|
+
const publicSkills = await readPublicSkillsFile(identity)
|
|
67
|
+
return syncSkillsJson(publicSkills, defaultPublicSkillsJson(identity))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function syncSkillsJson(existing: string, fresh: string): string {
|
|
71
|
+
try {
|
|
72
|
+
const existingParsed = JSON.parse(existing)
|
|
73
|
+
const freshParsed = JSON.parse(fresh)
|
|
74
|
+
const merged = {
|
|
75
|
+
...existingParsed,
|
|
76
|
+
...freshParsed,
|
|
77
|
+
skills: existingParsed.skills || freshParsed.skills,
|
|
78
|
+
inputModes: existingParsed.inputModes || freshParsed.inputModes,
|
|
79
|
+
outputModes: existingParsed.outputModes || freshParsed.outputModes,
|
|
80
|
+
}
|
|
81
|
+
if (!Object.hasOwn(freshParsed, 'imageUrl')) delete merged.imageUrl
|
|
82
|
+
return `${JSON.stringify(merged, null, 2)}\n`
|
|
83
|
+
} catch {
|
|
84
|
+
return fresh
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function ensurePublicSkillsFile(
|
|
89
|
+
identity: EthagentIdentity,
|
|
90
|
+
options: { fallback?: string | (() => Promise<string>) } = {},
|
|
91
|
+
): Promise<string> {
|
|
92
|
+
const ref = await ensureContinuityVault(identity)
|
|
93
|
+
if (await exists(ref.publicSkillsPath)) return readPublicSkillsFile(identity)
|
|
94
|
+
|
|
95
|
+
const fallback = await resolvePublicSkillsFallback(identity, options.fallback)
|
|
96
|
+
await atomicWriteText(ref.publicSkillsPath, ensureTrailingNewline(fallback), { mode: 0o644 })
|
|
97
|
+
return readPublicSkillsFile(identity)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function readPublicSkillsFile(identity: EthagentIdentity): Promise<string> {
|
|
101
|
+
const ref = await ensureContinuityVault(identity)
|
|
102
|
+
return readOrDefault(ref.publicSkillsPath, defaultPublicSkillsJson(identity))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export async function writePublicSkillsFile(identity: EthagentIdentity, content: string): Promise<ContinuityVaultRef> {
|
|
106
|
+
const ref = await ensureContinuityVault(identity)
|
|
107
|
+
await atomicWriteText(ref.publicSkillsPath, ensureTrailingNewline(content), { mode: 0o644 })
|
|
108
|
+
return ref
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function resolvePublicSkillsFallback(
|
|
112
|
+
identity: EthagentIdentity,
|
|
113
|
+
fallback: string | (() => Promise<string>) | undefined,
|
|
114
|
+
): Promise<string> {
|
|
115
|
+
if (typeof fallback === 'string') return fallback
|
|
116
|
+
if (fallback) {
|
|
117
|
+
try {
|
|
118
|
+
return await fallback()
|
|
119
|
+
} catch {
|
|
120
|
+
return defaultPublicSkillsJson(identity)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return defaultPublicSkillsJson(identity)
|
|
124
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import type { EthagentIdentity } from '../../../storage/config.js'
|
|
3
|
+
import type { ContinuityFiles } from '../envelope.js'
|
|
4
|
+
import { continuityVaultRef } from './paths.js'
|
|
5
|
+
import { exists, readContinuityFiles, statIfExists } from './files.js'
|
|
6
|
+
import { readPublicSkillsFile } from './scaffold.js'
|
|
7
|
+
import type { ContinuityPublishState, ContinuitySnapshotContentHashes, ContinuityVaultRef, ContinuityWorkingTreeStatus } from './types.js'
|
|
8
|
+
|
|
9
|
+
export async function continuityVaultStatus(identity: EthagentIdentity): Promise<{ ready: boolean; files: ContinuityVaultRef }> {
|
|
10
|
+
const ref = continuityVaultRef(identity)
|
|
11
|
+
const [soul, memory] = await Promise.all([exists(ref.soulPath), exists(ref.memoryPath)])
|
|
12
|
+
return { ready: soul && memory, files: ref }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function continuityWorkingTreeStatus(
|
|
16
|
+
identity: EthagentIdentity,
|
|
17
|
+
publishedSnapshot?: { contentHashes?: ContinuitySnapshotContentHashes },
|
|
18
|
+
): Promise<ContinuityWorkingTreeStatus> {
|
|
19
|
+
const ref = continuityVaultRef(identity)
|
|
20
|
+
const stats = await Promise.all([
|
|
21
|
+
statIfExists(ref.soulPath),
|
|
22
|
+
statIfExists(ref.memoryPath),
|
|
23
|
+
statIfExists(ref.publicSkillsPath),
|
|
24
|
+
])
|
|
25
|
+
const newestMs = Math.max(0, ...stats.flatMap(stat => stat ? [stat.mtimeMs] : []))
|
|
26
|
+
const ready = Boolean(stats[0] && stats[1])
|
|
27
|
+
const localContentHashes = ready
|
|
28
|
+
? await localContinuitySnapshotContentHashes(identity).catch(() => undefined)
|
|
29
|
+
: undefined
|
|
30
|
+
const publishedContentHashes = publishedSnapshot?.contentHashes
|
|
31
|
+
const publishState: ContinuityPublishState = !ready
|
|
32
|
+
? 'not-restored'
|
|
33
|
+
: !identity.backup?.cid
|
|
34
|
+
? 'not-published'
|
|
35
|
+
: !localContentHashes || !publishedContentHashes
|
|
36
|
+
? 'verify-needed'
|
|
37
|
+
: equalContinuitySnapshotHashes(localContentHashes, publishedContentHashes)
|
|
38
|
+
? 'published'
|
|
39
|
+
: 'local-changes'
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
ready,
|
|
43
|
+
...(newestMs > 0 ? { newestLocalChangeAt: new Date(newestMs).toISOString() } : {}),
|
|
44
|
+
localChangedAfterBackup: publishState === 'local-changes',
|
|
45
|
+
publishState,
|
|
46
|
+
...(localContentHashes ? { localContentHashes } : {}),
|
|
47
|
+
...(publishedContentHashes ? { publishedContentHashes } : {}),
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function localContinuitySnapshotContentHashes(
|
|
52
|
+
identity: EthagentIdentity,
|
|
53
|
+
): Promise<ContinuitySnapshotContentHashes> {
|
|
54
|
+
const privateFiles = await readContinuityFiles(identity)
|
|
55
|
+
const publicSkills = await readPublicSkillsFile(identity)
|
|
56
|
+
return continuitySnapshotContentHashes(privateFiles, publicSkills)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function continuitySnapshotContentHashes(
|
|
60
|
+
privateFiles: ContinuityFiles,
|
|
61
|
+
publicSkills: string,
|
|
62
|
+
): ContinuitySnapshotContentHashes {
|
|
63
|
+
return {
|
|
64
|
+
'SOUL.md': hashContinuitySnapshotContent(privateFiles['SOUL.md']),
|
|
65
|
+
'MEMORY.md': hashContinuitySnapshotContent(privateFiles['MEMORY.md']),
|
|
66
|
+
'skills.json': hashContinuitySnapshotContent(publicSkills),
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function equalContinuitySnapshotHashes(
|
|
71
|
+
a: ContinuitySnapshotContentHashes,
|
|
72
|
+
b: ContinuitySnapshotContentHashes,
|
|
73
|
+
): boolean {
|
|
74
|
+
return a['SOUL.md'] === b['SOUL.md']
|
|
75
|
+
&& a['MEMORY.md'] === b['MEMORY.md']
|
|
76
|
+
&& a['skills.json'] === b['skills.json']
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function hashContinuitySnapshotContent(value: string): string {
|
|
80
|
+
return createHash('sha256').update(normalizeSnapshotContent(value), 'utf8').digest('hex')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function normalizeSnapshotContent(value: string): string {
|
|
84
|
+
const normalized = value.replace(/\r\n?/g, '\n')
|
|
85
|
+
return normalized.endsWith('\n') ? normalized : `${normalized}\n`
|
|
86
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ContinuityFiles } from '../envelope.js'
|
|
2
|
+
|
|
3
|
+
export const PRIVATE_CONTINUITY_FILES = ['SOUL.md', 'MEMORY.md'] as const
|
|
4
|
+
export type PrivateContinuityFile = (typeof PRIVATE_CONTINUITY_FILES)[number]
|
|
5
|
+
|
|
6
|
+
export type ContinuityVaultRef = {
|
|
7
|
+
dir: string
|
|
8
|
+
soulPath: string
|
|
9
|
+
memoryPath: string
|
|
10
|
+
publicSkillsPath: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type IdentityMarkdownScaffold = ContinuityFiles & {
|
|
14
|
+
'skills.json': string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ContinuitySnapshotFile = PrivateContinuityFile | 'skills.json'
|
|
18
|
+
export type ContinuitySnapshotContentHashes = Record<ContinuitySnapshotFile, string>
|
|
19
|
+
export type ContinuityPublishState = 'not-restored' | 'not-published' | 'verify-needed' | 'local-changes' | 'published'
|
|
20
|
+
export type ContinuityWorkingTreeStatus = {
|
|
21
|
+
ready: boolean
|
|
22
|
+
newestLocalChangeAt?: string
|
|
23
|
+
localChangedAfterBackup: boolean
|
|
24
|
+
publishState: ContinuityPublishState
|
|
25
|
+
localContentHashes?: ContinuitySnapshotContentHashes
|
|
26
|
+
publishedContentHashes?: ContinuitySnapshotContentHashes
|
|
27
|
+
}
|