Initial import of NousResearch/hermes-agent
Deploy Site / deploy-vercel (push) Has been cancelled
Deploy Site / deploy-docs (push) Has been cancelled
Docker / shell lint / Lint Dockerfile (hadolint) (push) Has been cancelled
Docker / shell lint / Lint docker/ shell scripts (shellcheck) (push) Has been cancelled
Docker Build and Publish / build-amd64 (push) Has been cancelled
Docker Build and Publish / build-arm64 (push) Has been cancelled
Lint (ruff + ty) / ruff + ty diff (push) Has been cancelled
Lint (ruff + ty) / ruff enforcement (blocking) (push) Has been cancelled
Lint (ruff + ty) / Windows footguns (blocking) (push) Has been cancelled
Nix Lockfile Fix / auto-fix-main (push) Has been cancelled
Nix Lockfile Fix / fix (push) Has been cancelled
Nix / nix (macos-latest) (push) Has been cancelled
Nix / nix (ubuntu-latest) (push) Has been cancelled
OSV-Scanner / Scan lockfiles (push) Has been cancelled
Build Skills Index / build-index (push) Has been cancelled
Tests / test (1) (push) Has been cancelled
Tests / test (2) (push) Has been cancelled
Tests / test (3) (push) Has been cancelled
Tests / test (4) (push) Has been cancelled
Tests / test (5) (push) Has been cancelled
Tests / test (6) (push) Has been cancelled
Tests / e2e (push) Has been cancelled
uv.lock check / uv lock --check (push) Has been cancelled
Docker Build and Publish / merge (push) Has been cancelled
Build Skills Index / trigger-deploy (push) Has been cancelled
Tests / save-durations (push) Has been cancelled
Deploy Site / deploy-vercel (push) Has been cancelled
Deploy Site / deploy-docs (push) Has been cancelled
Docker / shell lint / Lint Dockerfile (hadolint) (push) Has been cancelled
Docker / shell lint / Lint docker/ shell scripts (shellcheck) (push) Has been cancelled
Docker Build and Publish / build-amd64 (push) Has been cancelled
Docker Build and Publish / build-arm64 (push) Has been cancelled
Lint (ruff + ty) / ruff + ty diff (push) Has been cancelled
Lint (ruff + ty) / ruff enforcement (blocking) (push) Has been cancelled
Lint (ruff + ty) / Windows footguns (blocking) (push) Has been cancelled
Nix Lockfile Fix / auto-fix-main (push) Has been cancelled
Nix Lockfile Fix / fix (push) Has been cancelled
Nix / nix (macos-latest) (push) Has been cancelled
Nix / nix (ubuntu-latest) (push) Has been cancelled
OSV-Scanner / Scan lockfiles (push) Has been cancelled
Build Skills Index / build-index (push) Has been cancelled
Tests / test (1) (push) Has been cancelled
Tests / test (2) (push) Has been cancelled
Tests / test (3) (push) Has been cancelled
Tests / test (4) (push) Has been cancelled
Tests / test (5) (push) Has been cancelled
Tests / test (6) (push) Has been cancelled
Tests / e2e (push) Has been cancelled
uv.lock check / uv lock --check (push) Has been cancelled
Docker Build and Publish / merge (push) Has been cancelled
Build Skills Index / trigger-deploy (push) Has been cancelled
Tests / save-durations (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { DEFAULT_THEME } from '../theme.js'
|
||||
import type { SessionActiveItem } from '../gatewayTypes.js'
|
||||
import {
|
||||
activeSessionCountLabel,
|
||||
canTypeOrchestratorPrompt,
|
||||
currentSessionSelectionIndex,
|
||||
orchestratorContextHint,
|
||||
orchestratorContextHintSegments,
|
||||
orchestratorGlobalHotkeyHint,
|
||||
orchestratorGlobalHotkeyHintSegments,
|
||||
orchestratorHintSegmentColor,
|
||||
clampOrchestratorSelection,
|
||||
closeFallbackAfterClose,
|
||||
draftModelArgFromPickerValue,
|
||||
draftModelDisplayLabel,
|
||||
fixedSessionColumnStyle,
|
||||
draftTitleFromPrompt,
|
||||
isNewSessionRow,
|
||||
newSessionMarkerColor,
|
||||
newSessionRowIndex,
|
||||
orchestratorRowClickAction,
|
||||
orchestratorVisibleRowIndexes,
|
||||
selectedSessionRowStyle
|
||||
} from '../components/activeSessionSwitcher.js'
|
||||
|
||||
describe('session orchestrator helpers', () => {
|
||||
it('labels live sessions compactly for tight overlays', () => {
|
||||
expect(activeSessionCountLabel(0)).toBe('0 live sessions')
|
||||
expect(activeSessionCountLabel(1)).toBe('1 live session')
|
||||
expect(activeSessionCountLabel(3)).toBe('3 live sessions')
|
||||
expect(activeSessionCountLabel(1)).not.toContain('in this TUI')
|
||||
})
|
||||
|
||||
it('keeps session orchestrator hotkey hints short and contextual', () => {
|
||||
expect(orchestratorContextHint(false)).toBe('Session row: Enter switch · Ctrl+D close')
|
||||
expect(orchestratorContextHint(true)).toBe('New row: type prompt · Enter start · Tab model')
|
||||
expect(orchestratorGlobalHotkeyHint).toBe('↑↓ move · Ctrl+N new · Ctrl+R refresh · Esc close')
|
||||
expect(orchestratorGlobalHotkeyHint.length).toBeLessThanOrEqual(56)
|
||||
})
|
||||
|
||||
it('assigns themed colors consistently to orchestrator labels and hotkeys', () => {
|
||||
expect(orchestratorContextHintSegments(false)).toEqual([
|
||||
{ role: 'label', text: 'Session row:' },
|
||||
{ role: 'text', text: ' ' },
|
||||
{ role: 'hotkey', text: 'Enter' },
|
||||
{ role: 'text', text: ' switch · ' },
|
||||
{ role: 'hotkey', text: 'Ctrl+D' },
|
||||
{ role: 'text', text: ' close' }
|
||||
])
|
||||
expect(orchestratorContextHintSegments(true)).toEqual([
|
||||
{ role: 'label', text: 'New row:' },
|
||||
{ role: 'text', text: ' type prompt · ' },
|
||||
{ role: 'hotkey', text: 'Enter' },
|
||||
{ role: 'text', text: ' start · ' },
|
||||
{ role: 'hotkey', text: 'Tab' },
|
||||
{ role: 'text', text: ' model' }
|
||||
])
|
||||
expect(orchestratorGlobalHotkeyHintSegments.filter(s => s.role === 'hotkey').map(s => s.text)).toEqual([
|
||||
'↑↓',
|
||||
'Ctrl+N',
|
||||
'Ctrl+R',
|
||||
'Esc'
|
||||
])
|
||||
expect(orchestratorHintSegmentColor(DEFAULT_THEME, 'hotkey')).toBe(DEFAULT_THEME.color.accent)
|
||||
expect(orchestratorHintSegmentColor(DEFAULT_THEME, 'label')).toBe(DEFAULT_THEME.color.label)
|
||||
expect(orchestratorHintSegmentColor(DEFAULT_THEME, 'text')).toBe(DEFAULT_THEME.color.muted)
|
||||
expect(newSessionMarkerColor(DEFAULT_THEME, false)).toBe(DEFAULT_THEME.color.label)
|
||||
expect(newSessionMarkerColor(DEFAULT_THEME, true)).toBe(DEFAULT_THEME.color.text)
|
||||
})
|
||||
|
||||
it('uses a readable selected row style instead of accent-on-accent inverse text', () => {
|
||||
const style = selectedSessionRowStyle(DEFAULT_THEME)
|
||||
|
||||
expect(style.backgroundColor).toBe(DEFAULT_THEME.color.selectionBg)
|
||||
expect(style.color).toBe(DEFAULT_THEME.color.text)
|
||||
expect(style.backgroundColor).not.toBe(DEFAULT_THEME.color.accent)
|
||||
expect(style.color).not.toBe(DEFAULT_THEME.color.accent)
|
||||
})
|
||||
|
||||
it('turns model picker values into session-scoped draft model args', () => {
|
||||
expect(draftModelArgFromPickerValue('kimi-k2.6 --provider ollama-cloud --tui-session')).toBe(
|
||||
'kimi-k2.6 --provider ollama-cloud'
|
||||
)
|
||||
expect(draftModelArgFromPickerValue('openai/gpt-5.5 --provider openai-codex --global')).toBe(
|
||||
'openai/gpt-5.5 --provider openai-codex'
|
||||
)
|
||||
})
|
||||
|
||||
it('highlights the current live session when the picker opens', () => {
|
||||
const sessions = [
|
||||
{ id: 'first', status: 'idle' },
|
||||
{ id: 'second', status: 'working', current: true },
|
||||
{ id: 'third', status: 'idle' }
|
||||
] satisfies SessionActiveItem[]
|
||||
|
||||
expect(currentSessionSelectionIndex(sessions, 'second')).toBe(1)
|
||||
expect(
|
||||
currentSessionSelectionIndex([{ id: 'first', status: 'idle' }, { id: 'third', status: 'idle' }], 'third')
|
||||
).toBe(1)
|
||||
expect(currentSessionSelectionIndex(sessions, 'missing')).toBe(1)
|
||||
expect(currentSessionSelectionIndex([], 'missing')).toBe(0)
|
||||
})
|
||||
|
||||
it('adds a selectable New row after the live sessions and gates prompt typing to it', () => {
|
||||
expect(newSessionRowIndex(0)).toBe(0)
|
||||
expect(newSessionRowIndex(3)).toBe(3)
|
||||
expect(clampOrchestratorSelection(-5, 2)).toBe(0)
|
||||
expect(clampOrchestratorSelection(99, 2)).toBe(2)
|
||||
expect(isNewSessionRow(0, 0)).toBe(true)
|
||||
expect(isNewSessionRow(1, 2)).toBe(false)
|
||||
expect(isNewSessionRow(2, 2)).toBe(true)
|
||||
expect(canTypeOrchestratorPrompt(1, 2)).toBe(false)
|
||||
expect(canTypeOrchestratorPrompt(2, 2)).toBe(true)
|
||||
expect(orchestratorVisibleRowIndexes(3, 3, 12)).toEqual([0, 1, 2, 3])
|
||||
expect(orchestratorVisibleRowIndexes(13, 13, 12)).toContain(13)
|
||||
})
|
||||
|
||||
it('selects a safe fallback after closing the current live session', () => {
|
||||
const remaining = [
|
||||
{ id: 'next', status: 'idle' },
|
||||
{ id: 'other', status: 'working' }
|
||||
] satisfies SessionActiveItem[]
|
||||
|
||||
expect(closeFallbackAfterClose('other', 'current', remaining)).toEqual({ action: 'stay' })
|
||||
expect(closeFallbackAfterClose('current', 'current', remaining)).toEqual({ action: 'activate', sessionId: 'next' })
|
||||
expect(closeFallbackAfterClose('current', 'current', [])).toEqual({ action: 'new' })
|
||||
})
|
||||
|
||||
it('shows clean draft model labels without picker flags or provider params', () => {
|
||||
expect(draftModelDisplayLabel('kimi-k2.6 --provider ollama-cloud --tui-session')).toBe('kimi-k2.6')
|
||||
expect(draftModelDisplayLabel('openai/gpt-5.5 --provider openai-codex --global')).toBe('gpt-5.5')
|
||||
expect(draftModelDisplayLabel('')).toBe('current/default')
|
||||
})
|
||||
|
||||
it('maps row clicks to existing-session activation or New-row focus', () => {
|
||||
const sessions = [
|
||||
{ id: 'a', status: 'idle' },
|
||||
{ id: 'b', status: 'idle' }
|
||||
] satisfies SessionActiveItem[]
|
||||
|
||||
expect(orchestratorRowClickAction(1, sessions)).toEqual({ action: 'activate', sessionId: 'b' })
|
||||
expect(orchestratorRowClickAction(2, sessions)).toEqual({ action: 'select-new' })
|
||||
expect(orchestratorRowClickAction(99, sessions)).toEqual({ action: 'select-new' })
|
||||
})
|
||||
|
||||
it('keeps fixed table columns from shrinking into adjacent columns', () => {
|
||||
expect(fixedSessionColumnStyle().flexShrink).toBe(0)
|
||||
})
|
||||
|
||||
it('builds a compact title from the orchestrator prompt', () => {
|
||||
expect(draftTitleFromPrompt(' Build the websocket orchestrator panel and make it robust. ', 24)).toBe(
|
||||
'Build the websocket orc…'
|
||||
)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user