diff --git a/apps/desktop/src/app/chat/sidebar/sessions-section.tsx b/apps/desktop/src/app/chat/sidebar/sessions-section.tsx index 4d7578bde94e..b582b5315b02 100644 --- a/apps/desktop/src/app/chat/sidebar/sessions-section.tsx +++ b/apps/desktop/src/app/chat/sidebar/sessions-section.tsx @@ -210,6 +210,7 @@ export function SidebarSessionsSection({ // so partition buckets stay truthful — but NEVER for pins, where a turn // finishing was floating background tasks over the user's fixed ranking. const preserveInputOrder = pinned || (sessionsDraggable && !dateGrouped) + const displayEntries = useMemo( () => flattenSessionsWithBranches(sessions, { preserveOrder: preserveInputOrder }), [sessions, preserveInputOrder] diff --git a/apps/desktop/src/app/session/hooks/use-message-stream/index.ts b/apps/desktop/src/app/session/hooks/use-message-stream/index.ts index 8a50465cdc21..d5ec67d384e8 100644 --- a/apps/desktop/src/app/session/hooks/use-message-stream/index.ts +++ b/apps/desktop/src/app/session/hooks/use-message-stream/index.ts @@ -257,6 +257,7 @@ export function useMessageStream({ // 30fps of text growth, expensive multi-stream flushes degrade text fps // instead of interactivity — capped so text never updates slower than 4/s. const sinceLast = performance.now() - lastFlushAtRef.current + const adaptiveFloor = Math.min( Math.max(STREAM_DELTA_FLUSH_MS, lastFlushCostRef.current * 3), MAX_STREAM_FLUSH_GAP_MS diff --git a/apps/desktop/src/app/session/hooks/use-prompt-actions/index.test.tsx b/apps/desktop/src/app/session/hooks/use-prompt-actions/index.test.tsx index d3aa1cfbb3b0..a0f0c20b80e6 100644 --- a/apps/desktop/src/app/session/hooks/use-prompt-actions/index.test.tsx +++ b/apps/desktop/src/app/session/hooks/use-prompt-actions/index.test.tsx @@ -934,6 +934,7 @@ describe('usePromptActions slash.exec dispatch payloads', () => { const focusedSessionId = 'work-runtime-session' const persistedModes = new Map() const sessionProfiles = new Map([[focusedSessionId, focusedProfile]]) + const requestGateway = vi.fn(async (method: string, params?: Record) => { if (method === 'slash.exec') { const sessionId = String(params?.session_id ?? '') @@ -949,6 +950,7 @@ describe('usePromptActions slash.exec dispatch payloads', () => { return {} as never }) + let handle: HarnessHandle | null = null render( diff --git a/apps/desktop/src/debug/perf-live.ts b/apps/desktop/src/debug/perf-live.ts index ce73b7738c99..7a15aa5ac6d0 100644 --- a/apps/desktop/src/debug/perf-live.ts +++ b/apps/desktop/src/debug/perf-live.ts @@ -42,7 +42,8 @@ interface LongFrame { scripts: Array<{ invoker: string; ms: number; src: string }> } -const RESIZE_SELECTOR = '[role="separator"], [data-slot="pane-resize-handle"], [class*="cursor-col-resize"], [class*="cursor-row-resize"]' +const RESIZE_SELECTOR = + '[role="separator"], [data-slot="pane-resize-handle"], [class*="cursor-col-resize"], [class*="cursor-row-resize"]' const TYPING_SELECTOR = '[contenteditable="true"], textarea, input[type="text"]' // A gesture is "over" once this long passes with no further input events. @@ -69,7 +70,8 @@ let lastReport: null | Sample = null let longFrames: LongFrame[] = [] const loafObserver = - typeof PerformanceObserver !== 'undefined' && PerformanceObserver.supportedEntryTypes?.includes('long-animation-frame') + typeof PerformanceObserver !== 'undefined' && + PerformanceObserver.supportedEntryTypes?.includes('long-animation-frame') ? new PerformanceObserver(list => { if (!active) { return @@ -100,9 +102,7 @@ const loafObserver = src: (s.sourceURL ?? '').split('/').pop() ?? '' })), // styleAndLayoutStart -> frame end is the engine's style+layout tail. - styleMs: e.styleAndLayoutStart - ? Math.round(e.startTime + e.duration - e.styleAndLayoutStart) - : 0 + styleMs: e.styleAndLayoutStart ? Math.round(e.startTime + e.duration - e.styleAndLayoutStart) : 0 }) } }) @@ -263,7 +263,6 @@ function on() { window.addEventListener('pointermove', onPointerMove, true) window.addEventListener('keydown', onKeyDown, true) - console.log( '%cperf-live%c armed — resize a pane or type in the composer; a report prints when you stop.', 'background:#5a7db0;color:#fff;padding:1px 6px;border-radius:3px', diff --git a/apps/desktop/src/lib/model-search-text.ts b/apps/desktop/src/lib/model-search-text.ts index a370182abe26..5cbe59d3ecae 100644 --- a/apps/desktop/src/lib/model-search-text.ts +++ b/apps/desktop/src/lib/model-search-text.ts @@ -15,11 +15,13 @@ const MODEL_SEARCH_ALIASES: Record = { /** Haystack for fuzzy/substring model search; never changes the wire id. */ export function modelSearchText(model: string): string { const id = model.trim() + if (!id) { return model } const aliases = MODEL_SEARCH_ALIASES[id.toLowerCase()] + if (!aliases?.length) { return id } diff --git a/apps/desktop/src/lib/session-branch-tree.ts b/apps/desktop/src/lib/session-branch-tree.ts index c415d873e2f1..07ef3c9911c1 100644 --- a/apps/desktop/src/lib/session-branch-tree.ts +++ b/apps/desktop/src/lib/session-branch-tree.ts @@ -106,9 +106,7 @@ export function flattenSessionsWithBranches( children?.forEach((child, index) => emit(child, index === children.length - 1 ? '└─ ' : '├─ ')) } - const roots = sessions - .filter(session => !nestedIds.has(session.id)) - .map((session, index) => ({ index, session })) + const roots = sessions.filter(session => !nestedIds.has(session.id)).map((session, index) => ({ index, session })) if (!options.preserveOrder) { roots.sort((a, b) => groupRecency(b.session) - groupRecency(a.session) || a.index - b.index) diff --git a/apps/desktop/src/store/subagents.test.ts b/apps/desktop/src/store/subagents.test.ts index 51c752ddea22..003d7e168a33 100644 --- a/apps/desktop/src/store/subagents.test.ts +++ b/apps/desktop/src/store/subagents.test.ts @@ -147,7 +147,9 @@ describe('subagent store', () => { pruneFinishedSessionSubagents('s1') - const ids = listFor('s1').map(item => item.id).sort() + const ids = listFor('s1') + .map(item => item.id) + .sort() expect(ids).toEqual(['live-a', 'live-b']) expect(activeSubagentCount(listFor('s1'))).toBe(2) }) @@ -181,6 +183,10 @@ describe('subagent store', () => { pruneFinishedSessionSubagents('s1') expect(listFor('s1').map(item => item.id)).toEqual(['a']) - expect(listFor('s2').map(item => item.id).sort()).toEqual(['c', 'd']) + expect( + listFor('s2') + .map(item => item.id) + .sort() + ).toEqual(['c', 'd']) }) }) diff --git a/ui-tui/src/lib/model-search-text.test.ts b/ui-tui/src/lib/model-search-text.test.ts index 9f8d3a0c5b36..5ef8e612c0e3 100644 --- a/ui-tui/src/lib/model-search-text.test.ts +++ b/ui-tui/src/lib/model-search-text.test.ts @@ -16,12 +16,7 @@ describe('modelSearchText', () => { }) describe('model picker search with aliases', () => { - const models = [ - 'kimi-k2.6', - 'kimi-k2.5', - 'k3', - 'kimi-for-coding', - ] + const models = ['kimi-k2.6', 'kimi-k2.5', 'k3', 'kimi-for-coding'] it('surfaces k3 when the user searches kimi', () => { const ranked = fuzzyRank(models, 'kimi', modelSearchText).map(r => r.item) diff --git a/ui-tui/src/lib/model-search-text.ts b/ui-tui/src/lib/model-search-text.ts index 3495e315ceb4..bc7732ad696e 100644 --- a/ui-tui/src/lib/model-search-text.ts +++ b/ui-tui/src/lib/model-search-text.ts @@ -9,17 +9,19 @@ * hermes_cli/model_search.py. */ const MODEL_SEARCH_ALIASES: Record = { - k3: ['kimi-k3', 'kimi'], + k3: ['kimi-k3', 'kimi'] } /** Haystack for fuzzy/substring model search; never changes the wire id. */ export function modelSearchText(model: string): string { const id = model.trim() + if (!id) { return model } const aliases = MODEL_SEARCH_ALIASES[id.toLowerCase()] + if (!aliases?.length) { return id }