ethagent 1.1.2 → 2.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/LICENSE +21 -21
- package/README.md +124 -32
- package/package.json +8 -3
- package/src/app/FirstRun.tsx +190 -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 +35 -15
- package/src/chat/ContextLimitView.tsx +4 -4
- package/src/chat/ContinuityEditReviewView.tsx +10 -22
- 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/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 +4 -2
- 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 -817
- 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/{screens → flows/continuity}/RecoveryConfirmScreen.tsx +28 -19
- 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 +23 -44
- 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 +164 -99
- 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 +20 -8
- package/src/ui/Spinner.tsx +38 -3
- package/src/ui/Surface.tsx +2 -2
- package/src/ui/TextInput.tsx +63 -20
- 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 -371
- package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -156
- package/src/identity/hub/screens/EditProfileFlow.tsx +0 -146
- package/src/identity/hub/screens/IdentitySummary.tsx +0 -106
- package/src/identity/hub/screens/MenuScreen.tsx +0 -117
- 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
|
@@ -1,40 +1,14 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
1
|
import path from 'node:path'
|
|
3
2
|
import { atomicWriteText } from '../../storage/atomicWrite.js'
|
|
4
|
-
import type { EthagentConfig
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from './storage.js'
|
|
3
|
+
import type { EthagentConfig } from '../../storage/config.js'
|
|
4
|
+
import { ensureContinuityVault } from './storage.js'
|
|
5
|
+
import { ensureTrailingNewline } from './storage/files.js'
|
|
6
|
+
import { applyPrivateContinuityEdit } from './privateEdit/apply.js'
|
|
7
|
+
import { renderPrivateContinuityDiff } from './privateEdit/diff.js'
|
|
8
|
+
import { privateContinuityPath, readPrivateContinuityFile } from './privateEdit/files.js'
|
|
9
|
+
import type { PreparedPrivateContinuityEdit, PrivateContinuityEditInput } from './privateEdit/types.js'
|
|
12
10
|
|
|
13
|
-
export type PrivateContinuityEditInput
|
|
14
|
-
file: PrivateContinuityFile
|
|
15
|
-
oldText?: string
|
|
16
|
-
newText?: string
|
|
17
|
-
appendToSection?: string
|
|
18
|
-
appendText?: string
|
|
19
|
-
replaceAll?: boolean
|
|
20
|
-
replaceWholeFile?: boolean
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type PreparedPrivateContinuityEdit = {
|
|
24
|
-
identity: EthagentIdentity
|
|
25
|
-
file: PrivateContinuityFile
|
|
26
|
-
fullPath: string
|
|
27
|
-
relativePath: string
|
|
28
|
-
directoryPath: string
|
|
29
|
-
existedBefore: boolean
|
|
30
|
-
previousContent: string
|
|
31
|
-
before: string
|
|
32
|
-
after: string
|
|
33
|
-
previewBefore: string
|
|
34
|
-
previewAfter: string
|
|
35
|
-
changeSummary: string
|
|
36
|
-
diff: string
|
|
37
|
-
}
|
|
11
|
+
export type { PreparedPrivateContinuityEdit, PrivateContinuityEditInput } from './privateEdit/types.js'
|
|
38
12
|
|
|
39
13
|
export async function preparePrivateContinuityEdit(
|
|
40
14
|
input: PrivateContinuityEditInput,
|
|
@@ -42,7 +16,7 @@ export async function preparePrivateContinuityEdit(
|
|
|
42
16
|
): Promise<PreparedPrivateContinuityEdit> {
|
|
43
17
|
const identity = config?.identity
|
|
44
18
|
if (!identity) {
|
|
45
|
-
throw new Error('
|
|
19
|
+
throw new Error('No active identity; create or load an identity before proposing private continuity edits')
|
|
46
20
|
}
|
|
47
21
|
|
|
48
22
|
const fullPath = privateContinuityPath(identity, input.file)
|
|
@@ -53,7 +27,7 @@ export async function preparePrivateContinuityEdit(
|
|
|
53
27
|
identity,
|
|
54
28
|
file: input.file,
|
|
55
29
|
fullPath,
|
|
56
|
-
relativePath:
|
|
30
|
+
relativePath: 'identity-vault/' + input.file,
|
|
57
31
|
directoryPath: path.dirname(fullPath),
|
|
58
32
|
existedBefore: existing.existedBefore,
|
|
59
33
|
previousContent: existing.existedBefore ? existing.content : '',
|
|
@@ -66,269 +40,7 @@ export async function preparePrivateContinuityEdit(
|
|
|
66
40
|
}
|
|
67
41
|
}
|
|
68
42
|
|
|
69
|
-
function applyPrivateContinuityEdit(input: PrivateContinuityEditInput, before: string, identity: EthagentIdentity) {
|
|
70
|
-
if (input.replaceWholeFile) {
|
|
71
|
-
throw new Error('private continuity files must be edited in place; whole-file replacement is disabled')
|
|
72
|
-
}
|
|
73
|
-
if (input.appendToSection || input.appendText) {
|
|
74
|
-
if (!input.appendToSection?.trim()) throw new Error('appendToSection is required for append edits')
|
|
75
|
-
if (!input.appendText?.trim()) throw new Error('appendText is required for append edits')
|
|
76
|
-
if (input.oldText || input.newText !== undefined) {
|
|
77
|
-
throw new Error('use either appendToSection+appendText or oldText+newText, not both')
|
|
78
|
-
}
|
|
79
|
-
return appendToMarkdownSection(identity, input.file, before, input.appendToSection, input.appendText)
|
|
80
|
-
}
|
|
81
|
-
if (!input.oldText?.trim()) {
|
|
82
|
-
throw new Error('oldText is required; private continuity edits must patch existing scaffold text')
|
|
83
|
-
}
|
|
84
|
-
if (input.newText === undefined) {
|
|
85
|
-
throw new Error('newText is required for targeted private continuity edits')
|
|
86
|
-
}
|
|
87
|
-
return applyRequestedEdit(
|
|
88
|
-
input.file,
|
|
89
|
-
before,
|
|
90
|
-
input.oldText,
|
|
91
|
-
input.newText,
|
|
92
|
-
input.replaceAll ?? false,
|
|
93
|
-
false,
|
|
94
|
-
)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
43
|
export async function writePreparedPrivateContinuityEdit(edit: PreparedPrivateContinuityEdit): Promise<void> {
|
|
98
44
|
await ensureContinuityVault(edit.identity)
|
|
99
45
|
await atomicWriteText(edit.fullPath, ensureTrailingNewline(edit.after), { mode: 0o600 })
|
|
100
46
|
}
|
|
101
|
-
|
|
102
|
-
function privateContinuityPath(identity: EthagentIdentity, file: PrivateContinuityFile): string {
|
|
103
|
-
const ref = continuityVaultRef(identity)
|
|
104
|
-
return file === 'SOUL.md' ? ref.soulPath : ref.memoryPath
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
async function readPrivateContinuityFile(
|
|
108
|
-
identity: EthagentIdentity,
|
|
109
|
-
file: PrivateContinuityFile,
|
|
110
|
-
fullPath: string,
|
|
111
|
-
): Promise<{ content: string; existedBefore: boolean }> {
|
|
112
|
-
try {
|
|
113
|
-
return { content: await fs.readFile(fullPath, 'utf8'), existedBefore: true }
|
|
114
|
-
} catch (err: unknown) {
|
|
115
|
-
if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
116
|
-
return { content: defaultContinuityFiles(identity)[file], existedBefore: false }
|
|
117
|
-
}
|
|
118
|
-
throw err
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function renderPrivateContinuityDiff(file: PrivateContinuityFile, before: string, after: string): string {
|
|
123
|
-
if (before === after) return '(no changes)'
|
|
124
|
-
const changedLines = changedMarkdownLines(before, after)
|
|
125
|
-
const lines = [
|
|
126
|
-
`--- ${file}`,
|
|
127
|
-
`+++ ${file}`,
|
|
128
|
-
...(changedLines.length > 0 ? changedLines : ['(only whitespace or line ending changes)']),
|
|
129
|
-
]
|
|
130
|
-
const diff = lines.join('\n')
|
|
131
|
-
return diff.length <= 2400 ? diff : `${diff.slice(0, 2397)}...`
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function changedMarkdownLines(before: string, after: string): string[] {
|
|
135
|
-
const beforeLines = markdownLines(before)
|
|
136
|
-
const afterLines = markdownLines(after)
|
|
137
|
-
const lengths = lcsLengths(beforeLines, afterLines)
|
|
138
|
-
const changed: string[] = []
|
|
139
|
-
let beforeIndex = 0
|
|
140
|
-
let afterIndex = 0
|
|
141
|
-
|
|
142
|
-
while (beforeIndex < beforeLines.length && afterIndex < afterLines.length) {
|
|
143
|
-
if (beforeLines[beforeIndex] === afterLines[afterIndex]) {
|
|
144
|
-
beforeIndex += 1
|
|
145
|
-
afterIndex += 1
|
|
146
|
-
continue
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const deleteScore = lengths[beforeIndex + 1]![afterIndex]!
|
|
150
|
-
const insertScore = lengths[beforeIndex]![afterIndex + 1]!
|
|
151
|
-
const deleteRevealsMatch = beforeLines[beforeIndex + 1] === afterLines[afterIndex]
|
|
152
|
-
const insertRevealsMatch = beforeLines[beforeIndex] === afterLines[afterIndex + 1]
|
|
153
|
-
|
|
154
|
-
if (insertRevealsMatch && insertScore >= deleteScore) {
|
|
155
|
-
changed.push(`+${afterLines[afterIndex]}`)
|
|
156
|
-
afterIndex += 1
|
|
157
|
-
} else if (deleteRevealsMatch && deleteScore >= insertScore) {
|
|
158
|
-
changed.push(`-${beforeLines[beforeIndex]}`)
|
|
159
|
-
beforeIndex += 1
|
|
160
|
-
} else if (deleteScore >= insertScore) {
|
|
161
|
-
changed.push(`-${beforeLines[beforeIndex]}`)
|
|
162
|
-
beforeIndex += 1
|
|
163
|
-
} else {
|
|
164
|
-
changed.push(`+${afterLines[afterIndex]}`)
|
|
165
|
-
afterIndex += 1
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
while (beforeIndex < beforeLines.length) {
|
|
170
|
-
changed.push(`-${beforeLines[beforeIndex]}`)
|
|
171
|
-
beforeIndex += 1
|
|
172
|
-
}
|
|
173
|
-
while (afterIndex < afterLines.length) {
|
|
174
|
-
changed.push(`+${afterLines[afterIndex]}`)
|
|
175
|
-
afterIndex += 1
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return changed
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function lcsLengths(beforeLines: string[], afterLines: string[]): number[][] {
|
|
182
|
-
const lengths = Array.from(
|
|
183
|
-
{ length: beforeLines.length + 1 },
|
|
184
|
-
() => Array<number>(afterLines.length + 1).fill(0),
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
for (let beforeIndex = beforeLines.length - 1; beforeIndex >= 0; beforeIndex -= 1) {
|
|
188
|
-
for (let afterIndex = afterLines.length - 1; afterIndex >= 0; afterIndex -= 1) {
|
|
189
|
-
lengths[beforeIndex]![afterIndex] = beforeLines[beforeIndex] === afterLines[afterIndex]
|
|
190
|
-
? lengths[beforeIndex + 1]![afterIndex + 1]! + 1
|
|
191
|
-
: Math.max(lengths[beforeIndex + 1]![afterIndex]!, lengths[beforeIndex]![afterIndex + 1]!)
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return lengths
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function appendToMarkdownSection(
|
|
199
|
-
identity: EthagentIdentity,
|
|
200
|
-
file: PrivateContinuityFile,
|
|
201
|
-
before: string,
|
|
202
|
-
section: string,
|
|
203
|
-
appendText: string,
|
|
204
|
-
) {
|
|
205
|
-
const heading = normalizeSectionHeading(section)
|
|
206
|
-
let working = before
|
|
207
|
-
let repairedMissingSection = false
|
|
208
|
-
let lines = working.split(/\r?\n/)
|
|
209
|
-
let bounds = findMarkdownSectionBounds(lines, heading)
|
|
210
|
-
if (!bounds) {
|
|
211
|
-
const repaired = insertDefaultScaffoldSection(identity, file, before, heading)
|
|
212
|
-
if (!repaired) {
|
|
213
|
-
throw new Error(`section "${section}" was not found in ${file}; target an existing scaffold section`)
|
|
214
|
-
}
|
|
215
|
-
working = repaired
|
|
216
|
-
repairedMissingSection = true
|
|
217
|
-
lines = working.split(/\r?\n/)
|
|
218
|
-
bounds = findMarkdownSectionBounds(lines, heading)
|
|
219
|
-
}
|
|
220
|
-
if (!bounds) {
|
|
221
|
-
throw new Error(`section "${section}" was not found in ${file}; target an existing scaffold section`)
|
|
222
|
-
}
|
|
223
|
-
const { start, end: insertAt } = bounds
|
|
224
|
-
const prefix = lines.slice(0, insertAt).join('\n').replace(/\s+$/g, '')
|
|
225
|
-
const suffix = insertAt >= lines.length ? '' : lines.slice(insertAt).join('\n').replace(/^\s+/g, '')
|
|
226
|
-
const normalizedAppend = ensureTrailingNewline(appendText.trim())
|
|
227
|
-
const after = ensureTrailingNewline(suffix
|
|
228
|
-
? `${prefix}\n${normalizedAppend}\n${suffix}`
|
|
229
|
-
: `${prefix}\n${normalizedAppend}`)
|
|
230
|
-
const afterLines = after.split(/\r?\n/)
|
|
231
|
-
const afterBounds = findMarkdownSectionBounds(afterLines, heading)
|
|
232
|
-
return {
|
|
233
|
-
before,
|
|
234
|
-
after,
|
|
235
|
-
summary: repairedMissingSection
|
|
236
|
-
? `repair ${heading} section and append to ${heading} in ${file}`
|
|
237
|
-
: `append to ${heading} in ${file}`,
|
|
238
|
-
previewBefore: repairedMissingSection
|
|
239
|
-
? `section "${heading}" was missing in ${file}; approval will add the scaffold section before appending.`
|
|
240
|
-
: previewText(sectionPreview(lines, start, insertAt)),
|
|
241
|
-
previewAfter: previewText(afterBounds
|
|
242
|
-
? sectionPreview(afterLines, afterBounds.start, afterBounds.end)
|
|
243
|
-
: normalizedAppend),
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function insertDefaultScaffoldSection(
|
|
248
|
-
identity: EthagentIdentity,
|
|
249
|
-
file: PrivateContinuityFile,
|
|
250
|
-
before: string,
|
|
251
|
-
heading: string,
|
|
252
|
-
): string | null {
|
|
253
|
-
const defaults = defaultContinuityFiles(identity)[file]
|
|
254
|
-
const defaultSection = extractMarkdownSection(defaults, heading)
|
|
255
|
-
if (!defaultSection) return null
|
|
256
|
-
|
|
257
|
-
const defaultHeadings = markdownSectionHeadings(defaults)
|
|
258
|
-
const targetIndex = defaultHeadings.indexOf(heading)
|
|
259
|
-
if (targetIndex === -1) return null
|
|
260
|
-
|
|
261
|
-
const lines = before.split(/\r?\n/)
|
|
262
|
-
const followingHeadings = new Set(defaultHeadings.slice(targetIndex + 1))
|
|
263
|
-
const followingIndex = lines.findIndex(line => followingHeadings.has(normalizeSectionHeading(line)))
|
|
264
|
-
if (followingIndex !== -1) {
|
|
265
|
-
return insertSectionAtLine(before, followingIndex, defaultSection)
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const previousHeadings = new Set(defaultHeadings.slice(0, targetIndex))
|
|
269
|
-
let insertAfterPrevious: number | null = null
|
|
270
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
271
|
-
if (!previousHeadings.has(normalizeSectionHeading(lines[index] ?? ''))) continue
|
|
272
|
-
const bounds = findMarkdownSectionBounds(lines, normalizeSectionHeading(lines[index] ?? ''))
|
|
273
|
-
if (bounds && bounds.end > (insertAfterPrevious ?? -1)) insertAfterPrevious = bounds.end
|
|
274
|
-
}
|
|
275
|
-
if (insertAfterPrevious !== null) {
|
|
276
|
-
return insertSectionAtLine(before, insertAfterPrevious, defaultSection)
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const firstHeading = lines.findIndex(line => /^#\s+/.test(line.trim()))
|
|
280
|
-
return insertSectionAtLine(before, firstHeading === -1 ? 0 : firstHeading + 1, defaultSection)
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function insertSectionAtLine(markdown: string, lineIndex: number, section: string): string {
|
|
284
|
-
const lines = markdown.split(/\r?\n/)
|
|
285
|
-
const before = lines.slice(0, lineIndex).join('\n').replace(/\s+$/g, '')
|
|
286
|
-
const after = lines.slice(lineIndex).join('\n').replace(/^\s+/g, '')
|
|
287
|
-
const block = section.trim()
|
|
288
|
-
if (!before) return ensureTrailingNewline(after ? `${block}\n\n${after}` : block)
|
|
289
|
-
return ensureTrailingNewline(after ? `${before}\n\n${block}\n\n${after}` : `${before}\n\n${block}`)
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function extractMarkdownSection(markdown: string, heading: string): string | null {
|
|
293
|
-
const lines = markdown.split(/\r?\n/)
|
|
294
|
-
const bounds = findMarkdownSectionBounds(lines, heading)
|
|
295
|
-
if (!bounds) return null
|
|
296
|
-
return lines.slice(bounds.start, bounds.end).join('\n').trim()
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function markdownSectionHeadings(markdown: string): string[] {
|
|
300
|
-
return markdown
|
|
301
|
-
.split(/\r?\n/)
|
|
302
|
-
.filter(line => /^##\s+/.test(line.trim()))
|
|
303
|
-
.map(normalizeSectionHeading)
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function findMarkdownSectionBounds(lines: string[], heading: string): { start: number; end: number } | null {
|
|
307
|
-
const start = lines.findIndex(line => normalizeSectionHeading(line) === heading && /^#{1,6}\s+/.test(line.trim()))
|
|
308
|
-
if (start === -1) return null
|
|
309
|
-
const nextSection = lines.findIndex((line, index) => index > start && /^##\s+/.test(line.trim()))
|
|
310
|
-
return { start, end: nextSection === -1 ? lines.length : nextSection }
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function normalizeSectionHeading(value: string): string {
|
|
314
|
-
return value.trim().replace(/^#+\s*/, '').trim()
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function sectionPreview(lines: string[], start: number, end: number): string {
|
|
318
|
-
return lines.slice(start, Math.min(end, start + 8)).join('\n')
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function markdownLines(value: string): string[] {
|
|
322
|
-
const lines = value.split(/\r?\n/)
|
|
323
|
-
if (lines[lines.length - 1] === '') lines.pop()
|
|
324
|
-
return lines
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
function ensureTrailingNewline(value: string): string {
|
|
328
|
-
return value.endsWith('\n') ? value : `${value}\n`
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
function previewText(text: string, max = 700): string {
|
|
332
|
-
if (text.length <= max) return text
|
|
333
|
-
return `${text.slice(0, max - 3)}...`
|
|
334
|
-
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EthagentIdentity } from '../../storage/config.js'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
type PublicSkill = {
|
|
4
4
|
id: string
|
|
5
5
|
name: string
|
|
6
6
|
description: string
|
|
@@ -8,7 +8,7 @@ export type PublicSkill = {
|
|
|
8
8
|
outputModes: string[]
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
type PublicSkillsProfile = {
|
|
12
12
|
name: string
|
|
13
13
|
description: string
|
|
14
14
|
version: string
|
|
@@ -16,14 +16,13 @@ export type PublicSkillsProfile = {
|
|
|
16
16
|
skills: PublicSkill[]
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
type AgentCard = {
|
|
20
20
|
name: string
|
|
21
21
|
description: string
|
|
22
22
|
version: string
|
|
23
23
|
protocolVersion: string
|
|
24
|
-
url
|
|
24
|
+
url?: string
|
|
25
25
|
image?: string
|
|
26
|
-
iconUrl?: string
|
|
27
26
|
defaultInputModes: string[]
|
|
28
27
|
defaultOutputModes: string[]
|
|
29
28
|
capabilities: {
|
|
@@ -93,7 +92,7 @@ export function renderPublicSkillsJson(profile: PublicSkillsProfile): string {
|
|
|
93
92
|
...(profile.imageUrl ? { imageUrl: profile.imageUrl } : {}),
|
|
94
93
|
inputModes,
|
|
95
94
|
outputModes,
|
|
96
|
-
boundary: 'Public discovery
|
|
95
|
+
boundary: 'Public discovery only. This is not executable code, private memory, or a skill installation manifest.',
|
|
97
96
|
capabilities: {
|
|
98
97
|
softwareEngineering: true,
|
|
99
98
|
workspaceTools: 'permissioned',
|
|
@@ -134,7 +133,7 @@ export function renderPublicSkillsJson(profile: PublicSkillsProfile): string {
|
|
|
134
133
|
return `${JSON.stringify(summary, null, 2)}\n`
|
|
135
134
|
}
|
|
136
135
|
|
|
137
|
-
export function createAgentCard(profile: PublicSkillsProfile, url
|
|
136
|
+
export function createAgentCard(profile: PublicSkillsProfile, url?: string): AgentCard {
|
|
138
137
|
const inputModes = unique(profile.skills.flatMap(skill => skill.inputModes))
|
|
139
138
|
const outputModes = unique(profile.skills.flatMap(skill => skill.outputModes))
|
|
140
139
|
return {
|
|
@@ -142,8 +141,8 @@ export function createAgentCard(profile: PublicSkillsProfile, url = 'ipfs://pend
|
|
|
142
141
|
description: profile.description,
|
|
143
142
|
version: profile.version,
|
|
144
143
|
protocolVersion: '0.2.6',
|
|
145
|
-
url,
|
|
146
|
-
...(profile.imageUrl ? { image: profile.imageUrl
|
|
144
|
+
...(url ? { url } : {}),
|
|
145
|
+
...(profile.imageUrl ? { image: profile.imageUrl } : {}),
|
|
147
146
|
defaultInputModes: inputModes.length ? inputModes : ['text/markdown'],
|
|
148
147
|
defaultOutputModes: outputModes.length ? outputModes : ['text/markdown'],
|
|
149
148
|
capabilities: {
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
type ContinuitySnapshotContentHashes,
|
|
10
10
|
} from './storage.js'
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
type PublishedContinuitySnapshot = {
|
|
13
13
|
version: 1
|
|
14
14
|
id: string
|
|
15
15
|
createdAt: string
|
|
@@ -30,12 +30,12 @@ export type PublishedContinuitySnapshot = {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
type RecordPublishedContinuitySnapshotInput = {
|
|
34
34
|
identity: EthagentIdentity
|
|
35
35
|
label?: string
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
function publishedContinuitySnapshotsPath(identity: EthagentIdentity): string {
|
|
39
39
|
return path.join(continuityVaultRef(identity).dir, '.published-snapshots.jsonl')
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -88,10 +88,10 @@ export async function updatePublishedContinuitySnapshotContentHashes(
|
|
|
88
88
|
const index = snapshots.findIndex(item => item.cid === cid)
|
|
89
89
|
if (index === -1) {
|
|
90
90
|
const base = current.find(item => item.cid === cid)
|
|
91
|
-
if (!base) throw new Error('
|
|
92
|
-
snapshots.push({ ...base, contentHashes })
|
|
91
|
+
if (!base) throw new Error('Published snapshot was not found')
|
|
92
|
+
snapshots.push(refreshPublishedSnapshotSidecars({ ...base, contentHashes }, identity))
|
|
93
93
|
} else {
|
|
94
|
-
snapshots[index] = { ...snapshots[index]!, contentHashes }
|
|
94
|
+
snapshots[index] = refreshPublishedSnapshotSidecars({ ...snapshots[index]!, contentHashes }, identity)
|
|
95
95
|
}
|
|
96
96
|
await atomicWriteText(
|
|
97
97
|
publishedContinuitySnapshotsPath(identity),
|
|
@@ -135,6 +135,17 @@ function enrichPublishedSnapshot(
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
function refreshPublishedSnapshotSidecars(
|
|
139
|
+
snapshot: PublishedContinuitySnapshot,
|
|
140
|
+
identity: EthagentIdentity,
|
|
141
|
+
): PublishedContinuitySnapshot {
|
|
142
|
+
return {
|
|
143
|
+
...snapshot,
|
|
144
|
+
...(identity.publicSkills?.cid ? { publicSkillsCid: identity.publicSkills.cid } : {}),
|
|
145
|
+
...(identity.publicSkills?.agentCardCid ? { agentCardCid: identity.publicSkills.agentCardCid } : {}),
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
138
149
|
async function readPublishedContinuitySnapshotFile(identity: EthagentIdentity): Promise<PublishedContinuitySnapshot[]> {
|
|
139
150
|
let raw: string
|
|
140
151
|
try {
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { EthagentIdentity } from '../../../storage/config.js'
|
|
2
|
+
import type { ContinuityAgentSnapshot, ContinuityFiles } from '../envelope.js'
|
|
3
|
+
import { defaultPublicSkillsProfile, renderPublicSkillsJson } from '../publicSkills.js'
|
|
4
|
+
import { renderPrivateIdentityBlock } from './markdown.js'
|
|
5
|
+
|
|
6
|
+
export function continuityAgentSnapshot(identity: EthagentIdentity): ContinuityAgentSnapshot {
|
|
7
|
+
const state = identity.state ?? {}
|
|
8
|
+
return {
|
|
9
|
+
...(identity.chainId ? { chainId: identity.chainId } : {}),
|
|
10
|
+
...(identity.identityRegistryAddress ? { identityRegistryAddress: identity.identityRegistryAddress } : {}),
|
|
11
|
+
...(identity.agentId ? { agentId: identity.agentId } : {}),
|
|
12
|
+
...(identity.agentUri ? { agentUri: identity.agentUri } : {}),
|
|
13
|
+
...(identity.metadataCid ? { metadataCid: identity.metadataCid } : {}),
|
|
14
|
+
...(typeof state.name === 'string' ? { name: state.name } : {}),
|
|
15
|
+
...(typeof state.description === 'string' ? { description: state.description } : {}),
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function defaultContinuityFiles(identity: EthagentIdentity, now = new Date()): ContinuityFiles {
|
|
20
|
+
const owner = identity.ownerAddress ?? identity.address
|
|
21
|
+
const created = now.toISOString().slice(0, 10)
|
|
22
|
+
const identityBlock = renderPrivateIdentityBlock({
|
|
23
|
+
owner,
|
|
24
|
+
token: identity.agentId ? `#${identity.agentId}` : 'pending registration',
|
|
25
|
+
chainId: identity.chainId ? identity.chainId.toString() : 'unknown',
|
|
26
|
+
registry: identity.identityRegistryAddress ?? 'unknown',
|
|
27
|
+
})
|
|
28
|
+
return {
|
|
29
|
+
'SOUL.md': [
|
|
30
|
+
'# SOUL.md',
|
|
31
|
+
'',
|
|
32
|
+
identityBlock,
|
|
33
|
+
'',
|
|
34
|
+
'## Persona',
|
|
35
|
+
'',
|
|
36
|
+
'- Describe the private agent persona, voice, and collaboration style.',
|
|
37
|
+
'- Keep standing behavior that should survive model switches and device restores.',
|
|
38
|
+
'- Prefer stable guidance over session-specific preferences.',
|
|
39
|
+
'',
|
|
40
|
+
'## Operating Principles',
|
|
41
|
+
'',
|
|
42
|
+
'- Record durable values, decision preferences, and owner-approved working principles.',
|
|
43
|
+
'- Keep implementation-specific facts in MEMORY.md unless they define behavior.',
|
|
44
|
+
'',
|
|
45
|
+
'## Private Instructions',
|
|
46
|
+
'',
|
|
47
|
+
'- Keep owner-specific standing instructions in this file.',
|
|
48
|
+
'- Do not share this file directly; save it via the Identity Hub encrypted snapshot.',
|
|
49
|
+
'- Public capabilities belong in skills.json.',
|
|
50
|
+
'',
|
|
51
|
+
'## Boundaries',
|
|
52
|
+
'',
|
|
53
|
+
'- Record private behavioral limits and owner-approved constraints here.',
|
|
54
|
+
'- Do not store seed phrases, private keys, raw wallet signatures, or API keys.',
|
|
55
|
+
'- Do not place public delegation claims here; keep them in skills.json.',
|
|
56
|
+
'',
|
|
57
|
+
'## Maintenance Rules',
|
|
58
|
+
'',
|
|
59
|
+
'- Keep the generated Agent Identity block intact; edit owner-authored sections below it.',
|
|
60
|
+
'- Do not duplicate the mutable public agent name here; it lives in the token URI and Agent Card.',
|
|
61
|
+
'- Move factual project memory to MEMORY.md when it is not persona or instruction material.',
|
|
62
|
+
'- Revise or remove stale guidance instead of accumulating contradictions.',
|
|
63
|
+
'',
|
|
64
|
+
'## Change Notes',
|
|
65
|
+
'',
|
|
66
|
+
'- Add dated notes when the persona or long-lived private guidance changes.',
|
|
67
|
+
'',
|
|
68
|
+
`Created: ${created}`,
|
|
69
|
+
].join('\n') + '\n',
|
|
70
|
+
'MEMORY.md': [
|
|
71
|
+
'# MEMORY.md',
|
|
72
|
+
'',
|
|
73
|
+
identityBlock,
|
|
74
|
+
'',
|
|
75
|
+
'## Durable User Preferences',
|
|
76
|
+
'',
|
|
77
|
+
'- Add long-lived owner preferences that should survive across sessions and model switches.',
|
|
78
|
+
'',
|
|
79
|
+
'## Project Context',
|
|
80
|
+
'',
|
|
81
|
+
'- Add stable project facts, repo conventions, and active workstreams.',
|
|
82
|
+
'',
|
|
83
|
+
'## Decisions and Rationale',
|
|
84
|
+
'',
|
|
85
|
+
'- Record important decisions and why they were made.',
|
|
86
|
+
'',
|
|
87
|
+
'## Facts to Revalidate',
|
|
88
|
+
'',
|
|
89
|
+
'- Add time-sensitive facts that should be checked before reuse, with dates or source context when available.',
|
|
90
|
+
'',
|
|
91
|
+
'## Maintenance Rules',
|
|
92
|
+
'',
|
|
93
|
+
'- Prefer stable facts, preferences, and decisions over chat transcripts.',
|
|
94
|
+
'- Do not duplicate the mutable public agent name here; it lives in the token URI and Agent Card.',
|
|
95
|
+
'- Add dates or source context when a note may become stale or environment-specific.',
|
|
96
|
+
'- Remove or rewrite stale memory instead of accumulating contradictions.',
|
|
97
|
+
'',
|
|
98
|
+
'## Boundaries',
|
|
99
|
+
'',
|
|
100
|
+
'- Do not store seed phrases, private keys, raw wallet signatures, or API keys.',
|
|
101
|
+
'- Do not store secrets unless the user explicitly asks and the risk is clear.',
|
|
102
|
+
'- Keep public capabilities in skills.json.',
|
|
103
|
+
'',
|
|
104
|
+
`Created: ${created}`,
|
|
105
|
+
].join('\n') + '\n',
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function defaultPublicSkillsJson(identity: EthagentIdentity): string {
|
|
110
|
+
return renderPublicSkillsJson(defaultPublicSkillsProfile(identity))
|
|
111
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import fs from 'node:fs/promises'
|
|
2
|
+
import { atomicWriteText } from '../../../storage/atomicWrite.js'
|
|
3
|
+
import type { EthagentIdentity } from '../../../storage/config.js'
|
|
4
|
+
import type { ContinuityFiles } from '../envelope.js'
|
|
5
|
+
import { defaultContinuityFiles } from './defaults.js'
|
|
6
|
+
import { continuityVaultRef } from './paths.js'
|
|
7
|
+
import type { ContinuityVaultRef } from './types.js'
|
|
8
|
+
|
|
9
|
+
export async function ensureContinuityVault(identity: EthagentIdentity): Promise<ContinuityVaultRef> {
|
|
10
|
+
const ref = continuityVaultRef(identity)
|
|
11
|
+
await fs.mkdir(ref.dir, { recursive: true, mode: 0o700 })
|
|
12
|
+
return ref
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function ensureContinuityFiles(identity: EthagentIdentity): Promise<ContinuityFiles> {
|
|
16
|
+
const ref = await ensureContinuityVault(identity)
|
|
17
|
+
const defaults = defaultContinuityFiles(identity)
|
|
18
|
+
await writeMissingPrivateFile(ref.soulPath, defaults['SOUL.md'])
|
|
19
|
+
await writeMissingPrivateFile(ref.memoryPath, defaults['MEMORY.md'])
|
|
20
|
+
return readContinuityFiles(identity)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function readContinuityFiles(identity: EthagentIdentity): Promise<ContinuityFiles> {
|
|
24
|
+
const ref = await ensureContinuityVault(identity)
|
|
25
|
+
const defaults = defaultContinuityFiles(identity)
|
|
26
|
+
return {
|
|
27
|
+
'SOUL.md': await readOrDefault(ref.soulPath, defaults['SOUL.md']),
|
|
28
|
+
'MEMORY.md': await readOrDefault(ref.memoryPath, defaults['MEMORY.md']),
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function writeContinuityFiles(identity: EthagentIdentity, files: ContinuityFiles): Promise<ContinuityVaultRef> {
|
|
33
|
+
const ref = await ensureContinuityVault(identity)
|
|
34
|
+
await atomicWriteText(ref.soulPath, ensureTrailingNewline(files['SOUL.md']), { mode: 0o600 })
|
|
35
|
+
await atomicWriteText(ref.memoryPath, ensureTrailingNewline(files['MEMORY.md']), { mode: 0o600 })
|
|
36
|
+
return ref
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function writeMissingPrivateFile(file: string, content: string): Promise<void> {
|
|
40
|
+
if (await exists(file)) return
|
|
41
|
+
await atomicWriteText(file, ensureTrailingNewline(content), { mode: 0o600 })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function readOrDefault(file: string, fallback: string): Promise<string> {
|
|
45
|
+
try {
|
|
46
|
+
return await fs.readFile(file, 'utf8')
|
|
47
|
+
} catch (err: unknown) {
|
|
48
|
+
if ((err as NodeJS.ErrnoException).code === 'ENOENT') return fallback
|
|
49
|
+
throw err
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function exists(file: string): Promise<boolean> {
|
|
54
|
+
try {
|
|
55
|
+
await fs.access(file)
|
|
56
|
+
return true
|
|
57
|
+
} catch {
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function statIfExists(file: string): Promise<import('node:fs').Stats | null> {
|
|
63
|
+
try {
|
|
64
|
+
return await fs.stat(file)
|
|
65
|
+
} catch {
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function ensureTrailingNewline(value: string): string {
|
|
71
|
+
return value.endsWith('\n') ? value : `${value}\n`
|
|
72
|
+
}
|