MANUAL

qué es MEDUSA y cómo funciona todo
← volver

1. Qué es MEDUSA

MEDUSA es un frontend personal sobre Claude Code y otros LLMs (Anthropic, OpenAI, OpenRouter, Ollama). Te da:

  • Una cara visible: un orbe animado que reacciona a estados (escuchando, pensando, hablando), o un escenario pixel-art con personajes que representan los módulos del sistema.
  • Voz bidireccional: TTS expresivo (ElevenLabs) + STT del navegador, con wake-word opcional.
  • Multi-dispositivo: el server escucha en tu Mac y se accede desde el iPhone, otra pantalla, etc. (Tailscale recomendado).
  • Proactividad: te avisa solo cuando llega un correo importante o tienes una reunión inminente — siempre y cuando NO estés en "no molestar".
  • Un workspace con proyectos persistentes, cada uno con su propia sesión de Claude Code.

El nombre "JARVIS" vive en el código como referencia interna; lo visible se llama MEDUSA.

2. Interfaz

2.1 El orbe

Vive en el centro de la pantalla principal. Sus estados:

  • idle: respira tranquilo. Modo reposo.
  • listening: te está oyendo (STT activo). Reacciona al volumen del micro.
  • thinking: el LLM está procesando. Glifos rotando.
  • speaking: respondiendo / TTS activo. Pulsa al ritmo de la voz.

Cuando se ejecuta un sub-agente Claude Code (Task), aparece un mini-orbe orbitando con su propia paleta. Cuando una skill se invoca, aparece un glifo con el color/símbolo declarado en su frontmatter.

2.2 El HUD (barra superior)

  • 🔔 / 🌙 (Proactividad): visible solo si el módulo proactivity está activado. Click → toggle DND. Color cambia según estado (silencio manual, quiet hours, snooze).
  • 📚 SecondBrain: abre el panel de la wiki personal.
  • $0.00 (coste): gasto del mes en USD. Si el provider es Claude Code, muestra tokens consumidos. Click → dashboard. Se pone amarillo en soft-cap, rojo en hard-cap.
  • STATE: replica del estado del orbe en texto.
  • ≡ panel toggle: muestra/oculta el panel de chat lateral.
  • ⚙ ajustes: panel completo de configuración.

2.3 El panel de chat

Lateral colapsable. Cada burbuja del asistente tiene botones 📋 copiar y ▶ leer (TTS bajo demanda). Drag & drop archivos sobre la pantalla → suben a workspace/uploads/ y aparecen en el autocompletado @.

2.4 El subtítulo flotante

Bajo el orbe, en grande, el último mensaje del asistente. Se auto-oculta a 10s. Cuando el panel de chat está abierto, el subtítulo desaparece (no duplicar).

3. Vistas alternativas

Todas comparten el mismo WebSocket — lo que pase en una se ve en las demás:

  • / — vista principal con orbe + HUD + chat.
  • /chat — chat-only fullscreen, sin orbe. Útil para tener el orbe en una pantalla y el chat en el iPhone, por ejemplo.
  • /pixels — escenario pixel-art con una oficina y personajes que reaccionan. Cada agente/skill/provider está representado en un mueble; cuando trabajan, animan typing en su PC.
  • /diagram — grafo SVG de los módulos del sistema y sus dependencias.
  • /sandbox/<módulo> — invoca tools de un módulo concreto en aislamiento.
  • /manual — esta página.

Coordinación cross-cliente: cuando hay un /chat abierto en otra pantalla, la vista principal oculta su panel local automáticamente.

4. Voz

4.1 TTS (texto → voz)

Síntesis vía ElevenLabs. Configurable en Ajustes → Voz:

  • API key de ElevenLabs (gratis con free tier de caracteres/mes).
  • Voz: selector con todas las voces de tu cuenta (premade + cloned + generated).
  • Modelo: eleven_multilingual_v2, eleven_turbo_v2_5, eleven_v3 (alpha — soporta tags expresivos [laughs] [curious]).
  • auto_speak: lee automáticamente las respuestas cuando la última entrada fue por voz. También controla los proactivos con is_voice=true.
  • stream_tts_by_sentence: empieza a hablar antes de que termine la respuesta — divide por frases y encadena.
  • stability / similarity_boost / style: ajustes de timbre y expresividad.

4.2 STT (voz → texto)

Web Speech API del navegador. Idioma configurable (stt_language, default es-ES). En iOS solo funciona desde HTTPS — usa tailscale serve --https=8000 http://localhost:8000 para acceder por Tailscale con TLS.

4.3 Wake word

Si activas wake_word_enabled, MEDUSA escucha pasivamente y se activa cuando dices alguna de las wake words configurables (default: "medusa"). Validación deferida 1.2s para evitar falsos positivos. Anti-eco: el STT no abre micro mientras el TTS está reproduciendo.

4.4 Modo conversación

Tras una respuesta vuelve a abrir el micro automáticamente — diálogo continuo sin tocar nada.

5. Providers

El "cerebro" detrás de cada respuesta. Selección en Ajustes → Provider:

  • anthropic — API directa de Claude (Sonnet, Opus, Haiku). Requiere API key.
  • claude_code — el binario claude headless. Permite ejecución de tools, lectura de archivos, sub-agentes Task. Sin coste por API key (consume tu suscripción de Claude). Permission modes: read_only (allowed-tools whitelist), confirm_writes, bypass_all (--dangerously-skip-permissions, solo desde 127.0.0.1).
  • openai — GPT vía API.
  • openrouter — multi-modelo proxy (cientos de modelos).
  • ollama — modelos locales (Llama, Mistral, Qwen…). Cero coste, cero red.
  • orchestrator — encadena providers según una chain configurada (ej. router → coder → reviewer).
  • smart_router — un LLM rápido (Haiku/Ollama) clasifica el intent (chat / code / reasoning / etc.) y delega al provider óptimo de los intents configurados.

Los tools declaradas por skills y módulos las puede invocar cualquier provider con stream_reply_with_tools — no hay allowlist hardcoded.

6. Targets y sesiones

Un target es el "espacio de trabajo activo" para Claude Code. Cada uno mantiene una sesión persistente independiente en ~/medusa-workspace/sessions/<target>.session.

  • orchestrator — tu conversación principal. Vive en ~/medusa-workspace/.
  • secondbrain — sub-agente dedicado a la wiki personal.
  • project:<slug> — sub-agente con su propio CLAUDE.md, su propio cwd, su propia sesión.
  • module:<id> — sub-agente sandboxed de un módulo (ej. module:google trabaja con gws).

Cambiar de target en el dropdown del HUD; el comando /projects lo lista; el atajo @ en el input lo permite mencionar.

7. Workspace y proyectos

El workspace vive fuera del repo en ~/medusa-workspace/ (configurable con workspace_dir). Crítico: si estuviese dentro del repo, Claude Code subiría buscando CLAUDE.md y se contaminaría con el meta-CLAUDE.md del propio frontend.

Estructura:

~/medusa-workspace/
├── CLAUDE.md            ← auto-generado, índice de proyectos
├── .claude/skills/jarvis-projects/   (auto-instalado)
├── projects/<slug>/
│   ├── PROJECT.md       ← metadatos (título, descripción)
│   ├── STATUS.md        ← estado dinámico (frontmatter YAML + cuerpo)
│   ├── .claude/settings.json
│   └── (tus archivos)
├── repos/               ← clones git desde la UI
├── uploads/             ← drag & drop
├── secondbrain/         ← wiki personal
└── sessions/            ← sesiones persistentes Claude Code

STATUS.md

Cada proyecto tiene un STATUS.md con frontmatter: state, last_activity, pending_tasks (lista), blockers, last_milestone. La auto-actualización post-turno (Haiku) parsea el último intercambio y patchea el STATUS si detecta cambios. Toggle en Ajustes → Sistema.

8. Módulos

El sistema modular permite añadir capacidades sin tocar el core. Cada módulo es una carpeta en modules/<id>/ con module.yaml, código Python (o sub-agente Claude Code), tools, rutas REST, hooks, settings_schema, healthcheck.

Módulos del sistema

idqué hace
voiceTTS ElevenLabs + voice-formatter post-proceso
mailTriage de Gmail vía sub-agente (compose, send, summarize, daily_briefing)
googleBase CLI gws para Gmail/Calendar/Drive/Docs/Sheets
gcalendarEventos próximos, hueco libre, schedule_meeting con detección de conflictos
logsBuffer de eventos del sistema (300 últimos, no persiste a disco)
costsTracker de gasto LLM por mes/día/modelo + soft/hard caps
secondbrainWiki personal mantenida por sub-agente
schedulerCronjobs (APScheduler) + webhooks entrantes
projectsCRUD de proyectos del workspace
web_searchBúsqueda web Tavily (primario) + Brave (fallback) + fetch_url
proactivityWatchers mail/calendar + DND + cola de aplazados (ver §12)

Cómo gestionarlos

Ajustes → Módulos. Cada tarjeta muestra:

  • Estado (enabled / disabled / pending_dep / error).
  • Healthcheck en vivo (verde / amarillo / rojo).
  • Botones: enable, disable, reload, ver logs, settings, sandbox (para tools tester).

El healthcheck se cachea 30s. Algunos verifican APIs externas (ElevenLabs, gws), otros estado interno (event count, schedules activos).

Crear un módulo nuevo

Ver GUIA/modulos.md en el repo para el contrato completo. Patrón mínimo:

modules/mi_modulo/
├── module.yaml         (id, kind, tools, routes, hooks)
├── settings_schema.yaml
└── python/
    ├── __init__.py
    ├── lifecycle.py    (enable + healthcheck)
    ├── routes.py       (APIRouter)
    └── tools.py        (handlers de tools)

Ojo: el ID no puede llevar guiones (-) — Python no acepta guiones en nombres de paquete. Usa underscores: web_search, no web-search.

9. Skills

Las skills son capacidades discretas en formato Claude Code (frontmatter YAML + cuerpo Markdown). El cuerpo entero es la description que ve el LLM. Pueden tener handler.py Python (ejecutables) o ser directive_only (solo contexto inyectable).

Skills del repo

  • get_current_time — devuelve hora local. Glifo reloj 🕐.
  • get_weather — meteorología. Glifo nube ☁.
  • workspace_status — directive_only, contexto sobre cómo consultar/actualizar STATUS.md.
  • update_project_status — actualizar STATUS.md programáticamente.

Crear / editar / eliminar

Ajustes → Skills tiene un editor: nombre, descripción, color (hex), glifo, handler Python opcional. También se puede generar una skill nueva por LLM (botón ✨ "generar skill"). Hot-reload: cada skill se importa con nombre único para evitar caché de Python.

10. SecondBrain

Tu wiki personal mantenida por un sub-agente Claude Code dedicado. Workflow:

  1. Subes archivos vía Ajustes → SecondBrain (drag&drop) o pasas texto en conversación con POST /api/secondbrain/ingest-text. Aparecen en workspace/secondbrain/raw/.
  2. Cambias el target a secondbrain y pides "ingesta lo último de raw/".
  3. El sub-agente lee, sintetiza y crea/actualiza páginas en workspace/secondbrain/wiki/.
  4. Búsqueda con GET /api/secondbrain/query?q=... — grep + scoring naive (NO pasa por LLM, es rápido).

Ajustes → SecondBrain también permite ver páginas, log de operaciones, borrar fuentes raw, reset destructivo.

11. Programación

Ajustes → Programación. Dos cosas distintas:

11.1 Cronjobs (proactivos programados)

Disparan un prompt a una hora fija. El resultado se broadcastea por WebSocket — aparece como burbuja proactiva en cualquier vista abierta y, si tienes auto_speak + is_voice=true, lo lee en alto.

Tipos de schedule:

  • daily — todos los días a HH:MM.
  • weekly — días de la semana específicos a HH:MM.
  • hourly — cada N horas.
  • cron — expresión cron cruda.

Cada schedule tiene target (orchestrator / project:slug / module:id), is_voice, enabled, last_run, last_result_preview. Botón ▶ para disparar manualmente.

11.2 Webhooks entrantes

URL pública POST /api/trigger/<id> con header X-Token: .... Cualquiera con el token puede disparar el prompt. Útil para Zapier, iOS Shortcuts, Home Assistant.

Cada webhook: name, token (uuid 32 chars, comparación constante-tiempo), prompt, target, is_voice. El body opcional {"extra": "..."} se appendea al prompt. El token se muestra UNA SOLA VEZ al crearlo (después solo enmascarado).

Ejemplo desde Shortcuts iOS:

curl -X POST https://medusa.tailnet.ts.net:8000/api/trigger/abc123 \
     -H "X-Token: tu-token-aqui" \
     -H "Content-Type: application/json" \
     -d '{"extra": "y dime el tiempo"}'

12. Proactividad

Hace que MEDUSA hable sola cuando algo importante pasa, sin que tú lo pidas. Activable en Ajustes → Módulos → proactivityenabled = true (opt-in explícito).

12.1 Watchers

  • mail watcher (cada 3 min, configurable): consulta Gmail vía gws, filtra is:unread newer_than:1h, pasa cada nuevo correo por la heurística. Si pasa el umbral, dispara proactivo.
  • calendar watcher (cada 2 min): mira eventos en los próximos 30 min. Avisa a 15 min antes (notice) y otra vez a 5 min (critical).

Primer arranque hace bootstrap: registra los IDs existentes SIN emitir, para no spammear con todo lo no leído al activarlo.

12.2 Heurística de importancia (correo)

Score 0–100. Ajustable en settings:

  • +40 si From está en vip_senders (acepta substrings, ej. @familia.com coge a todos del dominio).
  • +25 si el asunto matchea important_keywords (default: urgente, asap, deadline, factura, contrato).
  • +15 si el correo va directo a ti (no list-id, no bcc, no no-reply).
  • −20 si From en noisy_senders o si tiene List-Id (mailing list).
  • −30 si el asunto matchea noisy_keywords (newsletter, no-reply, digest, boletín).

Umbral default 50: bajo eso → ignorar. Entre 50 y 75 → notice. Por encima → critical.

12.3 Modo no molestar (DND)

Tres mecanismos que se combinan:

  1. Quiet hours: bloques de horarios. Default 22:00–08:00 todos los días. Cruzan medianoche correctamente. Editables vía PUT /api/proactivity/quiet-hours.
  2. Toggle manual: botón 🔔/🌙 en el HUD. Click → silencio total hasta nuevo click.
  3. Snooze: silencio temporal con duración (30 min, 1h, 4h, custom). Caduca solo.

Si dnd_critical_passes=true (default), las reuniones a 5 min siempre suenan aunque haya DND. Si lo apagas, ni eso.

12.4 Cola de aplazados

Cuando llega un proactivo durante DND, NO se pierde — se aplaza en una cola. Cuando el DND termina (snooze caducado, sales de quiet hours, o desactivas el toggle manual), MEDUSA emite UN proactivo-resumen consolidado del tipo "mientras silencio: 3 correos importantes (de Jefe, Mamá y Hacienda) y 1 reunión".

Se puede forzar el resumen ya con POST /api/proactivity/deferred/flush.

12.5 Throttling

  • min_gap_seconds: mínimo entre proactivos (default 300s = 5 min).
  • max_per_hour: ventana móvil 1h (default 6).
  • max_per_day: ventana móvil 24h (default 20).

Si supera caps, drop con warning en logs.

12.6 Endpoints útiles

GET  /api/proactivity                  # estado completo
POST /api/proactivity/dnd              # {mode: on|off|snooze|clear, minutes?}
POST /api/proactivity/test             # disparar proactivo de prueba
GET  /api/proactivity/deferred         # ver aplazados
POST /api/proactivity/deferred/flush   # forzar resumen
PUT  /api/proactivity/quiet-hours      # {blocks: [...]}

12.7 Notificaciones nativas (macOS)

Si os_native_notify=true y el server corre en macOS, dispara una notificación nativa con osascript. Llega aunque la pestaña esté cerrada. iOS / móvil sigue requiriendo la pestaña abierta (no hay PWA aún).

13. Búsqueda web

Módulo web_search con dos tools:

  • search(query, max_results) — devuelve lista de {title, url, snippet}.
  • fetch_url(url) — descarga la URL y devuelve el texto plano (HTML stripped). Limitado por fetch_max_chars para no quemar tokens.

Configurar API keys en Ajustes → Módulos → web_search:

  • Tavily (primario, recomendado) — free tier 1000 búsquedas/mes en tavily.com.
  • Brave Search (fallback) — free tier en brave.com/search/api.

Si Tavily falla y hay Brave configurado, cae automáticamente al fallback. Cuando MEDUSA no sabe algo o necesita información actual, invocará search sin que tengas que pedirlo explícitamente.

14. Costes

El módulo costs registra cada llamada al LLM en config/cost_log.json con tokens × precio (tabla en config/pricing.py). Para Claude Code usa el total_cost_usd reportado por el binario (incluye cache hits + sub-agents).

  • HUD: gasto del mes en USD (o tokens si provider es Claude Code).
  • Soft cap: warning visual cuando se alcanza, no bloquea.
  • Hard cap: bloquea llamadas. Aparece "tope mensual alcanzado" en chat.

Configurar en Ajustes → Sistema. Endpoint GET /api/costs devuelve resumen mes / día / top modelos / recent.

15. Diagnóstico

15.1 Event log

Buffer en memoria de 300 eventos con niveles (debug/info/warn/error) y categorías (api/voice/provider/router/skill/cost/ws/system). NO persiste a disco. Visible en Ajustes → Sistema.

15.2 Healthchecks

Cada módulo expone GET /api/modules/<id>/healthcheck. Algunos hacen ping real a APIs externas (ElevenLabs, gws). Cache 30s para no saturar.

15.3 /diagram

Visualización SVG de los módulos cargados, sus dependencias declaradas (depends_on) y sus tools. Útil para entender el grafo del sistema de un vistazo.

15.4 Token Dashboard

Solo si usas Claude Code: integra el ccusage dashboard para ver consumo por sesión, modelo, etc. GET /api/token-dashboard/status.

16. Atajos y comandos

16.1 Atajos de teclado

  • F — focus mode (oculta HUD y panel, solo orbe).
  • Cmd/Ctrl + Shift + R — recarga forzada (útil tras cambios en CSS/JS).

16.2 Slash commands en el input

Empieza un mensaje con / para abrir el palette:

  • /clear — limpia chat visual.
  • /reset — reinicia memoria del LLM.
  • /focus — focus mode.
  • /voice — toggle voz.
  • /help — ayuda rápida.
  • /skills — lista skills.
  • /agents — sub-agentes.
  • /projects — proyectos del workspace.
  • /cost — gasto del mes.
  • /status — estado de proyectos.

16.3 @-mentions

Empieza con @ para autocompletar:

  • Uploads recientes (drag&drop previo).
  • Proyectos del workspace.

17. Configuración avanzada

17.1 Variables de entorno (.env)

  • JARVIS_HOST=127.0.0.1 — restringe el bind a loopback (default 0.0.0.0, escucha en todas las interfaces incluido Tailscale/LAN).
  • JARVIS_DEV=1 — hot-reload Python en cambios.
  • ANTHROPIC_BASE_URL=... — apunta Claude Code a un proxy (claude-code-router, etc.).
  • API keys: ANTHROPIC_API_KEY, OPENAI_API_KEY, OPENROUTER_API_KEY, etc. También editables desde Ajustes → Variables (UI persistente).

17.2 Acceso desde móvil con HTTPS

iOS bloquea el micrófono fuera de localhost salvo HTTPS. Para STT desde el iPhone vía Tailscale:

tailscale serve --https=8000 http://localhost:8000

Luego desde el iPhone: https://<tu-mac>.tailnet.ts.net:8000/chat.

17.3 Permission modes Claude Code

  • read_only — allowed-tools whitelist mínima.
  • confirm_writes — pide confirmación humana antes de escribir.
  • bypass_all--dangerously-skip-permissions. Solo se puede activar desde 127.0.0.1 (no Tailscale, no LAN).

17.4 Personalidad y nombre

Ajustes → Personalidad: edita el system prompt del orquestador. Default: amigo cercano, tutea siempre, NO formal "señor". assistant_name (default "MEDUSA") es lo que aparece en el HUD y notificaciones.

17.5 Apariencia

  • Tema: paletas de color (cyan, ámbar, magenta…).
  • orb_size: small / medium / large / huge.
  • Modo Medusa (beta): 14 tentáculos sinuosos animados con onda transversal en lugar del orbe estándar.

17.6 Wake words

Editables desde Ajustes → Voz con chips removibles. Default: "medusa". Puedes añadir variantes ("hey medusa", "ok medusa").

18. Privacidad

  • API keys nunca en disco como texto plano: se guardan en config/runtime.json con flag de secreto (la UI las enmascara como sk-ant-•••••XXXX). Backend ignora valores con al actualizar (no se sobrescriben con la versión enmascarada).
  • Event log no persiste: buffer en memoria. Los detalles con campo api_key se sanean antes de loggear.
  • Workspace fuera del repo: tu trabajo en ~/medusa-workspace/, gitignored.
  • Endpoints de escritura (POST/PUT/DELETE) solo desde loopback + Tailscale (100.x) + LAN privada. Los GET son públicos a la red bindeada.
  • bypass_all de Claude Code SOLO se puede activar desde 127.0.0.1 (ni Tailscale).
  • Webhooks: el token se muestra UNA VEZ al crearlo. Después solo masked. Comparación constante-tiempo (HMAC).

19. Problemas frecuentes

"No veo X / aún veo la versión vieja"

Caché del navegador. Cmd+Shift+R (Mac) o Ctrl+Shift+R (Win/Linux). En iOS Safari: cierra la pestaña entera y vuelve a abrir. Antes de pensar que es bug, verifica que el server sirve la versión nueva con curl http://localhost:8000/static/<archivo>.

"El provider activo no responde"

Falta API key o caducada. Mira la badge del HUD y Ajustes → Provider. Para Ollama: que esté corriendo (ollama serve).

"gws auth caducada / mail watcher falla"

El healthcheck de proactivity / mail / google lo detecta. Resuelve con gws auth login en terminal. Tras 3 fallos consecutivos, los watchers hacen backoff (no spammean errores).

"Puerto 8000 ocupado"

lsof -ti:8000 | xargs kill -9

"iOS no me deja usar el micro"

Necesita HTTPS — usa tailscale serve --https=8000 http://localhost:8000 y conéctate por la URL TLS de Tailscale.

"Subí un archivo al panel y no aparece"

Tamaño máximo por archivo: 10 MB. Para más grandes, usa Ajustes → Repos para clonar repositorios completos via git.

"Las notificaciones del navegador no llegan"

Tienes que aceptar el permiso la primera vez (banner del navegador). Solo aparecen cuando la pestaña NO está visible. Para que lleguen aunque la pestaña esté cerrada: macOS con os_native_notify=true en proactivity (no funciona en iOS aún).

20. Para desarrolladores

Arrancar y reiniciar

pip install -r requirements.txt
python3 run.py                  # bind 0.0.0.0:8000

# Reiniciar tras cambio Python (no hay hot-reload por defecto):
lsof -ti:8000 | xargs kill -9; sleep 1; nohup python3 run.py > /tmp/jarvis.log 2>&1 &

Cambios en web/: solo recargar navegador. Cambios Python: reiniciar (o JARVIS_DEV=1).

Tests

python3 -m pip install -r requirements-dev.txt   # una vez
python3 -m pytest -q                              # ~1s, ~170 tests

Los tests usan TestClient de FastAPI (no levanta uvicorn real). Si añades feature, añade test. Tests del módulo en modules/<id>/tests/ se descubren automáticamente (pytest.ini: testpaths = tests modules).

Validar JS sin ejecutar

node -c web/orb.js
node -c web/app.js
node -c web/pixels.js

Imprescindible tras refactors grandes. El conteo de paréntesis a ojo es engañoso por strings.

Verificación rápida del backend

python3 -c "from core.server import app; print('OK')"

Idiosincrasia del loader de módulos

Importa archivos como <id>.python.X (sin modules. prefix); el resto del repo usa modules.<id>.python.X. Python ve dos módulos distintos en sys.modules aunque sean el mismo archivo.

  • Los singletons globales (events, tracker, workspace, secondbrain, scheduler) viven en core/ para evitar doble carga.
  • Los tests que mockean del módulo deben parchear AMBOS paths — patrón dual_patch en modules/proactivity/tests/conftest.py.

Roadmap

Lo pendiente vive en tareas.md en la raíz del repo. Mover de "En curso" a "Hechas" al terminar.