From e3d46bb5fb48c14d205f80a71daaff199005e802 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Fri, 21 Aug 2026 12:52:30 -0700 Subject: [PATCH] feat(desktop): error card offers Nous support link on Portal-auth sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sessions running on provider 'nous' get a 'Nous support' action on the failed-turn card, opening the portal help hub (https://portal.nousresearch.com/help — docs, Discord, GitHub) in the external browser. All five locales + docs updated. --- .../assistant-ui/thread/assistant-message.tsx | 23 ++++++++++++++++++- apps/desktop/src/i18n/ar.ts | 1 + apps/desktop/src/i18n/en.ts | 1 + apps/desktop/src/i18n/ja.ts | 1 + apps/desktop/src/i18n/types.ts | 1 + apps/desktop/src/i18n/zh-hant.ts | 1 + apps/desktop/src/i18n/zh.ts | 1 + website/docs/user-guide/desktop.md | 3 +++ 8 files changed, 31 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/components/assistant-ui/thread/assistant-message.tsx b/apps/desktop/src/components/assistant-ui/thread/assistant-message.tsx index a8b9729e78..4b9c12c881 100644 --- a/apps/desktop/src/components/assistant-ui/thread/assistant-message.tsx +++ b/apps/desktop/src/components/assistant-ui/thread/assistant-message.tsx @@ -31,6 +31,7 @@ import { Codicon } from '@/components/ui/codicon' import { CopyButton } from '@/components/ui/copy-button' import { useI18n } from '@/i18n' import { type ErrorSurface, formatErrorDiagnostics } from '@/lib/error-surface' +import { openExternalLink } from '@/lib/external-link' import { triggerHaptic } from '@/lib/haptics' import { AudioLines, GitForkIcon, Loader2Icon, RefreshCwIcon, SmilePlusIcon, VolumeXIcon, XIcon } from '@/lib/icons' import { extractPreviewTargets } from '@/lib/preview-targets' @@ -39,13 +40,17 @@ import { useEnterAnimation } from '@/lib/use-enter-animation' import { cn } from '@/lib/utils' import { playSpeechText, stopVoicePlayback } from '@/lib/voice-playback' import { notifyError } from '@/store/notifications' -import { $currentModel } from '@/store/session' +import { $currentModel, $currentProvider } from '@/store/session' import { $voicePlayback } from '@/store/voice-playback' // Stable empty identity for the settled-parts selector — a fresh [] per render // would re-derive the changed-files card on every message re-render. const EMPTY_PARTS: readonly unknown[] = [] +// Nous Portal's help hub (docs, Discord, GitHub, support) — the error card's +// "Nous support" action for sessions running on Portal auth. +const NOUS_SUPPORT_URL = 'https://portal.nousresearch.com/help' + // PERF: hoisted to module scope so the element OBJECT is identical on every // render of every assistant message. React bails out of re-rendering a child // whose element identity is unchanged, so a status flip on the message root @@ -490,6 +495,13 @@ const ErrorRecoveryActions: FC = () => { // child mounts only when one is (see SwitchProviderAction). const inRouter = useInRouterContext() const model = useStore($currentModel) + const provider = useStore($currentProvider) + + // Nous Portal users get a direct line to Nous support (docs/Discord/GitHub + // hub behind their portal account). Provider === 'nous' means the session + // runs on Portal auth — the "logged in" signal that's available + // synchronously without an extra billing query per error card. + const showNousSupport = provider.trim().toLowerCase() === 'nous' // Retry = assistant-ui reload (same wiring as the footer's refresh action): // re-runs the failed turn's prompt in place. Suppressed when the classifier @@ -541,6 +553,15 @@ const ErrorRecoveryActions: FC = () => { )} {showSwitchProvider && inRouter && } + {showNousSupport && ( + + )} {window.hermesDesktop?.logsRoot && (