## Llamadas online — Videollamadas con grabación (tipo Zoom/Riverside)

Template `livekit-svc`: sala de videoconferencia self-hosted con grabación HD server-side. El agente crea la sala, los participantes se unen por navegador (cámara + pantalla compartida), el servidor graba el layout completo en 1080p y al terminar sube el MP4 + transcript a tus Files de EasyBits. Sin servidores de terceros, sin límite de duración.

**MCP tools** (grupo `sandbox`):

### Crear llamada — one shot
`call_create({ room? })`
- Levanta el servidor, espera ~15s, configura WebRTC, retorna `{ sandboxId, room, roomUrl }`
- Comparte `roomUrl` con los participantes (abre en cualquier navegador)

### Grabar
`call_record({ sandboxId, room })`
- Chromium headless + ffmpeg capturan el layout completo en 1080p
- Retorna `{ recording: true, startedAt }`

### Detener → Files + transcript
`call_stop({ sandboxId })`
- Finaliza MP4, lo sube permanentemente a tus Files, genera transcript Whisper (.txt)
- Retorna `{ url, fileId }` — `url` sobrevive a la VM

### Estado de la sala
`call_status({ sandboxId })`
- Retorna `{ recording, room?, startedAt?, participants[] }` — si está grabando y quién está conectado

### Listar grabaciones
`call_files()`
- Retorna `[{ id, name, url, source, createdAt }]` — las grabaciones en tus Files (source: "studio")

### Transcript de la llamada
`call_transcript({ sandboxId? })`
- Devuelve el TEXTO del transcript inline (no un link), listo para resumir
- `status`: `transcribing` (Whisper procesando, reintenta ~1 min), `ready` (texto en `text`), `failed` (sin audio o Whisper falló), `unavailable` (grabación sin transcript), `no_recording` (nadie grabó)
- Con `sandboxId` = estado EN VIVO del box durante/tras la llamada; sin él = el transcript más reciente de tus Files
- Retorna `{ source, status, text?, chars? }`

### Cerrar la llamada
`call_destroy({ sandboxId })`
- Sube grabaciones pendientes, las rescata de la VM y destruye el servidor. Si no se llama, la sala se auto-destruye al TTL (3h)

**API REST:**
```
POST /api/v2/calls              body: { room? }      → { sandboxId, room, roomUrl }
POST /api/v2/calls/:id/record   body: { room }       → { recording: true }
POST /api/v2/calls/:id/stop                          → { url, fileId }
GET  /api/v2/calls/:id/status                        → { recording, room?, startedAt?, participants[] }
GET  /api/v2/calls/files                             → [{ id, name, url, source, createdAt }]
GET  /api/v2/calls/:id/transcript                    → { status, text?, chars? }  (estado en vivo)
GET  /api/v2/calls/transcript                        → { status, text?, fileId? }  (más reciente)
POST /api/v2/calls/:id/destroy                       → cierra la sala (sube pendientes + destruye)
```

**SDK:**
```ts
const call = await eb.calls.create({ room: "entrevista" });
// comparte call.roomUrl
await eb.calls.record(call.sandboxId, { room: call.room });
const { url } = await eb.calls.stop(call.sandboxId);
// url = MP4 permanente en Files
const st = await eb.calls.status(call.sandboxId);  // { recording, participants[] }
const recs = await eb.calls.files();               // grabaciones en Files
const t = await eb.calls.transcript(call.sandboxId); // { status, text } — texto inline
await eb.calls.destroy(call.sandboxId);            // cierra la sala
```

**Flujo WhatsApp/chat (una sola tool):**
```
call_create()               → { sandboxId, roomUrl }
[Ghosty manda roomUrl]
call_record({ sandboxId })  → grabando…
call_stop({ sandboxId })    → { url }  ← link permanente al MP4
```

**UI en sala:**
- Green room al entrar (preview + selector de cámara/mic)
- Pantalla compartida → grande; cámaras → tira lateral; click para destacar
- Grabación visible para todos (punto rojo + cronómetro)
- Suena al entrar/salir y al iniciar/detener grabación
- Funciona en móvil

**Notas:**
- Una grabación activa por sandbox; para sesiones paralelas usa `call_create` por separado
- Transcript via Whisper EMBEBIDO en la caja (español, on-device, sin costo extra ni proveedor externo). El .txt llega async tras `call_stop`; consúltalo con `call_transcript`
- MP4 + .txt en Files permanentemente aunque la VM se destruya
