ethagent 3.3.4 → 4.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/.claude-plugin/marketplace.json +11 -0
- package/.claude-plugin/plugin.json +35 -0
- package/LICENSE +1 -1
- package/README.md +64 -104
- package/commands/ethagent.md +40 -0
- package/package.json +16 -16
- package/src/app/keybindings/KeybindingProvider.tsx +1 -6
- package/src/app/keybindings/types.ts +1 -6
- package/src/cli/ResetConfirmView.tsx +54 -53
- package/src/cli/demo.ts +86 -0
- package/src/cli/hookIo.ts +45 -0
- package/src/cli/main.tsx +94 -123
- package/src/cli/memoryGuard.ts +49 -0
- package/src/cli/reset.ts +28 -70
- package/src/cli/sessionStart.ts +33 -0
- package/src/cli/status.ts +46 -0
- package/src/cli/sync.ts +167 -0
- package/src/cli/syncAdapters/claude-code.ts +86 -0
- package/src/cli/syncAdapters/codex.ts +66 -0
- package/src/cli/syncAdapters/index.ts +45 -0
- package/src/cli/syncAdapters/managedBlock.ts +175 -0
- package/src/cli/syncAdapters/shared.ts +63 -0
- package/src/identity/continuity/envelopeParse.ts +20 -1
- package/src/identity/continuity/publicSkills.ts +3 -1
- package/src/identity/continuity/skills/publicSkillsSync.ts +2 -1
- package/src/identity/continuity/skills/scaffold.ts +5 -2
- package/src/identity/continuity/snapshots.ts +12 -5
- package/src/identity/continuity/storage/defaults.ts +20 -19
- package/src/identity/continuity/storage/status.ts +1 -1
- package/src/identity/ens/ensLookup/constants.ts +1 -1
- package/src/identity/manager/IdentityManager.tsx +33 -0
- package/src/identity/{hub → manager}/OperationalRoutes.tsx +37 -18
- package/src/identity/{hub → manager}/Routes.tsx +48 -34
- package/src/identity/{hub → manager}/continuity/ContinuityDashboardScreen.tsx +9 -19
- package/src/identity/{hub → manager}/continuity/RebackupStorageScreen.tsx +3 -3
- package/src/identity/manager/continuity/RecoveryConfirmScreen.tsx +102 -0
- package/src/identity/{hub → manager}/continuity/SavePromptScreen.tsx +2 -3
- package/src/identity/{hub → manager}/continuity/completion.ts +1 -1
- package/src/identity/{hub → manager}/continuity/effects.ts +1 -1
- package/src/identity/{hub → manager}/continuity/skills/DeleteSkillConfirmScreen.tsx +2 -2
- package/src/identity/{hub → manager}/continuity/skills/NewSkillScreen.tsx +0 -5
- package/src/identity/{hub → manager}/continuity/skills/NewSkillVisibilityScreen.tsx +4 -4
- package/src/identity/{hub → manager}/continuity/skills/SkillActionsScreen.tsx +6 -22
- package/src/identity/{hub → manager}/continuity/skills/SkillsTreeScreen.tsx +5 -17
- package/src/identity/{hub → manager}/continuity/snapshot.ts +1 -1
- package/src/identity/{hub → manager}/continuity/vault.ts +1 -1
- package/src/identity/{hub → manager}/create/CreateFlow.tsx +59 -32
- package/src/identity/{hub → manager}/create/effects.ts +19 -10
- package/src/identity/manager/create/importScan.ts +122 -0
- package/src/identity/{hub → manager}/custody/CustodyEditFlow.tsx +17 -61
- package/src/identity/{hub → manager}/custody/actions.ts +1 -15
- package/src/identity/{hub → manager}/custody/routes.tsx +20 -40
- package/src/identity/{hub → manager}/custody/transactions.ts +1 -0
- package/src/identity/{hub → manager}/custody/types.ts +1 -2
- package/src/identity/{hub → manager}/custody/useCustodyEffects.ts +1 -1
- package/src/identity/{hub → manager}/ens/EnsEditAdvancedScreens.tsx +2 -2
- package/src/identity/{hub → manager}/ens/EnsEditMaintenanceScreens.tsx +12 -23
- package/src/identity/{hub → manager}/ens/EnsEditReviewScreens.tsx +18 -42
- package/src/identity/{hub → manager}/ens/EnsEditRunners.tsx +1 -1
- package/src/identity/{hub → manager}/ens/EnsEditShared.tsx +0 -2
- package/src/identity/{hub → manager}/ens/EnsEditSimpleScreens.tsx +10 -19
- package/src/identity/{hub → manager}/ens/EnsFlow.tsx +133 -41
- package/src/identity/{hub → manager}/ens/EnsOperatorWalletsScreen.tsx +14 -19
- package/src/identity/{hub → manager}/ens/editCopy.ts +1 -14
- package/src/identity/{hub → manager}/profile/EditProfileFlow.tsx +99 -66
- package/src/identity/{hub → manager}/profile/effects.ts +1 -3
- package/src/identity/{hub → manager}/profile/operatorSave.ts +1 -1
- package/src/identity/{hub → manager}/profile/state.ts +1 -1
- package/src/identity/{hub/identityHubReducer.ts → manager/reducer.ts} +25 -26
- package/src/identity/{hub → manager}/restore/RestoreFlow.tsx +16 -24
- package/src/identity/{hub → manager}/restore/apply.ts +1 -1
- package/src/identity/{hub → manager}/restore/auth.ts +1 -1
- package/src/identity/{hub → manager}/restore/discover.ts +1 -1
- package/src/identity/{hub → manager}/restore/fetch.ts +1 -1
- package/src/identity/{hub → manager}/restore/restoreAdmin.ts +1 -1
- package/src/identity/{hub → manager}/restore/useRestoreEffects.ts +2 -9
- package/src/identity/{hub → manager}/settings/StorageCredentialScreen.tsx +10 -25
- package/src/identity/{hub → manager}/shared/components/DetailsScreen.tsx +5 -7
- package/src/identity/{hub → manager}/shared/components/ErrorScreen.tsx +6 -10
- package/src/identity/{hub → manager}/shared/components/FlowTimeline.tsx +4 -3
- package/src/identity/{hub → manager}/shared/components/IdentitySummary.tsx +19 -59
- package/src/identity/manager/shared/components/LazyMenu.tsx +147 -0
- package/src/identity/manager/shared/components/MenuScreen.tsx +220 -0
- package/src/identity/manager/shared/components/OperationCompleteScreen.tsx +28 -0
- package/src/identity/{hub → manager}/shared/components/UnlinkedIdentityScreen.tsx +9 -10
- package/src/identity/{hub → manager}/shared/components/WalletApprovalScreen.tsx +1 -2
- package/src/identity/manager/shared/components/Wordmark.tsx +54 -0
- package/src/identity/{hub → manager}/shared/components/menuFlagsFromReconciliation.ts +39 -15
- package/src/identity/{hub → manager}/shared/effects/profilePrep.ts +1 -1
- package/src/identity/manager/shared/effects/types.ts +30 -0
- package/src/identity/{hub → manager}/shared/model/copy.ts +0 -4
- package/src/identity/{hub → manager}/shared/model/errors.ts +32 -3
- package/src/identity/{hub → manager}/shared/model/network.ts +2 -2
- package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/hook.ts +5 -0
- package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/run.ts +1 -1
- package/src/identity/{hub/shared/reconciliation/useAgentReconciliation.ts → manager/shared/reconciliation/index.ts} +6 -0
- package/src/identity/{hub → manager}/shared/utils.ts +6 -10
- package/src/identity/{hub → manager}/transfer/TokenTransferFlow.tsx +3 -3
- package/src/identity/{hub → manager}/transfer/TokenTransferScreens.tsx +4 -10
- package/src/identity/{hub → manager}/transfer/effects.ts +1 -1
- package/src/identity/{hub → manager}/types.ts +5 -6
- package/src/identity/{hub/useIdentityHubContinuity.ts → manager/useContinuity.ts} +59 -27
- package/src/identity/{hub/useIdentityHubController.ts → manager/useController.ts} +38 -35
- package/src/identity/{hub/useIdentityHubSideEffects.ts → manager/useSideEffects.ts} +40 -4
- package/src/identity/registry/erc8004/discovery.ts +3 -17
- package/src/identity/registry/erc8004/utils.ts +1 -1
- package/src/identity/storage/ipfs.ts +21 -1
- package/src/identity/wallet/browserWallet/html.ts +10 -2
- package/src/identity/wallet/browserWallet/http.ts +18 -0
- package/src/identity/wallet/browserWallet/requestServer.ts +5 -1
- package/src/identity/wallet/browserWallet/requests.ts +10 -28
- package/src/identity/wallet/browserWallet/session.ts +26 -33
- package/src/identity/wallet/browserWallet/validation.ts +14 -0
- package/src/identity/wallet/browserWallet/walletPageSource.ts +22 -40
- package/src/identity/wallet/page/boot.ts +43 -0
- package/src/identity/wallet/page/config.ts +59 -0
- package/src/identity/wallet/page/constants.ts +12 -0
- package/src/identity/wallet/page/copy.ts +47 -68
- package/src/identity/wallet/page/css.ts +638 -0
- package/src/identity/wallet/page/{errorView.ts → errors.ts} +5 -14
- package/src/identity/wallet/page/{controller.ts → flow.ts} +4 -71
- package/src/identity/wallet/page/markup.ts +44 -34
- package/src/identity/wallet/page/{walletProvider.ts → provider.ts} +0 -3
- package/src/identity/wallet/page/resize.ts +95 -0
- package/src/identity/wallet/page/state.ts +135 -8
- package/src/identity/wallet/page/timeline.ts +161 -0
- package/src/identity/wallet/page/view.ts +22 -302
- package/src/storage/config.ts +30 -80
- package/src/storage/reset.ts +31 -0
- package/src/storage/secrets.ts +1 -16
- package/src/ui/Select.tsx +27 -5
- package/src/ui/Spinner.tsx +16 -15
- package/src/ui/Surface.tsx +21 -17
- package/src/ui/TextArea.tsx +173 -0
- package/src/ui/TextInput.tsx +31 -133
- package/src/ui/theme.ts +22 -13
- package/src/utils/clipboard.ts +0 -140
- package/src/app/FirstRun.tsx +0 -577
- package/src/app/FirstRunTimeline.tsx +0 -51
- package/src/app/firstRunConfig.ts +0 -26
- package/src/app/hooks/useCancelRequest.ts +0 -22
- package/src/app/hooks/useDoublePress.ts +0 -46
- package/src/app/hooks/useExitOnCtrlC.ts +0 -36
- package/src/auth/openaiOAuth/credentials.ts +0 -47
- package/src/auth/openaiOAuth/crypto.ts +0 -23
- package/src/auth/openaiOAuth/index.ts +0 -238
- package/src/auth/openaiOAuth/landingPage.ts +0 -116
- package/src/auth/openaiOAuth/listener.ts +0 -151
- package/src/auth/openaiOAuth/refresh.ts +0 -70
- package/src/auth/openaiOAuth/shared.ts +0 -115
- package/src/chat/ChatBottomPane.tsx +0 -296
- package/src/chat/ChatScreen.tsx +0 -1685
- package/src/chat/ConversationStack.tsx +0 -56
- package/src/chat/MessageList.tsx +0 -638
- package/src/chat/SessionStatus.tsx +0 -53
- package/src/chat/chatEnvironment.ts +0 -16
- package/src/chat/chatScreenUtils.ts +0 -194
- package/src/chat/chatSessionState.ts +0 -146
- package/src/chat/chatTurnContext.ts +0 -50
- package/src/chat/chatTurnOrchestrator.ts +0 -603
- package/src/chat/chatTurnRows.ts +0 -64
- package/src/chat/commands.ts +0 -494
- package/src/chat/continuityEditReview.ts +0 -42
- package/src/chat/display/DiffView.tsx +0 -193
- package/src/chat/display/SyntaxText.tsx +0 -192
- package/src/chat/display/toolCallDisplay.ts +0 -103
- package/src/chat/display/toolResultDisplay.ts +0 -19
- package/src/chat/input/ChatInput.tsx +0 -625
- package/src/chat/input/chatInputHelpers.ts +0 -62
- package/src/chat/input/chatInputState.ts +0 -247
- package/src/chat/input/chatPaste.ts +0 -49
- package/src/chat/input/imageRefs.ts +0 -30
- package/src/chat/input/inputRendering.tsx +0 -93
- package/src/chat/input/textCursor.ts +0 -212
- package/src/chat/messageMarkdown.ts +0 -220
- package/src/chat/messageRows.ts +0 -43
- package/src/chat/planImplementation.ts +0 -62
- package/src/chat/slashCommandHandlers.ts +0 -122
- package/src/chat/slashCommandViews.ts +0 -120
- package/src/chat/transcript/TranscriptView.tsx +0 -184
- package/src/chat/transcript/transcriptViewport.ts +0 -295
- package/src/chat/views/ContextLimitView.tsx +0 -95
- package/src/chat/views/ContinuityEditReviewView.tsx +0 -50
- package/src/chat/views/CopyPicker.tsx +0 -50
- package/src/chat/views/PermissionPrompt.tsx +0 -156
- package/src/chat/views/PermissionsView.tsx +0 -165
- package/src/chat/views/PlanApprovalView.tsx +0 -91
- package/src/chat/views/ResumeView.tsx +0 -273
- package/src/chat/views/RewindView.tsx +0 -412
- package/src/cli/preview.tsx +0 -14
- package/src/cli/updateNotice.ts +0 -54
- package/src/identity/continuity/privateEdit/apply.ts +0 -170
- package/src/identity/continuity/privateEdit/diff.ts +0 -6
- package/src/identity/continuity/privateEdit/files.ts +0 -23
- package/src/identity/continuity/privateEdit/types.ts +0 -28
- package/src/identity/continuity/privateEdit.ts +0 -46
- package/src/identity/hub/IdentityHub.tsx +0 -14
- package/src/identity/hub/continuity/RecoveryConfirmScreen.tsx +0 -104
- package/src/identity/hub/ens/effects.ts +0 -218
- package/src/identity/hub/shared/components/MenuScreen.tsx +0 -241
- package/src/identity/hub/shared/effects/types.ts +0 -53
- package/src/identity/hub/shared/reconciliation/index.ts +0 -14
- package/src/identity/wallet/page/grainient.ts +0 -278
- package/src/identity/wallet/page/html.ts +0 -28
- package/src/identity/wallet/page/styles/base.ts +0 -260
- package/src/identity/wallet/page/styles/components.ts +0 -262
- package/src/identity/wallet/page/styles/index.ts +0 -5
- package/src/identity/wallet/page/styles/responsive.ts +0 -247
- package/src/identity/wallet/page.tsx +0 -38
- package/src/mcp/approvals.ts +0 -113
- package/src/mcp/config.ts +0 -235
- package/src/mcp/manager.ts +0 -482
- package/src/mcp/managerHelpers.ts +0 -70
- package/src/mcp/names.ts +0 -19
- package/src/mcp/output.ts +0 -96
- package/src/models/ModelPicker.tsx +0 -1009
- package/src/models/catalog.ts +0 -327
- package/src/models/huggingface.ts +0 -712
- package/src/models/huggingfaceStorage.ts +0 -136
- package/src/models/llamacpp.ts +0 -848
- package/src/models/llamacppCommands.ts +0 -44
- package/src/models/llamacppConfig.ts +0 -34
- package/src/models/llamacppDiscovery.ts +0 -176
- package/src/models/llamacppOutput.ts +0 -65
- package/src/models/llamacppPreflight.ts +0 -158
- package/src/models/modelDisplay.ts +0 -180
- package/src/models/modelPickerCatalogFlow.ts +0 -56
- package/src/models/modelPickerCredentials.ts +0 -166
- package/src/models/modelPickerData.ts +0 -41
- package/src/models/modelPickerDisplay.tsx +0 -132
- package/src/models/modelPickerHfFlow.ts +0 -192
- package/src/models/modelPickerLocalRunnerFlow.ts +0 -115
- package/src/models/modelPickerOptions.ts +0 -457
- package/src/models/modelPickerTypes.ts +0 -69
- package/src/models/modelPickerUninstallFlow.ts +0 -48
- package/src/models/modelPickerViewHelpers.ts +0 -174
- package/src/models/modelRecommendation.ts +0 -139
- package/src/models/providerDisplay.ts +0 -16
- package/src/models/runtimeDetection.ts +0 -81
- package/src/models/uncensoredCatalog.ts +0 -86
- package/src/providers/anthropic.ts +0 -290
- package/src/providers/contracts.ts +0 -71
- package/src/providers/errors.ts +0 -80
- package/src/providers/gemini.ts +0 -391
- package/src/providers/openai-chat.ts +0 -474
- package/src/providers/openai-responses-format.ts +0 -177
- package/src/providers/openai-responses.ts +0 -306
- package/src/providers/openaiChatWire.ts +0 -124
- package/src/providers/registry.ts +0 -120
- package/src/providers/retry.ts +0 -58
- package/src/providers/sse.ts +0 -93
- package/src/runtime/compaction.ts +0 -395
- package/src/runtime/cwd.ts +0 -43
- package/src/runtime/providerTurn.ts +0 -38
- package/src/runtime/sessionMode.ts +0 -55
- package/src/runtime/systemPrompt.ts +0 -213
- package/src/runtime/textToolParser.ts +0 -161
- package/src/runtime/toolClaimGuards.ts +0 -143
- package/src/runtime/toolExecution.ts +0 -304
- package/src/runtime/toolIntent.ts +0 -143
- package/src/runtime/turn.ts +0 -369
- package/src/runtime/turnNudges.ts +0 -223
- package/src/runtime/turnTypes.ts +0 -86
- package/src/storage/factoryReset.ts +0 -127
- package/src/storage/history.ts +0 -58
- package/src/storage/permissions.ts +0 -76
- package/src/storage/rewind.ts +0 -266
- package/src/storage/sessionExport.ts +0 -49
- package/src/storage/sessions.ts +0 -495
- package/src/tools/bashSafety.ts +0 -186
- package/src/tools/bashTool.ts +0 -140
- package/src/tools/changeDirectoryTool.ts +0 -213
- package/src/tools/contracts.ts +0 -192
- package/src/tools/deleteFileTool.ts +0 -116
- package/src/tools/editTool.ts +0 -165
- package/src/tools/editUtils.ts +0 -170
- package/src/tools/fileDiff.ts +0 -261
- package/src/tools/listDirectoryTool.ts +0 -55
- package/src/tools/listSkillFilesTool.ts +0 -77
- package/src/tools/listSkillsTool.ts +0 -68
- package/src/tools/mcpResourceTools.ts +0 -95
- package/src/tools/permissionRules.ts +0 -85
- package/src/tools/privateContinuityEditTool.ts +0 -187
- package/src/tools/privateContinuityReadTool.ts +0 -106
- package/src/tools/readSkillTool.ts +0 -107
- package/src/tools/readTool.ts +0 -85
- package/src/tools/registry.ts +0 -103
- package/src/tools/writeFileTool.ts +0 -167
- package/src/ui/BrandSplash.tsx +0 -133
- package/src/ui/terminalTitle.ts +0 -30
- package/src/utils/images.ts +0 -140
- package/src/utils/markdownSegments.ts +0 -51
- package/src/utils/messages.ts +0 -37
- package/src/utils/withRetry.ts +0 -324
- /package/src/identity/{hub → manager}/continuity/state.ts +0 -0
- /package/src/identity/{hub → manager}/custody/helpers.ts +0 -0
- /package/src/identity/{hub → manager}/custody/preflight.ts +0 -0
- /package/src/identity/{hub → manager}/custody/state.ts +0 -0
- /package/src/identity/{hub → manager}/custody/useCustodyFlow.tsx +0 -0
- /package/src/identity/{hub → manager}/ens/EnsEditFlow.tsx +0 -0
- /package/src/identity/{hub → manager}/ens/advancedEnsValidation.ts +0 -0
- /package/src/identity/{hub → manager}/ens/state.ts +0 -0
- /package/src/identity/{hub → manager}/ens/transactions.ts +0 -0
- /package/src/identity/{hub → manager}/ens/types.ts +0 -0
- /package/src/identity/{hub → manager}/profile/identity.ts +0 -0
- /package/src/identity/{hub → manager}/restore/envelopes.ts +0 -0
- /package/src/identity/{hub → manager}/restore/helpers.ts +0 -0
- /package/src/identity/{hub → manager}/restore/recovery.ts +0 -0
- /package/src/identity/{hub → manager}/restore/resolve.ts +0 -0
- /package/src/identity/{hub → manager}/shared/components/BusyScreen.tsx +0 -0
- /package/src/identity/{hub → manager}/shared/components/NetworkScreen.tsx +0 -0
- /package/src/identity/{hub → manager}/shared/components/PinataJwtInput.tsx +0 -0
- /package/src/identity/{hub → manager}/shared/effects/receipts.ts +0 -0
- /package/src/identity/{hub → manager}/shared/effects/sync.ts +0 -0
- /package/src/identity/{hub → manager}/shared/model/format.ts +0 -0
- /package/src/identity/{hub → manager}/shared/operatorWallets.ts +0 -0
- /package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/ownership.ts +0 -0
- /package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/types.ts +0 -0
- /package/src/identity/{hub → manager}/shared/reconciliation/walletSetup.ts +0 -0
- /package/src/identity/{hub → manager}/shared/txGuard.ts +0 -0
- /package/src/identity/{hub → manager}/transfer/progress.ts +0 -0
- /package/src/identity/{hub → manager}/transfer/state.ts +0 -0
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import {
|
|
4
|
-
continuityVaultRef,
|
|
5
|
-
} from '../identity/continuity/storage.js'
|
|
6
|
-
import { listSkillFiles } from '../identity/continuity/skills/loadSkills.js'
|
|
7
|
-
import type { Tool } from './contracts.js'
|
|
8
|
-
|
|
9
|
-
const schema = z.object({
|
|
10
|
-
name: z.string().min(1),
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
export const listSkillFilesTool: Tool<typeof schema> = {
|
|
14
|
-
name: 'list_private_skill_files',
|
|
15
|
-
kind: 'private-continuity-read',
|
|
16
|
-
readOnly: true,
|
|
17
|
-
description: [
|
|
18
|
-
'List every file inside a private skill folder for the active identity.',
|
|
19
|
-
'Returns each file as `- <relativePath> (<bytes>) — <absolutePath>`, including SKILL.md and any supporting files (references, examples, scripts).',
|
|
20
|
-
'Use this after list_private_skills shows a skill with `(+N supporting files)` to discover what is available, then call read_private_skill with `file` to load text content.',
|
|
21
|
-
'Pass the absolute path directly to run_bash to execute supporting scripts (e.g. `python "<absolutePath>" <args>`).',
|
|
22
|
-
].join(' '),
|
|
23
|
-
inputSchema: schema,
|
|
24
|
-
inputSchemaJson: {
|
|
25
|
-
type: 'object',
|
|
26
|
-
properties: {
|
|
27
|
-
name: { type: 'string', description: 'Skill folder name from the private skills index.' },
|
|
28
|
-
},
|
|
29
|
-
required: ['name'],
|
|
30
|
-
additionalProperties: false,
|
|
31
|
-
},
|
|
32
|
-
parse(input) {
|
|
33
|
-
return schema.parse(input)
|
|
34
|
-
},
|
|
35
|
-
async buildPermissionRequest(input, context) {
|
|
36
|
-
const identity = context.config?.identity
|
|
37
|
-
if (!identity) throw new Error('No active identity; create or load an identity before listing private skill files')
|
|
38
|
-
const ref = continuityVaultRef(identity)
|
|
39
|
-
const folder = input.name.replace(/^.*:/, '')
|
|
40
|
-
const skillDir = path.join(ref.skillsDir, folder)
|
|
41
|
-
return {
|
|
42
|
-
kind: 'private-skill-read',
|
|
43
|
-
path: skillDir,
|
|
44
|
-
relativePath: `identity-vault/skills/${folder}`,
|
|
45
|
-
directoryPath: skillDir,
|
|
46
|
-
title: 'Allow private skill folder list?',
|
|
47
|
-
subtitle: `List files in ${folder}/`,
|
|
48
|
-
skillName: input.name,
|
|
49
|
-
mode: 'list',
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
async execute(input, context) {
|
|
53
|
-
const identity = context.config?.identity
|
|
54
|
-
if (!identity) {
|
|
55
|
-
return { ok: false, summary: 'no active identity', content: 'No active identity; cannot list private skill files.' }
|
|
56
|
-
}
|
|
57
|
-
try {
|
|
58
|
-
const folder = input.name.replace(/^.*:/, '')
|
|
59
|
-
const files = await listSkillFiles(identity, folder)
|
|
60
|
-
if (files.length === 0) {
|
|
61
|
-
return { ok: true, summary: `no files in ${folder}`, content: `Skill folder ${folder}/ is empty or does not exist.` }
|
|
62
|
-
}
|
|
63
|
-
const lines = files.map(f => `- ${f.relativePath} (${f.sizeBytes} bytes) — ${f.absolutePath}`)
|
|
64
|
-
return {
|
|
65
|
-
ok: true,
|
|
66
|
-
summary: `listed ${files.length} file${files.length === 1 ? '' : 's'} in ${folder}`,
|
|
67
|
-
content: lines.join('\n'),
|
|
68
|
-
}
|
|
69
|
-
} catch (err: unknown) {
|
|
70
|
-
return {
|
|
71
|
-
ok: false,
|
|
72
|
-
summary: 'list failed',
|
|
73
|
-
content: (err as Error).message,
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import {
|
|
3
|
-
continuityVaultRef,
|
|
4
|
-
} from '../identity/continuity/storage.js'
|
|
5
|
-
import { listSkillsTree } from '../identity/continuity/skills/loadSkills.js'
|
|
6
|
-
import type { Tool } from './contracts.js'
|
|
7
|
-
|
|
8
|
-
const schema = z.object({})
|
|
9
|
-
|
|
10
|
-
export const listSkillsTool: Tool<typeof schema> = {
|
|
11
|
-
name: 'list_private_skills',
|
|
12
|
-
kind: 'private-continuity-read',
|
|
13
|
-
readOnly: true,
|
|
14
|
-
description: [
|
|
15
|
-
'List private skills available in the owner-authored skills tree for the active identity.',
|
|
16
|
-
'Returns each skill folder name with its one-line description; bodies are loaded separately via read_private_skill.',
|
|
17
|
-
'When a skill has supporting files beyond SKILL.md, the entry is annotated with (+N supporting files) so you know to call list_private_skill_files.',
|
|
18
|
-
'Use this when the user mentions a skill not in the injected skill index, or to discover what skills exist.',
|
|
19
|
-
].join(' '),
|
|
20
|
-
inputSchema: schema,
|
|
21
|
-
inputSchemaJson: {
|
|
22
|
-
type: 'object',
|
|
23
|
-
properties: {},
|
|
24
|
-
additionalProperties: false,
|
|
25
|
-
},
|
|
26
|
-
parse(input) {
|
|
27
|
-
return schema.parse(input ?? {})
|
|
28
|
-
},
|
|
29
|
-
async buildPermissionRequest(_input, context) {
|
|
30
|
-
const identity = context.config?.identity
|
|
31
|
-
if (!identity) throw new Error('No active identity; create or load an identity before listing private skills')
|
|
32
|
-
const ref = continuityVaultRef(identity)
|
|
33
|
-
return {
|
|
34
|
-
kind: 'private-skill-read',
|
|
35
|
-
path: ref.skillsDir,
|
|
36
|
-
relativePath: 'identity-vault/skills',
|
|
37
|
-
directoryPath: ref.skillsDir,
|
|
38
|
-
title: 'Allow private skills index read?',
|
|
39
|
-
subtitle: 'List skill names and descriptions from the private skills tree',
|
|
40
|
-
skillName: '*',
|
|
41
|
-
mode: 'list',
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
async execute(_input, context) {
|
|
45
|
-
const identity = context.config?.identity
|
|
46
|
-
if (!identity) {
|
|
47
|
-
return { ok: false, summary: 'no active identity', content: 'No active identity; cannot list private skills.' }
|
|
48
|
-
}
|
|
49
|
-
const { skills, supportingCounts } = await listSkillsTree(identity)
|
|
50
|
-
if (skills.length === 0) {
|
|
51
|
-
return { ok: true, summary: 'no private skills', content: 'The private skills tree is empty.' }
|
|
52
|
-
}
|
|
53
|
-
const lines = skills.map(entry => {
|
|
54
|
-
const display = entry.displayName ?? entry.name
|
|
55
|
-
const desc = entry.description ? ` — ${entry.description}` : ''
|
|
56
|
-
const when = entry.whenToUse ? ` (when: ${entry.whenToUse})` : ''
|
|
57
|
-
const vis = entry.visibility !== 'private' ? ` [visibility: ${entry.visibility}]` : ''
|
|
58
|
-
const supporting = supportingCounts[entry.name] ?? 0
|
|
59
|
-
const trailer = supporting > 0 ? ` (+${supporting} supporting file${supporting === 1 ? '' : 's'})` : ''
|
|
60
|
-
return `- ${display}${desc}${when}${vis}${trailer}`
|
|
61
|
-
})
|
|
62
|
-
return {
|
|
63
|
-
ok: true,
|
|
64
|
-
summary: `listed ${skills.length} private skill${skills.length === 1 ? '' : 's'}`,
|
|
65
|
-
content: lines.join('\n'),
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import type { Tool } from './contracts.js'
|
|
3
|
-
import { normalizeNameForMcp } from '../mcp/names.js'
|
|
4
|
-
|
|
5
|
-
const ListMcpResourcesInput = z.object({
|
|
6
|
-
server: z.string().min(1).optional(),
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
const ReadMcpResourceInput = z.object({
|
|
10
|
-
server: z.string().min(1),
|
|
11
|
-
uri: z.string().min(1),
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
export const listMcpResourcesTool: Tool<typeof ListMcpResourcesInput> = {
|
|
15
|
-
name: 'list_mcp_resources',
|
|
16
|
-
kind: 'mcp',
|
|
17
|
-
readOnly: true,
|
|
18
|
-
description: 'List resources exposed by connected MCP servers.',
|
|
19
|
-
inputSchema: ListMcpResourcesInput,
|
|
20
|
-
inputSchemaJson: {
|
|
21
|
-
type: 'object',
|
|
22
|
-
properties: {
|
|
23
|
-
server: { type: 'string', description: 'Optional MCP server name. Omit to list resources from every connected server.' },
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
parse(input) {
|
|
27
|
-
return ListMcpResourcesInput.parse(input)
|
|
28
|
-
},
|
|
29
|
-
async buildPermissionRequest(input) {
|
|
30
|
-
const serverName = input.server ?? '*'
|
|
31
|
-
return {
|
|
32
|
-
kind: 'mcp',
|
|
33
|
-
title: 'Allow MCP resource listing?',
|
|
34
|
-
subtitle: input.server ? `list resources from ${input.server}` : 'list resources from all connected MCP servers',
|
|
35
|
-
serverName,
|
|
36
|
-
normalizedServerName: normalizeNameForMcp(serverName),
|
|
37
|
-
toolName: 'list_mcp_resources',
|
|
38
|
-
toolKey: 'list_mcp_resources',
|
|
39
|
-
readOnly: true,
|
|
40
|
-
destructive: false,
|
|
41
|
-
openWorld: false,
|
|
42
|
-
canPersistServer: Boolean(input.server),
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
async execute(input, context) {
|
|
46
|
-
if (!context.mcp) return { ok: false, summary: 'MCP unavailable', content: 'MCP runtime is not available.' }
|
|
47
|
-
return {
|
|
48
|
-
ok: true,
|
|
49
|
-
summary: input.server ? `listed MCP resources from ${input.server}` : 'listed MCP resources',
|
|
50
|
-
content: await context.mcp.listResources(input.server),
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export const readMcpResourceTool: Tool<typeof ReadMcpResourceInput> = {
|
|
56
|
-
name: 'read_mcp_resource',
|
|
57
|
-
kind: 'mcp',
|
|
58
|
-
readOnly: true,
|
|
59
|
-
description: 'Read a specific resource from a connected MCP server.',
|
|
60
|
-
inputSchema: ReadMcpResourceInput,
|
|
61
|
-
inputSchemaJson: {
|
|
62
|
-
type: 'object',
|
|
63
|
-
properties: {
|
|
64
|
-
server: { type: 'string', description: 'The connected MCP server name.' },
|
|
65
|
-
uri: { type: 'string', description: 'The resource URI to read.' },
|
|
66
|
-
},
|
|
67
|
-
required: ['server', 'uri'],
|
|
68
|
-
},
|
|
69
|
-
parse(input) {
|
|
70
|
-
return ReadMcpResourceInput.parse(input)
|
|
71
|
-
},
|
|
72
|
-
async buildPermissionRequest(input) {
|
|
73
|
-
return {
|
|
74
|
-
kind: 'mcp',
|
|
75
|
-
title: 'Allow MCP resource read?',
|
|
76
|
-
subtitle: `${input.server} / ${input.uri}`,
|
|
77
|
-
serverName: input.server,
|
|
78
|
-
normalizedServerName: normalizeNameForMcp(input.server),
|
|
79
|
-
toolName: 'read_mcp_resource',
|
|
80
|
-
toolKey: `read_mcp_resource:${normalizeNameForMcp(input.server)}`,
|
|
81
|
-
readOnly: true,
|
|
82
|
-
destructive: false,
|
|
83
|
-
openWorld: false,
|
|
84
|
-
canPersistServer: true,
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
async execute(input, context) {
|
|
88
|
-
if (!context.mcp) return { ok: false, summary: 'MCP unavailable', content: 'MCP runtime is not available.' }
|
|
89
|
-
return {
|
|
90
|
-
ok: true,
|
|
91
|
-
summary: `read MCP resource ${input.uri}`,
|
|
92
|
-
content: await context.mcp.readResource(input.server, input.uri, context.abortSignal),
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import path from 'node:path'
|
|
2
|
-
import type { PermissionDecision, PermissionRequest, SessionPermissionRule } from './contracts.js'
|
|
3
|
-
|
|
4
|
-
export function buildPermissionRule(
|
|
5
|
-
decision: PermissionDecision,
|
|
6
|
-
request: PermissionRequest,
|
|
7
|
-
): SessionPermissionRule | undefined {
|
|
8
|
-
switch (decision) {
|
|
9
|
-
case 'allow-kind-project':
|
|
10
|
-
if (request.kind === 'read' || request.kind === 'edit' || request.kind === 'write' || request.kind === 'cd') return { kind: request.kind, scope: 'kind' }
|
|
11
|
-
return undefined
|
|
12
|
-
case 'allow-path-project':
|
|
13
|
-
if (request.kind === 'read' || request.kind === 'edit' || request.kind === 'write' || request.kind === 'cd') {
|
|
14
|
-
return { kind: request.kind, scope: 'path', path: request.path }
|
|
15
|
-
}
|
|
16
|
-
return undefined
|
|
17
|
-
case 'allow-directory-project':
|
|
18
|
-
if (request.kind === 'read' || request.kind === 'edit' || request.kind === 'write' || request.kind === 'cd') {
|
|
19
|
-
return { kind: request.kind, scope: 'directory', path: request.directoryPath }
|
|
20
|
-
}
|
|
21
|
-
return undefined
|
|
22
|
-
case 'allow-command-project':
|
|
23
|
-
if (request.kind === 'bash' && request.canPersistExact) {
|
|
24
|
-
return { kind: 'bash', scope: 'command', command: request.command, cwd: request.cwd }
|
|
25
|
-
}
|
|
26
|
-
return undefined
|
|
27
|
-
case 'allow-command-prefix-project':
|
|
28
|
-
if (request.kind === 'bash' && request.canPersistPrefix && request.commandPrefix) {
|
|
29
|
-
return { kind: 'bash', scope: 'prefix', commandPrefix: request.commandPrefix, cwd: request.cwd }
|
|
30
|
-
}
|
|
31
|
-
return undefined
|
|
32
|
-
case 'allow-mcp-tool-project':
|
|
33
|
-
if (request.kind === 'mcp') return { kind: 'mcp', scope: 'tool', toolKey: request.toolKey }
|
|
34
|
-
return undefined
|
|
35
|
-
case 'allow-mcp-server-project':
|
|
36
|
-
if (request.kind === 'mcp' && request.canPersistServer) {
|
|
37
|
-
return { kind: 'mcp', scope: 'server', normalizedServerName: request.normalizedServerName }
|
|
38
|
-
}
|
|
39
|
-
return undefined
|
|
40
|
-
default:
|
|
41
|
-
return undefined
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function matchPermissionRule(
|
|
46
|
-
rules: SessionPermissionRule[],
|
|
47
|
-
request: PermissionRequest,
|
|
48
|
-
): SessionPermissionRule | undefined {
|
|
49
|
-
return rules.find(rule => matchesPermissionRule(rule, request))
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function shouldPersistPermissionDecision(decision: PermissionDecision): boolean {
|
|
53
|
-
return decision !== 'allow-once' && decision !== 'deny'
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function matchesPermissionRule(rule: SessionPermissionRule, request: PermissionRequest): boolean {
|
|
57
|
-
if (rule.kind !== request.kind) return false
|
|
58
|
-
|
|
59
|
-
if (request.kind === 'read' || request.kind === 'edit' || request.kind === 'write' || request.kind === 'delete' || request.kind === 'cd') {
|
|
60
|
-
if (request.kind === 'delete') return false
|
|
61
|
-
if (rule.scope === 'kind') return true
|
|
62
|
-
if (rule.scope === 'path') return rule.path === request.path
|
|
63
|
-
if (rule.scope === 'directory') {
|
|
64
|
-
return request.path === rule.path || request.path.startsWith(`${rule.path}${path.sep}`)
|
|
65
|
-
}
|
|
66
|
-
return false
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (request.kind === 'bash') {
|
|
70
|
-
if (rule.scope === 'command') return rule.command === request.command && rule.cwd === request.cwd
|
|
71
|
-
if (rule.scope === 'prefix') {
|
|
72
|
-
const normalizedCommand = request.command.trim()
|
|
73
|
-
return (
|
|
74
|
-
rule.cwd === request.cwd &&
|
|
75
|
-
(normalizedCommand === rule.commandPrefix || normalizedCommand.startsWith(`${rule.commandPrefix} `))
|
|
76
|
-
)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (request.kind === 'mcp') {
|
|
81
|
-
if (rule.scope === 'tool') return rule.toolKey === request.toolKey
|
|
82
|
-
if (rule.scope === 'server') return rule.normalizedServerName === request.normalizedServerName
|
|
83
|
-
}
|
|
84
|
-
return false
|
|
85
|
-
}
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import {
|
|
3
|
-
preparePrivateContinuityEdit,
|
|
4
|
-
writePreparedPrivateContinuityEdit,
|
|
5
|
-
} from '../identity/continuity/privateEdit.js'
|
|
6
|
-
import { recordPrivateContinuityHistorySnapshot } from '../identity/continuity/history.js'
|
|
7
|
-
import { readContinuityFiles, readAgentCardFile } from '../identity/continuity/storage.js'
|
|
8
|
-
import type { Tool } from './contracts.js'
|
|
9
|
-
import { formatFileChangeResult } from './fileDiff.js'
|
|
10
|
-
|
|
11
|
-
const schema = z.object({
|
|
12
|
-
file: z.preprocess(normalizePrivateContinuityFile, z.enum(['SOUL.md', 'MEMORY.md'])),
|
|
13
|
-
oldText: z.string().optional(),
|
|
14
|
-
newText: z.string().optional(),
|
|
15
|
-
appendToSection: z.string().optional(),
|
|
16
|
-
appendText: z.string().optional(),
|
|
17
|
-
replaceAll: z.boolean().optional(),
|
|
18
|
-
replaceWholeFile: z.boolean().optional(),
|
|
19
|
-
}).superRefine((input, ctx) => {
|
|
20
|
-
const hasOldText = input.oldText !== undefined
|
|
21
|
-
const hasNewText = input.newText !== undefined
|
|
22
|
-
const hasAppendToSection = input.appendToSection !== undefined
|
|
23
|
-
const hasAppendText = input.appendText !== undefined
|
|
24
|
-
const targeted = hasOldText || hasNewText
|
|
25
|
-
const append = hasAppendToSection || hasAppendText
|
|
26
|
-
if (input.replaceWholeFile) {
|
|
27
|
-
ctx.addIssue({
|
|
28
|
-
code: z.ZodIssueCode.custom,
|
|
29
|
-
message: 'private continuity files must be edited in place; whole-file replacement is disabled',
|
|
30
|
-
path: ['replaceWholeFile'],
|
|
31
|
-
})
|
|
32
|
-
}
|
|
33
|
-
if (!targeted && !append) {
|
|
34
|
-
ctx.addIssue({
|
|
35
|
-
code: z.ZodIssueCode.custom,
|
|
36
|
-
message: 'file alone is not enough; provide either oldText+newText for a targeted edit or appendToSection+appendText for an in-place append',
|
|
37
|
-
})
|
|
38
|
-
}
|
|
39
|
-
if (targeted && append) {
|
|
40
|
-
ctx.addIssue({
|
|
41
|
-
code: z.ZodIssueCode.custom,
|
|
42
|
-
message: 'provide only one edit mode: oldText+newText or appendToSection+appendText',
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
if (targeted && !input.oldText?.trim()) {
|
|
46
|
-
ctx.addIssue({
|
|
47
|
-
code: z.ZodIssueCode.custom,
|
|
48
|
-
message: 'oldText is required for targeted private continuity edits',
|
|
49
|
-
path: ['oldText'],
|
|
50
|
-
})
|
|
51
|
-
}
|
|
52
|
-
if (targeted && !hasNewText) {
|
|
53
|
-
ctx.addIssue({
|
|
54
|
-
code: z.ZodIssueCode.custom,
|
|
55
|
-
message: 'newText is required for targeted private continuity edits',
|
|
56
|
-
path: ['newText'],
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
|
-
if (append && !input.appendToSection?.trim()) {
|
|
60
|
-
ctx.addIssue({
|
|
61
|
-
code: z.ZodIssueCode.custom,
|
|
62
|
-
message: 'appendToSection is required for private continuity appends',
|
|
63
|
-
path: ['appendToSection'],
|
|
64
|
-
})
|
|
65
|
-
}
|
|
66
|
-
if (append && !input.appendText?.trim()) {
|
|
67
|
-
ctx.addIssue({
|
|
68
|
-
code: z.ZodIssueCode.custom,
|
|
69
|
-
message: 'appendText is required for private continuity appends',
|
|
70
|
-
path: ['appendText'],
|
|
71
|
-
})
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
export const privateContinuityEditTool: Tool<typeof schema> = {
|
|
76
|
-
name: 'propose_private_continuity_edit',
|
|
77
|
-
kind: 'private-continuity-edit',
|
|
78
|
-
description: [
|
|
79
|
-
'Propose an explicit user-approved in-place edit to an existing private identity continuity scaffold.',
|
|
80
|
-
'Only the identity-vault SOUL.md and MEMORY.md are valid targets; do not create workspace files with these names.',
|
|
81
|
-
'Do not call read_file, list_directory, or run_bash to locate these files; pass file as SOUL.md or MEMORY.md and this tool resolves the vault path.',
|
|
82
|
-
'For new memories or preferences call exactly: {"file":"MEMORY.md","appendToSection":"Durable User Preferences","appendText":"- User preference or memory note."}.',
|
|
83
|
-
'For persona or standing behavior call exactly: {"file":"SOUL.md","appendToSection":"Persona","appendText":"- Persona or standing behavior note."}.',
|
|
84
|
-
'Prefer appendToSection+appendText to build on an existing scaffold section; use oldText+newText only for targeted replacement after exact text is known.',
|
|
85
|
-
'Whole-file replacement is disabled for private continuity.',
|
|
86
|
-
'Approved private continuity edits are not managed by /rewind.',
|
|
87
|
-
].join(' '),
|
|
88
|
-
inputSchema: schema,
|
|
89
|
-
inputSchemaJson: {
|
|
90
|
-
type: 'object',
|
|
91
|
-
properties: {
|
|
92
|
-
file: { type: 'string', enum: ['SOUL.md', 'MEMORY.md'], description: 'Private continuity file to edit. Use only the file name; do not pass a workspace path.' },
|
|
93
|
-
oldText: { type: 'string', description: 'Exact existing scaffold text to replace. Required for targeted replacement edits.' },
|
|
94
|
-
newText: { type: 'string', description: 'Replacement text for oldText. Do not use this as whole-file content.' },
|
|
95
|
-
appendToSection: { type: 'string', description: 'Existing markdown section heading to append under, for example "Durable User Preferences" or "Persona". Prefer this for new notes.' },
|
|
96
|
-
appendText: { type: 'string', description: 'New non-empty markdown bullet to append under appendToSection. This builds on the existing scaffold instead of overwriting it.' },
|
|
97
|
-
replaceAll: { type: 'boolean', description: 'Replace every exact oldText match. Prefer false unless certain.' },
|
|
98
|
-
},
|
|
99
|
-
required: ['file'],
|
|
100
|
-
additionalProperties: false,
|
|
101
|
-
},
|
|
102
|
-
parse(input) {
|
|
103
|
-
return schema.parse(normalizePrivateContinuityInput(input))
|
|
104
|
-
},
|
|
105
|
-
async buildPermissionRequest(input, context) {
|
|
106
|
-
const prepared = await preparePrivateContinuityEdit(input, context.config)
|
|
107
|
-
return {
|
|
108
|
-
kind: 'private-continuity-edit',
|
|
109
|
-
path: prepared.fullPath,
|
|
110
|
-
relativePath: prepared.relativePath,
|
|
111
|
-
directoryPath: prepared.directoryPath,
|
|
112
|
-
title: 'Approve Private Continuity Edit?',
|
|
113
|
-
subtitle: prepared.fullPath,
|
|
114
|
-
file: prepared.file,
|
|
115
|
-
before: prepared.previewBefore,
|
|
116
|
-
after: prepared.previewAfter,
|
|
117
|
-
diff: prepared.diff,
|
|
118
|
-
changeSummary: prepared.changeSummary,
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
async execute(input, context) {
|
|
122
|
-
const prepared = await preparePrivateContinuityEdit(input, context.config)
|
|
123
|
-
const [previousFiles, previousAgentCard] = await Promise.all([
|
|
124
|
-
readContinuityFiles(prepared.identity),
|
|
125
|
-
readAgentCardFile(prepared.identity),
|
|
126
|
-
])
|
|
127
|
-
await recordPrivateContinuityHistorySnapshot({
|
|
128
|
-
identity: prepared.identity,
|
|
129
|
-
file: prepared.file,
|
|
130
|
-
filePath: prepared.fullPath,
|
|
131
|
-
existedBefore: prepared.existedBefore,
|
|
132
|
-
previousContent: prepared.previousContent,
|
|
133
|
-
previousFiles,
|
|
134
|
-
previousAgentCard,
|
|
135
|
-
changeSummary: prepared.changeSummary,
|
|
136
|
-
sessionId: context.checkpoint?.sessionId,
|
|
137
|
-
turnId: context.checkpoint?.turnId,
|
|
138
|
-
promptSnippet: context.checkpoint?.promptSnippet,
|
|
139
|
-
checkpointLabel: context.checkpoint?.checkpointLabel,
|
|
140
|
-
})
|
|
141
|
-
await writePreparedPrivateContinuityEdit(prepared)
|
|
142
|
-
return {
|
|
143
|
-
ok: true,
|
|
144
|
-
summary: prepared.changeSummary,
|
|
145
|
-
content: formatFileChangeResult(
|
|
146
|
-
formatPrivateContinuityEditResult(prepared.file, prepared.fullPath),
|
|
147
|
-
prepared.diff,
|
|
148
|
-
),
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function formatPrivateContinuityEditResult(file: 'SOUL.md' | 'MEMORY.md', fullPath: string): string {
|
|
154
|
-
return [
|
|
155
|
-
'## Saved private continuity',
|
|
156
|
-
'',
|
|
157
|
-
`- File: \`identity-vault/${file}\``,
|
|
158
|
-
`- Review file: \`${fullPath}\``,
|
|
159
|
-
'- History: previous version saved to private identity history; `/rewind` does not restore identity continuity',
|
|
160
|
-
'- Open: Identity Hub > Soul, Memory, and Skills',
|
|
161
|
-
'- Save: Identity Hub > Recovery > Save Snapshot Now',
|
|
162
|
-
].join('\n')
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function normalizePrivateContinuityInput(input: Record<string, unknown>): Record<string, unknown> {
|
|
166
|
-
const normalized = { ...input }
|
|
167
|
-
if (normalized.appendToSection === undefined) {
|
|
168
|
-
normalized.appendToSection = normalized.section ?? normalized.heading
|
|
169
|
-
}
|
|
170
|
-
if (normalized.appendText === undefined) {
|
|
171
|
-
normalized.appendText = normalized.note ?? normalized.text ?? normalized.content
|
|
172
|
-
}
|
|
173
|
-
for (const key of ['oldText', 'newText', 'appendToSection', 'appendText'] as const) {
|
|
174
|
-
const value = normalized[key]
|
|
175
|
-
if (typeof value === 'string' && value.trim() === '') {
|
|
176
|
-
normalized[key] = undefined
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
return normalized
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function normalizePrivateContinuityFile(value: unknown): unknown {
|
|
183
|
-
if (typeof value !== 'string') return value
|
|
184
|
-
if (/^soul\.md$/i.test(value.trim())) return 'SOUL.md'
|
|
185
|
-
if (/^memory\.md$/i.test(value.trim())) return 'MEMORY.md'
|
|
186
|
-
return value
|
|
187
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import path from 'node:path'
|
|
2
|
-
import { z } from 'zod'
|
|
3
|
-
import {
|
|
4
|
-
continuityVaultRef,
|
|
5
|
-
ensureContinuityFiles,
|
|
6
|
-
} from '../identity/continuity/storage.js'
|
|
7
|
-
import type { Tool } from './contracts.js'
|
|
8
|
-
|
|
9
|
-
const schema = z.object({
|
|
10
|
-
file: z.preprocess(normalizePrivateContinuityFile, z.enum(['SOUL.md', 'MEMORY.md'])),
|
|
11
|
-
startLine: z.number().int().positive().optional(),
|
|
12
|
-
endLine: z.number().int().positive().optional(),
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
export const privateContinuityReadTool: Tool<typeof schema> = {
|
|
16
|
-
name: 'read_private_continuity_file',
|
|
17
|
-
kind: 'private-continuity-read',
|
|
18
|
-
description: [
|
|
19
|
-
'Read an explicitly user-approved private identity continuity file from the identity vault.',
|
|
20
|
-
'Only SOUL.md and MEMORY.md are valid targets; do not use workspace read_file for these files.',
|
|
21
|
-
'Use this before surgical removals or targeted replacements that need exact oldText.',
|
|
22
|
-
'Pass file as SOUL.md or MEMORY.md; this tool resolves the vault path.',
|
|
23
|
-
'Use startLine and endLine to limit the returned range when possible.',
|
|
24
|
-
].join(' '),
|
|
25
|
-
inputSchema: schema,
|
|
26
|
-
inputSchemaJson: {
|
|
27
|
-
type: 'object',
|
|
28
|
-
properties: {
|
|
29
|
-
file: { type: 'string', enum: ['SOUL.md', 'MEMORY.md'], description: 'Private continuity file to read. Use only the file name; do not pass a workspace path.' },
|
|
30
|
-
startLine: { type: 'number', description: 'Optional 1-based starting line.' },
|
|
31
|
-
endLine: { type: 'number', description: 'Optional 1-based ending line.' },
|
|
32
|
-
},
|
|
33
|
-
required: ['file'],
|
|
34
|
-
additionalProperties: false,
|
|
35
|
-
},
|
|
36
|
-
parse(input) {
|
|
37
|
-
return schema.parse(normalizePrivateContinuityReadInput(input))
|
|
38
|
-
},
|
|
39
|
-
async buildPermissionRequest(input, context) {
|
|
40
|
-
const prepared = preparePrivateContinuityRead(input, context.config)
|
|
41
|
-
return {
|
|
42
|
-
kind: 'private-continuity-read',
|
|
43
|
-
path: prepared.fullPath,
|
|
44
|
-
relativePath: prepared.relativePath,
|
|
45
|
-
directoryPath: prepared.directoryPath,
|
|
46
|
-
title: 'Allow private continuity read?',
|
|
47
|
-
subtitle: input.startLine || input.endLine
|
|
48
|
-
? `${prepared.fullPath} · lines ${input.startLine ?? 1}-${input.endLine ?? 'end'}`
|
|
49
|
-
: prepared.fullPath,
|
|
50
|
-
file: input.file,
|
|
51
|
-
range: input.startLine || input.endLine
|
|
52
|
-
? `lines ${input.startLine ?? 1}-${input.endLine ?? 'end'}`
|
|
53
|
-
: 'entire file',
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
async execute(input, context) {
|
|
57
|
-
const prepared = preparePrivateContinuityRead(input, context.config)
|
|
58
|
-
const files = await ensureContinuityFiles(prepared.identity)
|
|
59
|
-
return {
|
|
60
|
-
ok: true,
|
|
61
|
-
summary: `read private ${input.file}`,
|
|
62
|
-
content: numberedLineSlice(files[input.file], input.startLine, input.endLine),
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function preparePrivateContinuityRead(
|
|
68
|
-
input: z.infer<typeof schema>,
|
|
69
|
-
config: Parameters<Tool['buildPermissionRequest']>[1]['config'],
|
|
70
|
-
) {
|
|
71
|
-
const identity = config?.identity
|
|
72
|
-
if (!identity) {
|
|
73
|
-
throw new Error('No active identity; create or load an identity before reading private continuity files')
|
|
74
|
-
}
|
|
75
|
-
const ref = continuityVaultRef(identity)
|
|
76
|
-
const fullPath = input.file === 'SOUL.md' ? ref.soulPath : ref.memoryPath
|
|
77
|
-
return {
|
|
78
|
-
identity,
|
|
79
|
-
fullPath,
|
|
80
|
-
relativePath: `identity-vault/${input.file}`,
|
|
81
|
-
directoryPath: path.dirname(fullPath),
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function numberedLineSlice(content: string, startLine?: number, endLine?: number): string {
|
|
86
|
-
const lines = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n')
|
|
87
|
-
const start = Math.max(1, startLine ?? 1)
|
|
88
|
-
const end = Math.max(start, endLine ?? lines.length)
|
|
89
|
-
return lines.slice(start - 1, end).map((line, index) => `${start + index}: ${line}`).join('\n')
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function normalizePrivateContinuityReadInput(input: Record<string, unknown>): Record<string, unknown> {
|
|
93
|
-
const normalized = { ...input }
|
|
94
|
-
if (normalized.file === undefined) {
|
|
95
|
-
normalized.file = normalized.path ?? normalized.name
|
|
96
|
-
}
|
|
97
|
-
return normalized
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function normalizePrivateContinuityFile(value: unknown): unknown {
|
|
101
|
-
if (typeof value !== 'string') return value
|
|
102
|
-
const basename = path.basename(value.replaceAll('\\', '/')).trim()
|
|
103
|
-
if (/^soul\.md$/i.test(basename)) return 'SOUL.md'
|
|
104
|
-
if (/^memory\.md$/i.test(basename)) return 'MEMORY.md'
|
|
105
|
-
return value
|
|
106
|
-
}
|