mirror of
https://github.com/R0m1k3/EveFlow.git
synced 2026-10-20 21:58:56 +02:00
feat: serveur MCP, heures calmes, mode mission, widget glanceable, barge-in (v2.4.0) (#14)
Serveur MCP local - Endpoint /mcp (JSON-RPC 2.0, Streamable HTTP, réponses JSON) sur le serveur webhook : initialize, tools/list, tools/call, ping. Même secret que le webhook. 14 outils : capture_screen (image MCP), lock_session, open_app, open_url, media_key, clipboard_get/set, find_files, speak_text, notify_user, set_hud_state, get_app_status, get_conversation_history, show_message. Les outils UI transitent par IPC vers le renderer. - Les mêmes actions système sont proposées au modèle en chat completions. Notifications - Heures calmes (plage horaire, franchissement de minuit), mots prioritaires, échecs de crons toujours lus, résumé vocal des messages entrants (n premières phrases), thème nuit automatique, compteur de non-lus. Conversation - Mode mission : bouton dans la barre de commande, second modèle Hermes (hermes.missionModel) pour les tâches longues. - Barge-in : la parole de l'utilisateur coupe la voix ; seuil d'énergie et ratio relevés pendant la synthèse pour ignorer l'écho. Widget compact - Bandeau glanceable : état, dernière phrase, non-lus, indicateurs heures calmes et mission. Tests : 28 tests unitaires (heures calmes, priorité, résumé), e2e Electron sous Xvfb : MCP initialize/tools/list/tools/call (presse-papiers, capture en image, état et message via le renderer), bandeau compact, aucune erreur. Claude-Session: https://claude.ai/code/session_017Wn5VX9HNbJ7N54hR24u9Y Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
af8bdc5e9d
commit
6fc38d335b
24 files changed
+668
-31
No files matched your search
@@ -3,16 +3,35 @@
|
||||
* With the runs/sessions transports Hermes uses its own server-side toolsets instead.
|
||||
*/
|
||||
import { bridge } from '../../lib/bridge';
|
||||
import type { SystemAction } from '../../../shared/bridge';
|
||||
|
||||
export interface LocalToolContext {
|
||||
setEmotion: (emotion: string) => void;
|
||||
showMessage?: (text: string, title?: string) => void;
|
||||
speak: (text: string) => void;
|
||||
getStatus: () => Record<string, unknown>;
|
||||
getHistory: (n: number) => Array<{ role: string; content: string }>;
|
||||
notify: (title: string, body: string) => void;
|
||||
}
|
||||
|
||||
const fn = (name: string, description: string, properties: Record<string, unknown> = {}, required: string[] = []) => ({
|
||||
type: 'function',
|
||||
function: { name, description, parameters: { type: 'object', properties, required } }
|
||||
});
|
||||
|
||||
/** Tools executed on the user's PC through the Electron main process (allow-listed). */
|
||||
export const SYSTEM_TOOL_DEFINITIONS = [
|
||||
fn('lock_session', "Verrouille la session de l'utilisateur."),
|
||||
fn('open_app', "Lance une application du PC de l'utilisateur (bloc-notes, calculatrice, chrome, spotify, vscode, terminal, explorateur…).", { name: { type: 'string' } }, ['name']),
|
||||
fn('open_url', "Ouvre une URL http(s) dans le navigateur de l'utilisateur.", { url: { type: 'string' } }, ['url']),
|
||||
fn('media_key', 'Touche média : volume-up, volume-down, mute, play-pause, next, previous.', { key: { type: 'string', enum: ['volume-up', 'volume-down', 'mute', 'play-pause', 'next', 'previous'] } }, ['key']),
|
||||
fn('clipboard_get', 'Lit le texte du presse-papiers.'),
|
||||
fn('clipboard_set', 'Place un texte dans le presse-papiers.', { text: { type: 'string' } }, ['text']),
|
||||
fn('find_files', "Cherche des fichiers par nom dans Documents, Bureau, Téléchargements et Images (25 max).", { query: { type: 'string' } }, ['query'])
|
||||
];
|
||||
|
||||
export const LOCAL_TOOL_DEFINITIONS = [
|
||||
...SYSTEM_TOOL_DEFINITIONS,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
@@ -95,6 +114,29 @@ export async function executeLocalTool(name: string, rawArgs: string, ctx: Local
|
||||
case 'notify_user':
|
||||
ctx.notify(String(args.title ?? 'EveFlow'), String(args.body ?? ''));
|
||||
return JSON.stringify({ ok: true });
|
||||
case 'show_message':
|
||||
ctx.showMessage?.(String(args.text ?? ''), typeof args.title === 'string' ? args.title : undefined);
|
||||
return JSON.stringify({ ok: true });
|
||||
case 'lock_session':
|
||||
case 'open_app':
|
||||
case 'open_url':
|
||||
case 'media_key':
|
||||
case 'clipboard_get':
|
||||
case 'clipboard_set':
|
||||
case 'find_files': {
|
||||
const api = bridge();
|
||||
if (!api) return JSON.stringify({ error: 'system actions unavailable outside Electron' });
|
||||
const action: SystemAction =
|
||||
name === 'lock_session' ? { type: 'lock' }
|
||||
: name === 'open_app' ? { type: 'open-app', name: String(args.name ?? '') }
|
||||
: name === 'open_url' ? { type: 'open-url', url: String(args.url ?? '') }
|
||||
: name === 'media_key' ? { type: 'media', key: String(args.key ?? '') as 'mute' }
|
||||
: name === 'clipboard_get' ? { type: 'clipboard-read' }
|
||||
: name === 'clipboard_set' ? { type: 'clipboard-write', text: String(args.text ?? '') }
|
||||
: { type: 'find-files', query: String(args.query ?? '') };
|
||||
const result = await api.system.action(action);
|
||||
return JSON.stringify(result.ok ? { ok: true, message: result.message, data: result.data } : { error: result.message ?? 'failed' });
|
||||
}
|
||||
case 'write_shared_file': {
|
||||
const api = bridge();
|
||||
if (!api) return JSON.stringify({ error: 'file system unavailable outside Electron' });
|
||||
|
||||
@@ -10,6 +10,8 @@ export interface HermesConfig {
|
||||
reasoningEffort: '' | 'low' | 'medium' | 'high';
|
||||
/** Extra instructions layered on top of the Hermes system prompt. */
|
||||
instructions: string;
|
||||
/** Model used in "mission" mode (long tasks); empty = same as `model`. */
|
||||
missionModel: string;
|
||||
/** Expose EveFlow client tools in chat-completions mode. */
|
||||
localTools: boolean;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user