Files
aiturk-hermes-ide/apps/desktop/src/i18n/en.ts
T

3735 lines
165 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { FIELD_DESCRIPTIONS, FIELD_LABELS } from '@/app/settings/constants'
import { EN_RUNTIME_SETTINGS } from './en-runtime-settings'
import { EN_SURFACES } from './en-surfaces'
import type { Translations } from './types'
export const en: Translations = {
surfaces: EN_SURFACES,
common: {
apply: 'Apply',
back: 'Back',
save: 'Save',
saving: 'Saving…',
cancel: 'Cancel',
change: 'Change',
choose: 'Choose',
clear: 'Clear',
close: 'Close',
collapse: 'Collapse',
confirm: 'Confirm',
connect: 'Connect',
connecting: 'Connecting',
continue: 'Continue',
copied: 'Copied',
copy: 'Copy',
copyFailed: 'Copy failed',
delete: 'Delete',
docs: 'Docs',
done: 'Done',
error: 'Error',
expand: 'Expand',
failed: 'Failed',
formatJson: 'Format JSON',
free: 'Free',
loading: 'Loading…',
notSet: 'Not set',
refresh: 'Refresh',
remove: 'Remove',
replace: 'Replace',
retry: 'Retry',
run: 'Run',
send: 'Send',
set: 'Set',
skip: 'Skip',
update: 'Update',
tryHint: term => `Try “${term}”`,
on: 'On',
off: 'Off'
},
fileMenu: {
revealFinder: 'Reveal in Finder',
revealExplorer: 'Reveal in File Explorer',
revealFileManager: 'Open containing folder',
revealInSidebar: 'Reveal in filetree',
copyPath: 'Copy path',
copyRelativePath: 'Copy relative path',
download: 'Download',
downloadSaved: 'Saved',
downloadFailed: 'Download failed',
rename: 'Rename…',
delete: 'Delete',
renameTitle: 'Rename',
renameLabel: 'New name',
deleteTitle: name => `Delete ${name}?`,
deleteBody: 'It will be moved to the Trash — you can restore it from there.',
pathCopied: 'Path copied'
},
boot: {
ready: 'Hermes Desktop is ready',
desktopBootFailedWithMessage: message => `Desktop boot failed: ${message}`,
steps: {
connectingGateway: 'Connecting live desktop gateway',
loadingSettings: 'Loading Hermes settings',
loadingSessions: 'Loading recent sessions',
retryingRemoteBackend: 'Reconnecting to the remote Hermes backend…',
startingDesktopConnection: 'Starting desktop connection',
startingHermesDesktop: 'Starting Hermes Desktop…'
},
errors: {
backgroundExited: 'Hermes background process exited.',
backgroundExitedDuringStartup: 'Hermes background process exited during startup.',
backendStopped: 'Backend stopped',
desktopBootFailed: 'Desktop boot failed',
gatewayConnectionLost: 'Lost connection to the gateway',
gatewayConnectionLostDetail:
'Still retrying in the background. You can keep reading and drafting — open Gateway settings if this persists.',
gatewaySignInRequired: 'Gateway sign-in required',
ipcBridgeUnavailable: 'Desktop IPC bridge is unavailable.'
},
failure: {
title: "Hermes couldn't start",
description:
"The background gateway didn't come up. Try one of the recovery steps below. Nothing here deletes your chats or settings.",
remoteTitle: 'Remote gateway sign-in required',
remoteDescription:
'Your remote gateway session has expired. Sign in again to reconnect. Nothing here deletes your chats or settings.',
retry: 'Retry',
repairInstall: 'Repair install',
useLocalGateway: 'Use local gateway',
gatewaySettings: 'Gateway settings',
back: 'Back',
openLogs: 'Open logs',
repairHint: 'Repair re-runs the installer and can take a few minutes on a fresh machine.',
remoteSignInHint: signInLabel =>
`Signs out of the saved remote browser session, then opens ${signInLabel}. Use local gateway to switch to the bundled backend instead.`,
signOutAndSignIn: 'Sign out & sign in',
remoteFailureHint: 'Check the gateway URL and sign-in under Gateway settings, or switch to the local gateway.',
cloudDownTitle: 'Nous Cloud agent is down',
cloudDownDescription:
'The Nous-managed cloud agent this gateway connects to is returning a server error. It cannot be restarted from here — check its status, switch to the local gateway, or get support.',
cloudDownHint:
'The buttons below open the Nous Portal (instance status and controls) and our Discord for support.',
cloudDownCheckPortal: 'Check Portal status',
cloudDownDiscord: 'Get help on Discord',
hideRecentLogs: 'Hide recent logs',
showRecentLogs: 'Show recent logs',
signedInTitle: 'Signed in',
signedInMessage: 'Reconnecting to the remote gateway…',
signInIncompleteTitle: 'Sign-in incomplete',
signInIncompleteMessage: 'The login window closed before authentication finished.',
signInFailed: 'Sign-in failed',
signInToRemoteGateway: 'Sign in to remote gateway',
signInWithProvider: provider => `Sign in with ${provider}`,
identityProvider: 'your identity provider'
}
},
notifications: {
region: 'Notifications',
hide: 'Hide',
show: 'Show',
more: count => `${count} more ${count === 1 ? 'notification' : 'notifications'}`,
clearAll: 'Clear all',
dismiss: 'Dismiss notification',
details: 'Details',
copyDetail: 'Copy detail',
copyDetailFailed: 'Could not copy notification detail',
backendOutOfDateTitle: 'Backend out of date',
backendOutOfDateMessage:
'Your Hermes backend is older than this desktop build and may not work correctly. Update to align them.',
installMethodUnsupportedTitle: 'Unsupported install method',
updateHermes: 'Update Hermes',
updateReadyTitle: 'Update ready',
updateReadyMessage: count => `${count} new change${count === 1 ? '' : 's'} available.`,
updateReadyMessageUnknown: 'A new update is available.',
seeWhatsNew: "See what's new",
mcp: {
needsAuthTitle: 'MCP server needs re-authentication',
needsAuthMessage: name => `${name} MCP needs re-authentication.`,
errorTitle: 'MCP server unreachable',
errorMessage: name => `${name} MCP failed its health check.`,
signIn: 'Sign in',
view: 'View'
},
errors: {
elevenLabsNeedsKey: 'ElevenLabs STT needs ELEVENLABS_API_KEY.',
elevenLabsRejectedKey: 'ElevenLabs rejected the API key (401).',
diskFull: 'Disk full — free some space, then try again.',
gatewayAuthFailed: 'Gateway authentication failed — check your API_SERVER_KEY.',
methodNotAllowed:
'The desktop backend rejected that request (405 Method Not Allowed). Try restarting Hermes Desktop.',
microphonePermission: 'Microphone permission was denied.',
openaiRejectedApiKey: 'OpenAI rejected the API key.',
openaiRejectedApiKeyWithStatus: status => `OpenAI rejected the API key (${status} invalid_api_key).`,
openaiTtsNeedsKey: 'OpenAI TTS needs VOICE_TOOLS_OPENAI_KEY or OPENAI_API_KEY.',
codeSkewRestartRequired: 'This backend is running old code after an update. Restart it to load the new code.'
},
voice: {
configureSpeechToText: 'Configure speech-to-text to use voice mode.',
couldNotStartSession: 'Could not start voice session',
microphoneAccessDenied: 'Microphone access denied.',
microphoneConstraintsUnsupported: 'Microphone constraints are not supported by this device.',
microphoneFailed: 'Microphone failed',
microphoneInUse: 'Microphone is already in use by another app.',
microphonePermissionDenied: 'Microphone permission was denied.',
microphoneStartFailed: 'Could not start microphone recording.',
microphoneUnsupported: 'This runtime does not support microphone recording.',
noMicrophone: 'No microphone was found.',
noSpeechDetected: 'No speech detected',
playbackFailed: 'Voice playback failed',
recordingFailed: 'Voice recording failed',
sayStopToEnd: phrase => `Say "${phrase}" to end the voice chat.`,
transcriptionFailed: 'Voice transcription failed',
transcriptionUnavailable: 'Voice transcription is not available yet.',
tryRecordingAgain: 'Try recording again.',
unavailable: 'Voice unavailable'
},
native: {
approvalTitle: 'Approval needed',
approveAction: 'Approve',
rejectAction: 'Reject',
inputTitle: 'Input needed',
inputBody: 'Hermes is waiting for your response.',
turnDoneTitle: 'Hermes finished',
turnDoneBody: '',
turnErrorTitle: 'Turn failed',
backgroundDoneTitle: 'Background task finished',
backgroundFailedTitle: 'Background task failed',
creditsTitle: 'Credits'
}
},
remoteDisplayBanner: {
message: reason =>
`Software rendering active — remote display detected (${reason}). GPU acceleration is disabled to prevent flickering.`
},
billingBlock: {
titleNous: 'Out of Nous credits',
titleProvider: provider => `Out of credits — ${provider}`,
fallbackMessage: 'Your account is out of credits. Add credits to keep going.',
openBilling: 'Open billing',
addCredits: 'Add credits',
dismiss: 'Dismiss'
},
sendDiagnostics: {
title: 'Send diagnostics to Nous',
privacyNotice:
'This uploads a debug bundle to Nous-internal storage (not a public paste). It includes system info (OS, versions, provider, which API keys are configured — never the keys themselves) and full agent, gateway, and desktop logs (up to 512 KB each), which likely contain conversation content, tool outputs, and file paths. Secrets are redacted before upload. The bundle is viewable only by Nous staff and allowlisted Discord moderators, and auto-deletes after 14 days.',
upload: 'Upload',
uploading: 'Uploading…',
cancel: 'Cancel',
close: 'Close',
copyLink: 'Copy link',
uploadIdFallback: id => `No view link returned — quote upload ID ${id} to support`,
doneTitle: 'Diagnostics sent',
doneDescription:
'Your bundle was uploaded privately. Share the link below in your support thread so the team can see your logs.',
failedTitle: 'Upload failed',
failedHint:
'You can also run `hermes debug share --nous` from a terminal, or `hermes debug share --local` to print the report without uploading.',
handoffLead: 'Pick up the discussion in:',
links: {
github: 'GitHub Issues',
portal: 'Nous Portal Support',
discord: 'Discord'
}
},
titlebar: {
hideSidebar: 'Hide sidebar',
showSidebar: 'Show sidebar',
search: 'Search',
searchTitle: 'Search sessions, views, and actions',
swapSidebarSides: 'Swap sidebar sides',
hideRightSidebar: 'Hide right sidebar',
showRightSidebar: 'Show right sidebar',
unreadSessions: count => (count === 1 ? '1 unread session' : `${count} unread sessions`),
muteHaptics: 'Mute haptics',
unmuteHaptics: 'Unmute haptics',
openSettings: 'Open settings',
openStarmap: 'Open memory graph',
enterHud: 'HUD mode',
exitHud: 'Exit HUD mode',
resetHudLayout: 'Reset HUD size and position',
layoutEditor: 'Layout editor',
layoutEditorTitle: mod => `Layout editor — ${mod}-click resets the layout`
},
keybinds: {
title: 'Keyboard shortcuts',
subtitle: open => `Click a shortcut to rebind it · ${open} reopens this panel.`,
search: 'Search shortcuts…',
rebind: 'Rebind',
reset: 'Reset to default',
resetAll: 'Reset all',
pressKey: 'Press a key…',
set: 'set',
conflictWith: label => `Also bound to “${label}”`,
categories: {
composer: 'Composer',
profiles: 'Profiles',
session: 'Session',
navigation: 'Navigation',
view: 'View'
},
actions: {
'keybinds.openPanel': 'Open keyboard shortcuts',
'nav.commandPalette': 'Open command palette',
'nav.commandCenter': 'Open command center',
'nav.settings': 'Open settings',
'nav.profiles': 'Open profiles',
'nav.skills': 'Open skills',
'nav.messaging': 'Open messaging',
'nav.artifacts': 'Open artifacts',
'nav.cron': 'Open scheduled jobs',
'nav.agents': 'Open agents',
'session.new': 'New session',
'session.newTab': 'New session tab',
'session.newWindow': 'New window',
'session.next': 'Next session',
'session.prev': 'Previous session',
'session.slot.1': 'Switch to recent session 1',
'session.slot.2': 'Switch to recent session 2',
'session.slot.3': 'Switch to recent session 3',
'session.slot.4': 'Switch to recent session 4',
'session.slot.5': 'Switch to recent session 5',
'session.slot.6': 'Switch to recent session 6',
'session.slot.7': 'Switch to recent session 7',
'session.slot.8': 'Switch to recent session 8',
'session.slot.9': 'Switch to recent session 9',
'session.focusSearch': 'Search sessions',
'session.togglePin': 'Pin / unpin current session',
'session.archive': 'Archive current session',
'workspace.newWorktree': 'New worktree',
'workspace.openFolder': 'Open folder as project',
'composer.focus': 'Focus composer',
'composer.modelPicker': 'Open model picker',
'composer.voice': 'Start / stop voice conversation',
'view.toggleSidebar': 'Toggle sessions sidebar',
'view.toggleRightSidebar': 'Toggle file browser',
'view.toggleReview': 'Toggle review pane',
'view.toggleStatusbar': 'Toggle status bar',
'view.toggleTabStrip': 'Toggle tabs',
'view.showFiles': 'Show file browser',
'view.showBrowser': 'Open browser',
'view.toggleHud': 'Toggle HUD mode',
'hud.snapToPointer': 'Move HUD to pointer (global, while HUD is open)',
'view.showTerminal': 'Toggle terminal',
'view.newTerminal': 'New terminal',
'view.nextTerminal': 'Next terminal',
'view.prevTerminal': 'Previous terminal',
'view.closeTerminal': 'Close terminal',
'view.selectionToComposer': 'Send selection to composer',
'view.terminalCopy': 'Copy terminal selection',
'view.terminalPaste': 'Paste into terminal',
'view.closeTab': 'Close tab',
'view.reopenTab': 'Reopen closed tab',
'view.flipPanes': 'Swap sidebar sides',
'view.findInPage': 'Find in page',
'view.findNext': 'Find next match',
'view.findPrevious': 'Find previous match',
'appearance.toggleMode': 'Toggle light / dark',
'profile.default': 'Switch to default profile',
'profile.switch.1': 'Switch to profile 1',
'profile.switch.2': 'Switch to profile 2',
'profile.switch.3': 'Switch to profile 3',
'profile.switch.4': 'Switch to profile 4',
'profile.switch.5': 'Switch to profile 5',
'profile.switch.6': 'Switch to profile 6',
'profile.switch.7': 'Switch to profile 7',
'profile.switch.8': 'Switch to profile 8',
'profile.switch.9': 'Switch to profile 9',
'profile.switch.10': 'Switch to profile 10',
'profile.switch.11': 'Switch to profile 11',
'profile.switch.12': 'Switch to profile 12',
'profile.switch.13': 'Switch to profile 13',
'profile.switch.14': 'Switch to profile 14',
'profile.switch.15': 'Switch to profile 15',
'profile.switch.16': 'Switch to profile 16',
'profile.switch.17': 'Switch to profile 17',
'profile.switch.18': 'Switch to profile 18',
'profile.next': 'Next profile',
'profile.prev': 'Previous profile',
'profile.toggleAll': 'Toggle all-profiles view',
'profile.create': 'Create profile',
'composer.send': 'Send message',
'composer.newline': 'Insert newline',
'composer.steer': 'Steer the running turn',
'composer.queue': 'Queue message',
'composer.sendQueued': 'Send next queued turn',
'composer.mention': 'Reference files, folders, URLs',
'composer.slash': 'Slash command palette',
'composer.help': 'Quick help',
'composer.history': 'Cycle popover / history',
'composer.cancel': 'Close popover · cancel run'
}
},
findInPage: {
next: 'Next match',
previous: 'Previous match'
},
language: {
label: 'Language',
description: 'Choose the language for the desktop interface.',
saving: 'Saving language…',
saveError: 'Language update failed',
switchTo: 'Switch language',
searchPlaceholder: 'Search languages…',
noResults: 'No languages found'
},
settings: {
runtime: EN_RUNTIME_SETTINGS,
closeSettings: 'Close settings',
exportConfig: 'Export config',
importConfig: 'Import config',
resetToDefaults: 'Reset to defaults',
resetConfirm: 'Reset all settings to Hermes defaults?',
exportFailed: 'Export failed',
resetFailed: 'Reset failed',
nav: {
providers: 'Providers',
providerAccounts: 'Accounts',
providerApiKeys: 'API keys',
providerCustomEndpoints: 'Custom Endpoints',
providerLocalModels: 'Local Models',
gateway: 'Gateways',
apiKeys: 'Tools & Keys',
keybinds: 'Keyboard Shortcuts',
keysTools: 'Tools',
keysSettings: 'Settings',
mcp: 'MCP',
archivedChats: 'Archived Chats',
about: 'About',
billing: 'Billing',
notifications: 'Notifications',
plugins: 'Plugins'
},
plugins: {
title: 'Desktop plugins',
blurb: 'Bundled or dropped into the desktop-plugins folder. Disable to unload live.',
count: n => `${n} installed`,
openFolder: 'Open plugins folder',
rescan: 'Rescan',
reveal: 'Reveal in file manager',
enable: 'Enable',
disable: 'Disable',
failed: 'failed',
empty: 'No desktop plugins installed yet.',
kinds: { bundled: 'bundled', disk: 'on disk', runtime: 'runtime' },
agent: {
title: 'Agent plugins',
blurb:
'Plugins you installed into the Hermes backend — tools, skills, MCP servers, hooks, and slash commands. Portable ones are Agent Plugins packages (skills + MCP bundles that work in other agents too). Toggles apply to new sessions.',
appliesTo: 'Applies to:',
empty: 'No agent plugins installed yet.',
loadFailed: 'Could not load agent plugins',
portable: 'portable',
search: 'Search plugins…',
noMatches: 'No plugins match your search.',
toggleFailed: (name: string) => `Could not toggle ${name}`,
updateBackendToManage: 'Update the Hermes backend to manage this plugin from Desktop.',
sources: { bundled: 'bundled', user: 'user', git: 'git', project: 'project', entrypoint: 'pip' }
},
installModal: {
title: 'Install plugin',
description: 'Review what this repository contains before installing anything.',
repoLabel: 'Repository',
includesHeading: 'This package includes',
agentLabel: 'Agent plugin',
desktopLabel: 'Desktop UI',
agentTargetLocal: profile => `Installs into the ${profile} backend (~/.hermes/plugins/)`,
agentTargetRemote: profile => `Installs into the connected ${profile} backend`,
desktopTarget: "Installs into this app's local desktop-plugins folder",
desktopOnlyNote: 'Desktop-only packages do not install a backend agent plugin.',
insecureWarning: 'This URL uses an insecure or local scheme. Prefer https:// or git@ for production installs.',
securityHeading: 'Before you install',
securityIntro:
'Install only from sources you trust — review the repository below if you want to see what will be added.',
sourceHeading: 'Source code',
viewRepository: 'View repository',
viewPluginFiles: 'View plugin files',
gitCloneLabel: 'Git clone URL',
enableAgent: 'Enable agent plugin after install',
forceReinstall: 'Force reinstall (replace if already installed)',
install: 'Install',
installing: 'Installing…',
probing: 'Inspecting repository…',
probeUnavailable: 'Plugin inspection is unavailable in this environment.',
desktopUnavailable: 'Desktop plugin install is unavailable in this environment.',
selectComponent: 'Select at least one component to install.',
agentSuccess: name => `Agent plugin ${name} installed`,
desktopSuccess: name => `Desktop plugin ${name} installed`,
agentFailed: 'Agent plugin install failed',
desktopFailed: 'Desktop plugin install failed',
missingEnv: vars => `Missing env vars: ${vars}. Add them in Settings → Keys.`
}
},
notifications: {
title: 'Notifications',
intro: 'OS notifications (not in-app toasts). Per device.',
enableAll: 'Enable notifications',
enableAllDesc: 'Off silences every notification below.',
focusedHint: 'Completion alerts only fire while Hermes is in the background.',
kinds: {
approval: {
label: 'Approval needed',
description: 'A command is waiting for you to approve or reject it.'
},
input: {
label: 'Input needed',
description: 'Hermes asked a question or needs a password or secret.'
},
turnDone: {
label: 'Response ready',
description: 'A turn finished while Hermes was in the background.'
},
turnError: {
label: 'Turn failed',
description: 'Background turn errors.'
},
backgroundDone: {
label: 'Background task finished',
description: 'A backgrounded terminal command completed.'
},
credits: {
label: 'Credit alerts',
description: 'Credit access is paused or restored.'
},
plugin: {
label: 'Plugin notifications',
description: 'A desktop plugin sent a notification while Hermes was in the background.'
}
},
test: 'Send test notification',
testTitle: 'Hermes',
testBody: 'Notifications are working.',
testSent: 'Test sent. If nothing appears, check your OS notification permissions and Focus/Do Not Disturb.',
testUnsupported: 'This system does not support native notifications.',
completionSoundTitle: 'Completion Sound',
completionSoundDesc: 'Plays when an agent turn finishes. Pick a preset and preview it here.',
completionSoundPreview: 'Preview'
},
sections: {
browser: 'Browser',
model: 'Model',
chat: 'Chat',
appearance: 'Appearance',
workspace: 'Workspace',
safety: 'Safety',
memory: 'Memory & Context',
voice: 'Voice',
advanced: 'Advanced'
},
searchPlaceholder: {
about: 'About Hermes Desktop',
config: 'Search settings...',
gateway: 'Gateway connection...',
keys: 'Search API keys...',
mcp: 'Search MCP servers...',
sessions: 'Search archived sessions...'
},
modeOptions: {
light: { label: 'Light', description: 'Bright desktop surfaces' },
dark: { label: 'Dark', description: 'Low-glare workspace' },
system: { label: 'System', description: 'Follow OS appearance' }
},
appearance: {
title: 'Appearance',
intro: 'Desktop-only. Mode is brightness; theme is palette and chat chrome.',
colorMode: 'Color Mode',
colorModeDesc: 'Pick a fixed mode or let Hermes follow your system setting.',
toolViewTitle: 'Tool Call Display',
toolViewDesc: 'Product hides raw tool payloads; Technical shows full input/output.',
reasoningCollapsedTitle: 'Collapse thinking by default',
reasoningCollapsedDesc: 'Keep streamed reasoning available without expanding it until you open it.',
uiScaleTitle: 'UI Scale',
uiScaleDesc: (percent: number) =>
`Scales text and controls across the whole app. Cmd/Ctrl with +, - and 0 also works. Current: ${percent}%.`,
sessionDensityTitle: 'Session List Density',
sessionDensityDesc: 'Choose how much context appears beneath session titles in the sidebar.',
sessionDensityCompact: 'Compact',
sessionDensityComfortable: 'Comfortable',
sessionDensityDetailed: 'Detailed',
tabStripTitle: 'Tab Strip',
tabStripDesc: 'Show tabs above a zone. Auto hides them when a zone holds a single pane.',
tabStripAuto: 'Auto',
tabStripAlways: 'Always',
tabStripNever: 'Never',
terminalFontTitle: 'Terminal Font',
terminalFontDesc:
'Choose an installed font for Desktop terminals. Nerd Fonts render Powerlevel10k and shell icons; leave blank to use bundled JetBrains Mono.',
terminalFontPlaceholder: 'MesloLGS NF or a CSS font stack',
terminalFontPreview: 'Glyph preview',
terminalFontReset: 'Use default',
translucencyTitle: 'Window Translucency',
translucencyDesc: 'See your desktop through the whole window, text and all. Tuned separately for light and dark.',
translucencyGlassDesc:
'Matte glass: the desktop shows through as a smooth blur while text stays sharp. Tuned separately for light and dark.',
translucencyModeClear: 'Clear',
translucencyModeGlass: 'Glass',
translucencyTintTitle: 'Tint',
translucencyFadeTitle: 'Fade',
translucencyFrostTitle: 'Frost',
translucencyFrost: {
'under-window': 'Deep',
popover: 'Soft',
titlebar: 'Bright',
header: 'Glare'
},
translucencyScopeTitle: 'Area',
translucencyScope: {
window: 'Whole window',
sidebar: 'Sidebar only'
},
backdropTitle: 'Chat Backdrop',
backdropDesc: 'The faint statue image behind the conversation.',
introSplashTitle: 'Intro Splash',
introSplashDesc: 'The wordmark and prompt shown on an empty chat.',
reactionsTitle: 'Message Reactions',
reactionsDesc: 'iMessage-style emoji tapbacks — react to messages, and Hermes can react to yours.',
tipsTitle: 'In-App Tips',
tipsDesc:
'A small bubble pointing at one part of the app, shown occasionally while idle and by Hermes when it helps. Closing one retires it for good.',
tipsReset: (count: number) => `Bring back ${count} closed ${count === 1 ? 'tip' : 'tips'}`,
toursTitle: 'Guided Tours',
toursDesc: 'Let Hermes walk you through the app, dimming the screen and spotlighting each step.',
composerPopoutTitle: 'Floating Composer',
composerPopoutDesc: 'Allow dragging the composer out of its dock. Turn this off to keep it locked at the bottom.',
vibeHeartsTitle: 'Vibe Hearts',
vibeHeartsDesc:
'Floating hearts when you say thanks, ily, good bot, or send a heart. Separate from Message Reactions above.',
embedsTitle: 'Inline Embeds',
embedsDesc:
'Rich previews load from third-party sites (YouTube, X, …). Ask shows a placeholder until you allow each one; Always loads them automatically; Off keeps plain links.',
embedsAsk: 'Ask',
embedsAlways: 'Always',
embedsOff: 'Off',
embedsReset: (count: number) => `Reset ${count} allowed ${count === 1 ? 'service' : 'services'}`,
resumeLastSessionTitle: 'Reopen Last Chat on Launch',
resumeLastSessionDesc:
'When enabled, the app reopens your most recent chat on cold start. Turn off to always start with a fresh new chat.',
product: 'Product',
productDesc: 'Human-friendly tool activity with concise summaries.',
technical: 'Technical',
technicalDesc: 'Include raw tool args/results and low-level details.',
themeTitle: 'Theme',
themeDesc: 'Desktop palettes only. The selected mode is applied on top.',
themeProfileNote: profile => `Saved for the ${profile} profile — each profile keeps its own theme.`,
installTitle: 'Install from VS Code',
installDesc:
'Paste a Marketplace extension id (e.g. dracula-theme.theme-dracula) to convert its color theme into a desktop palette.',
installPlaceholder: 'publisher.extension',
installButton: 'Install',
installing: 'Installing…',
installError: 'Could not install that theme.',
installed: name => `Installed “${name}”.`,
removeTheme: 'Remove theme',
importedBadge: 'Imported',
pet: {
title: 'Pet',
intro:
'Adopt an animated petdex mascot that floats over the app and reacts to what Hermes is doing — running while tools execute, celebrating on success, sulking on errors.',
restartHint:
'Pets need a quick restart — the running app started before this feature was added. Quit and reopen Hermes, then come back here.',
on: 'On',
off: 'Off',
scaleTitle: 'Size',
scaleDesc: 'Resize the floating mascot. Applies everywhere instantly.',
roamTitle: 'Roam',
roamDesc: 'Let the pet wander the window on its own while idle.',
chooseTitle: 'Choose a pet',
chooseDesc: 'Picking one installs it (if needed) and makes it active.',
searchPlaceholder: 'Search pets…',
unreachable: "Couldn't reach the petdex gallery. Check your connection and reopen this page.",
noMatch: query => `No pets match "${query}".`,
installedTag: 'installed',
generatedTag: 'Generated',
countCapped: (cap, total) => `Showing ${cap} of ${total} — type to narrow it down.`,
count: n => `${n} pet${n === 1 ? '' : 's'}.`,
uninstall: name => `Uninstall ${name}`,
delete: name => `Delete ${name}`,
deleteTitle: name => `Delete ${name}?`,
deleteBody: "This permanently deletes the pet — it can't be reinstalled.",
deleteConfirm: 'Delete',
rename: name => `Rename ${name}`,
renameTitle: 'Rename pet',
renamePlaceholder: 'Name your pet',
renameSave: 'Save',
exportPet: name => `Export ${name}`,
adoptFailed: slug => `Could not adopt ${slug}`,
uninstallFailed: slug => `Could not uninstall ${slug}`,
renameFailed: slug => `Could not rename ${slug}`,
exportFailed: slug => `Could not export ${slug}`,
noneAvailable: 'No pets available to turn on right now.',
turnOnFailed: 'Could not turn the pet on.',
turnOffFailed: 'Could not turn the pet off.'
}
},
fieldLabels: FIELD_LABELS,
fieldDescriptions: FIELD_DESCRIPTIONS,
about: {
heading: 'Hermes Desktop',
version: value => `Version ${value}`,
versionUnavailable: 'Version unavailable',
bundleOutOfSync: 'App build out of date',
bundleOutOfSyncDesc:
'The Hermes runtime was updated, but the desktop app itself is still an older build — new interface features (like Bot Mode) will be missing until it updates. Run the update below to rebuild the app. If that doesn\u2019t clear this warning, reinstall from the latest desktop installer.',
bundleOutOfSyncAction: 'Get the installer',
bundleSwapPending: 'Restart to finish the update',
bundleSwapPendingDesc:
'The updated app is already installed — Hermes only needs to restart to load it. Chats and settings are untouched.',
bundleSwapPendingAction: 'Restart Hermes',
updates: 'Updates',
checkNow: 'Check now',
checking: 'Checking…',
seeWhatsNew: "See what's new",
updateNow: 'Update now',
releaseNotes: 'Release notes',
onLatest: "You're on the latest version.",
installing: 'An update is currently installing.',
cantUpdate: "This build can't update itself from inside the app.",
cantReach: "We couldn't reach the update server.",
tapCheck: 'Tap "Check now" to look for updates.',
updateReady: count => `A new update is ready (${count} change${count === 1 ? '' : 's'} included).`,
updateReadyUnknown: 'A new update is ready.',
lastChecked: age => `Last checked ${age}`,
justNowSuffix: ' · just now',
automaticUpdates: 'Automatic updates',
automaticUpdatesDesc:
'Hermes checks for updates automatically in the background and lets you know when one is ready.',
branchCommit: (branch, commit) => `Branch ${branch} · Commit ${commit}`,
never: 'never',
justNow: 'just now',
minAgo: count => `${count} min ago`,
hoursAgo: count => `${count} hours ago`,
daysAgo: count => `${count} days ago`
},
config: {
none: 'None',
noneParen: '(none)',
builtinOnly: 'Built-in only',
notSet: 'Not set',
commaSeparated: 'comma-separated values',
searchPlaceholder: 'Search…',
noResults: 'No results found',
systemDefault: 'System default',
loading: 'Loading Hermes configuration...',
emptyTitle: 'Nothing to configure',
emptyDesc: 'This section has no adjustable settings.',
failedLoad: 'Settings failed to load',
autosaveFailed: 'Autosave failed',
imported: 'Config imported',
invalidJson: 'Invalid config JSON',
toolsetsWipeConfirm:
'Remove all enabled toolsets? This disables memory, terminal, web search, delegation, and most other tools until you re-enable them.',
keepAwakeTitle: 'Keep computer awake',
keepAwakeDesc: 'Stop this machine from sleeping so long or overnight runs keep going. The display can still dim.',
disableF12Title: 'Disable F12 DevTools',
disableF12Desc: 'Block F12 from opening Developer Tools. Ctrl+Shift+I (or Cmd+Opt+I on Mac) still works.',
attachmentSizeTitle: 'Max preview / image load size',
attachmentSizeDesc:
'How big a local file Desktop will load for previews and image attach, in MB. Default is 16. Remote non-image attach uses a separate 256 MB cap. Setting this very high loads the whole file into memory and can freeze or crash the app.',
attachmentSizeUnit: 'MB',
attachmentSizeLabel: 'Max preview / image load size in megabytes'
},
quickEntry: {
enabledTitle: 'Quick Entry',
enabledDesc:
'Summon a small composer from anywhere with a global shortcut and fire a prompt without opening Hermes.',
shortcutTitle: 'Quick Entry shortcut',
shortcutDesc: 'Needs at least one modifier, e.g. CommandOrControl+Shift+Space.',
active: 'Shortcut is active.',
takenBy: 'Another app already uses this shortcut — pick a different one.',
invalidShortcut: 'Not a valid shortcut. Include at least one modifier key.'
},
credentials: {
pasteKey: 'Paste key',
pasteLabelKey: label => `Paste ${label} key`,
optional: 'Optional',
enterValueFirst: 'Enter a value first.',
couldNotSave: 'Could not save credential.',
remove: 'Remove',
getKey: 'Get a key',
saving: 'Saving'
},
envActions: {
actions: 'Actions',
manageInKeys: 'Manage in API Keys',
docs: 'Docs',
hideValue: 'Hide value',
revealValue: 'Reveal value',
replace: 'Replace',
set: 'Set',
clear: 'Clear'
},
// v2 multi-connection registry: Settings → Gateways.
connections: {
title: 'Registered gateways',
intro: 'Manage this device and every Hermes gateway it can reach through remote, SSH, or Cloud connections.',
stagedNote:
'Switch gateways from Sessions. Profiles, chats, messaging, and cron jobs stay with their gateway; work on other gateways keeps running.',
launchModeTitle: 'At startup, return to Sessions on the last-used gateway',
launchModeDesc: 'When off, Sessions opens on the Primary gateway.',
searchPlaceholder: 'Search gateways…',
noSearchResults: 'No gateways match your search.',
loadFailed: 'Could not load connections',
currentPill: 'Current',
primaryPill: 'Primary',
managedPill: 'App-managed',
addConnection: 'Add connection',
editConnection: 'Edit',
removeConnection: 'Remove',
removeConfirmTitle: 'Remove this connection?',
removeConfirmDesc: (label: string) =>
`“${label}” will be removed from this app. The instance itself is not touched — you can add it again any time.`,
makePrimary: 'Make primary',
testConnection: 'Test',
testOk: 'Reachable',
testFailed: 'Connection test failed',
saveFailed: 'Could not save the connection',
removeFailed: 'Could not remove the connection',
updateAll: 'Update all instances',
updateAllRunning: 'Updating all instances…',
updateAllDone: 'Updates dispatched',
updateAllFailed: 'Update fan-out failed',
updateSkippedCloud: 'Managed by Hermes Cloud',
kindLocal: 'Local',
kindRemote: 'Remote gateway',
kindCloud: 'Hermes Cloud',
kindSsh: 'SSH',
kindLocalDesc: 'The Hermes runtime managed by this app.',
kindRemoteDesc: 'A Hermes gateway reachable over HTTP(S) — LAN, Tailscale, or the internet.',
kindCloudDesc: 'A hosted instance discovered through your Hermes Cloud account.',
kindSshDesc: 'A Hermes install reached over SSH.',
labelTitle: 'Name',
labelDesc: 'Required. Shown everywhere this instance appears; must be unique (e.g. “Homelab”, “Work laptop”).',
labelPlaceholder: 'Homelab',
urlTitle: 'Gateway URL',
sshHostTitle: 'SSH host',
headersTitle: 'Extra gateway headers',
headersDesc:
'Sent with every HTTP and WebSocket request to this gateway — for access proxies such as Cloudflare Access (CF-Access-Client-Id / CF-Access-Client-Secret). Values are stored encrypted. Headers Hermes manages (Authorization, Cookie, Host…) are ignored.',
headerValuePlaceholder: 'Value',
headerValueSaved: 'Saved — leave blank to keep',
headerAdd: 'Add header',
headerRemove: 'Remove',
duplicateLocal: 'This app already manages a local connection — there can only be one.',
duplicateUrl: (label: string) => `A connection to this gateway URL already exists (“${label}”).`,
duplicateSsh: (label: string) => `A connection to this SSH host already exists (“${label}”).`,
sameBackendHint: (label: string) => `Same backend as “${label}”`,
localAddHint: 'Local is unavailable: the managed local connection already exists (there is only ever one).',
cloudAddHint:
'Tip: signing in under Hermes Cloud above discovers your agents automatically — use this form only to register a known instance URL by hand.',
save: 'Save connection',
saving: 'Saving…',
cancel: 'Cancel',
empty: 'No connections registered yet.'
},
managedUpdates: {
title: 'Managed updates',
intro:
'Update Desktop-managed SSH installs transactionally: sessions drain, the remote checkout updates, and every profile is restored with a correlated receipt.',
sshConnection: 'Desktop-managed SSH install',
update: 'Update',
updating: 'Updating…',
progress: 'Draining sessions, updating the remote install, and restoring profiles…',
updated: 'Updated',
partial: 'Updated — restore failed',
refused: 'Refused',
failed: 'Update failed',
alreadyRunning: 'Update already in progress',
receipt: (id: string, outcome: string) => `Receipt ${id} · ${outcome}`,
receiptVersions: (pre: string, post: string) => `${pre} → ${post}`,
scopesRestored: (profiles: string) => `Restored profiles: ${profiles}`,
scopeNotRestored: (profile: string, error: string) => `Profile “${profile}” not restored: ${error}`
},
gateway: {
loading: 'Loading gateway settings...',
unavailableTitle: 'Gateway settings unavailable',
unavailableDesc: 'The desktop IPC bridge does not expose gateway settings.',
title: 'Gateway Connection',
envOverride: 'env override',
intro:
'Local by default. Use remote when this app should drive a Hermes backend elsewhere. Gateway connections are machine-level; profiles are discovered from the gateways you connect.',
envOverrideTitle: 'Environment variables are controlling this desktop session.',
envOverrideDesc:
'Unset HERMES_DESKTOP_REMOTE_URL and HERMES_DESKTOP_REMOTE_TOKEN to use the saved setting below.',
modeTitle: 'Connection mode',
localTitle: 'Local gateway',
localDesc: 'Start a private Hermes backend on localhost. This is the default and works offline.',
remoteTitle: 'Remote gateway',
remoteDesc: 'Connect this desktop shell to a remote Hermes backend.',
remoteAuthHint: 'Hosted gateways use OAuth or a username and password; self-hosted ones may use a session token.',
cloudTitle: 'Hermes Cloud',
cloudDesc: 'Sign in once to Hermes Cloud and pick from the agents on your account — no URL to paste.',
cloudSignInTitle: 'Hermes Cloud',
cloudSignIn: 'Sign in to Hermes Cloud',
cloudSignedIn: 'Signed in to Hermes Cloud',
cloudNeedsSignIn: 'Sign in to Hermes Cloud to discover the agents on your account.',
cloudSignedInDesc: 'You are signed in. Pick an agent below; the session refreshes automatically.',
cloudAgentsTitle: 'Your agents',
cloudOrgPickerTitle: 'Choose an organization',
cloudOrgSelect: 'Select',
cloudOrgChange: 'Change org',
cloudOrgRole: role => `Role: ${role}`,
cloudLoadingAgents: 'Loading your agents…',
cloudNoAgents: {
before: 'No agents found on this account. Create one in the ',
linkText: 'Nous portal',
after: ', then refresh.'
},
cloudRefresh: 'Refresh',
cloudConnect: 'Connect',
cloudConnecting: 'Connecting…',
cloudDiscoverFailed: 'Could not load your Hermes Cloud agents',
cloudConnectFailed: 'Could not connect to that agent',
cloudSignInFailed: 'Hermes Cloud sign-in failed',
cloudSignedOutTitle: 'Signed out of Hermes Cloud',
cloudSignedOutMessage: 'Cleared the Hermes Cloud session.',
cloudConnectedTitle: 'Connected',
cloudConnectedPill: 'Connected',
cloudConnectedTo: name => `Connected to ${name}.`,
cloudAgentProvisioning: 'Provisioning…',
cloudStatusLabel: status => `Status: ${status}`,
remoteUrlTitle: 'Remote URL',
remoteUrlDesc: 'Base URL for the remote dashboard backend. Path prefixes are supported, for example /hermes.',
probing: 'Checking how this gateway authenticates…',
probeError: 'Could not reach this gateway yet. Check the URL — the auth method will appear once it responds.',
signedIn: 'Signed in',
signIn: 'Sign in',
signOut: 'Sign out',
signInWith: provider => `Sign in with ${provider}`,
authTitle: 'Authentication',
authSignedInPassword:
'This gateway uses a username and password. You are signed in; the session refreshes automatically.',
authSignedInOauth: 'This gateway uses OAuth. You are signed in; the session refreshes automatically.',
authNeedsPassword: 'This gateway uses a username and password. Sign in to authorize this desktop app.',
authNeedsOauth: provider => `This gateway uses OAuth. Sign in with ${provider} to authorize this desktop app.`,
tokenTitle: 'Session token',
tokenDesc: 'The dashboard session token used for REST and WebSocket access. Leave blank to keep the saved token.',
existingToken: value => `Existing token ${value}`,
savedToken: 'saved',
pasteSessionToken: 'Paste session token',
plainTextConfirmTitle: 'Store the gateway token in plain text?',
plainTextConfirmDesc:
'No OS keyring service was found on this machine, so the token would be saved unencrypted in the app’s connection settings file, readable by any process running as this user. Install or enable GNOME Keyring or KWallet for encrypted storage.',
plainTextConfirmAction: 'Save as plain text',
plainTextStoredTitle: 'Token stored in plain text',
plainTextStoredDesc:
'Secure storage is unavailable, so the saved token is stored unencrypted in the app’s connection settings file on this machine. Install or enable GNOME Keyring or KWallet to encrypt it.',
keychainEncryptionTitle: 'Encrypt saved secrets with the OS keychain',
keychainEncryptionDesc:
'Off by default. When on, gateway tokens and sign-in credentials are encrypted with your system keychain (Keychain Access, GNOME Keyring, or Windows DPAPI) — your system may ask for permission or a password. When off, they are stored as plain files readable only by your user account.',
keychainEncryptionFailed: 'Could not change secret encryption',
testRemote: 'Test remote',
saveForRestart: 'Save for next restart',
saveAndReconnect: 'Save and reconnect',
diagnostics: 'Diagnostics',
diagnosticsDesc: 'Reveal desktop.log in your file manager — useful when the gateway fails to start.',
openLogs: 'Open logs',
incompleteTitle: 'Remote gateway incomplete',
incompleteSignIn: 'Enter a remote URL and sign in before switching to remote.',
incompleteToken: 'Enter a remote URL and session token before switching to remote.',
incompleteSignInTest: 'Enter a remote URL and sign in before testing.',
incompleteTokenTest: 'Enter a remote URL and session token before testing.',
enterUrlFirst: 'Enter a remote URL first.',
restartingTitle: 'Gateway connection restarting',
savedTitle: 'Gateway settings saved',
restartingMessage: 'Hermes Desktop will reconnect using the saved settings — the shell stays open.',
savedMessage: 'Saved for the next restart.',
connectedTo: (baseUrl, version) => `Connected to ${baseUrl}${version ? ` · Hermes ${version}` : ''}`,
reachableTitle: 'Remote gateway reachable',
signedOutTitle: 'Signed out',
signedOutMessage: 'Cleared the remote gateway session.',
failedLoad: 'Gateway settings failed to load',
signInFailed: 'Sign-in failed',
signOutFailed: 'Sign-out failed',
testFailed: 'Remote gateway test failed',
applyFailed: 'Could not apply gateway settings',
saveFailed: 'Could not save gateway settings',
sshTitle: 'Connect via SSH',
sshDesc:
'Hermes is launched on the remote over SSH and tunneled to this app — nothing to start or expose yourself. Requires working key-based SSH access to the host.',
sshTrustHint: 'The first presented host key is trusted and pinned; later changes fail closed.',
sshHostTitle: 'Host',
sshHostDesc: 'user@host, or a Host alias from ~/.ssh/config.',
sshHostPick: 'Select a host…',
sshHostPickTitle: 'Host',
sshHostPickDesc: 'A Host alias from ~/.ssh/config, or Custom to type one.',
sshHostCustom: 'Custom (enter manually)…',
sshUserTitle: 'User',
sshUserDesc: 'Blank = ~/.ssh/config or your current user.',
sshUserPlaceholder: 'from ~/.ssh/config',
sshPortTitle: 'Port',
sshPortDesc: 'Blank = 22 or the ~/.ssh/config port.',
sshKeyTitle: 'Identity file',
sshKeyDesc: 'Private key path. Blank = ssh-agent or ~/.ssh/config.',
sshHermesPathTitle: 'Hermes path (optional)',
sshHermesPathDesc: 'Full path to the remote hermes binary. Blank = auto-detect.',
sshHermesPathPlaceholder: 'auto-detect',
sshTestConnection: 'Test SSH',
sshConnect: 'Connect',
sshButtonsHint: 'Save applies on the next launch. Connect reconnects now.',
sshReachable: (host, platform) => `Reachable: ${host} (${platform}) — Hermes found`,
sshIncompleteHost: 'Enter an SSH host before connecting.',
sshErrUnreachable: 'Could not reach that host over SSH. Check the host, port, and your network.',
sshErrAuth:
'SSH authentication failed. Load your key into the ssh-agent (ssh-add) or set an IdentityFile in ~/.ssh/config — Hermes runs ssh non-interactively.',
sshErrHostKey:
'The host key has CHANGED since you last connected. Verify this is expected, then run ssh-keygen -R <host> and reconnect.',
sshErrNotInstalled:
'Hermes is not installed on the remote host. Install it there (curl -fsSL https://hermes-agent.nousresearch.com/install.sh | sh) or set the Hermes path.',
sshErrPlatform:
'Unsupported remote platform. Hermes Desktop SSH mode supports Linux, macOS, and Windows remote hosts.',
sshErrTimeout: 'SSH connection timed out. The host may be unreachable or asleep.',
sshErrUpdateRequired: 'Update Hermes on the remote host before connecting with Desktop SSH.',
sshErrUnknown: 'SSH connection failed.'
},
keys: {
loading: 'Loading API keys and credentials...',
failedLoad: 'API keys failed to load',
empty: 'Nothing configured in this category yet.'
},
search: {
placeholder: 'Search all settings…',
pill: 'Search'
},
profileScope: {
appliesTo: 'Applies to',
editsProfile: profile => `Changes on this page apply to the “${profile}” profile.`
},
mcp: {
loading: 'Loading MCP servers...',
failedLoad: 'MCP config failed to load',
nameRequiredTitle: 'Name required',
nameRequiredMessage: 'Give this MCP server a config key.',
objectRequired: 'Server config must be a JSON object',
invalidJson: 'Invalid MCP JSON',
saveFailed: 'Save failed',
removeFailed: 'Remove failed',
gatewayUnavailableTitle: 'Gateway unavailable',
gatewayUnavailableMessage: 'Reconnect the gateway before reloading MCP.',
reloadedTitle: 'MCP tools reloaded',
reloadedMessage: 'New tool schemas apply to fresh turns.',
reloadFailed: 'MCP reload failed',
savedTitle: 'MCP server saved',
savedMessage: name => `${name} applies after MCP reload.`,
newServer: 'New server',
reload: 'Reload MCP',
reloading: 'Reloading...',
emptyTitle: 'No MCP servers',
emptyDesc: 'Add a stdio or HTTP server to expose MCP tools.',
disabled: 'disabled',
editServer: 'Edit server',
name: 'Name',
serverJson: 'Server JSON',
remove: 'Remove',
saveServer: 'Save server',
test: 'Test connection',
testing: 'Testing...',
testOk: count => `Connected — ${count} tool${count === 1 ? '' : 's'} available`,
testFailed: 'Connection failed',
enableServer: name => `Enable ${name}`,
disableServer: name => `Disable ${name}`,
serverEnabled: name => `${name} enabled — applies to new sessions.`,
serverDisabled: name => `${name} disabled — applies to new sessions.`,
toggleFailed: (name, enabled) => `Failed to turn ${name} ${enabled ? 'on' : 'off'}`,
tabServers: 'Servers',
tabCatalog: 'Catalog',
catalogLoading: 'Loading MCP catalog...',
catalogLoadFailed: 'MCP catalog failed to load',
catalogEmpty: 'No catalog entries available.',
catalogInstalled: 'Installed',
catalogEnabled: 'Enabled',
catalogNeedsInstall: 'Needs build',
catalogInstall: 'Install',
catalogInstalling: 'Installing...',
catalogInstallStarted: name => `Installing ${name}... applies to new sessions when done.`,
catalogInstallFailed: name => `Failed to install ${name}`,
catalogEnvPrompt: name => `${name} requires credentials`,
catalogEnvRequired: 'Fill in the required values before installing.',
capabilitySummary: (tools, prompts, resources) =>
`${[`${tools} tools`, ...(prompts ? [`${prompts} prompts`] : []), ...(resources ? [`${resources} resources`] : [])].join(', ')} enabled`,
costTokens: tokens => `~${tokens} tok/call`,
usage30d: uses => `${uses} uses/30d`,
unusedPill: 'unused',
statusConnecting: 'Connecting…',
statusNeedsAuth: 'Needs authentication',
statusError: 'Error',
statusOff: 'Off',
allServers: 'All servers',
authenticatedTitle: 'Authenticated',
authenticatedMessage: (server, count) => `${server}: ${count} tools`,
waitingForBrowser: 'Waiting for browser…',
authenticate: 'Authenticate',
unsavedConnect: 'Unsaved — save mcp.json to connect.',
enableTool: tool => `Enable ${tool}`,
disableTool: tool => `Disable ${tool}`,
noOutput: 'No output yet.',
deepLinkTitle: 'Add MCP server?',
deepLinkDescription:
'A link asked to add this MCP server to Hermes. Review the exact configuration below — it comes from the link, not from Hermes.',
deepLinkStdioWarning:
'This server runs a local process on your machine with the command shown below. Only continue if you trust its source.',
deepLinkConfirm: 'Add server',
deepLinkNameInvalid: 'Names use 1-64 letters, digits, dots, dashes, or underscores.',
deepLinkNameConflict: name => `A server named ${name} already exists — choose a different name or cancel.`,
deepLinkErrorTitle: 'MCP install link rejected',
deepLinkErrorName: 'The link\u2019s server name is missing or invalid.',
deepLinkErrorConfig: 'The link\u2019s config is not valid base64-encoded JSON.',
deepLinkErrorShape: 'The config must be a JSON object with a string `url` or `command` field.',
deepLinkErrorUrl: 'Only http:// and https:// server URLs are allowed.',
deepLinkErrorTooLarge: 'The config payload exceeds the 32KB limit.',
importButton: 'Import',
importPlaceholder: 'Paste an mcp.json snippet, npx/docker command, claude mcp add line, URL, or Cursor link…',
importNoMatch: 'No server config recognized in the pasted text.',
importConfirm: 'Add to mcp.json',
importConfirmMany: count => `Add ${count} servers to mcp.json`
},
model: {
loading: 'Loading model configuration...',
appliesDesc: 'Applies to new sessions. Use the model picker in the composer to hot-swap the active chat.',
provider: 'Provider',
model: 'Model',
applying: 'Applying...',
defaultsLabel: 'Defaults',
reasoning: 'Reasoning',
reasoningOff: 'Off',
defaultsFailed: 'Failed to save model defaults',
loadFailed: 'Could not load models',
restartRequired: 'This backend is running old code after an update. Restart it to load the new code.',
restartBackend: 'Restart backend',
restartingBackend: 'Restarting backend...',
restartFailed: 'Could not restart the backend',
auxiliaryTitle: 'Auxiliary models',
resetAllToMain: 'Reset all to main',
auxiliaryDesc: 'Helper tasks run on the main model by default. Assign a dedicated model to any task to override.',
setToMain: 'Set to main',
change: 'Change',
autoUseMain: 'auto · use main model',
providerDefault: '(provider default)',
fallbackAdd: 'Add fallback',
fallbackEmpty: 'No fallback models — the default model is used unless it fails.',
notInCatalog: "isn't in this provider's model list — calls may fall back to a backup.",
tasks: {
vision: { label: 'Vision', hint: 'Image analysis' },
compression: { label: 'Compression', hint: 'Context compaction' },
skills_hub: { label: 'Skills hub', hint: 'Skill search' },
approval: { label: 'Approval', hint: 'Smart auto-approve' },
mcp: { label: 'MCP', hint: 'MCP tool routing' },
title_generation: { label: 'Title gen', hint: 'Session titles' },
review: { label: 'Review', hint: '/review reviewer subagent' },
curator: { label: 'Curator', hint: 'Skill-usage review' }
}
},
localModels: {
title: 'Local Models',
runtimeTitle: 'Local runtime',
runtimeReady: backend => `Ready · ${backend}`,
serverRunning: 'Running',
runtimeInstalled: 'llama.cpp runtime installed',
runtimeInstalledDetail: (tag, backend) =>
`Build ${tag}, ${backend} backend. Hermes starts and manages the server for you.`,
installTitle: 'Install the local runtime',
installDetail:
'Downloads the llama.cpp inference engine (a few hundred MB). Models you download run entirely on this machine — no account, nothing leaves your computer.',
installAction: 'Install runtime',
installing: 'Installing runtime…',
installFailed: 'Runtime install failed',
hardwareTitle: 'This machine',
hardwareLoading: 'Checking your hardware…',
vram: label => `${label} GPU memory`,
ram: label => `${label} RAM`,
unifiedMemory: 'Unified memory',
modelsTitle: 'Models',
recommended: 'Recommended',
/* The Recommended badge's tooltip, keyed by the resolver branch that
made the pick. Qualitative on purpose: predictions order candidates,
they are not promises to print. */
recommendedReason: {
'best-quality-resident':
'The highest-quality model that runs entirely on your GPU at full speed. Picks weigh quality against predicted speed on this hardware.',
'speed-gated-quality':
'A higher-quality model fits this machine but would respond too slowly on its memory bandwidth — this is the best model that stays fast.',
'fastest-resident':
'No model reaches full speed on this hardware; this one comes closest while running entirely in GPU memory.',
'least-painful-spilled': 'No model fits entirely in GPU memory here — this one runs best from system RAM.'
} as Record<string, string>,
downloaded: 'Downloaded',
downloadAction: size => `Download · ${size}`,
downloadProgress: (done, total) => `Downloading ${done} of ${total}`,
downloadDoneToast: model => `${model} is ready.`,
installDoneToast: 'Local runtime installed and ready.',
quickstartTitle: 'Run a model on this machine',
quickstartDetail: (model, size) =>
`One click sets everything up: the local engine, ${model} (${size} download), and your default for new chats. Nothing leaves this computer.`,
quickstartDetailReady: model =>
`One click makes ${model} your default for new chats. Everything runs on this machine.`,
quickstartAction: 'Set up for me',
quickstartConfigure: 'Configure…',
quickstartDoneToast: model => `${model} is set up — new chats run on this machine.`,
quickstartFailed: 'Local model setup failed',
quickstartStageEngine: 'Engine',
quickstartStageModel: 'Model',
quickstartStageFinish: 'Finish',
useAction: 'Use',
activePill: 'Default',
updateTitle: 'Engine update available',
updateDetail: (next, current) =>
`A newer llama.cpp build (${next}) is ready to install — you're on ${current}. Models keep working during the download.`,
updateAction: 'Update engine',
updating: 'Updating engine…',
upToDateTitle: 'Engine up to date',
upToDateDetail: (tag, backend) => `Running llama.cpp ${tag} (${backend}) — the latest build Hermes ships.`,
updateToast: next => `A newer local engine build (${next}) is available. Update from Settings → Local Models.`,
activeDetail: 'New chats use this model — it loads when you send your first message',
activeNotLoaded: 'Loads on your first message',
loadedPill: 'In memory',
placementResident: 'all on GPU',
placementSpilled: 'partly in RAM',
placementResidentTip: 'Running entirely in GPU memory at this context window — full speed.',
placementSpilledTip:
'Part of this model runs from system RAM — it works, but slower. A more compact build or a smaller context would fit fully.',
loadingPill: 'Loading…',
ejectTip: 'Free GPU memory (loads again on the next message)',
ejected: 'Model unloaded — GPU memory freed.',
ejectFailed: 'Could not unload the model',
stopServer: 'Turn off',
startServer: 'Turn on',
runtimeRunningDetail:
'The local server is running. Turning it off frees all GPU memory and stops new chats from using local models until you turn it back on.',
serverStopped: 'Local server stopped — GPU memory freed.',
serverStarted: 'Local server running.',
serverStopFailed: 'Could not stop the local server',
serverStartFailed: 'Could not start the local server',
activating: 'Starting…',
activateFailed: model => `Could not switch to ${model}`,
activateDoneToast: model => `New chats use ${model}.`,
downloadFailed: model => `Download of ${model} failed`,
pillFitsGpu: 'Fits your GPU',
pillUsesRam: 'Uses system RAM',
pillTooBig: 'Too big for this machine',
browseTitle: 'Find more models',
browseHint:
'Search all of Hugging Face. Models you download here are sized to your machine automatically, but not tested by us.',
browsePlaceholder: 'Search models by name or author…',
browseSearching: 'Searching Hugging Face',
browseListing: 'Reading model files',
browseShowFiles: 'Show files',
browseRefresh: 'Refresh',
browseDownloads: 'downloads',
browseLikes: 'likes',
browseGated: 'requires Hugging Face sign-in',
browseNoGguf: 'No compatible model files found.',
browseFitUnknown: 'Fit unknown',
browseAlreadyDownloaded: 'Already downloaded.',
addedByYou: 'Added by you',
browseDownloadStarted: 'Downloading {name}',
browseDownloadAria: 'Download {name}',
sideloadButton: 'Add model file',
sideloadTitle: 'Choose a GGUF model file',
sideloadDone: 'Added {name}.',
sideloadAlreadyPresent: 'Already in your library.',
pillFullContext: max => `Full ${max} context`,
pillFullContextTip: "Runs at the model's complete context window from the start",
pillUpTo: max => `Up to ${max} context`,
pillGrowsTip: 'Grows automatically as your conversation needs more room',
pillVision: 'Sees images',
deleteAction: 'Delete model',
deleteConfirm: model => `Delete ${model} from disk?`,
deleted: model => `${model} deleted.`,
deleteFailed: 'Delete failed'
},
providers: {
connectAccount: 'Connect an account',
haveApiKey: 'Have an API key instead?',
intro:
'Sign in with a subscription — no API key to copy. Hermes runs the browser sign-in for you, right here in the app.',
connected: 'Connected',
collapse: 'Collapse',
connectAnother: 'Connect another provider',
otherProviders: 'Other providers',
disconnect: 'Disconnect',
disconnectInTerminal: 'Disconnect (runs the removal command in the terminal)',
removeConfirm: provider => `Remove ${provider}?`,
removeExternalGeneric: provider => `${provider} is managed by its own CLI — remove it there.`,
removeKeyManaged: provider => `${provider} is configured from an API key. Remove it from API Keys.`,
removeTerminalConfirm: (provider, command) =>
`Disconnect ${provider}? This runs "${command}" in the terminal to clear the credential.`,
removeTerminalRunning: provider => `Running ${provider} disconnect in the terminal…`,
removedTitle: 'Account removed',
removedMessage: provider => `${provider} was removed.`,
failedRemove: provider => `Could not remove ${provider}`,
noProviderKeys: 'No provider API keys available.',
searchKeys: 'Search providers…',
noKeysMatch: 'No providers match your search.',
localEndpoint: {
title: 'Local / custom endpoint',
description: 'Point Hermes at any OpenAI-compatible endpoint (Zyphra, vLLM, llama.cpp, Ollama, etc).'
},
loading: 'Loading providers...'
},
sessions: {
loading: 'Loading archived sessions…',
archivedTitle: 'Archived sessions',
archivedIntro:
'Archived chats are hidden from the sidebar but keep all their messages. Ctrl/⌘-click a chat in the sidebar to archive it.',
emptyArchivedTitle: 'Nothing archived',
emptyArchivedDesc: 'Archive a chat to hide it here.',
unarchive: 'Unarchive',
deletePermanently: 'Delete permanently',
messages: count => `${count} ${count === 1 ? 'message' : 'messages'}`,
restored: 'Restored',
deleteConfirm: title => `Permanently delete "${title}"? This cannot be undone.`,
autoArchiveTitle: 'Auto-archive stale chats',
autoArchiveDesc:
"Automatically archive chats you haven't touched in a while. Pinned chats are never archived, and nothing is deleted — archived chats just move here.",
autoArchiveDaysLabel: 'Archive after',
autoArchiveDaysUnit: 'days of inactivity',
autoArchiveFailed: 'Could not update auto-archive',
defaultDirTitle: 'Default project directory',
defaultDirDesc:
'New sessions start in this folder unless you pick another. Leave it unset to use your home directory.',
defaultDirUpdated: 'Default project directory updated — start a new chat (Ctrl/⌘+N) for it to take effect',
defaultsTo: label => `Defaults to ${label}.`,
change: 'Change',
choose: 'Choose',
clear: 'Clear',
notSet: 'Not set',
failedLoad: 'Could not load archived sessions',
unarchiveFailed: 'Unarchive failed',
deleteFailed: 'Delete failed',
updateDirFailed: 'Could not update default directory',
clearDirFailed: 'Could not clear default directory'
},
toolsets: {
loadingConfig: 'Loading configuration',
savedTitle: 'Credential saved',
savedMessage: key => `${key} updated.`,
removedTitle: 'Credential removed',
removedMessage: key => `${key} removed.`,
failedSave: key => `Failed to save ${key}`,
failedRemove: key => `Failed to remove ${key}`,
failedReveal: key => `Failed to reveal ${key}`,
removeConfirm: key => `Remove ${key} from .env?`,
set: 'Set',
notSet: 'Not set',
selectedTitle: 'Provider selected',
selectedMessage: provider => `${provider} is now active.`,
failedSelect: provider => `Failed to select ${provider}`,
failedLoad: 'Tool configuration failed to load',
noProviderOptions: 'This toolset has no provider options — enable it and it works with your current setup.',
noProviders: 'No providers are available for this toolset right now.',
ready: 'Ready',
needsSignIn: 'Needs sign-in',
needsSetup: 'Setup required',
activeBackend: 'Active',
activeBackendHint: 'This is your active backend',
useBackend: 'Use this backend',
nousIncluded: 'Included with a Nous subscription — sign in to Nous Portal to activate.',
nousAuthNeededTitle: 'Sign in to Nous Portal',
nousAuthNeededMessage: provider => `${provider} is saved but won't activate until you sign in to Nous Portal.`,
nousAuthSignIn: 'Sign in',
nousAuthDoneTitle: 'Nous Portal connected',
nousAuthDoneMessage: 'Your subscription backends are now active.',
nousAuthFailed: 'Nous Portal sign-in did not complete',
noApiKeyRequired: 'No API key required.',
postSetupHint: step =>
`This backend needs a one-time install (${step}). Runs on this machine — may take a few minutes.`,
postSetupInstalledHint: 'Installed. Re-run setup only if something is broken.',
postSetupRun: 'Run setup',
postSetupRerun: 'Re-run setup',
postSetupInstalled: 'Installed',
postSetupRunning: 'Installing…',
postSetupStarting: 'Starting…',
postSetupCompleteTitle: 'Setup complete',
postSetupCompleteMessage: step => `${step} installed.`,
postSetupErrorTitle: 'Setup finished with errors',
postSetupErrorMessage: step => `Check the ${step} log.`,
postSetupFailed: step => `Failed to run ${step} setup`,
webSearchActive: backend => `Search: ${backend}`,
webExtractActive: backend => `Extract: ${backend}`,
webCapabilityUnset: 'not set',
webUseForSearch: 'Use for Search',
webUseForExtract: 'Use for Extract',
webUsedForSearch: 'Search backend',
webUsedForExtract: 'Extract backend',
webCapabilitySelectedMessage: (provider, capability) => `${provider} now handles web ${capability}.`,
failedSelectCapability: provider => `Failed to set ${provider}`,
loadingModels: 'Loading model catalog...',
modelSectionTitle: 'Model',
modelCount: count => `${count} model${count === 1 ? '' : 's'}`,
modelInUse: 'In use',
modelDefault: 'default',
modelInactiveHint: 'Select this backend first to change its model.',
modelSelectedTitle: 'Model selected',
modelSelectedMessage: model => `${model} applies to new sessions.`,
failedSelectModel: model => `Failed to select ${model}`,
terminalBackend: {
sectionTitle: 'Execution backend',
loading: 'Checking execution backends…',
failedLoad: 'Could not load terminal backends',
ready: 'Ready',
needsSetup: 'Needs setup',
unavailable: 'Unavailable',
inUse: 'In use',
selectedTitle: 'Backend selected',
selectedMessage: backend => `Terminal commands now run via ${backend}. Applies to new sessions.`,
failedSelect: backend => `Failed to select ${backend}`,
needsSetupHint: 'You can select this backend now — commands will fail until setup is complete.'
},
browserRealProfile: {
label: 'Use My Real Browser Profile',
description:
"Copies your default browser's logins and cookies into a managed snapshot the agent browses with. Your live profile is never opened directly. Applies to new sessions.",
enabledTitle: 'Real-profile browsing on',
enabledMessage: 'New sessions will browse with a snapshot of your default browser profile.',
disabledTitle: 'Real-profile browsing off',
disabledMessage: 'The profile snapshot will be deleted; new sessions use a clean browser.',
failedSave: 'Could not save the real-profile setting',
prompt: {
title: 'Stay signed in to your sites',
body: 'Let Hermes browse with a snapshot of your default browser profile, so sites open already signed in.',
bulletSnapshot: 'Cookies and logins are copied into a managed snapshot.',
bulletLiveProfile: 'Your live browser profile is never opened directly.',
bulletLocal: 'Nothing leaves this computer.',
dontShowAgain: "Don't show again",
notNow: 'Not now',
enable: 'Use my profile'
}
}
}
},
skills: {
tabSkills: 'Skills',
tabToolsets: 'Tools',
configuringProfile: 'Configuring:',
tabMcp: 'MCP',
all: 'All',
searchSkills: 'Search skills...',
searchToolsets: 'Search tools...',
refresh: 'Refresh skills',
refreshing: 'Refreshing skills',
loading: 'Loading capabilities...',
noSkillsTitle: 'No skills found',
noSkillsDesc: 'Try a broader search or different category.',
noToolsetsTitle: 'No toolsets found',
noToolsetsDesc: 'Try a broader search query.',
noDescription: 'No description.',
configured: 'Configured',
needsKeys: 'Needs keys',
visionModelHint:
'Vision uses your auxiliary model configuration — the image-capable model is picked there, not per-provider here.',
visionModelLink: 'Choose vision model in Settings → Models',
toolsetsEnabled: (enabled, total) => `${enabled}/${total} toolsets enabled`,
configureToolset: label => `Configure ${label}`,
toggleToolset: (label, enabled) => `Turn ${label} toolset ${enabled ? 'on' : 'off'}`,
skillsLoadFailed: 'Skills failed to load',
toolsetsRefreshFailed: 'Toolsets failed to refresh',
skillEnabled: 'Skill enabled',
skillDisabled: 'Skill disabled',
toolsetEnabled: 'Toolset enabled',
toolsetDisabled: 'Toolset disabled',
appliesToNewSessions: name => `${name} applies to new sessions.`,
failedToUpdate: name => `Failed to update ${name}`,
sortMostUsed: 'Most used',
sortAlpha: 'A–Z',
sortMostUsedDesc: '↓ Most used',
sortLeastUsedAsc: '↑ Least used',
enableAll: 'Enable all',
disableAll: 'Disable all',
disableUnused: 'Disable unused',
bulkUpdated: count => `Updated ${count} ${count === 1 ? 'item' : 'items'} for new sessions.`,
bulkNoChange: 'Nothing to change.',
usageCount: count => `used ${count}×`,
provenance: {
agent: 'Learned',
bundled: 'Built-in',
hub: 'Hub'
},
emptyNoneFound: noun => `No ${noun} found`,
emptyNothingMatches: query => `Nothing matches “${query}”.`,
emptyNoneAvailable: noun => `No ${noun} available yet.`,
changesApplyNewSessions: 'Changes apply to new sessions.',
skillUpdated: 'Skill updated',
edit: 'Edit',
archive: 'Archive',
skillArchivedTitle: 'Skill archived',
skillArchivedMessage: 'Restorable via hermes curator restore.',
officialCatalog: 'Available to install',
officialPill: 'Official',
hub: {
searchPlaceholder: 'Search the skill hub',
search: 'Search',
searching: 'Searching...',
connectingHubs: 'Connecting to skill hubs...',
connectedHubs: 'Connected hubs:',
featured: 'Featured skills',
landingHint:
'Search the hub to browse installable skills from the official index, GitHub, and community sources.',
noResults: 'No matching skills found in the hub.',
resultCount: (count, ms) => `${count} result${count === 1 ? '' : 's'}${ms !== null ? ` in ${ms}ms` : ''}`,
timedOut: sources => `Timed out: ${sources}`,
installed: 'Installed',
install: 'Install',
installing: 'Installing...',
uninstall: 'Uninstall',
uninstalling: 'Uninstalling...',
updateAll: 'Update installed',
updating: 'Updating...',
preview: 'Preview',
scan: 'Scan',
scanning: 'Scanning...',
close: 'Close',
files: 'Files',
noReadme: 'This skill has no SKILL.md preview.',
trust: {
builtin: 'builtin',
trusted: 'trusted',
community: 'community'
},
verdictSafe: 'Safe',
verdictCaution: 'Caution',
verdictDangerous: 'Dangerous',
policyAllow: 'Install allowed',
policyAsk: 'Review before installing',
policyBlock: 'Install blocked by policy',
findings: count => `${count} finding${count === 1 ? '' : 's'}`,
noFindings: 'No security findings.',
installStarted: name => `Installing ${name}...`,
uninstallStarted: name => `Uninstalling ${name}...`,
updateStarted: 'Updating installed skills...',
actionFailed: 'Skill action failed',
actionLog: 'Action log',
alreadyInstalled: (name: string) => `"${name}" is already installed`,
pickerTitle: 'Skills Hub',
pickerBrowse: 'Browse the full hub',
pickerHide: 'Hide the hub browser',
pickerHint: 'Hit "+ Add to this Agent" on any skill — it installs and appears in the list above.',
loadFailed: 'Skill hub failed to load',
previewFailed: 'Skill preview failed',
scanFailed: 'Security scan failed',
searchFailed: 'Hub search failed'
}
},
starmap: {
title: 'Memory Graph',
subtitle: (nodes, clusters) => `${nodes} skills across ${clusters} categories`,
close: 'Close memory graph',
refresh: 'Refresh',
memory: 'Memory',
filterAll: 'All',
filterUsed: 'Used',
filterLearned: 'Learned',
viewGraph: 'Graph',
loadFailed: 'Could not load memory graph',
loading: 'Loading…',
emptyTitle: 'Nothing learned yet',
emptyDesc: 'As Hermes builds skills and memories for your work, they appear here.',
share: 'Share map',
shareHint:
'Copy the code to share this map, or paste one to load. It only includes the layout, not your memory or skill text.',
shareTitle: 'Import / export map',
sharePlaceholder: 'Paste a map code…',
copy: 'Copy map code',
copied: 'Copied!',
importMap: 'Import a map',
importBtn: 'Load',
importEmpty: 'Paste a map code to load it.',
importSuccess: nodes => `Loaded a map with ${nodes} ${nodes === 1 ? 'node' : 'nodes'}.`,
importedBadge: 'imported map',
resetToMine: 'Back to my map'
},
agents: {
close: 'Close agents',
title: 'Spawn tree',
subtitle: 'Live subagent activity for the current turn.',
emptyTitle: 'No live subagents',
emptyDesc: 'When a turn delegates work, child agents stream their progress here.',
running: 'Running',
failed: 'Failed',
done: 'Done',
streaming: 'Streaming',
files: 'Files',
moreFiles: count => `+${count} more files`,
delegation: index => `Delegation ${index}`,
workers: count => `${count} workers`,
workersActive: count => `${count} active`,
agentsCount: count => `${count} ${count === 1 ? 'agent' : 'agents'}`,
activeCount: count => `${count} active`,
failedCount: count => `${count} failed`,
toolsCount: count => `${count} tools`,
filesCount: count => `${count} files`,
updatedAgo: age => `updated ${age}`,
ageNow: 'now',
ageSeconds: seconds => `${seconds}s ago`,
ageMinutes: minutes => `${minutes}m ago`,
ageHours: hours => `${hours}h ago`,
ageDays: days => `${days}d ago`,
durationSeconds: seconds => `${seconds}s`,
durationMinutes: (minutes, seconds) => `${minutes}m ${seconds}s`,
tokens: value => `${value} tok`
},
commandCenter: {
close: 'Close command center',
paletteTitle: 'Command palette',
back: 'Back',
searchPlaceholder: 'Search sessions, views, and actions',
goTo: 'Go to',
goToSession: 'Go to session',
branches: 'Branches',
projects: 'Projects',
openFolder: 'Open folder as project…',
openFolderAt: path => `Open folder as project — ${path}`,
newSessionInProject: project => `New session in ${project}`,
commands: 'Commands',
startInBranch: branch => `New conversation in ${branch}`,
commandCenter: 'Command Center',
appearance: 'Appearance',
settings: 'Settings',
changeTheme: 'Change theme',
changeColorMode: 'Change color mode…',
pets: {
title: 'Pets',
placeholder: 'Search pets…',
loading: 'Loading petdex gallery…',
error: 'Could not reach the petdex gallery.',
staleBackend: 'Restart Hermes to use pets — the backend predates this feature.',
empty: 'No matching pets.',
turnOff: 'Turn off',
turnOn: 'Turn on',
installed: 'Installed',
generatedTag: 'Generated',
adoptFailed: 'Could not adopt that pet.',
toggleFailed: enabled => `Could not turn the pet ${enabled ? 'on' : 'off'}.`,
noneAvailable: 'No pets available — pick one below to install.'
},
generatePet: {
title: 'Generate a pet',
placeholder: 'Describe a pet to generate…',
promptHint: 'Type a description, then press Enter to draft four looks.',
readyHint: 'Press Enter to draft four looks from your description.',
generate: 'Generate',
generating: 'Generating…',
retry: 'Retry',
hatch: 'Hatch',
spawning: 'Spawning…',
hatching: 'Hatching your pet…',
hatchingSub: 'Bringing it to life…',
hatched: 'It hatched!',
hatchRow: (_state, done, total) => `Sketching frame ${done} of ${total}…`,
hatchComposing: 'Piecing it together…',
hatchSaving: 'Almost there…',
namePlaceholder: 'Name your pet',
staleBackend: 'Update Hermes to generate pets.',
backgroundHint: 'You can close this — Hermes will notify you when it’s done.',
slowProviderHint: 'This can take several minutes',
remix: 'Remix',
remixConfirmTitle: 'Remix this look?',
remixConfirmBody:
'This generates a fresh set of drafts using this one as the starting point. It can take several minutes.',
genericError: 'Generation failed — try again or pick a suggestion.',
referenceImageTooLarge: 'Reference image is too large. Use one under 16 MB.',
referenceImageInvalid: 'Could not read that reference image. Try a PNG, JPG, WebP, or GIF.',
adopt: 'Adopt',
startOver: 'Start over'
},
installTheme: {
title: 'Install theme…',
pageTitle: 'Install theme',
placeholder: 'Search the VS Code Marketplace...',
loading: 'Searching the Marketplace...',
error: 'Could not reach the Marketplace.',
empty: 'No matching themes.',
install: 'Install',
installing: 'Installing...',
installed: 'Installed',
installs: count => `${count} installs`
},
settingsFields: 'Settings fields',
mcpServers: 'MCP servers',
archivedChats: 'Archived chats',
sections: { maintenance: 'Maintenance', sessions: 'Sessions', system: 'System', usage: 'Usage' },
sectionDescriptions: {
maintenance: 'Diagnostics, backups, curator, and memory data',
sessions: 'Search and manage sessions',
system: 'Status, logs, and system actions',
usage: 'Token, cost, and skill activity over time'
},
nav: {
newChat: { title: 'New session', detail: 'Start a fresh session' },
settings: { title: 'Settings', detail: 'Configure Hermes desktop' },
skills: { title: 'Capabilities', detail: 'Skills, tools, and MCP servers' },
messaging: { title: 'Messaging', detail: 'Set up Telegram, Slack, Discord, and more' },
artifacts: { title: 'Artifacts', detail: 'Browse generated outputs' }
},
sectionEntries: {
sessions: { title: 'Sessions panel', detail: 'Search, pin, and manage sessions' },
system: { title: 'System panel', detail: 'Gateway status, logs, restart/update' },
usage: { title: 'Usage panel', detail: 'Token, cost, and skill activity' }
},
providerNavigate: 'Navigate',
providerSessions: 'Sessions',
refresh: 'Refresh',
refreshing: 'Refreshing...',
noResults: 'No matching results found.',
pinSession: 'Pin session',
unpinSession: 'Unpin session',
exportSession: 'Export session',
deleteSession: 'Delete session',
noSessions: 'No sessions yet.',
gatewayRunning: 'Messaging gateway running',
gatewayStopped: 'Messaging gateway stopped',
hermesActiveSessions: (version, count) => `Hermes ${version} · Active sessions ${count}`,
restartGateway: 'Restart gateway',
openBrowser: 'Open browser',
gatewayRestartFailed: 'Gateway restart failed.',
updateHermes: 'Update Hermes',
reloadWindow: 'Reload window',
actionRunning: 'running',
actionDone: 'done',
actionFailed: 'failed',
actionStartedWaiting: 'Action started, waiting for status...',
loadingStatus: 'Loading status...',
recentLogs: 'Recent logs',
noLogs: 'No logs loaded yet.',
days: count => `${count}d`,
statSessions: 'Sessions',
statApiCalls: 'API calls',
statTokens: 'Tokens in/out',
statCost: 'Est. cost',
actualCost: cost => `actual ${cost}`,
loadingUsage: 'Loading usage...',
noUsage: period => `No usage in the last ${period} days.`,
retry: 'Retry',
dailyTokens: 'Daily tokens',
input: 'input',
output: 'output',
noDailyActivity: 'No daily activity.',
topModels: 'Top models',
noModelUsage: 'No model usage yet.',
topSkills: 'Top skills',
noSkillActivity: 'No skill activity yet.',
actions: count => `${count} actions`,
logFile: 'Log file',
logLevel: 'Level',
logSearchPlaceholder: 'Filter log lines...',
maintenance: {
runOps: 'Diagnostics',
doctor: 'Run doctor',
doctorDesc: 'Health-check the install, config, and providers',
securityAudit: 'Security audit',
securityAuditDesc: 'Scan config and skills for risky settings',
backup: 'Create backup',
backupDesc: 'Zip config, memories, skills, and sessions',
debugShare: 'Debug share',
debugShareDesc: 'Upload a redacted report + logs, get shareable links (auto-deletes in 6h)',
debugShareRunning: 'Uploading debug report...',
debugShareLinks: 'Share links',
debugShareFailed: 'Debug share failed',
copyLink: 'Copy link',
linkCopied: 'Link copied',
curator: 'Skill curator',
curatorDesc: 'Background review that archives stale agent-created skills',
curatorPaused: 'Paused',
curatorActive: 'Active',
curatorDisabled: 'Disabled',
curatorLastRun: when => `Last run ${when}`,
curatorNeverRan: 'Never ran',
pause: 'Pause',
resume: 'Resume',
runNow: 'Run now',
memoryData: 'Memory data',
memoryDataDesc: 'Built-in memory files injected into every session',
memoryProvider: name => `Active provider: ${name}`,
builtinMemory: 'built-in',
memoryFile: 'Agent memory (MEMORY.md)',
userFile: 'User profile (USER.md)',
bytes: size => size,
empty: 'empty',
resetMemory: 'Reset memory',
resetUser: 'Reset profile',
resetAll: 'Reset both',
resetConfirm: target => `Delete ${target}? This cannot be undone.`,
resetDone: files => `Deleted ${files}.`,
resetFailed: 'Memory reset failed',
actionStarted: name => `${name} started — tailing log...`,
actionFailed: name => `${name} failed to start`,
running: 'Running...',
viewLog: 'Action log'
}
},
messaging: {
search: 'Search messaging...',
loading: 'Loading messaging platforms...',
loadFailed: 'Messaging platforms failed to load',
states: {
connected: 'Connected',
connecting: 'Connecting',
disabled: 'Disabled',
fatal: 'Error',
gateway_stopped: 'Messaging gateway stopped',
not_configured: 'Needs setup',
pending_restart: 'Restart needed',
retrying: 'Retrying',
startup_failed: 'Startup failed'
},
unknown: 'Unknown',
hintPendingRestart: 'Restart the gateway from the status bar to apply this change.',
hintGatewayStopped: 'Start the gateway from the status bar to connect.',
credentialsSet: 'Credentials set',
needsSetup: 'Needs setup',
gatewayStopped: 'Messaging gateway stopped',
getCredentials: 'Get your credentials',
openSetupGuide: 'Open setup guide',
required: 'Required',
recommended: 'Recommended',
advanced: count => `Advanced (${count})`,
noTokenNeeded: 'This platform does not need a token here. Use the setup guide above, then enable it below.',
enabled: 'Enabled',
disabled: 'Disabled',
unsavedChanges: 'Unsaved changes',
saving: 'Saving...',
saveChanges: 'Save changes',
saved: 'Saved',
replaceValue: 'Replace current value',
openDocs: 'Open docs',
clearField: key => `Clear ${key}`,
enableAria: name => `Enable ${name}`,
disableAria: name => `Disable ${name}`,
platformEnabled: name => `${name} enabled`,
platformDisabled: name => `${name} disabled`,
restartToApply: 'This change takes effect after a gateway restart.',
setupSaved: name => `${name} setup saved`,
restartToReconnect: 'New credentials take effect after a gateway restart.',
keyCleared: key => `${key} cleared`,
setupUpdated: name => `${name} setup was updated.`,
failedUpdate: name => `Failed to update ${name}`,
failedSave: name => `Failed to save ${name}`,
failedClear: key => `Failed to clear ${key}`,
pendingRequests: count => `Pending requests (${count})`,
pendingAria: count => `${count} pending pairing ${count === 1 ? 'request' : 'requests'}`,
approvedUsers: count => `Approved users (${count})`,
approve: 'Approve',
approving: 'Approving...',
revoke: 'Revoke',
revoking: 'Revoking...',
revokeAria: name => `Revoke ${name}`,
revokeTitle: 'Revoke access',
revokeDesc: (name: string) => `${name} will lose access and stop being recognized on their next message.`,
approvedUser: name => `${name} approved`,
approvedHint: 'They are recognized automatically on their next message.',
revokedUser: name => `${name} revoked`,
failedApprove: name => `Failed to approve ${name}`,
failedRevoke: name => `Failed to revoke ${name}`,
pairingLockedOut: 'Too many failed approvals — this platform is locked out. Try again later.',
waitingSince: minutes => (minutes < 1 ? 'just now' : `${minutes}m ago`),
fieldCopy: {
TELEGRAM_BOT_TOKEN: {
label: 'Bot token',
help: 'Create a bot with @BotFather, then paste the token it gives you.',
placeholder: 'Paste Telegram bot token'
},
TELEGRAM_ALLOWED_USERS: {
label: 'Allowed Telegram user IDs',
help: 'Recommended. Comma-separated numeric IDs from @userinfobot. Without this, anyone can DM your bot.'
},
TELEGRAM_PROXY: { label: 'Proxy URL', help: 'Only needed on networks where Telegram is blocked.' },
DISCORD_BOT_TOKEN: {
label: 'Bot token',
help: 'Create an application in the Discord Developer Portal, add a bot, then paste its token.'
},
DISCORD_ALLOWED_USERS: {
label: 'Allowed Discord user IDs',
help: 'Recommended. Comma-separated Discord user IDs.'
},
DISCORD_REPLY_TO_MODE: { label: 'Reply style', help: 'first, all, or off.' },
DISCORD_ALLOW_ALL_USERS: {
label: 'Allow all Discord users',
help: 'Development only. When true, anyone can DM the bot without an allowlist.'
},
DISCORD_HOME_CHANNEL: {
label: 'Home channel ID',
help: 'Channel where the bot sends proactive messages (cron output, reminders).'
},
DISCORD_HOME_CHANNEL_NAME: {
label: 'Home channel name',
help: 'Display name for the home channel in logs and status output.'
},
BLUEBUBBLES_ALLOW_ALL_USERS: {
label: 'Allow all iMessage users',
help: 'When true, skip the BlueBubbles allowlist.'
},
MATTERMOST_ALLOW_ALL_USERS: { label: 'Allow all Mattermost users' },
MATTERMOST_HOME_CHANNEL: { label: 'Home channel' },
QQ_ALLOW_ALL_USERS: { label: 'Allow all QQ users' },
QQBOT_HOME_CHANNEL: { label: 'QQ home channel', help: 'Default channel or group for cron delivery.' },
QQBOT_HOME_CHANNEL_NAME: { label: 'QQ home channel name' },
SLACK_BOT_TOKEN: {
label: 'Slack bot token',
help: 'Use the bot token from OAuth & Permissions after installing your Slack app.',
placeholder: 'Paste Slack bot token'
},
SLACK_APP_TOKEN: {
label: 'Slack app token',
help: 'Use the app-level token required for Socket Mode.',
placeholder: 'Paste Slack app token'
},
SLACK_ALLOWED_USERS: { label: 'Allowed Slack user IDs', help: 'Recommended. Comma-separated Slack user IDs.' },
MATTERMOST_URL: { label: 'Server URL', placeholder: 'https://mattermost.example.com' },
MATTERMOST_TOKEN: { label: 'Bot token' },
MATTERMOST_ALLOWED_USERS: {
label: 'Allowed user IDs',
help: 'Recommended. Comma-separated Mattermost user IDs.'
},
MATRIX_HOMESERVER: { label: 'Homeserver URL', placeholder: 'https://matrix.org' },
MATRIX_ACCESS_TOKEN: { label: 'Access token' },
MATRIX_USER_ID: { label: 'Bot user ID', placeholder: '@hermes:example.org' },
MATRIX_ALLOWED_USERS: {
label: 'Allowed Matrix user IDs',
help: 'Recommended. Comma-separated user IDs in @user:server format.'
},
SIGNAL_HTTP_URL: {
label: 'Signal bridge URL',
placeholder: 'http://127.0.0.1:8080',
help: 'URL of a running signal-cli REST bridge.'
},
SIGNAL_ACCOUNT: { label: 'Phone number', help: 'The number registered with your signal-cli bridge.' },
SIGNAL_ALLOWED_USERS: { label: 'Allowed Signal users', help: 'Recommended. Comma-separated Signal identifiers.' },
WHATSAPP_ENABLED: {
label: 'Enable WhatsApp bridge',
help: 'Set automatically by the toggle below. Leave alone unless you know you need it.'
},
WHATSAPP_MODE: { label: 'Bridge mode' },
WHATSAPP_ALLOWED_USERS: {
label: 'Allowed WhatsApp users',
help: 'Recommended. Comma-separated phone numbers or WhatsApp IDs.'
}
},
platformIntro: {}
},
webhooks: {
search: 'Search webhooks...',
loading: 'Loading webhooks...',
loadFailed: 'Webhooks failed to load',
subscriptions: (count: number) => `Subscriptions (${count})`,
hint: 'Subscription changes hot-reload once the receiver is running. Disabled subscriptions reject incoming events.',
empty: 'No webhook subscriptions yet.',
disabledTitle: 'Webhook receiver disabled',
disabledBody:
'Webhooks are their own gateway platform. Enable them here to accept incoming HTTP events; chat channels are only needed when a subscription delivers to Telegram, Discord, Slack, or another channel.',
enable: 'Enable webhooks',
enabling: 'Enabling...',
enabled: (name: string) => `Enabled: "${name}"`,
disabled: (name: string) => `Disabled: "${name}"`,
enableRow: 'Enable',
disableRow: 'Disable',
delete: 'Delete',
deleting: 'Deleting...',
deleted: 'Webhook deleted',
deleteTitle: 'Delete webhook',
deleteDescPrefix: 'This will permanently remove ',
deleteDescSuffix: '. This cannot be undone.',
deleteFailed: (name: string) => `Failed to delete "${name}"`,
toggleFailed: (name, enabled) => `Failed to turn "${name}" ${enabled ? 'on' : 'off'}`,
newSubscription: 'New subscription',
restarting: 'Gateway restarting...',
restartNeeded: 'Webhooks are enabled, but the gateway still needs a restart before the receiver can come online.',
restartGateway: 'Restart gateway',
restartingGateway: 'Restarting...',
restartFailed: (detail: string) => `Gateway restart failed${detail}`,
enabledRestarting: 'Webhooks enabled; gateway restarting...',
all: '(all)',
deliverOnly: 'deliver only',
createdTitle: 'Subscription created',
createdSecretHint: 'Copy the secret now — it is only shown once.',
webhookUrl: 'Webhook URL',
secretOnce: 'Secret (shown once)',
done: 'Done',
fieldName: 'Name',
fieldNamePlaceholder: 'e.g. github-push',
fieldDescription: 'Description',
fieldDescriptionPlaceholder: 'What this webhook does (optional)',
fieldEvents: 'Events',
fieldEventsPlaceholder: 'comma-separated, leave empty for all',
fieldSkills: 'Skills',
fieldSkillsPlaceholder: 'comma-separated skill names (optional)',
fieldDeliver: 'Deliver to',
fieldDeliverOnly: 'Deliver payload only',
fieldPrompt: 'Prompt',
fieldPromptPlaceholder: 'Instructions for the agent when this webhook fires (optional)',
nameRequired: 'Name required',
create: 'Create',
creating: 'Creating...',
created: 'Created',
createFailed: (detail: string) => `Failed to create: ${detail}`,
copy: 'Copy',
deliverOptions: {
log: 'Log',
telegram: 'Telegram',
discord: 'Discord',
slack: 'Slack',
email: 'Email',
github_comment: 'GitHub comment'
}
},
profiles: {
close: 'Close profiles',
nameHint: 'Lowercase letters, digits, hyphens, and underscores. Must start with a letter or digit.',
title: 'Profiles',
count: count => `${count} ${count === 1 ? 'profile' : 'profiles'}`,
search: 'Search profiles...',
loading: 'Loading profiles...',
newProfile: 'New profile',
importProfile: 'Import profile…',
exportProfile: 'Export profile…',
imported: 'Profile imported',
exported: 'Profile exported',
failedImport: 'Failed to import profile',
failedExport: 'Failed to export profile',
allProfiles: 'All profiles',
showAllProfiles: 'Show all profiles',
switchToProfile: name => `Switch to ${name}`,
switchToConnection: name => `Switch to ${name}`,
switchConnectionFailed: name => `Could not connect to ${name}`,
manageProfiles: 'Manage profiles…',
connectGateway: 'Manage gateways…',
fleet: {
allOnGateway: 'All profiles on this gateway',
gateway: gateway => `Profiles on ${gateway}`,
gatewayUnreachable: gateway => `${gateway} · unreachable`,
onGateway: (name, gateway) => `${name} · ${gateway}`,
switchTo: (name, gateway) => `Switch to ${name} on ${gateway}`,
deleteOn: gateway => ` on ${gateway}`
},
remoteOverride: {
menuItem: 'Connect to a remote host…',
badge: (host: string) => `Runs on ${host}`,
title: (profile: string) => `Connect ${profile} to a remote host`,
description: 'Sessions in this profile will run on the remote Hermes you point it at, instead of this computer.',
urlLabel: 'Remote address',
urlPlaceholder: 'https://hermes.example.com',
urlInvalid: 'Enter a full address starting with http:// or https://',
tokenLabel: 'Access token',
tokenPlaceholder: 'Paste the remote session token',
tokenSavedHint: 'A token is already saved. Leave blank to keep it.',
plainTextOptIn:
'This computer has no secure key storage, so the token would be saved unencrypted on disk. Save it anyway.',
collisionWarning: (label: string) =>
`A gateway named “${label}” already exists in Settings. This profile connection is separate and will not change it.`,
confirmTitle: 'Connect this profile to a remote host?',
confirmNote: (profile: string, host: string) =>
`New chats in ${profile} will run on ${host}. That computer will run commands and read files there, not on this one. Only connect to a host you trust.`,
confirmBack: 'Back',
connect: 'Connect',
connecting: 'Connecting…',
disconnect: 'Remove remote connection',
savedTitle: 'Profile connected',
savedMessage: (profile: string, host: string) => `${profile} now runs on ${host}`,
removedTitle: 'Remote connection removed',
removedMessage: (profile: string) => `${profile} now runs on this computer`,
removeFailed: 'Could not remove the remote connection',
authFailedTitle: 'Remote host rejected the saved token',
authFailedMessage: (profile: string, host: string) =>
`${host} refused the token saved for ${profile}. It may have been changed on the remote side.`,
updateToken: 'Enter new token…'
},
actions: 'Actions',
color: 'Color…',
colorFor: 'Color',
setColor: color => `Set color ${color}`,
autoColor: 'Auto',
noProfiles: 'No profiles yet.',
selectPrompt: 'Select a profile to view its details.',
refresh: 'Refresh profiles',
refreshing: 'Refreshing profiles',
default: 'default',
skills: count => `${count} ${count === 1 ? 'skill' : 'skills'}`,
env: 'env',
defaultBadge: 'Default',
rename: 'Rename',
renameMenu: 'Rename…',
exportMenu: 'Export…',
editSoul: 'Edit SOUL.md…',
copySetup: 'Copy setup',
copying: 'Copying...',
modelLabel: 'Model',
skillsLabel: 'Skills',
notSet: 'Not set',
soulDesc: 'The system prompt and persona instructions baked into this profile.',
soulOptional: 'optional',
soulPlaceholder: mode => `The system prompt / persona for this profile.\nLeave blank to keep the ${mode} default.`,
soulPlaceholderCloned: 'cloned',
soulPlaceholderEmpty: 'empty',
unsavedChanges: 'Unsaved changes',
loadingSoul: 'Loading SOUL.md...',
emptySoul: 'Empty SOUL.md — start writing the persona...',
saving: 'Saving...',
saveSoul: 'Save SOUL.md',
deleteTitle: 'Delete profile?',
deleteDescPrefix: 'This will delete ',
deleteDescMid: ' and remove its ',
deleteDescSuffix: ' directory. This cannot be undone.',
deleting: 'Deleting...',
createDesc: 'Profiles are independent Hermes environments: separate config, skills, and SOUL.md.',
nameLabel: 'Name',
cloneFrom: 'Clone from',
cloneFromNone: 'None (blank)',
cloneFromDesc: 'Copies config, skills, and SOUL.md from the selected source profile.',
cloneFromDefault: 'Clone from default',
cloneFromDefaultDesc: 'Copy config, skills, and SOUL.md from your default profile.',
invalidName: hint => `Invalid name. ${hint}`,
nameRequired: 'Name is required.',
creating: 'Creating...',
createAction: 'Create profile',
renameTitle: 'Rename profile',
renameDescPrefix: 'Renaming updates the profile directory and any wrapper scripts in ',
renameDescSuffix: '.',
displayNameTitle: 'Name this agent',
displayNameDesc: 'Sets a display name shown across the app. The internal profile ID stays "default".',
displayNameLabel: 'Display name',
newNameLabel: 'New name',
renaming: 'Renaming...',
created: 'Profile created',
renamed: 'Profile renamed',
deleted: 'Profile deleted',
setupCopied: 'Setup command copied',
soulSaved: 'SOUL.md saved',
failedLoad: 'Failed to load profiles',
failedDelete: 'Failed to delete profile',
failedCopy: 'Failed to copy setup command',
failedLoadSoul: 'Failed to load SOUL.md',
failedSaveSoul: 'Failed to save SOUL.md',
failedCreate: 'Failed to create profile',
failedRename: 'Failed to rename profile'
},
cron: {
close: 'Close cron',
title: 'Scheduled jobs',
count: count => `${count} ${count === 1 ? 'job' : 'jobs'}`,
modelImpact: {
title: 'Scheduled jobs need review',
message: count =>
`${count} scheduled ${count === 1 ? 'job' : 'jobs'} will be skipped until you review their model settings.`,
detailMore: (names, remaining) => `${names} and ${remaining} more`,
review: 'Review scheduled jobs',
saveFailed: 'Hermes did not save that model change.',
confirmTitle: 'Model Selection Warning',
confirmDetail: 'Confirm only if you accept this trade-off.',
confirmAction: 'Confirm',
declined: 'Model change cancelled — you declined the data-training tier warning.'
},
search: 'Search cron jobs...',
loading: 'Loading cron jobs...',
states: {
enabled: 'enabled',
scheduled: 'scheduled',
running: 'running',
paused: 'paused',
disabled: 'disabled',
error: 'error',
completed: 'completed'
},
deliveryLabels: {
local: 'This desktop',
telegram: 'Telegram',
discord: 'Discord',
slack: 'Slack',
email: 'Email'
},
scheduleLabels: {
daily: 'Daily',
weekdays: 'Weekdays',
weekly: 'Weekly',
monthly: 'Monthly',
hourly: 'Hourly',
'every-15-minutes': 'Every 15 minutes',
custom: 'Custom'
},
scheduleHints: {
daily: 'Every day at 9:00 AM',
weekdays: 'Monday through Friday at 9:00 AM',
weekly: 'Every Monday at 9:00 AM',
monthly: 'The first day of each month at 9:00 AM',
hourly: 'At the top of every hour',
'every-15-minutes': 'Every 15 minutes',
custom: 'Cron syntax or natural language'
},
days: {
'0': 'Sunday',
'1': 'Monday',
'2': 'Tuesday',
'3': 'Wednesday',
'4': 'Thursday',
'5': 'Friday',
'6': 'Saturday',
'7': 'Sunday'
},
dayFallback: value => `day ${value}`,
everyDayAt: time => `Every day at ${time}`,
weekdaysAt: time => `Weekdays at ${time}`,
everyDayOfWeekAt: (day, time) => `Every ${day} at ${time}`,
monthlyOnDayAt: (dayOfMonth, time) => `Monthly on day ${dayOfMonth} at ${time}`,
topOfHour: 'At the top of every hour',
everyHourAt: minute => `Every hour at :${minute}`,
newCron: 'New cron',
emptyDescNew:
'Schedule a prompt to run on a cron expression. Hermes will run it and deliver results to the destination you pick.',
emptyDescSearch: 'Try a broader search query.',
emptyTitleNew: 'No scheduled jobs yet',
emptyTitleSearch: 'No matches',
last: 'Last:',
next: 'Next:',
noRuns: 'No runs yet',
manage: 'Manage',
showRuns: 'Show runs',
hideRuns: 'Hide runs',
runHistory: 'Run history',
actionsTitle: 'Cron job actions',
resume: 'Resume cron',
pause: 'Pause cron',
resumeTitle: 'Resume',
pauseTitle: 'Pause',
triggerNow: 'Trigger now',
edit: 'Edit cron',
deleteTitle: 'Delete cron job?',
deleteDescPrefix: 'This will remove ',
deleteDescSuffix: ' permanently. It will stop firing immediately.',
deleting: 'Deleting...',
resumed: 'Cron resumed',
paused: 'Cron paused',
triggered: 'Cron triggered',
deleted: 'Cron deleted',
created: 'Cron created',
updated: 'Cron updated',
failedLoad: 'Failed to load cron jobs',
failedUpdate: 'Failed to update cron job',
failedTrigger: 'Failed to trigger cron job',
failedDelete: 'Failed to delete cron job',
failedSave: 'Failed to save cron job',
editTitle: 'Edit cron job',
createTitle: 'New cron job',
editDesc: 'Update the schedule, prompt, or delivery target. Changes apply on next run.',
createDesc: 'Schedule a prompt to run automatically. Use cron syntax or a natural phrase like "every 15 minutes".',
nameLabel: 'Name',
namePlaceholder: 'Morning briefing',
promptLabel: 'Prompt',
promptPlaceholder: 'Summarize my unread Slack threads and email me the top 5...',
frequencyLabel: 'Frequency',
deliverLabel: 'Deliver to',
deliverNeedsHomeChannel: 'set a home channel first',
modelLabel: 'Model',
modelDefault: 'Default (global model)',
customScheduleLabel: 'Custom schedule',
customPlaceholder: '0 9 * * * or weekdays at 9am',
customHint: 'Cron expression, or phrases like "every hour" or "weekdays at 9am".',
optional: 'Optional',
promptRequired: 'Prompt is required.',
promptScheduleRequired: 'Prompt and schedule are required.',
scheduleRequired: 'Schedule is required.',
scriptOnlyEditHint: 'Script-only job (no AI prompt). Job id:',
saveChanges: 'Save changes',
createAction: 'Create cron',
tabs: {
jobs: 'Jobs',
blueprints: 'Blueprints'
},
blueprints: {
tab: 'Blueprints',
startFrom: 'Start from',
custom: 'Custom',
subtitle: 'Ready-made automations',
dialogDesc: 'Fill in the details and schedule it.',
scheduleIt: 'Schedule it',
scheduling: 'Scheduling...',
scheduled: 'Blueprint scheduled',
loading: 'Loading blueprints...',
failedLoad: 'Failed to load blueprints',
emptyTitle: 'No blueprints available',
emptyDesc: 'No automation blueprints are available on this backend.'
}
},
artifacts: {
search: 'Search artifacts...',
refresh: 'Refresh artifacts',
refreshing: 'Refreshing artifacts',
indexing: 'Indexing recent session artifacts',
tabAll: 'All',
tabImages: 'Images',
tabFiles: 'Files',
tabLinks: 'Links',
noArtifactsTitle: 'No artifacts found',
noArtifactsDesc: 'Generated images and file outputs will appear here as sessions produce them.',
failedLoad: 'Artifacts failed to load',
openFailed: 'Open failed',
itemsImage: 'images',
itemsLink: 'links',
itemsFile: 'files',
itemsGeneric: 'items',
zero: '0',
rangeOf: (start, end, total) => `${start}-${end} of ${total}`,
goToPage: (itemLabel, page) => `Go to ${itemLabel} page ${page}`,
colTitleLink: 'Link title',
colTitleFile: 'Name',
colTitleDefault: 'Title / name',
colLocationLink: 'URL',
colLocationFile: 'Path',
colLocationDefault: 'Location',
colSession: 'Session',
kindImage: 'image',
kindFile: 'file',
kindLink: 'link',
chat: 'Chat',
copyUrl: 'Copy URL',
copyPath: 'Copy path'
},
artifactCard: {
kind: { code: 'Code', html: 'Interactive page', svg: 'Graphic' },
generating: lines => `Generating… ${lines} lines`,
versionBadge: count => `${count} versions`,
open: 'Open'
},
artifactPreview: {
versionOf: (current, total) => `v${current} of ${total}`,
olderVersion: 'Older version',
newerVersion: 'Newer version',
latest: 'Latest',
copyContent: 'Copy content',
download: 'Download',
openInBrowser: 'Open in browser',
openInBrowserFailed: 'Could not open in browser',
missingTitle: 'Artifact unavailable',
missingBody: 'This artifact is no longer in the local registry.'
},
sidebar: {
nav: {
'new-session': 'New session',
skills: 'Capabilities',
messaging: 'Messaging',
artifacts: 'Artifacts',
cron: 'Scheduled jobs'
},
searchAria: 'Search sessions',
searchPlaceholder: 'Search sessions…',
clearSearch: 'Clear search',
noMatch: query => `No sessions match “${query}”.`,
results: 'Results',
pinned: 'Pinned',
sessions: 'Sessions',
cronJobs: 'Cron jobs',
groupAriaGrouped: 'Show sessions as a single list',
groupAriaUngrouped: 'Group sessions by workspace',
showProjects: 'Show projects',
showSessions: 'Show sessions',
groupTitleGrouped: 'Ungroup sessions',
groupTitleUngrouped: 'Group by workspace',
allPinned: 'Everything here is pinned. Unpin a chat to show it in recents.',
shiftClickHint: 'Shift-click a chat to pin',
noWorkspace: 'No workspace',
projectEmpty: 'No sessions yet',
noSessions: 'No sessions yet',
noFilterMatches: 'No sessions match these filters',
projects: {
sectionLabel: 'Projects',
home: 'Home',
newButton: 'New project',
createTitle: 'New project',
createDesc: 'Name a workspace and add one or more folders.',
renameTitle: 'Rename project',
addFolderTitle: 'Add folder',
namePlaceholder: 'e.g. Skunkworks',
foldersLabel: 'Folders',
ideaLabel: 'Idea',
ideaPlaceholder: "What's this project about? (saved to IDEA.md)",
ideaGenerate: 'Generate idea',
ideaGenerating: 'Generating…',
ideaShuffle: 'Shuffle templates',
noFolders: 'No folders added yet.',
addFolder: 'Add folder',
primaryBadge: 'primary',
removeFolder: 'Remove',
create: 'Create',
menu: 'Actions',
menuRename: 'Rename…',
menuAppearance: 'Appearance',
noColor: 'No color',
menuAddFolder: 'Add folder',
menuSetActive: 'Set active',
menuDelete: 'Delete',
moveToProject: 'Move to project',
movedTo: name => `Moved to ${name}`,
moveFailed: 'Could not move session',
moveNoFolder: 'That project has no folder to move into',
moveNoProjects: 'No other projects',
reveal: 'Reveal in folder',
copyPath: 'Copy path',
removeFromSidebar: 'Hide from sidebar',
createFailed: 'Could not create project',
staleBackend:
'Update the Hermes backend to create projects — your backend is older than this desktop app (Settings → Updates → Backend).',
deleteConfirm: 'This removes the saved project from Hermes. Files, git repos, and worktrees stay untouched.',
startWork: 'New worktree',
newWorktreeTitle: 'New worktree',
newWorktreeDesc: 'Name the branch for this worktree.',
branchPlaceholder: 'e.g. my-feature',
branchOff: () => ({ after: '', before: 'branch off ' }),
baseBranchPlaceholder: 'Search branches…',
baseBranchNone: 'No branches found',
startWorkFailed: 'Could not create worktree',
worktreeStaleBackend:
'Update the Hermes backend to create worktrees over this remote connection — it predates the git worktree API.',
worktreeProjectLabel: 'Project',
worktreeProjectPlaceholder: 'Search projects…',
worktreeProjectNone: 'No projects with a folder',
convertBranch: 'Convert a branch…',
convertBranchTitle: 'Convert a branch',
convertBranchDesc: 'Open checked-out branches, or create a worktree for a free branch.',
convertBranchPlaceholder: 'Search branches…',
convertBranchInstead: 'Convert an existing branch',
branchOpenExisting: 'open',
branchSwitchHome: 'switch home',
branchCreateWorktree: 'new worktree',
branchTrackRemote: 'track remote',
branchesLoading: 'Loading branches…',
noBranches: 'No branches found',
removeWorktree: 'Remove worktree',
removeWorktreeFailed: 'Could not remove worktree (uncommitted changes?)',
removeWorktreeConfirm:
'Remove it from git (deletes the worktree directory; the branch stays), or just hide the lane from the sidebar and leave the worktree on disk.',
removeWorktreeDirty:
'This worktree has uncommitted changes. Force-remove it (discards those changes), or just hide the lane and keep it on disk.',
forceRemove: 'Force remove',
enter: label => `Open ${label}`,
reorder: label => `Reorder ${label}`,
toggle: (label, open) => `${open ? 'Show' : 'Hide'} ${label} sessions`,
back: 'All projects'
},
newSessionIn: label => `New session in ${label}`,
showMoreIn: (count, label) => `Show ${count} more in ${label}`,
loading: 'Loading…',
loadMore: 'Load more',
loadCount: step => `Load ${step} more`,
messageCount: count => `${count} ${count === 1 ? 'message' : 'messages'}`,
toolCallCount: count => `${count} ${count === 1 ? 'tool call' : 'tool calls'}`,
row: {
pin: 'Pin',
unpin: 'Unpin',
markUnread: 'Mark as unread',
markRead: 'Mark as read',
unreadFailed: 'Could not update unread state',
copyId: 'Copy ID',
export: 'Export',
branchFrom: 'Branch',
rename: 'Rename…',
archive: 'Archive',
newWindow: 'New window',
openInTerminal: 'Open in terminal',
hideTabBar: 'Hide tab bar',
openInNewTab: 'Open in new tab',
openInSplit: 'Open in split',
copyIdFailed: 'Could not copy session ID',
sessionActions: 'Session actions',
sessionRunning: 'Session running',
needsInput: 'Needs your input',
waitingForAnswer: 'Waiting for your answer',
finishedUnread: 'Finished — unread',
backgroundRunning: 'Background task running',
draftSession: 'Draft — nothing sent yet',
handoffOrigin: platform => `Handed off from ${platform}`,
ownedByProfile: profile => `Profile: ${profile}`,
renamed: 'Renamed',
renameFailed: 'Rename failed',
renameTitle: 'Rename session',
renameDesc: 'Leave empty to clear.',
untitledPlaceholder: 'Untitled session',
deleteTitle: 'Delete session?',
deleteDesc: title => `This will permanently delete “${title}”. This cannot be undone.`,
deleting: 'Deleting…',
deleted: 'Session deleted',
untitledChat: id => `Chat ${id}`,
messageCount: count => `${count} ${count === 1 ? 'message' : 'messages'}`,
todoProgress: 'Tasks completed',
ageNow: 'now',
ageDay: 'd',
ageHour: 'h',
ageMin: 'm'
},
dateDivider: {
today: 'Earlier today',
yesterday: 'Yesterday',
thisWeek: 'Earlier this week',
lastWeek: 'Last week',
thisMonth: 'Earlier this month'
},
statusDivider: {
working: 'Working',
done: 'Done'
},
markAllRead: 'Mark all as read'
},
composer: {
message: 'Message',
wakingProfile: profile => `Waking up ${profile}…`,
placeholderStarting: 'Starting Hermes...',
placeholderReconnecting: 'Reconnecting to Hermes…',
placeholderFollowUp: 'Send follow-up',
newSessionPlaceholders: [
'What are we building?',
'Give Hermes a task',
"What's on your mind?",
'Describe what you need',
'What should we tackle?',
'Ask anything',
'Start with a goal'
],
followUpPlaceholders: [
'Send a follow-up',
'Add more context',
'Refine the request',
"What's next?",
'Keep it going',
'Push it further',
'Adjust or continue'
],
startVoice: 'Start voice conversation',
openDirective: 'Open',
queueMessage: 'Queue message',
steer: 'Steer the current run',
stop: 'Stop',
send: 'Send',
speaking: 'Speaking',
transcribing: 'Transcribing',
thinking: 'Thinking',
muted: 'Muted',
listening: 'Listening',
muteMic: 'Mute microphone',
unmuteMic: 'Unmute microphone',
stopListening: 'Stop listening and send',
stopShort: 'Stop',
endConversation: 'End voice conversation',
endShort: 'End',
stopDictation: 'Stop dictation',
transcribingDictation: 'Transcribing dictation',
voiceControls: 'Voice',
voiceDictation: 'Voice dictation',
speakReplies: 'Read replies aloud',
stopSpeakingReplies: 'Stop reading replies aloud',
wakeWordListening: phrase => `Wake word: "${phrase}" — listening`,
wakeWordOff: phrase => `Wake word: "${phrase}" — off`,
wakeWordPausedVoice: phrase => `Wake word: "${phrase}" — paused during voice chat`,
lookupLoading: 'Looking up…',
lookupNoMatches: 'No matches.',
lookupTry: 'Try',
lookupOr: 'or',
commonCommands: 'Common commands',
hotkeys: 'Hotkeys',
helpFooter: 'opens the full panel · backspace dismisses',
commandDescs: {
'/help': 'full list of commands + hotkeys',
'/clear': 'start a new session',
'/resume': 'resume a prior session',
'/details': 'control transcript detail level',
'/copy': 'copy selection or last assistant message',
'/quit': 'exit hermes'
},
hotkeyDescs: {
'composer.mention': 'reference files, folders, urls, git',
'composer.slash': 'slash command palette',
'composer.help': 'this quick help (delete to dismiss)',
'composer.sendNewline': 'send · Shift+Enter for newline',
'composer.sendQueued': 'send next queued turn',
'keybinds.openPanel': 'all keyboard shortcuts',
'composer.cancel': 'close popover · cancel run',
'composer.history': 'cycle popover / history'
},
attachUrlTitle: 'Attach a URL',
attachUrlDesc: 'Hermes will fetch the page and include it as context for this turn.',
urlPlaceholder: 'https://example.com/post',
urlHintPre: 'Include the full URL, e.g. ',
attach: 'Attach',
queued: count => `${count} Queued`,
queuedPaused: count => `${count} Queued — paused`,
attachmentOnly: 'Attachment-only turn',
emptyTurn: 'Empty turn',
attachments: count => `${count} attachment${count === 1 ? '' : 's'}`,
editingInComposer: 'Editing in composer',
editingQueuedInComposer: 'Editing queued turn in composer',
queueEdit: 'Edit',
queueSendNext: 'Next',
queueSteer: 'Steer — redirect the live turn now',
queueSend: 'Send',
queueDelete: 'Delete',
queueResume: 'Resume',
queueResumeTip: 'Paused by Stop — resume sending the queued turns',
queueStuckTitle: 'Queued message not sent',
queueStuckBody: 'A queued turn kept failing to send. It is still in the queue — try sending it again.',
previewUnavailable: 'Preview unavailable',
previewLabel: label => `Preview ${label}`,
couldNotPreview: label => `Could not preview ${label}`,
removeAttachment: label => `Remove ${label}`,
dictating: 'Dictating',
preparingAudio: 'Preparing audio',
speakingResponse: 'Speaking response',
readingAloud: 'Reading aloud',
themeSuggestions: 'Desktop theme suggestions',
noMatchingThemes: 'No matching themes.',
themeTryPre: 'Try ',
themeTryPost: '.',
attachLabel: 'Attach',
files: 'Files…',
folder: 'Folder…',
images: 'Images…',
pasteImage: 'Paste image',
url: 'URL…',
promptSnippets: 'Prompt snippets…',
tipPre: 'Tip: type ',
tipPost: ' to reference files inline.',
snippetsTitle: 'Prompt snippets',
snippetsDesc: 'Pick a starter prompt to drop into the composer.',
dropFiles: 'Drop files to attach',
dropSession: 'Drop to link this chat',
mcpSuggestions: {
label: server => `Add ${server}`,
tip: keyword => `Suggested because you mentioned “${keyword}” — click to connect`,
connecting: server => `Connecting ${server}…`,
cancelTip: 'Click to cancel',
added: server => `Added ${server}`,
addedTip: 'Connected — its tools are ready in this chat',
connectFailed: server => `Could not connect ${server}`
},
skillSuggestions: {
label: skill => `Use skill: ${skill}`,
tip: skill => `You mentioned “${skill}” — click to lead with that skill`,
done: skill => `Added /${skill}`,
doneTip: 'The skill loads when you send'
},
githubSuggestions: {
label: 'Set up GitHub',
tip: 'GitHub works through the gh CLI skills here — click to connect your account',
done: 'Added /github-auth',
doneTip: 'Send the message and the agent walks you through GitHub sign-in'
},
repairSuggestions: {
label: server => `Reconnect ${server}`,
tip: server => `A ${server} call just failed with a connection error`,
working: server => `Reconnecting ${server}…`,
workingTip: 'Click to cancel',
done: server => `Reconnected ${server}`,
doneTip: 'Fresh credentials are live in this chat',
failed: server => `Could not reconnect ${server}`
},
cronSuggestions: {
label: 'Schedule this',
tip: phrase => `“${phrase}” sounds recurring — run it on a schedule instead`,
prefix: 'Set this up as a scheduled job:',
done: 'Marked for scheduling',
doneTip: 'Send it and the agent creates the job'
},
snippets: {
codeReview: {
label: 'Code review',
description: 'Audit the current change for regressions, dropped edge cases, and missing tests.',
text: 'Please review this for bugs, regressions, and missing tests.'
},
implementationPlan: {
label: 'Implementation plan',
description: 'Outline an approach before touching code so the diff stays focused.',
text: 'Please make a concise implementation plan before changing code.'
},
explainThis: {
label: 'Explain this',
description: 'Walk through how the selected code works and link to the key files.',
text: 'Please explain how this works and point me to the key files.'
}
}
},
statusStack: {
agents: 'Agents',
background: count => `${count} Background`,
goalActive: 'Goal active',
goalDone: 'Goal done',
goalPaused: 'Goal paused',
goalWaiting: 'Goal waiting',
subagents: count => `${count} Subagent${count === 1 ? '' : 's'}`,
todos: (done, total) => `Tasks ${done}/${total}`,
running: 'Running',
stop: 'Stop',
dismiss: 'Dismiss',
exit: code => `exit ${code}`,
coding: {
title: 'Working tree',
noBranch: 'No branch',
detached: 'detached',
clean: 'Clean',
changed: count => `${count} changed`,
ahead: count => `${count} ahead`,
behind: count => `${count} behind`,
review: 'Review',
close: 'Close',
openChanges: 'Open changes',
openFile: 'Open file',
stage: 'Stage',
unstage: 'Unstage',
stageAll: 'Stage all',
viewAsTree: 'View as tree',
viewAsList: 'View as list',
revert: 'Revert',
revertAll: 'Revert all',
revertConfirm: 'Discard changes to this file and restore it to the committed state? This cannot be undone.',
revertAllConfirm: 'Discard every change and restore files to the committed state? This cannot be undone.',
staged: 'Staged',
noChanges: 'No changes',
notRepo: 'Not a git repository',
noDiff: 'No diff to show',
scopeUncommitted: 'Uncommitted',
scopeBranch: 'Branch',
scopeLastTurn: 'Last turn',
commit: 'Commit',
commitAndPush: 'Commit & Push',
commitPlaceholder: shortcut => `Message (${shortcut} to commit)`,
generateCommitMessage: 'Generate commit message',
stopGenerating: 'Stop generating',
createPr: 'Create PR',
openPr: 'Open PR',
ghMissing: 'Install the GitHub CLI (gh) and sign in to open PRs',
agentShip: 'Ask Hermes to open PR',
agentShipUnavailable: "The chat that owns these changes isn't on screen.",
agentShipPrompt:
'Review the current changes, commit them with a clear conventional-commit message, push the branch, and open a pull request.',
newBranch: 'New branch',
branchOffFrom: base => `New branch from ${base}`,
switchTo: branch => `Switch to ${branch}`,
switchFailed: branch => `Could not switch to ${branch}`,
worktrees: 'Worktrees'
}
},
updates: {
stages: {
idle: 'Getting ready…',
prepare: 'Getting ready…',
fetch: 'Downloading…',
pull: 'Almost there…',
pydeps: 'Finishing up…',
update: 'Updating Hermes…',
rebuild: 'Rebuilding the desktop app…',
restart: 'Restarting Hermes…',
done: 'Update complete',
manual: 'Update from your terminal',
guiSkew: 'Update the desktop app',
error: 'Update paused'
},
checking: 'Looking for updates…',
checkFailedTitle: 'Couldn’t check for updates',
tryAgain: 'Try again',
notAvailableTitle: 'Update not available',
unsupportedMessage: 'This version of Hermes can’t update itself from inside the app.',
connectionRetry: 'Check your connection and try again.',
latestBody: 'You’re running the latest version.',
latestBodyBackend: 'The backend is running the latest version.',
allSetTitle: 'You’re all set',
availableTitle: 'New update available',
availableBody: 'A new version of Hermes is ready to install.',
availableTitleBackend: 'Backend update available',
availableBodyBackend: 'A newer version of the connected Hermes backend is ready to install.',
availableBodyNoChangelog: 'A newer version is ready. Release notes aren’t available for this install type.',
updateNow: 'Update now',
maybeLater: 'Maybe later',
moreChanges: count => `+ ${count} more change${count === 1 ? '' : 's'} included.`,
manualTitle: 'Update from your terminal',
manualBody: 'You installed Hermes from the command line, so updates run there too. Paste this into your terminal:',
manualPickedUp: 'Hermes will pick up the new version next time you launch it.',
guiSkewTitle: 'Update the desktop app',
guiSkewBody:
'The backend was updated, but this desktop app package wasn’t changed. Update or reinstall the Hermes desktop app (your AppImage / .deb / .rpm) to match.',
copy: 'Copy',
copied: 'Copied',
done: 'Done',
applyingBody:
'The Hermes updater takes over in its own window and reopens Hermes automatically when it’s done. Please don’t reopen Hermes yourself while it’s updating.',
applyingBodyBackend:
'The remote backend is applying the update and will restart. Hermes reconnects automatically when it’s back.',
applyingClose: 'This window will close while the update runs, then Hermes reopens on its own.',
errorTitle: 'Update didn’t finish',
errorBody: 'No worries — nothing was lost. You can try again now.',
blockerTitle: 'Close local previews to update Hermes?',
blockerBody:
'Hermes needs to stop these local previews before updating. This will not modify or delete your files.',
foreignBlockerTitle: 'Close other processes to update Hermes',
foreignBlockerBody:
'Hermes can’t safely close these processes automatically. Close the app, terminal, or service that owns each one, then try the update again.',
mixedBlockerBody:
'Hermes can close the local previews listed below. Other processes must be closed manually before the update can continue.',
closePreviewsAndUpdate: 'Close previews and update',
closePreviewsAndCheckAgain: 'Close previews and check again',
localPreview: 'Local preview',
portLabel: port => `Port ${port}`,
pidLabel: pid => `PID ${pid}`,
technicalDetails: 'Technical details',
notNow: 'Not now',
clientAlsoBehindTitle: 'Desktop app is behind',
clientAlsoBehindMessage:
'The backend is up to date, but this desktop app is still on an older version. Update it to pick up the latest fixes.',
clientAlsoBehindAction: 'Update desktop app',
everythingDispatched: 'Update dispatched',
everythingSkipped: 'Skipped',
everythingRowFailed: 'Update failed',
everythingFanoutFailedTitle: 'Couldn’t update other instances',
applyStatus: {
preparing: 'Updating backend…',
pulling: 'Backend updating…',
restarting: 'Backend restarting to load the update…',
notAvailable: 'Update not available for this backend.',
failed: 'Backend update failed.',
noReturn: 'Backend didn’t come back online. The update may not have completed — check the backend host.'
}
},
install: {
stageStates: {
pending: 'Pending',
running: 'Installing',
succeeded: 'Done',
skipped: 'Skipped',
failed: 'Failed'
},
oneTimeTitle: 'Hermes needs a one-time install',
unsupportedDesc: platform =>
`Automated first-launch install isn’t available on ${platform} yet. Open Terminal and run the command below, then relaunch this app. Subsequent launches will skip this step.`,
installCommand: 'Install command',
copyCommand: 'Copy command',
viewDocs: 'View install docs',
installTo: 'Will install to',
retryAfterRun: 'I’ve run it -- retry',
setupChoiceTitle: 'Set up Hermes Desktop',
setupChoiceDesc:
'Connect this app to a Hermes gateway you already run, or install Hermes locally on this computer.',
connectExistingTitle: 'Connect to existing Hermes',
connectExistingShort: 'Connect existing',
connectExistingDesc: 'Use a remote backend with a session token or browser sign-in. No local install will start.',
installLocalTitle: 'Install Hermes locally',
installLocalDesc: 'Download Hermes, create its Python environment, and run the backend on this computer.',
localStartUnavailable: 'Local installation could not start. Restart Hermes Desktop and try again.',
remoteSetupTitle: 'Connect to existing Hermes',
remoteSetupDesc: 'Enter your gateway URL. Hermes Desktop will detect whether it needs a token or browser sign-in.',
remoteUrlTitle: 'Gateway URL',
remoteUrlDesc: 'Use the base URL of the Hermes gateway, including https:// when remote.',
remoteUrlPlaceholder: 'https://gateway.example.com/hermes',
probing: 'Detecting gateway authentication...',
probeError: 'Could not reach that Hermes gateway.',
identityProvider: 'your identity provider',
authTitle: 'Authentication',
authNeedsOauth: provider => `Sign in with ${provider} before testing this gateway.`,
authSignedIn: 'Browser sign-in completed.',
connected: 'Connected',
signIn: 'Sign in',
signInWith: provider => `Sign in with ${provider}`,
enterUrlFirst: 'Enter a gateway URL first.',
signInIncomplete: 'The sign-in window closed before authentication completed.',
tokenTitle: 'Session token',
tokenDesc: 'Paste the session token from the remote gateway .env file.',
pasteSessionToken: 'Paste session token',
incompleteSignInTest: 'Sign in before testing this OAuth-gated gateway.',
incompleteTokenTest: 'Enter a session token before testing this gateway.',
testConnection: 'Test connection',
testSucceeded: (baseUrl, version) => `Connected to ${baseUrl}${version ? ` (${version})` : ''}.`,
applyRemote: 'Apply and reconnect',
backToSetup: 'Back',
failedTitle: 'Installation failed',
settingUpTitle: 'Setting up Hermes Agent',
finishingTitle: 'Finishing up',
failedDesc:
'One of the install steps failed. On Windows, this can happen if another Hermes CLI or desktop instance is running. Stop any running Hermes instances, then retry. Check the details below or the desktop log for the full transcript.',
activeDesc:
'This is a one-time setup. The Hermes installer is downloading dependencies and configuring your machine. Subsequent launches will skip this step.',
progress: (completed, total) => `${completed} of ${total} steps complete`,
currentStage: stage => ` -- now: ${stage}`,
fetchingManifest: 'Fetching installer manifest...',
error: 'Error',
hideOutput: 'Hide installer output',
showOutput: 'Show installer output',
lines: count => `${count} line${count === 1 ? '' : 's'}`,
noOutput: 'No output yet.',
cancelling: 'Cancelling...',
cancelInstall: 'Cancel install',
transcriptSaved: 'Full transcript saved to',
copiedOutput: 'Copied!',
copyOutput: 'Copy output',
reloadRetry: 'Reload and retry'
},
onboarding: {
headerTitle: "Let's get you setup with Hermes Agent",
headerDesc: 'Connect a model provider to start chatting. Most options take one click.',
preparingInstall: 'Hermes is finishing install. This usually takes under a minute on first run.',
starting: 'Starting Hermes…',
lookingUpProviders: 'Looking up providers...',
collapse: 'Collapse',
otherProviders: 'Other providers',
haveApiKey: 'I have an API key',
chooseLater: "I'll choose a provider later",
recommended: 'Recommended',
connected: 'Connected',
featuredPitch: 'One subscription, 300+ frontier models — the recommended way to run Hermes',
fireworksPitch: 'Direct model API — Fireworks-hosted frontier models',
localModelsTitle: 'Run models locally',
localModelsPitch: 'No account needed — download a model and run it on this machine',
openRouterPitch: 'One key, hundreds of models — a solid default',
apiKeyOptions: {
fireworks: {
short: 'direct model API',
description: 'Direct access to models hosted by Fireworks AI.'
},
openrouter: {
short: 'one key, many models',
description: 'Hosts hundreds of models behind a single key. Good default for new installs.'
},
openai: { short: 'GPT-class models', description: 'Direct access to OpenAI models.' },
gemini: { short: 'Gemini models', description: 'Direct access to Google Gemini models.' },
xai: { short: 'Grok models', description: 'Direct access to xAI Grok models.' },
local: {
short: 'self-hosted',
description: 'Point Hermes at a local or self-hosted OpenAI-compatible endpoint (vLLM, llama.cpp, Ollama, etc).'
}
},
backToSignIn: 'Back to sign in',
getKey: 'Get a key',
replaceCurrent: 'Replace current value',
pasteApiKey: 'Paste API key',
localApiKeyPlaceholder: 'API key (optional — only if your endpoint requires one)',
couldNotSave: 'Could not save credential.',
connecting: 'Connecting',
update: 'Update',
flowSubtitles: {
pkce: 'Opens your browser to sign in, then continues here',
device_code: 'Opens a verification page in your browser — Hermes connects automatically',
external: 'Sign in once in your terminal, then come back to chat'
},
startingSignIn: provider => `Starting sign-in for ${provider}...`,
verifyingCode: provider => `Verifying your code with ${provider}...`,
connectedProvider: provider => `${provider} connected`,
connectedPicking: provider => `${provider} connected. Picking a default model...`,
signInFailed: 'Sign-in failed. Try again.',
signInExpired:
'Sign-in expired waiting for authorization. This usually means the sign-in page stalled in the opened tab (server-side issue) — finish signing in there, then try again. If it keeps failing, use an API key or the CLI fallback instead.',
pickDifferentProvider: 'Pick a different provider',
signInWith: provider => `Sign in with ${provider}`,
openedBrowser: provider => `We opened ${provider} in your browser.`,
authorizeThere: 'Authorize Hermes there.',
copyAuthCode: 'Copy the authorization code and paste it below.',
pasteAuthCode: 'Paste authorization code',
reopenAuthPage: 'Re-open authorization page',
autoBrowser: provider =>
`We opened ${provider} in your browser. Authorize Hermes there and you'll be connected automatically — nothing to copy or paste.`,
reopenSignInPage: 'Re-open sign-in page',
waitingAuthorize: 'Waiting for you to authorize...',
externalPending: provider =>
`${provider} signs in through its own CLI. Run this command in a terminal, then come back and pick "I've signed in":`,
signedIn: "I've signed in",
deviceCodeOpened: provider => `We opened ${provider} in your browser. Enter this code there:`,
reopenVerification: 'Re-open verification page',
copy: 'Copy',
defaultModel: 'Default model',
freeTier: 'Free tier',
pro: 'Pro',
free: 'Free',
price: (input, output) => `${input} in / ${output} out per Mtok`,
change: 'Change',
startChatting: 'Begin',
docs: provider => `${provider} docs`
},
modelPicker: {
title: 'Switch model',
current: 'current:',
unknown: '(unknown)',
search: 'Filter providers and models...',
noModels: 'No models found.',
addProvider: 'Add provider',
loadFailed: 'Could not load models',
loadingIntoMemory: 'Loading into memory',
downloading: 'Downloading',
localDownloadsHeading: 'Local',
noAuthenticatedProviders: 'No authenticated providers.',
pro: 'Pro',
proNeedsSubscription: 'Pro models need a paid Nous subscription.',
free: 'Free',
freeTier: 'Free tier',
priceTitle: 'Input / Output price per million tokens',
wasPrice: 'was'
},
modelVisibility: {
title: 'Models',
search: 'Search models',
noAuthenticatedProviders: 'No authenticated providers.',
addProvider: 'Add provider…'
},
shell: {
windowControls: 'Window controls',
paneControls: 'Pane controls',
appControls: 'App controls',
modelMenu: {
search: 'Search models',
noModels: 'No models found',
editModels: 'Edit models…',
refreshModels: 'Refresh models',
fast: 'Fast'
},
modelOptions: {
comboDetails: 'Model / combo details',
noOptions: 'No options for this model',
options: 'Options',
thinking: 'Thinking',
fast: 'Fast',
effort: 'Effort',
minimal: 'Minimal',
low: 'Low',
medium: 'Medium',
high: 'High',
xhigh: 'Extra High',
max: 'Max',
ultra: 'Ultra',
updateFailed: 'Model option update failed',
fastFailed: 'Fast mode update failed'
},
gatewayMenu: {
gateway: 'Gateway',
connected: 'Connected',
connecting: 'Connecting',
offline: 'Offline',
inferenceReady: 'Inference ready',
inferenceNotReady: 'Inference not ready',
checkingInference: 'Checking inference',
disconnected: 'Disconnected',
reconnectGateway: 'Reconnect gateway',
openSystem: 'Open system panel',
connection: label => `Connection: ${label}`,
recentActivity: 'Recent activity',
viewAllLogs: 'View all logs →',
messagingPlatforms: 'Messaging platforms'
},
approvalMode: {
title: 'Approval mode',
ariaLabel: mode => `Approval mode: ${mode}`,
manual: 'Manual',
manualDescription: 'Ask before actions that require approval',
smart: 'Smart',
smartDescription: 'Automatically assess actions and ask when needed',
off: 'Off',
offDescription: 'Run without approval prompts'
},
statusbar: {
unknown: 'unknown',
restart: 'restart',
update: 'update',
updateInProgress: 'Update in progress',
commitsBehind: (count, branch) => `${count} commit${count === 1 ? '' : 's'} behind ${branch}`,
desktopVersion: version => `Hermes Desktop v${version}`,
backendVersion: version => `Backend v${version}`,
clientLabel: version => `client v${version}`,
connectionSsh: host => `SSH: ${host}`,
connectionRemote: host => `Remote: ${host}`,
connectionCloud: host => `Cloud: ${host}`,
connectionCloudTooltip: host => `Hermes Cloud · ${host}`,
connectionSshTooltip: host => `SSH · ${host}`,
connectionRemoteTooltip: host => `Remote · ${host}`,
backendLabel: version => `backend v${version}`,
commit: sha => `commit ${sha}`,
branch: branch => `branch ${branch}`,
closeCommandCenter: 'Close Command Center',
openCommandCenter: 'Open Command Center',
showTerminal: 'Show terminal',
hideTerminal: 'Hide terminal',
gateway: 'Gateway',
gatewayReady: 'ready',
gatewayNeedsSetup: 'needs setup',
gatewayUnavailable: 'inference unavailable',
gatewayChecking: 'checking',
gatewayConnecting: 'connecting',
gatewayOffline: 'offline',
gatewayRestarting: 'restarting…',
gatewayTitle: 'Gateway',
customizeTitle: 'Show in status bar',
hideStatusbar: 'Hide status bar',
resetStatusbar: 'Reset to defaults',
toggleApprovalMode: 'Approvals',
toggleBackendVersion: 'Backend version',
toggleCacheHitRate: 'Cache hit rate',
toggleCommandCenter: 'Command Center',
toggleContextUsage: 'Context meter',
toggleRunningTimer: 'Turn timer',
toggleSessionTimer: 'Session timer',
toggleTerminal: 'Terminal',
toggleTokensPerSecond: 'Tokens per second',
toggleVersion: 'Version & updates',
toggleWorkspace: 'Workspace',
cacheHitRateTitle: 'Prompt cache hit rate this session — cached tokens cost less, so higher is cheaper',
tokensPerSecondTitle: 'Output tokens per second, averaged over the last 10 model calls',
agents: 'Agents',
closeAgents: 'Close agents',
openAgents: 'Open agents',
subagents: count => `${count} subagent${count === 1 ? '' : 's'}`,
failed: count => `${count} failed`,
running: count => `${count} running`,
cron: 'Cron',
openCron: 'Open cron jobs',
webhooks: 'Webhooks',
openWebhooks: 'Open webhooks',
starmap: 'Memory Graph',
openStarmap: 'Open memory graph',
turnRunning: 'Running',
contextUsage: 'Context usage',
systemResources: {
title: 'System Resources',
loading: 'Resources…',
gpuUtilization: 'GPU utilization',
gpuMemory: 'GPU memory',
ram: 'RAM',
unifiedNote: 'Unified memory — the GPU and system share this pool.',
toggle: 'System resources'
},
contextUsagePanel: {
categories: {
conversation: 'Conversation',
mcp: 'MCP',
memory: 'Memory',
rules: 'Rules',
skills: 'Skills',
subagent_definitions: 'Subagent definitions',
system_prompt: 'System prompt',
tool_definitions: 'Tool definitions'
},
empty: 'No context data yet',
loading: 'Loading breakdown…',
percentFull: percent => `${percent}% Full`,
title: 'Context Usage',
tokenSummary: (used, max) => `${used} / ${max} Tokens`
},
session: 'Session',
yoloOn: 'YOLO on — auto-approving dangerous commands. Shift+click toggles globally.',
yoloOff: 'YOLO off. Shift+click toggles globally.',
modelNone: 'none',
noModel: 'no model',
switchModel: 'Switch model',
openModelPicker: 'Open model picker',
modelPinned: 'pinned by you; new chats use this instead of the Settings default',
modelTitle: (provider, model) => `Model · ${provider}: ${model}`,
providerModelTitle: (provider, model) => `${provider} · ${model}`
}
},
rightSidebar: {
aria: 'Right sidebar',
panelsAria: 'Right sidebar panels',
files: 'File system',
terminal: 'Terminal',
noFolderSelected: 'No folder selected',
changeCwdTitle: 'Change working directory',
remotePickerTitle: 'Choose remote folder',
remotePickerDescription: 'Browse folders on the connected backend.',
remotePickerSelect: 'Select folder',
folderTip: cwd => cwd,
openFolder: 'Open folder',
refreshTree: 'Refresh tree',
collapseAll: 'Collapse all folders',
previewUnavailable: 'Preview unavailable',
couldNotPreview: path => `Could not preview ${path}`,
noProjectTitle: 'No project',
noProjectBody: 'Open a project to browse its files and review changes.',
noProjectOpen: 'No project open',
noDiffs: 'No diffs',
unreadableTitle: 'Unreadable',
unreadableBody: error => `Could not read this folder (${error}).`,
emptyTitle: 'Empty',
emptyBody: 'This folder is empty.',
treeErrorTitle: 'Tree error',
treeErrorBody: 'The file tree hit an error rendering this folder.',
tryAgain: 'Try again',
loadingTree: 'Loading file tree',
loadingFiles: 'Loading files',
terminalHide: 'Hide terminal',
terminalsAria: 'Terminals',
terminalNew: 'New terminal',
terminalCloseOthers: 'Close others',
terminalCloseAll: 'Close all',
addToChat: 'Add to chat'
},
preview: {
tab: 'Preview',
closePane: 'Close preview pane',
loading: 'Loading preview',
unavailable: 'Preview unavailable',
opening: 'Opening...',
hide: 'Hide',
openPreview: 'Open preview',
openInBrowser: 'Open in browser',
openInExternal: 'Open in external',
popIn: 'Pop in',
popOut: 'Pop out',
linkHint: '⌘/Ctrl-click for preview pane',
sourceLineTitle: 'Click to select · shift-click to extend · drag to composer',
source: 'SOURCE',
renderedPreview: 'PREVIEW',
diff: 'DIFF',
unknownSize: 'unknown size',
binaryTitle: 'This looks like a binary file',
binaryBody: label => `Previewing ${label} may show unreadable text.`,
largeTitle: 'This file is large',
largeBody: (label, size) => `${label} is ${size}. Hermes will only show the first 512 KB.`,
previewAnyway: 'Preview anyway',
truncated: 'Showing first 512 KB.',
noInlineTitle: 'No inline preview',
noInlineBody: mimeType => `${mimeType || 'This file type'} can still be attached as context.`,
edit: 'Edit',
editing: 'Editing',
unsavedChanges: 'Unsaved changes',
saveFailed: message => `Couldn't save: ${message}`,
diskChangedTitle: 'File changed on disk',
diskChangedBody:
'This file changed since you opened it. Overwrite it with your version, or discard your edits and reload?',
overwrite: 'Overwrite',
discardReload: 'Discard & reload',
console: {
deselect: 'Deselect entry',
select: 'Select entry',
copyFailed: 'Could not copy console output',
copyEntry: 'Copy this entry',
sendEntry: 'Send this entry to chat',
messages: count => `${count} console messages`,
resize: 'Resize preview console',
title: 'Preview Console',
selected: count => `${count} selected`,
sendToChat: 'Send to chat',
copySelected: 'Copy selected to clipboard',
copyAll: 'Copy all to clipboard',
copy: 'Copy',
clear: 'Clear',
empty: 'No console messages yet.',
promptHeader: 'Preview console:',
sentTitle: 'Sent to chat',
sentMessage: count => `${count} log entr${count === 1 ? 'y' : 'ies'} added to composer`
},
web: {
appFailedToBoot: 'Preview app failed to boot',
serverNotFound: 'Server not found',
remoteLoopback:
'This address points at the machine running your agent, not this one. The browser pane loads pages locally, so a remote dev server needs a port forward or a reachable hostname.',
failedToLoad: 'Preview failed to load',
tryAgain: 'Try again',
restarting: 'Hermes is restarting...',
askRestart: 'Ask Hermes to restart the server',
lookingRestart: taskId => `Hermes is looking for a preview server to restart (${taskId})`,
restartingTitle: 'Restarting preview server',
restartingMessage: 'Hermes is working in the background. Watch the preview console for progress.',
startRestartFailed: message => `Could not start server restart: ${message}`,
restartFailed: 'Server restart failed',
hideConsole: 'Hide preview console',
showConsole: 'Show preview console',
hideDevTools: 'Hide preview DevTools',
openDevTools: 'Open preview DevTools',
goBack: 'Back',
goForward: 'Forward',
reload: 'Reload page',
address: 'Address',
addressPlaceholder: 'Enter address',
blankPageBody: 'Type an address above to browse, or ask Hermes to open a page.',
finishedRestarting: message => `Hermes finished restarting the preview server${message ? `: ${message}` : ''}`,
failedRestarting: message => `Server restart failed: ${message}`,
unknownError: 'unknown error',
restartedTitle: 'Preview server restarted',
reloadingNow: 'Reloading the preview now.',
restartFailedTitle: 'Preview restart failed',
restartFailedMessage: 'Hermes could not restart the server.',
stillWorking:
'Hermes is still working, but no restart result has arrived yet. The server command may be running in the foreground.',
workspaceReloading: 'Workspace changed, reloading preview',
fileChanged: url => `File changed, reloading preview: ${url}`,
filesChanged: (count, url) => `${count} file changes, reloading preview: ${url}`,
watchFailed: message => `Could not watch preview file: ${message}`,
moduleMimeDescription:
'Module scripts are being served with the wrong MIME type. This usually means a static file server is serving a Vite/React app instead of the project dev server.',
loadFailedConsole: (code, message) => `Load failed${code ? ` (${code})` : ''}: ${message}`,
unreachableDescription: 'The preview page could not be reached.',
openTarget: url => `Open ${url}`,
fallbackTitle: 'Preview',
annotate: 'Annotate',
annotateOn: 'Stop annotating',
annotateNeedPage: 'Open a page in the in-app browser first.',
annotateFailed: 'Could not start annotation mode',
commenting: 'Commenting',
addComments: count => (count === 1 ? 'Add 1 comment' : `Add ${count} comments`),
commentPlaceholder: 'Add a comment...',
commentTitle: n => `Comment ${n}`,
saveComment: 'Save',
cancelComment: 'Cancel comment'
}
},
zones: {
bots: 'Bots',
showTabStrip: 'Show tabs',
hideTabStrip: 'Hide tabs',
showStripTab: title => `Show ${title}`,
hideStripTab: title => `Hide ${title}`,
lastTabKeptTitle: 'Last tab stays',
lastTabKeptBody: 'This zone needs at least one visible tab. Show another tab first, or collapse the whole sidebar.',
toggleStripTab: title => `Toggle ${title} tab`,
minimize: 'Minimize',
restore: 'Restore',
closeRunningTitle: 'Close running tab?',
closeRunningBody:
'This chat is still working (or waiting on your input). Closing the tab hides it — the session keeps its progress and can be reopened from the sidebar.',
closeRunningConfirm: 'Close tab',
reload: 'Reload',
closeOthers: 'Close others',
closeToRight: 'Close to the right',
closeAll: 'Close all',
newSessionTab: 'New session tab',
newTab: 'New tab',
pluginDisabled: pluginId => `Plugin "${pluginId}" disabled`,
pluginDisabledBody: 'Re-enable it in Settings → Plugins to bring the pane back.',
missingPane: paneId => `missing pane: ${paneId}`,
editTitle: 'Layouts',
editHint: 'Pick a layout, or drag panes between zones.',
reset: 'Reset',
templates: 'Templates',
custom: 'Custom',
newGridLayout: 'New grid layout',
saveCurrentAs: 'Save current arrangement as a template',
nameLayoutPlaceholder: 'Name this layout…',
deletePreset: name => `Delete ${name}`,
zoneEditorTitle: 'Zone editor',
editorHintPre: 'click to split · ',
editorHintPost: ' flips the line · drag across zones to merge · drag shared edges to resize',
templateColumns: 'Columns',
templateRows: 'Rows',
templateGrid: 'Grid',
templatePriority: 'Priority',
zoneTag: index => `zone ${index}`,
mergeZones: count => `Merge ${count} zones`,
customZoneName: count => `Custom ${count}-zone`,
layoutNamePlaceholder: fallback => `Layout name (${fallback})`,
saveApply: 'Save & apply',
notExpressible: 'this arrangement interlocks (pinwheel) — not expressible as nested splits yet',
zoneCount: count => `${count} zones`,
tabCount: count => `${count} tabs`
},
contextMenu: {
link: {
openInApp: 'Open in in-app browser',
openExternal: 'Open in external browser',
copyUrl: 'Copy URL',
copyResolvedUrl: 'Copy resolved URL'
},
image: {
copyImage: 'Copy image',
copyImageAddress: 'Copy image address',
saveImageAs: 'Save image as…'
},
edit: {
cut: 'Cut',
paste: 'Paste',
selectAll: 'Select all',
addToDictionary: 'Add to dictionary'
},
page: {
copyPageUrl: 'Copy page URL',
inspectElement: 'Inspect element'
}
},
assistant: {
thread: {
loadingSession: 'Loading session',
showEarlier: 'Show earlier messages',
loadingResponse: 'Hermes is loading a response',
loadingLocalModel: model => `Loading ${model} into memory`,
processingPrompt: 'Processing prompt',
resumeWhenBackgroundDone: count =>
count === 1
? 'Will resume when the background task finishes'
: `Will resume when ${count} background tasks finish`,
thinking: 'Thinking',
thought: 'Thought',
thoughtBriefly: 'Thought briefly',
thoughtFor: duration => `Thought for ${duration}`,
turnDuration: duration => `This turn took ${duration}`,
today: time => `Today, ${time}`,
yesterday: time => `Yesterday, ${time}`,
copy: 'Copy',
refresh: 'Refresh',
moreActions: 'More actions',
branchNewChat: 'Branch in new chat',
react: 'React',
dismissError: 'Dismiss error',
errorLayers: {
auth: 'Authentication error',
billing: 'Out of credits',
disk: 'Disk full',
endpoint: 'Custom endpoint error',
gateway: 'Gateway error',
generic: 'Turn failed',
provider: 'Provider error',
runtime: 'Local runtime error',
streaming: 'Streaming connection error'
},
errorRetry: 'Retry',
errorSwitchProvider: 'Switch provider',
errorOpenLogs: 'Open logs',
errorOpenLogsFailed: 'Could not open the logs folder',
errorOpenDesktopLogs: 'Open Desktop logs',
errorCopyDiagnostics: 'Copy error details',
errorSendDiagnostics: 'Send diagnostics',
filesChanged: count => (count === 1 ? '1 file changed' : `${count} files changed`),
reviewChanges: 'Review',
readAloudFailed: 'Read aloud failed',
preparingAudio: 'Preparing audio...',
stopReading: 'Stop reading',
readAloud: 'Read aloud',
editMessage: 'Edit message',
expandMessage: 'Expand message',
scrollToBottom: 'Scroll to bottom',
stop: 'Stop',
restorePrevious: 'Restore previous checkpoint',
restoreCheckpoint: 'Restore checkpoint',
restoreFromHere: 'Restore checkpoint — rerun from this prompt',
restoreTitle: 'Restore to this checkpoint?',
restoreBody:
'Everything after this prompt is removed from the conversation, and the prompt runs again from here.',
restoreConfirm: 'Restore & rerun',
restoreNext: 'Restore next checkpoint',
goForward: 'Go forward',
sendEdited: 'Send edited message',
attachingFile: 'Attaching…'
},
approval: {
gatewayDisconnected: 'Hermes gateway is not connected',
sendFailed: 'Could not send approval response',
run: 'Run',
command: 'Command',
moreOptions: 'More approval options',
allowSession: 'Allow this session',
alwaysAllowMenu: 'Always allow…',
jumpToApproval: 'Approval needed',
reject: 'Reject',
alwaysTitle: 'Always allow this command?',
alwaysDescription: pattern =>
`This adds the “${pattern}” pattern to your permanent allowlist (~/.hermes/config.yaml). Hermes won’t ask again for commands like this — in this session or any future one.`,
alwaysAllow: 'Always allow'
},
clarify: {
notReady: 'Clarify request is not ready yet',
gatewayDisconnected: 'Hermes gateway is not connected',
sendFailed: 'Could not send clarify response',
loadingQuestion: 'Loading question…',
other: 'Other (type your answer)',
placeholder: 'Type your answer…',
skip: 'Skip',
skipped: 'Skipped',
continueLabel: 'Continue',
confirmAndContinueLabel: 'Confirm and continue',
answeredBadge: 'Answered',
questionProgress: (answered, total) => `${answered} of ${total} answered`,
lateAnswer: (question, choice) => `Re: "${question}" — my answer: ${choice}`,
lateAnswerTip: 'Draft this answer as a follow-up message',
lateAnswerHint: 'This prompt is no longer waiting. Pick an option to draft it as a follow-up message.'
},
mcpSetup: {
installTitle: server => `Add the ${server} MCP server?`,
enableTitle: server => `Enable the ${server} MCP server?`,
authorizeTitle: server => `Authorize the ${server} MCP server?`,
installAction: 'Install',
enableAction: 'Enable',
authorizeAction: 'Authorize',
decline: 'Not now',
declined: 'Declined',
installed: server => `Installed ${server}`,
enabled: server => `Enabled ${server}`,
authorized: server => `Authorized ${server}`,
failed: server => `Setup failed for ${server}`,
unanswered: 'No response',
toolCount: count => (count === 1 ? '1 tool' : `${count} tools`),
notInCatalog: server => `“${server}” is not in the MCP catalog`,
catalogSource: 'From the Nous-approved catalog',
envRequired: 'Fill in the required credentials first',
sendFailed: 'Could not send MCP setup response',
reloadFailed: 'Server saved, but reloading MCP tools failed — they load next session',
gatewayDisconnected: 'Hermes gateway is not connected'
},
tool: {
copyCode: 'Copy code',
renderingImage: 'Rendering image',
copyOutput: 'Copy output',
copyCommand: 'Copy command',
copyContent: 'Copy content',
copyUrl: 'Copy URL',
copyResults: 'Copy results',
copyQuery: 'Copy query',
copyFile: 'Copy file',
copyPath: 'Copy path',
outputAlt: 'Tool output',
rawResponse: 'Raw response',
copyActivity: 'Copy activity',
recoveredOne: 'Recovered after 1 failed step',
recoveredMany: count => `Recovered after ${count} failed steps`,
failedOne: '1 step failed',
failedMany: count => `${count} steps failed`,
statusRunning: 'Running',
statusError: 'Error',
statusRecovered: 'Recovered',
statusDone: 'Done',
memoryWriteNoted: 'Memory write noted',
actions: {
read: 'Read',
reading: 'Reading',
opened: 'Opened',
opening: 'Opening',
failedToOpen: 'Failed to open',
searched: 'Searched',
searching: 'Searching',
ran: 'Ran',
running: 'Running',
ranCode: 'Ran code',
runningCode: 'Scripting'
},
prefixes: {
browser: 'Browser',
web: 'Web'
},
titleTemplates: {
actionCommand: (action, command) => `${action} ${command}`,
actionQuoted: (action, value) => `${action} “${value}”`,
actionTarget: (action, target) => `${action} ${target}`,
prefixedDone: (prefix, action) => `${prefix} ${action}`,
runningPrefixedTool: (prefix, action) => `Running ${prefix.toLowerCase()} ${action.toLowerCase()}`,
runningTool: action => `Running ${action.toLowerCase()}`
},
titles: {
browser_click: { done: 'Clicked page element', pending: 'Clicking page element', pendingAction: 'Clicking' },
browser_fill: { done: 'Filled form field', pending: 'Filling form field', pendingAction: 'Filling' },
browser_navigate: { done: 'Opened page', pending: 'Opening page', pendingAction: 'Opening' },
browser_snapshot: {
done: 'Captured page snapshot',
pending: 'Capturing page snapshot',
pendingAction: 'Capturing'
},
browser_take_screenshot: {
done: 'Captured screenshot',
pending: 'Capturing screenshot',
pendingAction: 'Capturing'
},
browser_type: { done: 'Typed on page', pending: 'Typing on page', pendingAction: 'Typing' },
clarify: { done: 'Asked a question', pending: 'Asking a question', pendingAction: 'Asking' },
cronjob: { done: 'Cron job', pending: 'Scheduling cron job', pendingAction: 'Scheduling' },
edit_file: { done: 'Edited file', pending: 'Editing file', pendingAction: 'Editing' },
execute_code: { done: 'Ran code', pending: 'Scripting', pendingAction: 'Scripting' },
image_generate: { done: 'Generated image', pending: 'Generating image', pendingAction: 'Generating' },
list_files: { done: 'Listed files', pending: 'Listing files', pendingAction: 'Listing' },
memory: { done: 'Saved to memory', pending: 'Saving to memory', pendingAction: 'Saving' },
patch: { done: 'Patched file', pending: 'Patching file', pendingAction: 'Patching' },
read_file: { done: 'Read file', pending: 'Reading file', pendingAction: 'Reading' },
search_files: { done: 'Searched files', pending: 'Searching files', pendingAction: 'Searching' },
session_search_recall: {
done: 'Searched session history',
pending: 'Searching session history',
pendingAction: 'Searching'
},
terminal: { done: 'Ran command', pending: 'Running command', pendingAction: 'Running' },
todo: { done: 'Updated todos', pending: 'Updating todos', pendingAction: 'Updating' },
vision_analyze: { done: 'Analyzed image', pending: 'Analyzing image', pendingAction: 'Analyzing' },
web_extract: { done: 'Read webpage', pending: 'Reading webpage', pendingAction: 'Reading' },
web_search: { done: 'Searched web', pending: 'Searching web', pendingAction: 'Searching' },
write_file: { done: 'Edited file', pending: 'Editing file', pendingAction: 'Editing' }
}
}
},
prompts: {
gatewayDisconnected: 'Hermes gateway is not connected',
sudoSendFailed: 'Could not send sudo password',
secretSendFailed: 'Could not send secret',
sudoTitle: 'Administrator password',
sudoDesc: 'Hermes needs your sudo password to run a privileged command. It is sent only to your local agent.',
sudoPlaceholder: 'sudo password',
secretTitle: 'Secret required',
secretDesc: 'Hermes needs a credential to continue.',
secretPlaceholder: 'secret value'
},
desktop: {
audioReadFailed: 'Could not read recorded audio',
sessionUnavailable: 'Session unavailable',
createSessionFailed: 'Could not create a new session',
promptFailed: 'Prompt failed',
providerCredentialRequired: 'Add a provider credential before sending your first message.',
emptySlashCommand: 'empty slash command',
desktopCommands: 'Desktop commands',
skillCommandsAvailable: count => `${count} skill commands available.`,
warningLine: message => `warning: ${message}`,
yoloArmed: 'YOLO armed for this chat',
yoloOff: 'YOLO off',
yoloSystem: active => `YOLO ${active ? 'on' : 'off'} for this session`,
yoloTitle: 'YOLO',
yoloToggleFailed: 'Could not toggle YOLO',
profileStatus: current =>
`Profile: ${current}. Use /profile <name> or the "New session" picker to start a chat in another profile.`,
unknownProfile: 'Unknown profile',
noProfileNamed: (target, available) => `No profile named "${target}". Available: ${available}`,
newChatsProfile: name => `New chats will use profile ${name}.`,
setProfileFailed: 'Failed to set profile',
sttDisabled: 'Speech-to-text is disabled in settings.',
stopFailed: 'Stop failed',
regenerateFailed: 'Regenerate failed',
editFailed: 'Edit failed',
editTurnUnavailable: 'This turn is no longer in server history (it may have been compressed away).',
resumeFailed: 'Resume failed',
readOnlyTranscriptTitle: 'Opened read-only',
readOnlyTranscriptBody:
'No connected backend claims this older chat yet, so it opened as a read-only transcript. Its history is intact; sending is disabled until a backend claims it.',
readOnlyTranscriptSendBlocked: 'This chat is open as a read-only transcript — sending is disabled.',
resumeStrandedTitle: "Couldn't load this session",
resumeStrandedBody:
'The connection to this session failed and automatic retries gave up. Check that the gateway is running, then try again.',
resumeRetry: 'Retry',
nothingToBranch: 'Nothing to branch',
branchNeedsChat: 'Start or resume a chat before branching.',
sessionBusy: 'Session busy',
branchStopCurrent: 'Stop the current turn before branching this chat.',
branchNoText: 'This message has no text to branch from.',
branchTitle: n => `Draft: Branch #${n}`,
branchFailed: 'Branch failed',
deleteFailed: 'Delete failed',
archived: 'Archived',
archiveFailed: 'Archive failed',
cwdChangeFailed: 'Working directory change failed',
cwdStagedTitle: 'Working directory staged',
cwdStagedMessage: 'Restart the desktop backend to apply cwd changes to this active session.',
modelSwitchFailed: 'Model switch failed',
hydrationSyncing: (profile: string) => `Syncing ${profile}\u2026`,
sessionExported: 'Session exported',
sessionExportFailed: 'Could not export session',
imageSaved: 'Image saved',
downloadStarted: 'Download started',
restartToUseSaveImage: 'Restart Hermes Desktop to use Save Image.',
restartToSaveImages: 'Restart Hermes Desktop to save images',
imageDownloadFailed: 'Image download failed',
openImage: 'Open image',
downloadImage: 'Download image',
savingImage: 'Saving image',
imagePreviewFailed: 'Image preview failed',
imageAttach: 'Image attach',
imageWriteFailed: 'Failed to write image to disk.',
imageAttachFailed: 'Image attach failed',
attachImages: 'Attach images',
clipboard: 'Clipboard',
noClipboardImage: 'No image found in clipboard',
clipboardPasteFailed: 'Clipboard paste failed',
dropFiles: 'Drop files',
handoff: {
pickPlatform: 'Choose a destination',
success: platform => `Handed off to ${platform}. Resume here anytime.`,
systemNote: platform => `↻ Handed off to ${platform} — resume here anytime.`,
failed: error => `Handoff failed: ${error}`,
timedOut: 'Timed out waiting for the gateway. Is `hermes gateway` running?'
}
},
tips: {
close: "Don't show this tip again",
items: {
'new-session': {
title: 'Start fresh',
text: 'A new chat gets its own context, terminal and working directory.'
},
skills: {
title: 'Teach it once',
text: 'Skills are folders of instructions Hermes loads when the work calls for them.'
},
messaging: {
title: 'Hermes away from your desk',
text: 'Connect Telegram, Discord, Slack and more — same agent, same memory.'
},
artifacts: {
title: 'Everything Hermes made',
text: 'Images, files and links from every session, indexed in one place.'
},
cron: {
title: 'Work that runs itself',
text: 'Schedule a prompt hourly, nightly, or on a cron expression.'
},
'command-palette': {
title: 'One box for everything',
text: 'Sessions, settings, skills and commands all answer to the palette.'
},
profiles: {
title: 'Profiles are separate',
text: 'Each one is its own Hermes — own keys, own memory, own sessions.'
},
'composer-mentions': {
title: 'Attach and command',
text: 'Type @ to bring a file into the conversation, / to run a command.'
},
'local-setup': {
title: 'This machine can run models locally',
text: 'Your hardware can serve a local model. Chats stay on your computer and cost nothing.',
action: 'Set it up'
},
'right-pane': {
title: 'The working pane',
text: 'Files, terminal, review and the in-app browser share the right side.'
}
}
},
errors: {
genericFailure: 'Something went wrong',
boundaryTitle: 'Something broke in the interface',
boundaryDesc: 'The view hit an unexpected error. Your chats and settings are safe.',
reloadWindow: 'Reload window',
openLogs: 'Open logs'
},
ui: {
search: {
clear: 'Clear search'
},
pagination: {
label: 'pagination',
previous: 'Prev',
previousAria: 'Go to previous page',
next: 'Next',
nextAria: 'Go to next page'
},
sidebar: {
title: 'Sidebar',
description: 'Displays the mobile sidebar.',
toggle: open => `${open ? 'Show' : 'Hide'} sidebar`
}
}
}