mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-02 12:13:05 +00:00
chore(desktop): keep the diff surgical
Revert the repo-wide prettier churn the earlier fmt pass pulled into files unrelated to this work; run prettier/eslint scoped to the touched files only.
This commit is contained in:
parent
6776b2f9b5
commit
5a2906a11b
11 changed files with 55 additions and 30 deletions
|
|
@ -2266,7 +2266,9 @@ async function handOffWindowsBootstrapRecovery(reason) {
|
|||
// --repair (full venv recreate) and drove reinstall loops. The venv interpreter
|
||||
// and the bootstrap-complete marker are present earlier and are better signals.
|
||||
const haveRealInstall =
|
||||
fileExists(venvPython) || fileExists(venvHermes) || fileExists(path.join(updateRoot, '.hermes-bootstrap-complete'))
|
||||
fileExists(venvPython) ||
|
||||
fileExists(venvHermes) ||
|
||||
fileExists(path.join(updateRoot, '.hermes-bootstrap-complete'))
|
||||
const updaterArgs = haveRealInstall ? ['--update', '--branch', branch] : ['--repair', '--branch', branch]
|
||||
|
||||
await releaseBackendLockForUpdate(updateRoot)
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ const OVERLAY_FALLBACK_WIDTH = 144
|
|||
* the Electron overlay (Windows, WSLg, and plain Linux KDE/GNOME), so they all
|
||||
* reserve the fallback width.
|
||||
*
|
||||
* @param {{ isMac?: boolean }} opts
|
||||
* @param {{ isWindows?: boolean, isWsl?: boolean, isMac?: boolean }} opts
|
||||
*/
|
||||
function nativeOverlayWidth({ isMac = false } = {}) {
|
||||
function nativeOverlayWidth({ isWindows = false, isWsl = false, isMac = false } = {}) {
|
||||
if (isMac) return 0
|
||||
return OVERLAY_FALLBACK_WIDTH
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,13 +43,21 @@ test('findOnPath tries PATHEXT extensions before the bare (empty) name on Window
|
|||
test('Windows bootstrap recovery chooses --update when any real-install signal is present', () => {
|
||||
const source = readMain()
|
||||
assert.match(source, /const haveRealInstall =/, 'recovery must compute haveRealInstall')
|
||||
assert.match(source, /fileExists\(venvPython\)/, 'recovery must accept the venv interpreter as a real-install signal')
|
||||
assert.match(
|
||||
source,
|
||||
/fileExists\(venvPython\)/,
|
||||
'recovery must accept the venv interpreter as a real-install signal'
|
||||
)
|
||||
assert.match(
|
||||
source,
|
||||
/\.hermes-bootstrap-complete/,
|
||||
'recovery must accept the bootstrap-complete marker as a real-install signal'
|
||||
)
|
||||
assert.match(source, /updaterArgs = haveRealInstall \? \['--update'/, 'updaterArgs must gate on haveRealInstall')
|
||||
assert.match(
|
||||
source,
|
||||
/updaterArgs = haveRealInstall \? \['--update'/,
|
||||
'updaterArgs must gate on haveRealInstall'
|
||||
)
|
||||
// The old too-narrow check (only venv\Scripts\hermes.exe) must not return.
|
||||
assert.doesNotMatch(
|
||||
source,
|
||||
|
|
|
|||
|
|
@ -1149,7 +1149,8 @@ export function ChatSidebar({
|
|||
|
||||
const showSessionSkeletons = sessionsLoading && sortedSessions.length === 0
|
||||
|
||||
const showSessionSections = showSessionSkeletons || sortedSessions.length > 0 || projectModel.length > 0
|
||||
const showSessionSections =
|
||||
showSessionSkeletons || sortedSessions.length > 0 || projectModel.length > 0
|
||||
|
||||
// Each reorderable list reports its OWN new id order; persisting is a direct,
|
||||
// typed write — no id-prefix sniffing to figure out which level moved.
|
||||
|
|
@ -1627,7 +1628,12 @@ function SidebarBlankState({ onNewProject }: { onNewProject: () => void }) {
|
|||
<div className="flex flex-col items-center gap-2">
|
||||
<Codicon className="text-(--ui-text-quaternary)" name="root-folder" size="1.25rem" />
|
||||
<p className="text-xs text-(--ui-text-tertiary)">{s.noSessions}</p>
|
||||
<Button className="mt-0.5 text-(--ui-text-secondary)" onClick={onNewProject} size="sm" variant="ghost">
|
||||
<Button
|
||||
className="mt-0.5 text-(--ui-text-secondary)"
|
||||
onClick={onNewProject}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
>
|
||||
<Codicon name="add" size="0.75rem" />
|
||||
{s.projects.newButton}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -149,7 +149,10 @@ export function ProjectDialog() {
|
|||
|
||||
return (
|
||||
<Dialog onOpenChange={onOpenChange} open={open}>
|
||||
<DialogContent className="max-w-md" onInteractOutside={event => event.preventDefault()}>
|
||||
<DialogContent
|
||||
className="max-w-md"
|
||||
onInteractOutside={event => event.preventDefault()}
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
{mode === 'create' && <DialogDescription>{p.createDesc}</DialogDescription>}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,16 @@ import { getActionStatus, getLogs, getStatus, getUsageAnalytics, restartGateway,
|
|||
import type { ActionStatusResponse, AnalyticsResponse, StatusResponse } from '@/hermes'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { sessionTitle } from '@/lib/chat-runtime'
|
||||
import { Activity, AlertCircle, BarChart3, Bookmark, BookmarkFilled, Download, MessageCircle, Trash2 } from '@/lib/icons'
|
||||
import {
|
||||
Activity,
|
||||
AlertCircle,
|
||||
BarChart3,
|
||||
Bookmark,
|
||||
BookmarkFilled,
|
||||
Download,
|
||||
MessageCircle,
|
||||
Trash2
|
||||
} from '@/lib/icons'
|
||||
import { exportSession } from '@/lib/session-export'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { upsertDesktopActionTask } from '@/store/activity'
|
||||
|
|
|
|||
|
|
@ -685,9 +685,7 @@ export function useSessionActions({
|
|||
if (warmHit) {
|
||||
const cachedRuntimeId = warmHit.runtimeId
|
||||
const cachedState = warmHit.state
|
||||
|
||||
const stored =
|
||||
$sessions.get().find(session => sessionMatchesStoredId(session, storedSessionId)) ?? storedForProfile
|
||||
const stored = $sessions.get().find(session => sessionMatchesStoredId(session, storedSessionId)) ?? storedForProfile
|
||||
|
||||
const cachedViewState =
|
||||
!cachedState.model && stored?.model != null
|
||||
|
|
@ -754,10 +752,7 @@ export function useSessionActions({
|
|||
setSelectedStoredSessionId(storedSessionId)
|
||||
selectedStoredSessionIdRef.current = storedSessionId
|
||||
setSessionStartedAt(Date.now())
|
||||
|
||||
const stored =
|
||||
$sessions.get().find(session => sessionMatchesStoredId(session, storedSessionId)) ?? storedForProfile
|
||||
|
||||
const stored = $sessions.get().find(session => sessionMatchesStoredId(session, storedSessionId)) ?? storedForProfile
|
||||
applyStoredSessionPreviewRuntimeInfo(stored)
|
||||
|
||||
if (stored) {
|
||||
|
|
|
|||
|
|
@ -191,7 +191,9 @@ export function ModelSettings({ onMainModelChanged }: ModelSettingsProps) {
|
|||
// MoA reference/aggregator slots must never be the moa virtual provider —
|
||||
// that would create a recursive MoA tree (the backend rejects it on save).
|
||||
// Hide it from the slot selectors so it isn't offered as a dead choice.
|
||||
const moaSlotProviderOptions = providerOptions.filter(provider => (provider.slug || '').toLowerCase() !== 'moa')
|
||||
const moaSlotProviderOptions = providerOptions.filter(
|
||||
provider => (provider.slug || '').toLowerCase() !== 'moa'
|
||||
)
|
||||
|
||||
const selectedProviderRow = useMemo(
|
||||
() => providers.find(provider => provider.slug === selectedProvider),
|
||||
|
|
@ -781,7 +783,6 @@ export function ModelSettings({ onMainModelChanged }: ModelSettingsProps) {
|
|||
...moa,
|
||||
default_preset: selectedMoaPreset || moa.default_preset
|
||||
}
|
||||
|
||||
void saveMoa(next)
|
||||
}}
|
||||
size="sm"
|
||||
|
|
@ -799,14 +800,12 @@ export function ModelSettings({ onMainModelChanged }: ModelSettingsProps) {
|
|||
const presets = { ...moa.presets }
|
||||
delete presets[selectedMoaPreset]
|
||||
const fallback = Object.keys(presets)[0]
|
||||
|
||||
const next: MoaConfigResponse = {
|
||||
...moa,
|
||||
presets,
|
||||
default_preset: moa.default_preset === selectedMoaPreset ? fallback : moa.default_preset,
|
||||
active_preset: moa.active_preset === selectedMoaPreset ? '' : moa.active_preset
|
||||
}
|
||||
|
||||
setSelectedMoaPreset(Object.keys(moa.presets).find(name => name !== selectedMoaPreset) || '')
|
||||
void saveMoa(next)
|
||||
}}
|
||||
|
|
@ -825,7 +824,6 @@ export function ModelSettings({ onMainModelChanged }: ModelSettingsProps) {
|
|||
disabled={!newMoaPresetName.trim() || !!moa.presets[newMoaPresetName.trim()] || applying}
|
||||
onClick={() => {
|
||||
const name = newMoaPresetName.trim()
|
||||
|
||||
const next: MoaConfigResponse = {
|
||||
...moa,
|
||||
presets: {
|
||||
|
|
@ -833,7 +831,6 @@ export function ModelSettings({ onMainModelChanged }: ModelSettingsProps) {
|
|||
[name]: { ...currentMoaPreset, reference_models: [...currentMoaPreset.reference_models] }
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedMoaPreset(name)
|
||||
setNewMoaPresetName('')
|
||||
void saveMoa(next)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ describe('withActive', () => {
|
|||
const curated = ['hermes-4', 'hermes-4-mini']
|
||||
|
||||
it('prepends a custom model missing from the curated list', () => {
|
||||
expect(withActive(curated, 'anthropic/claude-opus-4.7')).toEqual(['anthropic/claude-opus-4.7', ...curated])
|
||||
expect(withActive(curated, 'anthropic/claude-opus-4.7')).toEqual([
|
||||
'anthropic/claude-opus-4.7',
|
||||
...curated
|
||||
])
|
||||
})
|
||||
|
||||
it('leaves the list untouched when the active model is already curated', () => {
|
||||
|
|
|
|||
|
|
@ -280,11 +280,7 @@ function ClarifyToolPending({ args }: ToolCallMessagePartProps) {
|
|||
|
||||
if (loading) {
|
||||
return (
|
||||
<ClarifyShell
|
||||
aria-label={copy.loadingQuestion}
|
||||
className="grid min-h-12 place-items-center px-2.5 py-3"
|
||||
role="status"
|
||||
>
|
||||
<ClarifyShell aria-label={copy.loadingQuestion} className="grid min-h-12 place-items-center px-2.5 py-3" role="status">
|
||||
<Loader2 aria-hidden className="size-4 animate-spin text-(--ui-text-tertiary)" />
|
||||
</ClarifyShell>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1540,7 +1540,10 @@ function dynamicTitle(
|
|||
}
|
||||
|
||||
const failed =
|
||||
part.isError || result.success === false || result.ok === false || Boolean(firstStringField(result, ['error']))
|
||||
part.isError ||
|
||||
result.success === false ||
|
||||
result.ok === false ||
|
||||
Boolean(firstStringField(result, ['error']))
|
||||
|
||||
if (failed) {
|
||||
const failAction = translateNow('assistant.tool.actions.failedToOpen')
|
||||
|
|
@ -1553,7 +1556,10 @@ function dynamicTitle(
|
|||
|
||||
const action = verb(translateNow('assistant.tool.actions.opening'), translateNow('assistant.tool.actions.opened'))
|
||||
|
||||
return titledAction(action, translateNow('assistant.tool.titleTemplates.actionTarget', action, hostnameOf(url)))
|
||||
return titledAction(
|
||||
action,
|
||||
translateNow('assistant.tool.titleTemplates.actionTarget', action, hostnameOf(url))
|
||||
)
|
||||
}
|
||||
|
||||
if (part.toolName === 'web_search') {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue