mirror of
https://github.com/R0m1k3/EveFlow.git
synced 2026-10-11 17:29:03 +02:00
Complete rewrite of the application: - Toolchain: Electron 44, Vite 8, React 19, TypeScript 5.9, Zustand, Vitest; main process rewritten in TypeScript (electron/), typed IPC contract (shared/), sandboxed renderer with webSecurity on and an HTTP/SSE proxy in the main process. - Voice: AudioWorklet microphone capture at 16 kHz with adaptive energy VAD and auto-stop, WAV encoder, OpenAI-compatible STT, TTS queue with sentence-level streaming, prefetch and Web Audio playback feeding an analyser; hands-free mode, global push-to-talk hotkey, system-voice fallback. - Hermes: full API client (capabilities, health, models, skills, toolsets, sessions, jobs) with automatic transport selection: runs API (SSE lifecycle, approvals, steer, stop) > sessions stream > chat completions with local tools; tolerant event normalisation; webhook receiver hardened (secret, size limit). - UI: JARVIS arc-reactor core on Canvas 2D reacting to the real audio signal, holographic HUD layout (transcript, core, Hermes ops panel with tools/crons/ skills/sessions/link tabs, telemetry), settings drawer, approval modals, compact floating widget, four themes, tray icon and global shortcuts. - Removed the 3D Eve robot, three.js and legacy assets; migrated 1.x settings. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Wn5VX9HNbJ7N54hR24u9Y
58 lines
3.5 KiB
TypeScript
58 lines
3.5 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { normalizeCompletionChunk, normalizeLifecycleEvent } from '../src/services/hermes/events';
|
|
import { hermesBaseUrl, resolveTransport } from '../src/services/hermes/client';
|
|
import type { HermesConfig } from '../src/services/hermes/types';
|
|
|
|
describe('normalizeLifecycleEvent', () => {
|
|
it('maps runs API events', () => {
|
|
expect(normalizeLifecycleEvent('message.delta', { delta: 'Hi' })).toEqual([{ kind: 'delta', text: 'Hi' }]);
|
|
const [start] = normalizeLifecycleEvent('tool.started', { tool: 'terminal', preview: 'ls', run_id: 'r1' });
|
|
expect(start).toMatchObject({ kind: 'tool.start', name: 'terminal', args: 'ls' });
|
|
const [end] = normalizeLifecycleEvent('tool.completed', { tool: 'terminal', error: 'boom' });
|
|
expect(end).toMatchObject({ kind: 'tool.end', ok: false });
|
|
const [approval] = normalizeLifecycleEvent('approval.request', { command: 'rm -rf x', choices: ['once', 'deny'] });
|
|
expect(approval).toMatchObject({ kind: 'approval.request', description: 'rm -rf x', options: ['once', 'deny'] });
|
|
const done = normalizeLifecycleEvent('run.completed', { output: 'ok', usage: { total_tokens: 3 }, session_id: 's1' });
|
|
expect(done[0]).toMatchObject({ kind: 'completed', text: 'ok', usage: { total_tokens: 3 } });
|
|
expect(done[1]).toEqual({ kind: 'session', sessionId: 's1' });
|
|
expect(normalizeLifecycleEvent('run.failed', { error: 'bad' })[0]).toEqual({ kind: 'error', message: 'bad' });
|
|
});
|
|
|
|
it('maps sessions stream and chat-completions tool progress', () => {
|
|
expect(normalizeLifecycleEvent('assistant.delta', { delta: 'a' })[0]).toEqual({ kind: 'delta', text: 'a' });
|
|
const [progress] = normalizeLifecycleEvent('hermes.tool.progress', { tool_name: 'web_search', status: 'started' });
|
|
expect(progress).toMatchObject({ kind: 'tool.progress', name: 'web_search' });
|
|
expect(normalizeLifecycleEvent('gateway.ready', {})).toEqual([]);
|
|
expect(normalizeLifecycleEvent('weird.event', { x: 1 })[0]).toMatchObject({ kind: 'raw' });
|
|
});
|
|
});
|
|
|
|
describe('normalizeCompletionChunk', () => {
|
|
it('accumulates streamed tool calls', () => {
|
|
const calls = new Map();
|
|
normalizeCompletionChunk({ choices: [{ delta: { tool_calls: [{ index: 0, id: 'c1', function: { name: 'set_', arguments: '{"a' } }] } }] }, calls);
|
|
const { events, finishReason } = normalizeCompletionChunk(
|
|
{ choices: [{ delta: { content: 'x', tool_calls: [{ index: 0, function: { name: 'hud', arguments: '":1}' } }] }, finish_reason: 'tool_calls' }] },
|
|
calls
|
|
);
|
|
expect(events).toEqual([{ kind: 'delta', text: 'x' }]);
|
|
expect(finishReason).toBe('tool_calls');
|
|
expect(calls.get(0)).toEqual({ id: 'c1', name: 'set_hud', arguments: '{"a":1}' });
|
|
});
|
|
});
|
|
|
|
describe('client helpers', () => {
|
|
const cfg = { transport: 'auto' } as HermesConfig;
|
|
it('normalises base urls', () => {
|
|
expect(hermesBaseUrl('http://h:8642/v1/')).toBe('http://h:8642');
|
|
expect(hermesBaseUrl('http://h:8642/v1/chat/completions')).toBe('http://h:8642');
|
|
expect(hermesBaseUrl('http://h:8642')).toBe('http://h:8642');
|
|
});
|
|
it('resolves transports from capabilities', () => {
|
|
expect(resolveTransport(cfg, null)).toBe('completions');
|
|
expect(resolveTransport(cfg, { features: { run_submission: true, run_events_sse: true } })).toBe('runs');
|
|
expect(resolveTransport(cfg, { endpoints: { session_chat: true } })).toBe('sessions');
|
|
expect(resolveTransport({ transport: 'completions' } as HermesConfig, { features: { run_submission: true, run_events_sse: true } })).toBe('completions');
|
|
});
|
|
});
|