From 5c14e08493d06911303d6c29a13e1d4bc6897515 Mon Sep 17 00:00:00 2001 From: Simos Mikelatos Date: Tue, 30 Jun 2026 20:59:40 +0000 Subject: [PATCH] feat: improve Hermes provider support --- server/hermes-cli.js | 35 ++++--- server/hermes/acp-client.js | 28 ++++-- .../list/hermes/hermes-models.provider.ts | 35 ++++--- .../list/hermes/hermes-sessions.provider.ts | 96 ++++++++++++++++--- .../services/shell-websocket.service.ts | 16 ++++ server/routes/agent.js | 3 +- .../chat/hooks/useChatComposerState.ts | 7 +- .../chat/hooks/useChatProviderState.ts | 9 ++ .../ProviderSelectionEmptyState.tsx | 10 +- .../hooks/useProviderAuthStatus.ts | 4 + src/components/provider-auth/types.ts | 13 +-- .../tabs/agents-settings/AgentListItem.tsx | 7 +- .../sections/content/AccountContent.tsx | 27 +++++- 13 files changed, 212 insertions(+), 78 deletions(-) diff --git a/server/hermes-cli.js b/server/hermes-cli.js index 1c6f2730..3dc7c708 100644 --- a/server/hermes-cli.js +++ b/server/hermes-cli.js @@ -48,15 +48,23 @@ function readStopReason(result) { return result.stopReason || result.stop_reason || result.reason || null; } -function buildPromptParams(sessionId, command, model) { - const params = { +function buildPromptParams(sessionId, command) { + return { sessionId, prompt: [{ type: 'text', text: command }], }; - if (model) { - params.modelId = model; - } - return params; +} + +function buildSessionSetupParams(sessionId, workingDir) { + return { + ...(sessionId ? { sessionId } : {}), + cwd: workingDir, + mcpServers: [], + }; +} + +function canLoadSession(connection) { + return connection?.initializeResult?.agentCapabilities?.loadSession === true; } function findPermissionOption(options, kinds, fallbackOptionIds = []) { @@ -233,7 +241,7 @@ async function spawnHermes(command, options = {}, ws) { }; try { - const resolvedModel = await providerModelsService.resolveResumeModel(PROVIDER, sessionId, requestedModel); + await providerModelsService.resolveResumeModel(PROVIDER, sessionId, requestedModel); const connection = await hermesConnectionManager.getConnection(workingDir); activeHermesSessions.set(activeKey, { connection, @@ -278,20 +286,21 @@ async function spawnHermes(command, options = {}, ws) { try { let sessionResult; - if (sessionId) { + if (sessionId && canLoadSession(connection)) { try { - sessionResult = await connection.request('session/load', { sessionId, cwd: workingDir }); + sessionResult = await connection.request('session/load', buildSessionSetupParams(sessionId, workingDir)); } catch { sessionResult = { sessionId }; } } else { - sessionResult = await connection.request('session/new', { - cwd: workingDir, - }); + sessionResult = await connection.request('session/new', buildSessionSetupParams(null, workingDir)); } registerSession(readSessionId(sessionResult) || sessionId, connection); - const promptResult = await connection.request('session/prompt', buildPromptParams(capturedSessionId, command, resolvedModel)); + if (!capturedSessionId) { + throw new Error('Hermes ACP did not return a session id.'); + } + const promptResult = await connection.request('session/prompt', buildPromptParams(capturedSessionId, command)); const finalSessionId = capturedSessionId || readSessionId(promptResult) || sessionId || activeKey; const stopReason = readStopReason(promptResult) || 'completed'; const active = activeHermesSessions.get(finalSessionId) || activeHermesSessions.get(activeKey); diff --git a/server/hermes/acp-client.js b/server/hermes/acp-client.js index 2e536f92..9bf87e5b 100644 --- a/server/hermes/acp-client.js +++ b/server/hermes/acp-client.js @@ -19,6 +19,7 @@ class AcpClient extends EventEmitter { this.buffer = ''; this.requestHandlers = new Map(); this.initialized = false; + this.initializeResult = null; } start() { @@ -54,19 +55,20 @@ class AcpClient extends EventEmitter { } this.start(); - await this.request('initialize', { + this.initializeResult = await this.request('initialize', { protocolVersion: 1, + clientCapabilities: { + fs: { + readTextFile: false, + writeTextFile: false, + }, + terminal: false, + }, clientInfo: { name: 'CloudCLI', + title: 'CloudCLI', version: '1.0.0', }, - capabilities: { - fs: false, - terminal: false, - session: { - requestPermission: true, - }, - }, }); this.initialized = true; this.notify('initialized', {}); @@ -95,7 +97,7 @@ class AcpClient extends EventEmitter { const payload = { jsonrpc: '2.0', id, method, params }; return new Promise((resolve, reject) => { - this.pending.set(id, { resolve, reject }); + this.pending.set(id, { resolve, reject, method, params }); this.writeMessage(payload); }); } @@ -171,7 +173,13 @@ class AcpClient extends EventEmitter { } this.pending.delete(message.id); if (message.error) { - pending.reject(new Error(message.error.message || JSON.stringify(message.error))); + const messageText = message.error.message || JSON.stringify(message.error); + const error = new Error(`ACP ${pending.method} failed: ${messageText}`); + error.code = message.error.code; + error.data = message.error.data; + error.method = pending.method; + error.params = pending.params; + pending.reject(error); } else { pending.resolve(message.result); } diff --git a/server/modules/providers/list/hermes/hermes-models.provider.ts b/server/modules/providers/list/hermes/hermes-models.provider.ts index 89b21c45..01e4d8bb 100644 --- a/server/modules/providers/list/hermes/hermes-models.provider.ts +++ b/server/modules/providers/list/hermes/hermes-models.provider.ts @@ -9,11 +9,7 @@ import type { ProviderModelsDefinition, ProviderSessionActiveModelChange, } from '@/shared/types.js'; -import { - buildDefaultProviderCurrentActiveModel, - readOptionalString, - writeProviderSessionActiveModelChange, -} from '@/shared/utils.js'; +import { readOptionalString } from '@/shared/utils.js'; export const HERMES_CONFIGURED_MODEL = '__hermes_configured_model__'; @@ -105,24 +101,21 @@ export class HermesProviderModels implements IProviderModels { return HERMES_FALLBACK_MODELS; } - const options = [ - { value: activeModel, label: activeModel }, - ...HERMES_FALLBACK_MODELS.OPTIONS, - ]; - return { - OPTIONS: options, - DEFAULT: activeModel, + OPTIONS: [ + { + value: HERMES_CONFIGURED_MODEL, + label: 'Configured in Hermes', + description: `Current Hermes model: ${activeModel}`, + }, + ], + DEFAULT: HERMES_CONFIGURED_MODEL, }; } async getCurrentActiveModel(): Promise { const configured = await this.readConfiguredModel(); - if (configured) { - return { model: configured }; - } - - return buildDefaultProviderCurrentActiveModel(await this.getSupportedModels()); + return { model: configured ?? HERMES_CONFIGURED_MODEL }; } async changeActiveModel(input: ProviderChangeActiveModelInput): Promise { @@ -136,7 +129,13 @@ export class HermesProviderModels implements IProviderModels { }; } - return writeProviderSessionActiveModelChange('hermes', input); + return { + provider: 'hermes', + sessionId: input.sessionId, + supported: false, + changed: false, + model: null, + }; } private async readConfiguredModel(): Promise { diff --git a/server/modules/providers/list/hermes/hermes-sessions.provider.ts b/server/modules/providers/list/hermes/hermes-sessions.provider.ts index 9d4cd0cc..7d1a8ecd 100644 --- a/server/modules/providers/list/hermes/hermes-sessions.provider.ts +++ b/server/modules/providers/list/hermes/hermes-sessions.provider.ts @@ -60,6 +60,44 @@ function readEventSessionId(raw: AnyRecord, sessionId: string | null): string | return readOptionalString(raw.sessionId) ?? readOptionalString(raw.session_id) ?? sessionId; } +function readTextContent(value: unknown): string | null { + const direct = readOptionalString(value); + if (direct !== undefined) { + return direct; + } + + if (Array.isArray(value)) { + const parts = value + .map((entry) => readTextContent(entry)) + .filter((entry): entry is string => Boolean(entry?.trim())); + return parts.length > 0 ? parts.join('') : null; + } + + const record = readObjectRecord(value); + if (!record) { + return null; + } + + const nestedContent = record.content; + const nestedText = nestedContent === value ? null : readTextContent(nestedContent); + + return readOptionalString(record.text) + ?? readOptionalString(record.content) + ?? nestedText + ?? readOptionalString(record.delta) + ?? readOptionalString(record.rawOutput) + ?? readOptionalString(record.raw_output) + ?? readOptionalString(record.output) + ?? null; +} + +function readToolPayload(raw: AnyRecord): AnyRecord { + return readObjectRecord(raw.toolCall) + ?? readObjectRecord(raw.tool_call) + ?? readObjectRecord(raw.tool) + ?? raw; +} + function normalizeHermesEvent(rawMessage: unknown, sessionId: string | null, history = false): NormalizedMessage[] { const envelope = readObjectRecord(rawMessage); if (!envelope) { @@ -75,10 +113,10 @@ function normalizeHermesEvent(rawMessage: unknown, sessionId: string | null, his const baseId = readOptionalString(raw.id) ?? readOptionalString(raw.messageId) ?? readOptionalString(raw.message_id) ?? generateMessageId(PROVIDER); if (['agent_message_chunk', 'assistant_message_chunk', 'message_delta', 'text_delta', 'text'].includes(type)) { - const content = readOptionalString(raw.content) + const content = readTextContent(raw.content) ?? readOptionalString(raw.text) ?? readOptionalString(raw.delta) - ?? readOptionalString(readObjectRecord(raw.message)?.content) + ?? readTextContent(readObjectRecord(raw.message)?.content) ?? ''; if (!content.trim()) { return []; @@ -96,9 +134,9 @@ function normalizeHermesEvent(rawMessage: unknown, sessionId: string | null, his if (['agent_message', 'assistant_message', 'message'].includes(type)) { const role = readOptionalString(raw.role) === 'user' ? 'user' : 'assistant'; - const content = readOptionalString(raw.content) + const content = readTextContent(raw.content) ?? readOptionalString(raw.text) - ?? readOptionalString(readObjectRecord(raw.message)?.content) + ?? readTextContent(readObjectRecord(raw.message)?.content) ?? ''; if (!content.trim()) { return []; @@ -115,7 +153,7 @@ function normalizeHermesEvent(rawMessage: unknown, sessionId: string | null, his } if (['agent_thought_chunk', 'thought_delta', 'thinking', 'reasoning'].includes(type)) { - const content = readOptionalString(raw.content) ?? readOptionalString(raw.text) ?? readOptionalString(raw.delta) ?? ''; + const content = readTextContent(raw.content) ?? readOptionalString(raw.text) ?? readOptionalString(raw.delta) ?? ''; if (!content.trim()) { return []; } @@ -130,8 +168,15 @@ function normalizeHermesEvent(rawMessage: unknown, sessionId: string | null, his } if (['tool_call', 'tool_use', 'tool_call_start'].includes(type)) { - const tool = readObjectRecord(raw.tool); - const toolId = readOptionalString(raw.toolCallId) ?? readOptionalString(raw.tool_call_id) ?? readOptionalString(raw.toolId) ?? baseId; + const tool = readToolPayload(raw); + const toolId = readOptionalString(raw.toolCallId) + ?? readOptionalString(raw.tool_call_id) + ?? readOptionalString(raw.toolId) + ?? readOptionalString(tool.toolCallId) + ?? readOptionalString(tool.tool_call_id) + ?? readOptionalString(tool.toolId) + ?? readOptionalString(tool.id) + ?? baseId; return [createNormalizedMessage({ id: baseId, sessionId: eventSessionId, @@ -143,22 +188,51 @@ function normalizeHermesEvent(rawMessage: unknown, sessionId: string | null, his ?? readOptionalString(raw.title) ?? readOptionalString(raw.name) ?? readOptionalString(tool?.name) + ?? readOptionalString(tool?.title) ?? 'Tool', - toolInput: raw.rawInput ?? raw.raw_input ?? raw.input ?? raw.arguments ?? raw.params ?? tool?.input ?? {}, + toolInput: raw.rawInput + ?? raw.raw_input + ?? raw.input + ?? raw.arguments + ?? raw.params + ?? tool?.rawInput + ?? tool?.raw_input + ?? tool?.input + ?? tool?.arguments + ?? {}, toolId, })]; } if (['tool_call_update', 'tool_result', 'tool_call_result', 'tool_call_done'].includes(type)) { + const tool = readToolPayload(raw); + const content = readTextContent(raw.content) + ?? readTextContent(raw.rawOutput) + ?? readTextContent(raw.raw_output) + ?? readTextContent(raw.output) + ?? readTextContent(raw.result) + ?? readTextContent(tool.rawOutput) + ?? readTextContent(tool.raw_output) + ?? readTextContent(tool.output) + ?? readTextContent(tool.result) + ?? ''; return [createNormalizedMessage({ id: baseId, sessionId: eventSessionId, timestamp, provider: PROVIDER, kind: 'tool_result', - toolId: readOptionalString(raw.toolCallId) ?? readOptionalString(raw.tool_call_id) ?? readOptionalString(raw.toolId) ?? '', - content: formatContent(raw.output ?? raw.result ?? raw.content ?? raw.delta ?? ''), - isError: Boolean(raw.error) || raw.status === 'error', + toolId: readOptionalString(raw.toolCallId) + ?? readOptionalString(raw.tool_call_id) + ?? readOptionalString(raw.toolId) + ?? readOptionalString(tool.toolCallId) + ?? readOptionalString(tool.tool_call_id) + ?? readOptionalString(tool.toolId) + ?? readOptionalString(tool.id) + ?? '', + content: content || formatContent(raw.delta ?? ''), + isError: Boolean(raw.error) || raw.status === 'error' || raw.status === 'failed', + toolUseResult: raw.result ?? raw.output ?? raw.rawOutput ?? raw.raw_output ?? tool.result ?? tool.output ?? tool.rawOutput ?? tool.raw_output, })]; } diff --git a/server/modules/websocket/services/shell-websocket.service.ts b/server/modules/websocket/services/shell-websocket.service.ts index 55680b8c..14b44766 100644 --- a/server/modules/websocket/services/shell-websocket.service.ts +++ b/server/modules/websocket/services/shell-websocket.service.ts @@ -109,6 +109,12 @@ function resolveResumeSessionId( return resolvedSessionId; } +function getHermesShellCommand(): string { + return (process.env.HERMES_COMMAND_PATH || process.env.HERMES_CLI_PATH || 'hermes') + .trim() + .split(/\s+/)[0] || 'hermes'; +} + /** * Resolves provider command line for plain shell and agent-backed shell modes. */ @@ -161,6 +167,14 @@ function buildShellCommand( return initialCommand || 'opencode'; } + if (provider === 'hermes') { + const command = initialCommand || getHermesShellCommand(); + if (resumeSessionId) { + return `${command} --resume "${resumeSessionId}"`; + } + return command; + } + const command = initialCommand || 'claude'; if (resumeSessionId) { if (os.platform() === 'win32') { @@ -481,6 +495,8 @@ export function handleShellConnection( ? 'Gemini' : provider === 'opencode' ? 'OpenCode' + : provider === 'hermes' + ? 'Hermes' : 'Claude'; welcomeMsg = hasSession && resumeSessionId ? `\x1b[36mResuming ${providerName} session ${resumeSessionId} in: ${projectPath}\x1b[0m\r\n` diff --git a/server/routes/agent.js b/server/routes/agent.js index af09bc47..b8a1acb6 100644 --- a/server/routes/agent.js +++ b/server/routes/agent.js @@ -946,7 +946,6 @@ router.post('/', validateExternalApiKey, async (req, res) => { const codexModels = (await providerModelsService.getProviderModels('codex')).models; const geminiModels = (await providerModelsService.getProviderModels('gemini')).models; const opencodeModels = (await providerModelsService.getProviderModels('opencode')).models; - const hermesModels = (await providerModelsService.getProviderModels('hermes')).models; // Start the appropriate session if (provider === 'claude') { @@ -1006,7 +1005,7 @@ router.post('/', validateExternalApiKey, async (req, res) => { projectPath: finalProjectPath, cwd: finalProjectPath, sessionId: sessionId || null, - model: model || (hermesModels.DEFAULT === HERMES_CONFIGURED_MODEL ? undefined : hermesModels.DEFAULT) + model: model === HERMES_CONFIGURED_MODEL ? undefined : model }, writer); } diff --git a/src/components/chat/hooks/useChatComposerState.ts b/src/components/chat/hooks/useChatComposerState.ts index 74466e83..1728d4eb 100644 --- a/src/components/chat/hooks/useChatComposerState.ts +++ b/src/components/chat/hooks/useChatComposerState.ts @@ -174,7 +174,6 @@ export function useChatComposerState({ codexModel, geminiModel, opencodeModel, - hermesModel, isLoading, canAbortSession, tokenBudget, @@ -339,7 +338,7 @@ export function useChatComposerState({ : provider === 'opencode' ? opencodeModel : provider === 'hermes' - ? (hermesModel === '__hermes_configured_model__' ? undefined : hermesModel) + ? undefined : claudeModel, tokenUsage: tokenBudget, }; @@ -395,7 +394,6 @@ export function useChatComposerState({ cursorModel, geminiModel, opencodeModel, - hermesModel, handleBuiltInCommand, handleCustomCommand, input, @@ -737,7 +735,7 @@ export function useChatComposerState({ : provider === 'opencode' ? opencodeModel : provider === 'hermes' - ? (hermesModel === '__hermes_configured_model__' ? undefined : hermesModel) + ? undefined : claudeModel; // One message shape for every provider. The backend resolves the @@ -783,7 +781,6 @@ export function useChatComposerState({ executeCommand, geminiModel, opencodeModel, - hermesModel, isLoading, onSessionProcessing, onSessionEstablished, diff --git a/src/components/chat/hooks/useChatProviderState.ts b/src/components/chat/hooks/useChatProviderState.ts index 9233a5a1..1372c256 100644 --- a/src/components/chat/hooks/useChatProviderState.ts +++ b/src/components/chat/hooks/useChatProviderState.ts @@ -417,6 +417,15 @@ export function useChatProviderState({ selectedSession, selectedProject }: UseCh model: string, sessionId?: string | null, ) => { + if (targetProvider === 'hermes') { + setStoredProviderModel(targetProvider, model); + return { + scope: 'default' as const, + changed: false, + model, + }; + } + const normalizedSessionId = typeof sessionId === 'string' ? sessionId.trim() : ''; if (!normalizedSessionId) { setStoredProviderModel(targetProvider, model); diff --git a/src/components/chat/view/subcomponents/ProviderSelectionEmptyState.tsx b/src/components/chat/view/subcomponents/ProviderSelectionEmptyState.tsx index 3325b0cf..1c19a74c 100644 --- a/src/components/chat/view/subcomponents/ProviderSelectionEmptyState.tsx +++ b/src/components/chat/view/subcomponents/ProviderSelectionEmptyState.tsx @@ -289,15 +289,11 @@ export default function ProviderSelectionEmptyState({ >
{model.label}
- {/* - // * Temporarly commented out because the description of models from claude - // * was a bit inconsistent. Will return it back when it becomes more consistent. - */} - {/* {model.description && ( + {model.description && (
{model.description}
- )} */} + )}
{isSelected && ( @@ -332,7 +328,7 @@ export default function ProviderSelectionEmptyState({ defaultValue: "Ready with OpenCode {{model}}", }), hermes: t("providerSelection.readyPrompt.hermes", { - model: hermesModel, + model: provider === "hermes" ? currentModelLabel : hermesModel, defaultValue: "Ready with Hermes {{model}}", }), }[provider] diff --git a/src/components/provider-auth/hooks/useProviderAuthStatus.ts b/src/components/provider-auth/hooks/useProviderAuthStatus.ts index 9231e770..81501160 100644 --- a/src/components/provider-auth/hooks/useProviderAuthStatus.ts +++ b/src/components/provider-auth/hooks/useProviderAuthStatus.ts @@ -12,6 +12,7 @@ import type { } from '../types'; type ProviderAuthStatusPayload = { + installed?: boolean; authenticated?: boolean; email?: string | null; method?: string | null; @@ -34,6 +35,7 @@ const toProviderAuthStatus = ( payload: ProviderAuthStatusPayload, fallbackError: string | null = null, ): ProviderAuthStatus => ({ + installed: Boolean(payload.installed), authenticated: Boolean(payload.authenticated), email: payload.email ?? null, method: payload.method ?? null, @@ -78,6 +80,7 @@ export function useProviderAuthStatus( if (!response.ok) { const status: ProviderAuthStatus = { + installed: false, authenticated: false, email: null, method: null, @@ -95,6 +98,7 @@ export function useProviderAuthStatus( } catch (caughtError) { console.error(`Error checking ${provider} auth status:`, caughtError); const status: ProviderAuthStatus = { + installed: false, authenticated: false, email: null, method: null, diff --git a/src/components/provider-auth/types.ts b/src/components/provider-auth/types.ts index 178efdcf..6125b91e 100644 --- a/src/components/provider-auth/types.ts +++ b/src/components/provider-auth/types.ts @@ -1,6 +1,7 @@ import type { LLMProvider } from '../../types/app'; export type ProviderAuthStatus = { + installed: boolean; authenticated: boolean; email: string | null; method: string | null; @@ -22,10 +23,10 @@ export const PROVIDER_AUTH_STATUS_ENDPOINTS: Record = { }; export const createInitialProviderAuthStatusMap = (loading = true): ProviderAuthStatusMap => ({ - claude: { authenticated: false, email: null, method: null, error: null, loading }, - cursor: { authenticated: false, email: null, method: null, error: null, loading }, - codex: { authenticated: false, email: null, method: null, error: null, loading }, - gemini: { authenticated: false, email: null, method: null, error: null, loading }, - opencode: { authenticated: false, email: null, method: null, error: null, loading }, - hermes: { authenticated: false, email: null, method: null, error: null, loading }, + claude: { installed: false, authenticated: false, email: null, method: null, error: null, loading }, + cursor: { installed: false, authenticated: false, email: null, method: null, error: null, loading }, + codex: { installed: false, authenticated: false, email: null, method: null, error: null, loading }, + gemini: { installed: false, authenticated: false, email: null, method: null, error: null, loading }, + opencode: { installed: false, authenticated: false, email: null, method: null, error: null, loading }, + hermes: { installed: false, authenticated: false, email: null, method: null, error: null, loading }, }); diff --git a/src/components/settings/view/tabs/agents-settings/AgentListItem.tsx b/src/components/settings/view/tabs/agents-settings/AgentListItem.tsx index fd524498..ac6932e0 100644 --- a/src/components/settings/view/tabs/agents-settings/AgentListItem.tsx +++ b/src/components/settings/view/tabs/agents-settings/AgentListItem.tsx @@ -72,6 +72,7 @@ export default function AgentListItem({ }: AgentListItemProps) { const config = agentConfig[agentId]; const colors = colorClasses[config.color]; + const isReady = agentId === 'hermes' ? authStatus.installed : authStatus.authenticated; if (isMobile) { return ( @@ -87,7 +88,7 @@ export default function AgentListItem({
{config.name} - {authStatus.authenticated && ( + {isReady && ( )}
@@ -107,10 +108,10 @@ export default function AgentListItem({ > {config.name} - {authStatus.authenticated ? ( + {isReady ? ( ) : authStatus.loading ? ( - + ) : null} ); diff --git a/src/components/settings/view/tabs/agents-settings/sections/content/AccountContent.tsx b/src/components/settings/view/tabs/agents-settings/sections/content/AccountContent.tsx index 4062e286..0e3673ff 100644 --- a/src/components/settings/view/tabs/agents-settings/sections/content/AccountContent.tsx +++ b/src/components/settings/view/tabs/agents-settings/sections/content/AccountContent.tsx @@ -125,6 +125,8 @@ const hermesActionGroups: HermesActionGroup[] = [ export default function AccountContent({ agent, authStatus, onLogin }: AccountContentProps) { const { t } = useTranslation('settings'); const config = agentConfig[agent]; + const isHermes = agent === 'hermes'; + const hermesReady = authStatus.installed; return (
@@ -145,11 +147,17 @@ export default function AccountContent({ agent, authStatus, onLogin }: AccountCo
- {t('agents.connectionStatus')} + {isHermes + ? t('agents.hermes.setupStatus.title', { defaultValue: 'Setup status' }) + : t('agents.connectionStatus')}
{authStatus.loading ? ( t('agents.authStatus.checkingAuth') + ) : isHermes ? ( + hermesReady + ? t('agents.hermes.setupStatus.readyDescription', { defaultValue: 'Hermes ACP is installed. Credentials and models are managed by Hermes.' }) + : t('agents.hermes.setupStatus.needsSetupDescription', { defaultValue: 'Install Hermes or run the ACP check to validate the adapter.' }) ) : authStatus.authenticated ? ( t('agents.authStatus.loggedInAs', { email: authStatus.email || t('agents.authStatus.authenticatedUser'), @@ -164,6 +172,19 @@ export default function AccountContent({ agent, authStatus, onLogin }: AccountCo {t('agents.authStatus.checking')} + ) : isHermes ? ( + + {hermesReady + ? t('agents.hermes.setupStatus.ready', { defaultValue: 'ACP ready' }) + : t('agents.hermes.setupStatus.needsSetup', { defaultValue: 'Needs setup' })} + ) : authStatus.authenticated ? ( {t('agents.authStatus.connected')} @@ -176,7 +197,7 @@ export default function AccountContent({ agent, authStatus, onLogin }: AccountCo
- {authStatus.method !== 'api_key' && ( + {!isHermes && authStatus.method !== 'api_key' && (
@@ -201,7 +222,7 @@ export default function AccountContent({ agent, authStatus, onLogin }: AccountCo
)} - {agent === 'hermes' && ( + {isHermes && (
{t('agents.hermes.actions.title', { defaultValue: 'Hermes tools' })}