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,107 +0,0 @@
|
|
|
1
|
-
import path from 'node:path'
|
|
2
|
-
import { z } from 'zod'
|
|
3
|
-
import {
|
|
4
|
-
continuityVaultRef,
|
|
5
|
-
} from '../identity/continuity/storage.js'
|
|
6
|
-
import {
|
|
7
|
-
readSkill,
|
|
8
|
-
readSkillFile,
|
|
9
|
-
} from '../identity/continuity/skills/loadSkills.js'
|
|
10
|
-
import type { Tool } from './contracts.js'
|
|
11
|
-
|
|
12
|
-
const schema = z.object({
|
|
13
|
-
name: z.string().min(1),
|
|
14
|
-
file: z.string().min(1).optional(),
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
export const readSkillTool: Tool<typeof schema> = {
|
|
18
|
-
name: 'read_private_skill',
|
|
19
|
-
kind: 'private-continuity-read',
|
|
20
|
-
readOnly: true,
|
|
21
|
-
description: [
|
|
22
|
-
'Read a file from the owner-authored private skill folder for the active identity.',
|
|
23
|
-
'Pass the skill folder name as `name` (e.g. "writing-obit"). Without `file`, returns the SKILL.md entry point.',
|
|
24
|
-
'With `file`, returns a supporting file from the same folder (e.g. file: "references/api.md"). Supporting-file responses include an absolute filesystem `path=` attribute; run executable scripts via run_bash using that absolute path.',
|
|
25
|
-
'List available supporting files with list_private_skill_files. Bodies are markdown and may include private instructions; treat them like SOUL.md guidance.',
|
|
26
|
-
].join(' '),
|
|
27
|
-
inputSchema: schema,
|
|
28
|
-
inputSchemaJson: {
|
|
29
|
-
type: 'object',
|
|
30
|
-
properties: {
|
|
31
|
-
name: { type: 'string', description: 'Skill folder name from the private skills index, e.g. "writing-obit".' },
|
|
32
|
-
file: { type: 'string', description: 'Optional supporting file path inside the skill folder. Defaults to SKILL.md.' },
|
|
33
|
-
},
|
|
34
|
-
required: ['name'],
|
|
35
|
-
additionalProperties: false,
|
|
36
|
-
},
|
|
37
|
-
parse(input) {
|
|
38
|
-
return schema.parse(input)
|
|
39
|
-
},
|
|
40
|
-
async buildPermissionRequest(input, context) {
|
|
41
|
-
const identity = context.config?.identity
|
|
42
|
-
if (!identity) throw new Error('No active identity; create or load an identity before reading a private skill')
|
|
43
|
-
const ref = continuityVaultRef(identity)
|
|
44
|
-
const folder = input.name.replace(/^.*:/, '')
|
|
45
|
-
const file = input.file ?? 'SKILL.md'
|
|
46
|
-
const skillPath = path.join(ref.skillsDir, folder, file)
|
|
47
|
-
const display = input.file ? `${folder}/${input.file}` : `${folder}/SKILL.md`
|
|
48
|
-
return {
|
|
49
|
-
kind: 'private-skill-read',
|
|
50
|
-
path: skillPath,
|
|
51
|
-
relativePath: `identity-vault/skills/${display}`,
|
|
52
|
-
directoryPath: path.dirname(skillPath),
|
|
53
|
-
title: 'Allow private skill read?',
|
|
54
|
-
subtitle: `Load ${display}`,
|
|
55
|
-
skillName: input.name,
|
|
56
|
-
mode: 'read',
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
async execute(input, context) {
|
|
60
|
-
const identity = context.config?.identity
|
|
61
|
-
if (!identity) {
|
|
62
|
-
return { ok: false, summary: 'no active identity', content: 'No active identity; cannot read private skill.' }
|
|
63
|
-
}
|
|
64
|
-
try {
|
|
65
|
-
const folder = input.name.replace(/^.*:/, '')
|
|
66
|
-
if (input.file && input.file !== 'SKILL.md') {
|
|
67
|
-
const result = await readSkillFile(identity, folder, input.file)
|
|
68
|
-
return {
|
|
69
|
-
ok: true,
|
|
70
|
-
summary: `read ${result.relativePath}`,
|
|
71
|
-
content: [
|
|
72
|
-
`<private_skill_file name="${folder}" file="${input.file}" path="${result.absolutePath}">`,
|
|
73
|
-
result.content,
|
|
74
|
-
'</private_skill_file>',
|
|
75
|
-
].join('\n'),
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
const skill = await readSkill(identity, folder)
|
|
79
|
-
const meta: string[] = []
|
|
80
|
-
meta.push(`name: ${skill.displayName ?? skill.name}`)
|
|
81
|
-
if (skill.whenToUse) meta.push(`when_to_use: ${skill.whenToUse}`)
|
|
82
|
-
if (skill.argumentHint) meta.push(`argument_hint: ${skill.argumentHint}`)
|
|
83
|
-
if (skill.version) meta.push(`version: ${skill.version}`)
|
|
84
|
-
if (skill.tags && skill.tags.length > 0) meta.push(`tags: ${skill.tags.join(', ')}`)
|
|
85
|
-
meta.push(`visibility: ${skill.visibility}`)
|
|
86
|
-
const content = [
|
|
87
|
-
`<private_skill name="${skill.name}" visibility="${skill.visibility}">`,
|
|
88
|
-
...meta,
|
|
89
|
-
'',
|
|
90
|
-
skill.body,
|
|
91
|
-
'</private_skill>',
|
|
92
|
-
].join('\n')
|
|
93
|
-
return {
|
|
94
|
-
ok: true,
|
|
95
|
-
summary: `read private skill ${skill.name}`,
|
|
96
|
-
content,
|
|
97
|
-
}
|
|
98
|
-
} catch (err: unknown) {
|
|
99
|
-
const message = (err as Error).message
|
|
100
|
-
return {
|
|
101
|
-
ok: false,
|
|
102
|
-
summary: 'read failed',
|
|
103
|
-
content: message,
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
}
|
package/src/tools/readTool.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
|
-
import os from 'node:os'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
import { z } from 'zod'
|
|
5
|
-
import type { EthagentConfig } from '../storage/config.js'
|
|
6
|
-
import type { Tool } from './contracts.js'
|
|
7
|
-
|
|
8
|
-
const schema = z.object({
|
|
9
|
-
path: z.string().min(1),
|
|
10
|
-
startLine: z.number().int().positive().optional(),
|
|
11
|
-
endLine: z.number().int().positive().optional(),
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
export const readTool: Tool<typeof schema> = {
|
|
15
|
-
name: 'read_file',
|
|
16
|
-
kind: 'read',
|
|
17
|
-
description: 'Read a text file from the current workspace. Use startLine and endLine to limit the range when the file is large.',
|
|
18
|
-
inputSchema: schema,
|
|
19
|
-
inputSchemaJson: {
|
|
20
|
-
type: 'object',
|
|
21
|
-
properties: {
|
|
22
|
-
path: { type: 'string', description: 'Path to the file to read.' },
|
|
23
|
-
startLine: { type: 'number', description: 'Optional 1-based starting line.' },
|
|
24
|
-
endLine: { type: 'number', description: 'Optional 1-based ending line.' },
|
|
25
|
-
},
|
|
26
|
-
required: ['path'],
|
|
27
|
-
},
|
|
28
|
-
parse(input) {
|
|
29
|
-
return schema.parse(input)
|
|
30
|
-
},
|
|
31
|
-
async buildPermissionRequest(input, context) {
|
|
32
|
-
assertNotPrivateContinuityWorkspacePath(input.path, context.config)
|
|
33
|
-
const fullPath = resolveWorkspacePath(context.workspaceRoot, input.path)
|
|
34
|
-
const relativePath = path.relative(context.workspaceRoot, fullPath) || path.basename(fullPath)
|
|
35
|
-
return {
|
|
36
|
-
kind: 'read',
|
|
37
|
-
path: fullPath,
|
|
38
|
-
relativePath,
|
|
39
|
-
directoryPath: path.dirname(fullPath),
|
|
40
|
-
title: 'Allow file read?',
|
|
41
|
-
subtitle: input.startLine || input.endLine
|
|
42
|
-
? `${fullPath} · lines ${input.startLine ?? 1}-${input.endLine ?? 'end'}`
|
|
43
|
-
: fullPath,
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
async execute(input, context) {
|
|
47
|
-
assertNotPrivateContinuityWorkspacePath(input.path, context.config)
|
|
48
|
-
const fullPath = resolveWorkspacePath(context.workspaceRoot, input.path)
|
|
49
|
-
const raw = await fs.readFile(fullPath, 'utf8')
|
|
50
|
-
const lines = raw.replace(/\r\n/g, '\n').split('\n')
|
|
51
|
-
const start = Math.max(1, input.startLine ?? 1)
|
|
52
|
-
const end = Math.max(start, input.endLine ?? lines.length)
|
|
53
|
-
const slice = lines.slice(start - 1, end)
|
|
54
|
-
const numbered = slice.map((line, i) => `${start + i}: ${line}`).join('\n')
|
|
55
|
-
return {
|
|
56
|
-
ok: true,
|
|
57
|
-
summary: `read ${path.relative(context.workspaceRoot, fullPath) || path.basename(fullPath)}`,
|
|
58
|
-
content: numbered,
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function assertNotPrivateContinuityWorkspacePath(
|
|
64
|
-
requestedPath: string,
|
|
65
|
-
config: EthagentConfig | undefined,
|
|
66
|
-
): void {
|
|
67
|
-
if (!config?.identity) return
|
|
68
|
-
const basename = path.basename(requestedPath.replaceAll('\\', '/')).toUpperCase()
|
|
69
|
-
if (basename !== 'SOUL.MD' && basename !== 'MEMORY.MD') return
|
|
70
|
-
throw new Error(
|
|
71
|
-
`read_file must not read ${basename} from the workspace; use read_private_continuity_file with file "${basename === 'SOUL.MD' ? 'SOUL.md' : 'MEMORY.md'}"`,
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function resolveWorkspacePath(workspaceRoot: string, requestedPath: string): string {
|
|
76
|
-
const expandedPath = requestedPath.startsWith('~')
|
|
77
|
-
? path.join(os.homedir(), requestedPath.slice(1))
|
|
78
|
-
: requestedPath
|
|
79
|
-
const fullPath = path.resolve(workspaceRoot, expandedPath)
|
|
80
|
-
const rel = path.relative(workspaceRoot, fullPath)
|
|
81
|
-
if (rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
82
|
-
throw new Error(`path escapes workspace: ${requestedPath}`)
|
|
83
|
-
}
|
|
84
|
-
return fullPath
|
|
85
|
-
}
|
package/src/tools/registry.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import type { AnthropicToolDefinition } from '../providers/anthropic.js'
|
|
2
|
-
import type { GeminiToolDefinition } from '../providers/gemini.js'
|
|
3
|
-
import type { OpenAIToolDefinition } from '../providers/openai-chat.js'
|
|
4
|
-
import type { Tool } from './contracts.js'
|
|
5
|
-
import { modePolicy, type SessionMode } from '../runtime/sessionMode.js'
|
|
6
|
-
import { bashTool } from './bashTool.js'
|
|
7
|
-
import { changeDirectoryTool } from './changeDirectoryTool.js'
|
|
8
|
-
import { deleteFileTool } from './deleteFileTool.js'
|
|
9
|
-
import { editTool } from './editTool.js'
|
|
10
|
-
import { listDirectoryTool } from './listDirectoryTool.js'
|
|
11
|
-
import { listSkillsTool } from './listSkillsTool.js'
|
|
12
|
-
import { listSkillFilesTool } from './listSkillFilesTool.js'
|
|
13
|
-
import { privateContinuityEditTool } from './privateContinuityEditTool.js'
|
|
14
|
-
import { privateContinuityReadTool } from './privateContinuityReadTool.js'
|
|
15
|
-
import { readSkillTool } from './readSkillTool.js'
|
|
16
|
-
import { readTool } from './readTool.js'
|
|
17
|
-
import { listMcpResourcesTool, readMcpResourceTool } from './mcpResourceTools.js'
|
|
18
|
-
import { writeFileTool } from './writeFileTool.js'
|
|
19
|
-
|
|
20
|
-
export const BUILTIN_TOOLS: Tool[] = [
|
|
21
|
-
changeDirectoryTool,
|
|
22
|
-
listDirectoryTool,
|
|
23
|
-
readTool,
|
|
24
|
-
privateContinuityReadTool,
|
|
25
|
-
listSkillsTool,
|
|
26
|
-
listSkillFilesTool,
|
|
27
|
-
readSkillTool,
|
|
28
|
-
listMcpResourcesTool,
|
|
29
|
-
readMcpResourceTool,
|
|
30
|
-
writeFileTool,
|
|
31
|
-
editTool,
|
|
32
|
-
privateContinuityEditTool,
|
|
33
|
-
deleteFileTool,
|
|
34
|
-
bashTool,
|
|
35
|
-
]
|
|
36
|
-
|
|
37
|
-
export type ToolAvailabilityContext = {
|
|
38
|
-
hasIdentity?: boolean
|
|
39
|
-
dynamicTools?: Tool[]
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function getTool(name: string, context: ToolAvailabilityContext = {}): Tool | undefined {
|
|
43
|
-
return [...(context.dynamicTools ?? []), ...BUILTIN_TOOLS].find(tool => tool.name === name)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function toolsForMode(mode: SessionMode = 'chat', context: ToolAvailabilityContext = {}): Tool[] {
|
|
47
|
-
const policy = modePolicy(mode)
|
|
48
|
-
const allTools = [...BUILTIN_TOOLS, ...(context.dynamicTools ?? [])]
|
|
49
|
-
return allTools.filter(tool => {
|
|
50
|
-
if (mode === 'plan' && tool.kind === 'mcp' && tool.readOnly !== true) return false
|
|
51
|
-
if (!policy.exposesToolKind(tool.kind)) return false
|
|
52
|
-
if ((tool.kind === 'private-continuity-read' || tool.kind === 'private-continuity-edit') && !context.hasIdentity) return false
|
|
53
|
-
return true
|
|
54
|
-
})
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function anthropicTools(mode: SessionMode = 'chat', context: ToolAvailabilityContext = {}): AnthropicToolDefinition[] {
|
|
58
|
-
return toolsForMode(mode, context).map(tool => ({
|
|
59
|
-
name: tool.name,
|
|
60
|
-
description: tool.description,
|
|
61
|
-
input_schema: tool.inputSchemaJson,
|
|
62
|
-
}))
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function openAITools(mode: SessionMode = 'chat', context: ToolAvailabilityContext = {}): OpenAIToolDefinition[] {
|
|
66
|
-
return toolsForMode(mode, context).map(tool => ({
|
|
67
|
-
type: 'function',
|
|
68
|
-
function: {
|
|
69
|
-
name: tool.name,
|
|
70
|
-
description: tool.description,
|
|
71
|
-
parameters: tool.inputSchemaJson,
|
|
72
|
-
},
|
|
73
|
-
}))
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const GEMINI_DROP_KEYS = new Set([
|
|
77
|
-
'additionalProperties',
|
|
78
|
-
'$schema',
|
|
79
|
-
'$ref',
|
|
80
|
-
'$defs',
|
|
81
|
-
'definitions',
|
|
82
|
-
])
|
|
83
|
-
|
|
84
|
-
function sanitizeForGemini(schema: unknown): unknown {
|
|
85
|
-
if (Array.isArray(schema)) return schema.map(sanitizeForGemini)
|
|
86
|
-
if (schema && typeof schema === 'object') {
|
|
87
|
-
const out: Record<string, unknown> = {}
|
|
88
|
-
for (const [k, v] of Object.entries(schema as Record<string, unknown>)) {
|
|
89
|
-
if (GEMINI_DROP_KEYS.has(k)) continue
|
|
90
|
-
out[k] = sanitizeForGemini(v)
|
|
91
|
-
}
|
|
92
|
-
return out
|
|
93
|
-
}
|
|
94
|
-
return schema
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function geminiTools(mode: SessionMode = 'chat', context: ToolAvailabilityContext = {}): GeminiToolDefinition[] {
|
|
98
|
-
return toolsForMode(mode, context).map(tool => ({
|
|
99
|
-
name: tool.name,
|
|
100
|
-
description: tool.description,
|
|
101
|
-
parameters: sanitizeForGemini(tool.inputSchemaJson) as GeminiToolDefinition['parameters'],
|
|
102
|
-
}))
|
|
103
|
-
}
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { z } from 'zod'
|
|
4
|
-
import { recordRewindSnapshot } from '../storage/rewind.js'
|
|
5
|
-
import type { EthagentConfig } from '../storage/config.js'
|
|
6
|
-
import type { Tool } from './contracts.js'
|
|
7
|
-
import { formatFileChangeResult, renderUnifiedFileDiff } from './fileDiff.js'
|
|
8
|
-
import { resolveWorkspacePath } from './readTool.js'
|
|
9
|
-
|
|
10
|
-
const schema = z.object({
|
|
11
|
-
path: z.string().min(1),
|
|
12
|
-
content: z.string(),
|
|
13
|
-
overwrite: z.boolean().optional(),
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
export const writeFileTool: Tool<typeof schema> = {
|
|
17
|
-
name: 'write_file',
|
|
18
|
-
kind: 'write',
|
|
19
|
-
description: 'Create a new text file, or replace an entire existing file only when overwrite is true. Prefer edit_file for targeted changes to existing files.',
|
|
20
|
-
inputSchema: schema,
|
|
21
|
-
inputSchemaJson: {
|
|
22
|
-
type: 'object',
|
|
23
|
-
properties: {
|
|
24
|
-
path: { type: 'string', description: 'Workspace-relative file path to write.' },
|
|
25
|
-
content: { type: 'string', description: 'Complete file contents to write.' },
|
|
26
|
-
overwrite: { type: 'boolean', description: 'Set true only when intentionally replacing an existing file.' },
|
|
27
|
-
},
|
|
28
|
-
required: ['path', 'content'],
|
|
29
|
-
},
|
|
30
|
-
parse(input) {
|
|
31
|
-
return schema.parse(normalizeWriteFileInput(input))
|
|
32
|
-
},
|
|
33
|
-
async buildPermissionRequest(input, context) {
|
|
34
|
-
const prepared = await prepareWrite(input, context)
|
|
35
|
-
return {
|
|
36
|
-
kind: 'write',
|
|
37
|
-
path: prepared.fullPath,
|
|
38
|
-
relativePath: prepared.relativePath,
|
|
39
|
-
directoryPath: path.dirname(prepared.fullPath),
|
|
40
|
-
title: prepared.existedBefore ? 'Allow file rewrite?' : 'Allow file creation?',
|
|
41
|
-
subtitle: prepared.fullPath,
|
|
42
|
-
before: previewText(prepared.before),
|
|
43
|
-
after: previewText(input.content),
|
|
44
|
-
diff: renderUnifiedFileDiff({ filePath: prepared.relativePath, before: prepared.before, after: input.content }),
|
|
45
|
-
changeSummary: prepared.existedBefore ? `replace entire ${prepared.relativePath}` : `create ${prepared.relativePath}`,
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
async execute(input, context) {
|
|
49
|
-
const prepared = await prepareWrite(input, context)
|
|
50
|
-
const rewindWarning = await tryRecordRewindSnapshot({
|
|
51
|
-
workspaceRoot: context.workspaceRoot,
|
|
52
|
-
filePath: prepared.fullPath,
|
|
53
|
-
relativePath: prepared.relativePath,
|
|
54
|
-
existedBefore: prepared.existedBefore,
|
|
55
|
-
previousContent: prepared.before,
|
|
56
|
-
changeSummary: prepared.existedBefore ? `replace entire ${prepared.relativePath}` : `create ${prepared.relativePath}`,
|
|
57
|
-
createdAt: new Date().toISOString(),
|
|
58
|
-
sessionId: context.checkpoint?.sessionId,
|
|
59
|
-
turnId: context.checkpoint?.turnId,
|
|
60
|
-
messageRole: context.checkpoint?.messageRole,
|
|
61
|
-
promptSnippet: context.checkpoint?.promptSnippet,
|
|
62
|
-
checkpointLabel: context.checkpoint?.checkpointLabel,
|
|
63
|
-
})
|
|
64
|
-
await fs.mkdir(path.dirname(prepared.fullPath), { recursive: true })
|
|
65
|
-
await fs.writeFile(prepared.fullPath, input.content, 'utf8')
|
|
66
|
-
return {
|
|
67
|
-
ok: true,
|
|
68
|
-
summary: prepared.existedBefore ? `replace entire ${prepared.relativePath}` : `create ${prepared.relativePath}`,
|
|
69
|
-
content: formatFileChangeResult(
|
|
70
|
-
rewindWarning
|
|
71
|
-
? `updated ${prepared.fullPath}\nwarning: ${rewindWarning}`
|
|
72
|
-
: `updated ${prepared.fullPath}`,
|
|
73
|
-
renderUnifiedFileDiff({ filePath: prepared.relativePath, before: prepared.before, after: input.content }),
|
|
74
|
-
),
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function normalizeWriteFileInput(input: unknown): unknown {
|
|
80
|
-
let value: unknown = input
|
|
81
|
-
if (typeof value === 'string') {
|
|
82
|
-
const trimmed = value.trim()
|
|
83
|
-
if (trimmed.startsWith('{')) {
|
|
84
|
-
try {
|
|
85
|
-
value = JSON.parse(trimmed)
|
|
86
|
-
} catch {
|
|
87
|
-
return input
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) return value
|
|
92
|
-
const normalized: Record<string, unknown> = { ...(value as Record<string, unknown>) }
|
|
93
|
-
if (typeof normalized.path === 'string' && normalized.path.trim() === '') {
|
|
94
|
-
normalized.path = undefined
|
|
95
|
-
}
|
|
96
|
-
return normalized
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async function prepareWrite(
|
|
100
|
-
input: z.infer<typeof schema>,
|
|
101
|
-
context: { workspaceRoot: string; config?: EthagentConfig },
|
|
102
|
-
) {
|
|
103
|
-
assertSafeWritePath(input.path)
|
|
104
|
-
assertNotPrivateContinuityWorkspacePath(input.path, context.config, 'write_file')
|
|
105
|
-
if (input.content.length === 0) {
|
|
106
|
-
throw new Error('Tool write_file content is empty; provide non-empty file contents')
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const fullPath = resolveWorkspacePath(context.workspaceRoot, input.path)
|
|
110
|
-
const relativePath = path.relative(context.workspaceRoot, fullPath) || path.basename(fullPath)
|
|
111
|
-
const { before, existedBefore } = await readExistingFile(fullPath)
|
|
112
|
-
|
|
113
|
-
return { fullPath, relativePath, before, existedBefore }
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async function readExistingFile(fullPath: string): Promise<{ before: string; existedBefore: boolean }> {
|
|
117
|
-
try {
|
|
118
|
-
const stats = await fs.stat(fullPath)
|
|
119
|
-
if (stats.isDirectory()) throw new Error('Tool write_file path points to a directory; provide a file path')
|
|
120
|
-
return { before: await fs.readFile(fullPath, 'utf8'), existedBefore: true }
|
|
121
|
-
} catch (error: unknown) {
|
|
122
|
-
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { before: '', existedBefore: false }
|
|
123
|
-
throw error
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async function tryRecordRewindSnapshot(
|
|
128
|
-
snapshot: Parameters<typeof recordRewindSnapshot>[0],
|
|
129
|
-
): Promise<string | undefined> {
|
|
130
|
-
try {
|
|
131
|
-
await recordRewindSnapshot(snapshot)
|
|
132
|
-
return undefined
|
|
133
|
-
} catch (error: unknown) {
|
|
134
|
-
return `rewind checkpoint was not recorded (${(error as Error).message || 'unknown error'})`
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function assertSafeWritePath(requestedPath: string): void {
|
|
139
|
-
const trimmed = requestedPath.trim()
|
|
140
|
-
if (trimmed !== requestedPath || trimmed.length === 0) {
|
|
141
|
-
throw new Error('Tool write_file path must be a clean workspace-relative file path')
|
|
142
|
-
}
|
|
143
|
-
if (/[|;&<>`]/.test(trimmed)) {
|
|
144
|
-
throw new Error('Tool write_file path must not contain shell operators')
|
|
145
|
-
}
|
|
146
|
-
if (/^(?:rm|del|erase|rmdir|remove-item|mkdir|type|cat|echo|copy|move|mv|cp)\b/i.test(trimmed)) {
|
|
147
|
-
throw new Error('Tool write_file path looks like a shell command; pass only the file path')
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function assertNotPrivateContinuityWorkspacePath(
|
|
152
|
-
requestedPath: string,
|
|
153
|
-
config: EthagentConfig | undefined,
|
|
154
|
-
toolName: string,
|
|
155
|
-
): void {
|
|
156
|
-
if (!config?.identity) return
|
|
157
|
-
const basename = path.basename(requestedPath.replaceAll('\\', '/')).toUpperCase()
|
|
158
|
-
if (basename !== 'SOUL.MD' && basename !== 'MEMORY.MD') return
|
|
159
|
-
throw new Error(
|
|
160
|
-
`${toolName} must not create or overwrite ${basename}; use propose_private_continuity_edit to patch the existing identity-vault scaffold`,
|
|
161
|
-
)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function previewText(text: string, max = 700): string {
|
|
165
|
-
if (text.length <= max) return text
|
|
166
|
-
return `${text.slice(0, max - 3)}...`
|
|
167
|
-
}
|
package/src/ui/BrandSplash.tsx
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react'
|
|
2
|
-
import { Text, Box } from 'ink'
|
|
3
|
-
import { theme, gradientColor } from './theme.js'
|
|
4
|
-
|
|
5
|
-
const glyphs = {
|
|
6
|
-
ethagent: `░░░░░░░╗░░░░░░░░╗░░╗ ░░╗ █████╗ ██████╗ ███████╗███╗ ██╗████████╗
|
|
7
|
-
░░╔════╝╚══░░╔══╝░░║ ░░║██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝
|
|
8
|
-
░░░░░╗ ░░║ ░░░░░░░║███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║
|
|
9
|
-
░░╔══╝ ░░║ ░░╔══░░║██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║
|
|
10
|
-
░░░░░░░╗ ░░║ ░░║ ░░║██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║
|
|
11
|
-
╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ `,
|
|
12
|
-
eyes: `
|
|
13
|
-
-+:
|
|
14
|
-
:=- -%@@@%.
|
|
15
|
-
*@@@@@#- *@@-
|
|
16
|
-
+@@. +@
|
|
17
|
-
@@= -#=-+++=+:
|
|
18
|
-
#% .:===-: -@* +@@@@%
|
|
19
|
-
*@-+@@@@@: %@@+ @@@=#@
|
|
20
|
-
*@= @@@@@@@- .@.@@@@@@@ :
|
|
21
|
-
@@+=@@@@@@@@@@@@: .% *@@@@@*-=
|
|
22
|
-
#:-@ -@@@@@@@@@-+% @ -@@@- #
|
|
23
|
-
: #+ @@@@@@@- -% =# =
|
|
24
|
-
-@: *@ .+%%
|
|
25
|
-
:%#: --
|
|
26
|
-
.-:
|
|
27
|
-
`,
|
|
28
|
-
tagline: ' privacy-first AI agent with a portable Ethereum identity ',
|
|
29
|
-
ellipsis: '…',
|
|
30
|
-
frame: {
|
|
31
|
-
topLeft: '╔═',
|
|
32
|
-
topRight: '╗',
|
|
33
|
-
side: '║',
|
|
34
|
-
bottomLeft: '╚═',
|
|
35
|
-
bottomRight: '╝',
|
|
36
|
-
horizontal: '═',
|
|
37
|
-
},
|
|
38
|
-
} as const
|
|
39
|
-
|
|
40
|
-
const Eyes = () => {
|
|
41
|
-
const lines = glyphs.eyes.split('\n')
|
|
42
|
-
const maxWidth = Math.max(1, ...lines.map(l => l.length))
|
|
43
|
-
return (
|
|
44
|
-
<Box flexDirection="column">
|
|
45
|
-
{lines.map((line, li) => (
|
|
46
|
-
<Text key={li}>
|
|
47
|
-
{[...line].map((ch, ci) => (
|
|
48
|
-
<Text key={ci} color={gradientColor(maxWidth <= 1 ? 0 : ci / (maxWidth - 1))}>{ch}</Text>
|
|
49
|
-
))}
|
|
50
|
-
</Text>
|
|
51
|
-
))}
|
|
52
|
-
</Box>
|
|
53
|
-
)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
type SplashProps = {
|
|
57
|
-
contextLine?: string
|
|
58
|
-
tipLine?: string
|
|
59
|
-
updateNotice?: string | null
|
|
60
|
-
compact?: boolean
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export const BrandSplash: React.FC<SplashProps> = ({ contextLine, tipLine, updateNotice, compact }) => {
|
|
64
|
-
const [width, setWidth] = useState<number>(() => process.stdout.columns ?? 80)
|
|
65
|
-
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
const stdout = process.stdout
|
|
68
|
-
const handleResize = () => setWidth(stdout.columns ?? 80)
|
|
69
|
-
stdout.on('resize', handleResize)
|
|
70
|
-
return () => {
|
|
71
|
-
stdout.off('resize', handleResize)
|
|
72
|
-
}
|
|
73
|
-
}, [])
|
|
74
|
-
|
|
75
|
-
const renderCompact = compact ?? width < 72
|
|
76
|
-
|
|
77
|
-
if (renderCompact) {
|
|
78
|
-
return (
|
|
79
|
-
<Box flexDirection="column" alignSelf="flex-start" padding={1}>
|
|
80
|
-
<Eyes />
|
|
81
|
-
<Text bold color={theme.accentWhite}>ethagent</Text>
|
|
82
|
-
{contextLine ? <Text color={theme.dim}>{contextLine}</Text> : null}
|
|
83
|
-
{tipLine ? <Text color={theme.dim}>{tipLine}</Text> : null}
|
|
84
|
-
{updateNotice ? <Text color={theme.dim}>{updateNotice}</Text> : null}
|
|
85
|
-
</Box>
|
|
86
|
-
)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const w = 69
|
|
90
|
-
const logoLines = glyphs.ethagent.split('\n').map(line => line.padEnd(w, ' '))
|
|
91
|
-
|
|
92
|
-
const bottomInline = contextLine ? ` ${truncateToFit(contextLine, w - 4)} ` : ''
|
|
93
|
-
const bottomPad = Math.max(0, w - bottomInline.length - 1)
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
<Box flexDirection="column" alignSelf="flex-start" padding={1}>
|
|
97
|
-
<Eyes />
|
|
98
|
-
<Text>
|
|
99
|
-
<Text color={theme.dim}>{glyphs.frame.topLeft}</Text>
|
|
100
|
-
<Text color={theme.dim}>{glyphs.tagline}</Text>
|
|
101
|
-
<Text color={theme.dim}>{glyphs.frame.horizontal.repeat(Math.max(0, w - glyphs.tagline.length - 1))}{glyphs.frame.topRight}</Text>
|
|
102
|
-
</Text>
|
|
103
|
-
{logoLines.map((line, i) => (
|
|
104
|
-
<Box key={i}>
|
|
105
|
-
<Text color={theme.dim}>{glyphs.frame.side}</Text>
|
|
106
|
-
<Text color={theme.dim}>{line}</Text>
|
|
107
|
-
<Text color={theme.dim}>{glyphs.frame.side}</Text>
|
|
108
|
-
</Box>
|
|
109
|
-
))}
|
|
110
|
-
{bottomInline ? (
|
|
111
|
-
<Text>
|
|
112
|
-
<Text color={theme.dim}>{glyphs.frame.bottomLeft}</Text>
|
|
113
|
-
<Text color={theme.dim}>{bottomInline}</Text>
|
|
114
|
-
<Text color={theme.dim}>{glyphs.frame.horizontal.repeat(bottomPad)}{glyphs.frame.bottomRight}</Text>
|
|
115
|
-
</Text>
|
|
116
|
-
) : (
|
|
117
|
-
<Text color={theme.dim}>{glyphs.frame.bottomLeft.slice(0, 1) + glyphs.frame.horizontal.repeat(w) + glyphs.frame.bottomRight}</Text>
|
|
118
|
-
)}
|
|
119
|
-
{tipLine || updateNotice ? (
|
|
120
|
-
<Box marginTop={1} flexDirection="column">
|
|
121
|
-
{tipLine ? <Text color={theme.dim}>{tipLine}</Text> : null}
|
|
122
|
-
{updateNotice ? <Text color={theme.dim}>{updateNotice}</Text> : null}
|
|
123
|
-
</Box>
|
|
124
|
-
) : null}
|
|
125
|
-
</Box>
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function truncateToFit(text: string, max: number): string {
|
|
130
|
-
if (text.length <= max) return text
|
|
131
|
-
if (max <= 1) return text.slice(0, Math.max(0, max))
|
|
132
|
-
return text.slice(0, max - 1) + glyphs.ellipsis
|
|
133
|
-
}
|
package/src/ui/terminalTitle.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export const TITLE_STATIC = 'ethagent'
|
|
2
|
-
export const TITLE_ANIMATION_FRAMES = [
|
|
3
|
-
'ethagent ⠋',
|
|
4
|
-
'ethagent ⠙',
|
|
5
|
-
'ethagent ⠹',
|
|
6
|
-
'ethagent ⠸',
|
|
7
|
-
'ethagent ⠼',
|
|
8
|
-
'ethagent ⠴',
|
|
9
|
-
'ethagent ⠦',
|
|
10
|
-
'ethagent ⠧',
|
|
11
|
-
'ethagent ⠇',
|
|
12
|
-
'ethagent ⠏',
|
|
13
|
-
] as const
|
|
14
|
-
export const TITLE_ANIMATION_INTERVAL_MS = 80
|
|
15
|
-
|
|
16
|
-
export function setTerminalTitle(title: string): void {
|
|
17
|
-
const clean = title.replace(/[\x00-\x1f]/g, '')
|
|
18
|
-
if (process.platform === 'win32') {
|
|
19
|
-
process.title = clean
|
|
20
|
-
}
|
|
21
|
-
if (process.stdout.isTTY) {
|
|
22
|
-
process.stdout.write(`\x1b]0;${clean}\x07`)
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function clearTerminalTitle(): void {
|
|
27
|
-
if (process.stdout.isTTY) {
|
|
28
|
-
process.stdout.write('\x1b]0;\x07')
|
|
29
|
-
}
|
|
30
|
-
}
|