Lo básico para sacarle valor en el día a día.
/talk)4 personajes, cada uno con su modelo y especialidad. Vos siempre hablas con TUNIX — los demás son sub-agentes silenciosos que él delega.
| Le decís | TUNIX hace |
|---|---|
| "¿quién soy?" | Responde tu perfil cargado del Memory Forge |
| "¿qué tengo hoy?" | Lista reuniones del día + tareas top |
| "¿qué recordatorios tengo para el lunes?" | query_reminders en forge_user_context |
| "recordame X mañana a las 10" | create_reminder (parse fecha con Haiku) |
| "¿quién es Natalia Garcés?" | Cascada deep_search → forge_global_search → search_entities |
| "que GOJAN prepare propuesta para X" | Spawnea GOJAN background |
| "que DEVIX revise por qué falla X" | Spawnea DEVIX |
| "GOJAN urgente, prepara X" | Spawnea con important=true (interrumpe al terminar) |
| "¿de qué hablamos la última vez?" | Lee summaries previos de tunix_talk_sessions |
| "búscame cuando hablamos sobre Nicolás" | search_talk_history (RAG embeddings) |
| "analizame en profundidad cómo está armado X" | delegate_to_claude_max (Opus 4.7, sin truncar) |
| "mejorate y agregame la capacidad de Y" | tunix_self_improve flow (análisis → confirma → aplica) |
| "pará / esperame" | Pausa voz inmediato |
| "continuá" | Retoma desde donde paró |
| "dale en tungsteno" | Activa modo autónomo (ver 3.2) |
| Botón | Función |
|---|---|
| 🧠 ALTO RAZONAMIENTO | Toggle al lado del verde. ON = TUNIX delega A CADA respuesta no-trivial a Opus 4.7. OFF = router automático según complejidad. Persistente en localStorage. |
| 📞 / 🛑 Verde / Rojo | Iniciar / colgar llamada. Rojo libera mic + cierra WS + persiste sesión. |
| ⏸️ PAUSA | Mic muteado + audio TUNIX cortado + WS sigue conectado. Útil en bus/ruido. Cambia a ▶️ REANUDAR. |
| ▶️ CONTINUAR | Le pide a TUNIX retomar su última idea (cuando él hizo pausa por largo). |
| ❓ Header | Abre este manual. |
Podés dejar el Talk abierto indefinido:
Lógica de decisión y aprendizaje. Cómo elige a quién delegar y cómo recuerda.
TUNIX matchea la profundidad de la respuesta a la profundidad de la pregunta. Calidad ingeniería sobre velocidad — preferible esperar 30s con respuesta completa que 5s vaga.
| Tipo de pregunta | Quién contesta | Tiempo | Profundidad |
|---|---|---|---|
| Simple (1 step, lookup, comando) | gpt-realtime directo | 1-2s | natural corta |
| Mediana atómica (1 tool) | TRUNKS (Haiku) | 2-3s | factual |
| Mediana razonamiento | DEVIX / GOJAN (Sonnet) | 5-15s | completa sin truncar |
| Compleja análisis | Opus 4.7 (quick=false) | 15-30s | calidad ingeniería |
| Razonamiento extenso (estratégico) | Opus quick=false explícito | 30-60s | máxima |
Cuando preguntás "¿quién es X?" / "¿qué sabes de Y?", TUNIX escala automático hasta encontrar:
NIVEL 1 — deep_search(query) ~135ms ILIKE 9 tablas clave ↓ si 0 hits NIVEL 2 — forge_global_search ~2s ts_vector full-text 20+ tablas ↓ si 0 hits NIVEL 3a — search_entities ~220ms axis_entities (Gemini 768) NIVEL 3b — axis_hybrid_search ~330ms AXIS memory (vector + keyword) ↓ si 0 hits NIVEL 4 — delegate_to_claude_max ~15-30s Opus + MCP Supabase SQL custom ↓ si 0 hits "No encontré nada en mi sistema. ¿Probamos otra fuente?"
Arquitectura post 25-may-2026. Realtime NO recuerda nada por sí mismo (es voz + orquestación pura). La memoria vive en Supabase con embeddings Gemini 768d.
El endpoint /api/tunix-realtime-tools consulta las últimas 5 sesiones Talk de 72h y las inyecta al system prompt al iniciar cada llamada. Formato:
• "título" [hace 3h, 12 turnos] · temas: X, Y, Z resumen: ...
Sin tool call. Sin latencia. Solo da contexto a grandes rasgos — para detalles textuales TUNIX usa Capa 2.
recall(query, hours_back?) — vector search directo (~1.5s)Embedding Gemini 768 de la query → RPC tunix_talk_recall(p_query_embedding, p_hours_back, p_max_results, p_min_similarity) → top-5 turnos exactos rankeados por similarity × 0.7 + recency_decay × 0.3.
Disparadores: "qué hablamos de X", "qué te dije sobre Y", "te acuerdas cuando...", "leeme lo que dijimos de Z".
Devuelve: {session_id, session_title, when, turn_number, patricio_dijo, tunix_dijo, similarity, score}. TUNIX lee los hits TEXTUAL, mencionando cuándo se dijo y nombrando que vino de Capa 2.
memory_librarian(query) — Sonnet sintetiza (~6s)Top-15 fragmentos del mismo RPC → pasados a Sonnet 4.6 vía tunix-claude-max (OAuth Max, $0) → devuelve narrativa cerrada lista para que TUNIX lea TEXTUAL en voz alta.
Disparadores: "hazme un resumen todo lo hablado sobre X esta semana", "qué nos quedó pendiente con Y", "punteo de decisiones de Z".
read_memory(slug) y list_memories() — canon documental en forge_memory + carpeta ~/.claude/projects/.../memory/_corerecall_lessons(query) — lecciones consolidadas por REM agent nocturnoforge_global_search(query) — búsqueda híbrida sobre 25 tablas con embeddingsforge_tunix_talk_sessions.embeddingforge_memory con slug auto-generadoSi la sesión tiene ≥4 turns + summary OK, Haiku analiza el transcript completo y decide si hay algo NUEVO que valga la pena guardar como memoria persistente. Criterios:
Si pasa el filtro → INSERT en forge_memory con slug auto-generado. Costo ~$0.0003/sesión.
TUNIX puede mejorarse a sí mismo modificando su propio código. Flow obligatorio en 2 pasos con OK humano:
tunix_self_improve(observation) → Claude Max lee codebase + devuelve plan JSON: {commit_sha, files, requires_apk_rebuild, already_applied, blocker, summary}. NO modifica nada.tunix_self_improve(observation, proposed_change, confirmed_apply=true) → Claude Max edita + commit + push.android/* o capacitor.config.ts, TUNIX avisa que se requiere compilar APK nueva (más complejo, mejor desde tu PC).Cada mejora queda registrada en forge_tunix_improvements con observación, plan, commit_sha, status.
Reglas duras que TUNIX hereda de Claude Code. Qué NUNCA hace solo.
2 capas:
TUNIX sabe qué requiere confirmación, doble OK por voz para destructivos, "ingresá a la APK a confirmar" para los mayores.
isDestructive(task) bloquea automático si match keywords:
drop table | truncate | delete from (sin WHERE) git push --force | git reset --hard | git branch -D | git clean -fd rm -rf | wipe | destroy | nuke | format database modificar .env | modificar secret | modificar token | modificar oauth docker rm -f | docker kill -f DROP DATABASE | DROP SCHEMA
Si match + confirmed_destructive=false → devuelve error destructive_action_requires_confirmation. TUNIX debe pedir confirmación humana y volver con confirmed_destructive=true.
Qué es: operación autónoma orquestada. TUNIX coordina sub-agentes sin pedir permiso intermedio, hasta cerrar el objetivo. Te reporta al final.
Flow obligatorio (TUNIX SIEMPRE pasa por esto antes de arrancar):
| Tipo | Confirmación requerida |
|---|---|
| Destructivo menor (delete con WHERE específico, rm de archivo único) | Doble OK por voz: "esto va a [X]. ¿confirmás?" → si sí → "última vez: ¿seguro? Esto no se deshace." → si sí → ejecuta |
| Destructivo mayor (drop database, wipe disk, push -f, mass delete) | TUNIX NO ejecuta por voz aunque digas sí dos veces. Te pide: "esto es muy grueso, ingresá a la APK y confirmalo manualmente en /tunix-ops" |
| Mensaje WSP/email a TERCEROS (clientes, prospects) | SIEMPRE pedir OK explícito mensaje por mensaje. TUNIX nunca manda solo |
| Modificar workflows n8n críticos | Solo vía scripts/lib/n8n_safe.py con verify_callback |
Si un agente intenta la misma tarea 2+ veces y falla, el sistema lo marca stuck:
🚧 [agente] entrampadoHeurística backend: agent-run.js cuenta tasks recientes (4h) del mismo agent + prompt similar con status='failed'. Si ≥2 → marca stuck inmediato, NO ejecuta.
Lo que diferencia a TUNIX de otros asistentes.
TUNIX puede operar tu PC física con Claude Code SDK real. Arquitectura pull-based (sin tunnels, sin endpoints inbound expuestos):
TUNIX (voz) ↓ delegate_to_pc(task) INSERT en forge_pc_jobs status='queued' ↓ (poll cada 8s vía RPC claim_pc_job FOR UPDATE SKIP LOCKED) forge_pc_agent.py corriendo en TU PC ↓ ejecuta: claude --print --permission-mode acceptEdits Claude Code SDK opera tu filesystem ↓ stdout capturado UPDATE forge_pc_jobs status='done' + result ↓ trigger push notif al celu TUNIX te lee el resultado
cd e:\TUNGSTENO\forge-os .\scripts\setup-pc-agent-task.ps1 # PowerShell admin # Auto-arranca al login, restart si crashea, sobrevive batería
TUNIX puede disparar DEVIX, GOJAN y TRUNKS en paralelo. Mientras trabajan, vos seguís conversando otras cosas. Cuando terminan:
Heurística automática: si decís "urgente", "ya", "ahora" o "interrumpime" en el prompt, el backend fuerza important=true aunque TUNIX olvide marcarlo.
| Método | Cómo | Comportamiento |
|---|---|---|
| Por voz | "Tunix espera" / "pará" / "un momento" | Silencio inmediato. WS sigue. |
| Hablando | Empezás a hablar durante TUNIX | VAD detecta (threshold 0.4) + interrupt_response=true → corta automático |
| Botón ⏸️ PAUSA | Tap en UI | Mic muteado + audio cortado + WS abierto. Cambia a ▶️ REANUDAR. |
| Para retomar | "continuá" / "seguí" o botón ▶️ | TUNIX retoma desde la idea exacta donde paró |
Web Push VAPID a forge_push_subscriptions. Disparado por:
Cliente: scripts/forge_push.py o directo POST /api/push con Bearer.
Qué se paga y cómo optimizar.
| Componente | Costo | De dónde |
|---|---|---|
| Voz TUNIX (OpenAI Realtime gpt-realtime) | ~$0.12/min audio | OpenAI API |
| Sub-agentes DEVIX/GOJAN/TRUNKS | $0 extra | Tu OAuth Max ($200/mes) |
| delegate_to_claude_max | $0 extra | Tu OAuth Max |
| delegate_to_pc | $0 extra | Tu OAuth Max en tu PC |
| Memoria persistente (Haiku summary) | ~$0.0003/sesión | API Anthropic |
| Reflexión post-sesión (Haiku) | ~$0.0003/sesión | API Anthropic |
| Embeddings OpenAI text-embedding-3-small | ~$0.00001/embed | OpenAI |
| Embeddings Gemini text-embedding-001 (axis_*) | ~$0.00001/embed | Gemini API |
| STT/TTS (cuando aplica fuera Realtime) | centavos | Deepgram + ElevenLabs |
| Total estimado/mes | ~$30-50 USD | uso típico 1h/día |
/usage en Claude Code para ver consumoPara entender qué hay debajo del capot. Necesario cuando debuggeás o extendés.
┌─────────────────────────────────────────────────────────────┐
│ FRONTEND (APK Capacitor + WebView + public/talk.html) │
│ · gpt-realtime WebSocket (audio in/out 24kHz PCM) │
│ · Plugin nativo ForgeRecording (bypass WebView mic bug) │
│ · TalkForegroundService (specialUse, celu bloqueado OK) │
│ · Splash SVG (cubo+T blur→define) + sessionStorage flag │
│ · Polling agent-status cada 6s → inyecta msg si stuck │
│ · Pending tool calls counter (response.create 1 sola vez) │
└────────────────┬─────────────────────────────────────────────┘
│ tool calls
▼
┌─────────────────────────────────────────────────────────────┐
│ BACKEND VERCEL (api/tunix-tool-exec.js, maxDuration 300s) │
│ · 30+ tools registradas │
│ · isDestructive() regex guard pre-execute │
│ · embedQuery(text, dims=1536|768) OpenAI ó Gemini │
│ · Audit a forge_tunix_tool_audit en cada call │
└─────────┬──────────┬───────────────┬─────────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌─────────┐ ┌──────────────┐
│ Supabase │ │ Bridges │ │ Container │
│ RPCs + │ │ WSP/etc │ │ tunix-claude │
│ tables │ │ │ │ -agent (VPS) │
└──────────┘ └─────────┘ └──────┬───────┘
│ Claude Code SDK
▼
┌──────────────────┐
│ Opus 4.7 / OAuth │
│ Max (sin $extra) │
└──────────────────┘
| Categoría | Tools |
|---|---|
| Lectura datos | query_tasks, query_meetings_today, query_meetings_upcoming, query_recent_wsp_audios, query_recent_meetings, query_reminders, get_emotional_state |
| Búsqueda cascada | deep_search → forge_global_search → axis_hybrid_search → delegate_to_claude_max |
| Búsqueda especializada | search_entities, wsp_search_history, search_history, search_talk_history |
| Escritura | mark_task_done, cancel_meeting, create_task, create_reminder, record_emotion |
| WSP (confirmación previa) | send_wsp_audio, send_wsp_text, preview_wsp_audio_reply |
| Multi-agent | spawn_agent (devix/gojan/trunks), check_agent_done, list_running_agents |
| Delegación profunda | delegate_to_claude_max (quick/full), delegate_to_pc, check_pc_job, delegate_to_claude_deep |
| Memoria | read_memory, list_memories |
| Sync runtime | sync_forge_now |
| Auto-evolución | tunix_self_improve, list_improvements |
El holding usa 2 modelos de embedding según la tabla. embedQuery(text, dims) elige automático.
| Modelo | Dims | Tablas |
|---|---|---|
| OpenAI text-embedding-3-small | 1536 | forge_chat_history.embedding, forge_decisions, forge_helix_knowledge, forge_ledger_blueprints, forge_ledger_chat_logs, forge_meeting_embeddings, forge_notes, forge_tunix_talk_sessions, kumelemu_rag, tm_documentos, axis_codex, axis_sessions |
| Gemini text-embedding-001 (output_dim 768) | 764 | axis_entities, axis_episodic_memory, axis_procedural_memory, axis_semantic_memory, axis_query_cache, axis_prefetch_cache, axis_tasks, forge_skills.when_to_use_embedding, forge_os_lessons, sys_mind_wiki, forge_chat_history.gemini_embedding |
embedQuery ahora pide dims explícito por tool. Esto era el bug que rompía search_entities.
forge_global_search(query_text, max_results=30) → 20+ tablas Forge + AXIS, full-text ILIKE axis_hybrid_search(query_embedding, query_text, max_results=10) → vector + keyword sobre axis_semantic_memory + episodic + procedural + entities + codex search_entities(query_embedding, filter_type, max_results) → personas/empresas/conceptos axis_entities wsp_search_history(p_query, p_brand, p_contact_name, p_limit) → mensajes WSP histórico search_talk_sessions(query_embedding, match_count, similarity_threshold) → conversaciones Talk previas con summary embeddings forge_memory_search(p_query, p_project, p_limit) → Memory Forge full-text devix_search_knowledge(q, k=5) → knowledge técnico DEVIX claim_pc_job(target='patricio_pc') → atómico FOR UPDATE SKIP LOCKED para el agent PC search_chats_semantic / search_chat_history_hybrid / search_codex / search_decisions / search_blueprints / search_notes / search_tasks / search_sys_mind / search_tm_docs / kumelemu_search_rag / skill_match
| Modelo | Latencia | Costo | Cuándo |
|---|---|---|---|
| gpt-realtime (actual) | 1-3s/turn | $0.12/min | DEFAULT |
| gpt-realtime-mini | 1-2s/turn | $0.045/min | Más barato, antes tuvo bug truncate |
| gpt-4o-realtime | 1-3s/turn | $0.32/min | Premium, no usado |
Voz default: cedar (neutro latino). Sample rate: 24kHz PCM in/out.
Doble defensa para precisión en español acentuado y términos del dominio Forge OS.
gpt-4o-transcribeReemplazó a gpt-4o-mini-transcribe. ~10× más preciso en español, +~$0.05/día. Configurado en talk.html dentro de session.update.audio.input.transcription:
transcription: {
model: 'gpt-4o-transcribe',
language: 'es',
prompt: 'Patricio Canquil, TUNIX, Forge OS, Tungsteno, ...' // 110 términos
}
El campo prompt es un hint léxico que se envía UNA VEZ al iniciar la sesión. El STT lo usa para sesgar su modelo acústico hacia el vocabulario del dominio. NO cuenta como tokens de audio (gratis).
~60 mappings "transcripción confundida → forma canónica" que el LLM aplica antes de razonar o disparar tool calls. Cubre:
| Componente | Costo | Latencia |
|---|---|---|
| transcription.prompt | $0 (hint, no tokens facturados) | +50-80ms una sola vez al conectar |
| LEXICON system prompt (~600 tokens) | ~$0.00009/día (centavos al año) | 0ms por turno (prompt caching OpenAI Realtime) |
Margen para crecer: hasta ~3000 tokens de lexicon antes de competir por espacio útil. Estamos en ~600.
transcription.prompt y la sección LEXICON del system prompt. Pedile a TUNIX "agrega [X] al lexicon" y lo hace solo via tunix_self_improve.
delegate_to_claude_max default quick=false (calidad ingeniería completa). quick=true SOLO para preguntas con respuesta corta natural (factuales puntuales).Cuando TUNIX delega a Opus con quick=false, el flujo NO es request/response tradicional sino Server-Sent Events bidireccional. Patricio ve la respuesta de Opus "escribiéndose en vivo" en el chat con markdown render incremental, en vez de esperar 15-30s en silencio + golpe.
Frontend talk.html (streamClaudeMax)
↓ POST /api/tunix-claude-max-stream (fetch streaming)
Vercel proxy (api/tunix-claude-max-stream.js)
↓ POST /agent-stream (SSE pass-through, Bearer auth)
Container tunix-claude-agent v10 (server.mjs)
↓ for await msg of query({...})
Claude Code SDK (OAuth Max)
↓ Opus 4.7 generando token-by-token
Para CADA text block → emit data: {"type":"chunk","text":"..."}\n\n
Para tool_use → emit data: {"type":"tool_use","name":"...","input":{...}}\n\n
Al final → emit data: {"type":"done","full_text":"...","result":{...}}\n\n
| type | Payload | Frontend action |
|---|---|---|
chunk | {text: "..."} | Acumula + renderiza markdown cada 250ms con cursor blinking |
tool_use | {name, input} | addMsg('tool', '[Opus] toolName(args)') |
repo | {status: "git pull result"} | Debug log |
done | {full_text, tool_uses, result, duration_ms} | Render final + envía function_call_output al WS OpenAI con texto completo |
error | {error, needs_login} | Render error en msg-rich + corta polling |
quick=true se usa la ruta JSON tradicional (más rápida para respuestas cortas).scripts/tunix-claude-agent/server.mjs v10: endpoint /agent-stream agregado, mantiene /agent JSON originalapi/tunix-claude-max-stream.js nuevo: proxy SSE con maxDuration 300spublic/talk.html streamClaudeMax(args) nueva función: fetch streaming + parsing SSE manual + render incremental con marked.jsexecuteTool intercepta delegate_to_claude_max cuando quick !== true y desvía al path streamingCache de tools de lectura idempotentes con TTL corto. Reduce latencia 4.5x en preguntas frecuentes.
forge_tunix_tool_cache (tool_name, args_hash md5, result jsonb, expires_at, hit_count)getCached/setCached en tunix-tool-exec.js con .select().eq() y filtro expires_at > now()15s list_running_agents
30s sync_forge_now / query_recent_wsp_audios
45s query_tasks / deep_search
60s query_reminders / query_meetings_upcoming / get_emotional_state /
forge_global_search / search_talk_history / list_improvements / wsp_search_history
90s axis_hybrid_search
120s query_meetings_today / query_recent_meetings / search_entities / search_history
300s list_memories
Call 1 (cache miss): 187ms — ejecuta query real + guarda Call 2 (cache hit) : 41ms — devuelve cached con hit_count++ Call 3 (cache hit) : 41ms — mismo cached Speedup: 4.5x
Audit log forge_tunix_tool_audit NO se duplica con cache hits (solo registra ejecuciones reales). Response trae flag cached: true|false + meta _cache: {age_sec, hit_count}.
Auto-evolución continua: cada lunes 9am Chile, Sonnet 4.6 analiza la semana de TUNIX y propone mejoras concretas listas para aplicar.
forge_tunix_meta_reports (week_start, sessions_analyzed, tool_calls_analyzed, patterns_detected jsonb, failures jsonb, proposed_improvements jsonb, status)/api/tunix-meta-weekly con maxDuration 300s0 12 * * 1 (lunes 12 UTC = 9am Chile)read_meta_report (TUNIX lee al user), meta_report_decision (approved|dismissed)- Sesiones Talk con summary (últimos 7 días) - Tool audit (qué tools llamó, success rate, latencias) — top 10 más usadas - Sub-agent tasks (cuántas stuck, cuántas done) por agente - Auto-mejoras aplicadas (commit_sha, files_affected) - Emotional log (patron emocional semanal) - Key topics recurrentes (top 10 de todas las sesiones) - Pending_actions no cerradas (acumulados de últimas sesiones)
{
"patterns_detected": {
"temas_frecuentes": [...],
"horarios_pico": "...",
"estado_emocional_general": "..."
},
"failures": {
"tools_problematicas": [{tool, fail_rate_pct, razon_probable}],
"agentes_stuck_recurrentes": [{agent, casos, patron}],
"gaps_capacidades": [...]
},
"proposed_improvements": [
{
"type": "tool_new|prompt_update|memory_save|tool_fix|architecture",
"priority": "high|medium|low",
"description": "...",
"suggested_observation": "Frase EXACTA para tunix_self_improve(observation=...)",
"expected_impact": "..."
}
],
"executive_summary": "1-2 frases honestas"
}
Lunes 9am → cron dispara /api/tunix-meta-weekly
↓ Sonnet 4.6 analiza 7d de datos (~30s)
↓ Insert forge_tunix_meta_reports status=pending_review
↓ Push notif al celu: "📊 Reporte META semanal listo"
Patricio en Talk dice "leeme el meta report"
↓ TUNIX usa read_meta_report
↓ lee executive_summary + proposed_improvements por voz
↓ Patricio decide cuáles aprobar
↓ TUNIX usa meta_report_decision('approved')
↓ Para cada improvement aprobada, TUNIX itera con
tunix_self_improve(observation=suggested_observation, confirmed_apply=true)
↓ Claude Max aplica + commit + push + Vercel deploya
↓ Push final: "✅ N mejoras aplicadas tras META review"
61 tool calls analizados 41% fail rate detectado 6 mejoras propuestas con suggested_observation listas Executive summary: "Semana operativamente comprometida..."
Mientras search_talk_history busca por sesión (summary), search_episodic_memory busca por TURN exacto — un intercambio user→tunix con tri-vector embeddings (input + output + combined). Mismo patrón que axis_episodic_memory de AXIS.
forge_tunix_episodic_memory (session_id, turn_number, user_text, tunix_text, tools_used, emotional_context, input_embedding, output_embedding, combined_embedding) con vectores 768 dims Gemini/api/tunix-episodic-save: recibe turn, genera 3 embeddings en paralelo (Gemini text-embedding-001 768 dims), insertcommitTunixMsg dispara fire-and-forget al endpoint con (last user_text, current tunix_text, turn_number)search_episodic_memory(query, top_k, threshold): hybrid search — text ILIKE primero (instantáneo, captura matches literales), vector RPC search_tunix_episodic_v2 como fallback semántico| Tool | Granularidad | Ejemplo |
|---|---|---|
| search_talk_history | SESIÓN | "¿de qué hablamos en la sesión del lunes?" |
| search_episodic_memory | TURN exacto | "¿cuándo te dije que Natalia es mi esposa?" |
.ilike() en user_text + tunix_text en paralelo + merge dedupe. Hit rate alto, latencia <100msembedQuery(text, 768) Gemini + RPC search_tunix_episodic_v2 con cosine similarity sobre combined_embeddingTUNIX ahora ve lo que vos le mostrás. Foto/screenshot → Sonnet 4.6 Vision → análisis técnico → TUNIX comenta por voz.
forge_tunix_vision_uploads (session_id, source, image_url, ai_analysis, ai_model, context_tags, processed)/api/tunix-vision-analyze: acepta image_base64 o image_url, llama Claude Sonnet 4.6 Vision con prompt enfocado en análisis técnicoanalyze_recent_image: TUNIX consulta la última imagen analizada de la sesióncapture="environment" abre cámara nativa Android directamentemsg-rich + se inyecta contexto al WS para que TUNIX comente por voz naturalPatricio tap 📸 FOTO en /talk
↓ input file con capture="environment"
Cámara Android nativa abre
↓ foto capturada
FileReader.readAsDataURL → base64
↓ POST /api/tunix-vision-analyze {image_base64}
Vercel handler
↓ Claude Sonnet 4.6 Vision API
Análisis técnico (~2-5s)
↓ INSERT forge_tunix_vision_uploads
↓ render markdown en msg-rich del chat
↓ inyectar conversation.item.create al WS:
"[SISTEMA] Patricio subió foto. Sonnet Vision: ..."
gpt-realtime habla natural comentando lo que ve
Test imagen ícono Forge (192x192 PNG): Análisis: "rayo eléctrico, blanco sobre negro, representa energía" Latencia: 2.3s Modelo: claude-sonnet-4-6 Costo: ~$0.003 por análisis
Toggle configurable en /talk para activar always-listening on-device. Cuando está ON, di "Hey TUNIX" / "tunix" y la llamada arranca sola. OFF por default (requisito explícito: usuario elige).
SpeechRecognition (es-CL, continuous, gratis on-device). Funciona en Chrome y Android WebViewforge_tunix_wake_settings (user_id, enabled, keyword, sensitivity) + forge_tunix_wake_events (log con confidence + triggered_call)/api/tunix-wake-settings (GET/POST upsert) y /api/tunix-wake-event (log cada detección)forge_tunix_wake_increment (atomic counter de triggers)#wakeWordToggle verde al lado del 🧠 Alto Razonamiento, persiste localStorage tunix.wakeWord.enabled + sync Supabasewindow.__wakeWord.pause() dentro de startCall). Re-arranca al colgarCooldown anti-doble-trigger: 5000ms Auto-restart on errors no-fatales (network/no-speech/aborted): 500-1500ms Permiso explícito getUserMedia al activar (mejor UX) Si permiso denegado → toggle OFF auto + persist OFF Cuando matchea: stop recognition → 250ms → startCall()
Porcupine Picovoice plugin Capacitor — wake word on-device nativo · Mucho más preciso (modelo entrenado) · Custom keyword "Hey TUNIX" requiere tier $9/mo · Foreground service Android para listening con app en background · Schema actual ya soporta engine='porcupine' (campo engine en wake_settings)
Cron diario 06:00 UTC (03:00 Chile) que consolida memoria episódica → semántica. Sonnet 4.6 dedupea turnos, extrae lecciones nuevas y las promueve a la base de conocimiento de largo plazo.
/api/tunix-rem-consolidate (Vercel 0 6 * * *, maxDuration 300s)forge_tunix_episodic_memory (hasta 500 turnos, 200 al prompt)forge_tunix_rem_reports (cycle_date, turns_analyzed, duplicates_merged, lessons_extracted jsonb, semantic_promotions jsonb, emotional_summary, raw_synthesis, duration_ms, status)forge_tunix_lessons (lesson, category, source_turns int[], embedding vector(768), confidence, active)gemini-embedding-001 con outputDimensionality:768 (mismo modelo que axis_entities)recall_lessons(query, category, top_k) — accede a lecciones consolidadas, cacheada 180s{
"duplicates_detected": int,
"new_lessons": [{lesson, category, confidence 0.6-1.0, source_turn_indices[]}],
"semantic_promotions": [string],
"emotional_summary": string,
"executive_summary": string
}
Solo lecciones genuinamente nuevas y útiles para próximas sesiones
NO promover lecciones triviales ("Patricio dijo hola")
NO duplicar lecciones ya en la lista activa
Si día rutinario → arrays vacíos (mejor calidad que cantidad)
Skip silencioso si no hay turnos (no falla el cron)
1 turno analizado, 0 lecciones promovidas, summary capturado, duration 5.4s, status ok ✓
Cron cada 15min que escanea silenciosamente y dispara push solo si hay algo accionable. Dedupe + cooldown por tipo evitan spam.
/api/tunix-ambient-tick (Vercel */15 * * * *, maxDuration 60s)forge_tunix_ambient_alerts con UNIQUE(alert_type, dedupe_key)scanMeetings: wsp_meetings + forge_meetings en próximos 60minscanOverdueTasks: forge_user_context kind=task status=open due_at < nowscanStaleResearch: forge_research_briefs pending_review >24hscanEmotional: avg energy ≤3.5 sostenido en 4h + emociones "agotado/frustrado"scanned: 1, new_pushed: 1
breakdown: { meetings:0, overdue:0, research:1, emotional:0 }
→ Detectó research brief stale real, mandó push ✓
TUNIX ahora puede ejecutar múltiples tools en paralelo respetando dependencias, en lugar de serializarlas. Para briefings con 5+ tools, el speedup es ~Nx.
/api/tunix-compose-workflow (POST, maxDuration 300s)Promise.all por layer{{step_id.path.al.valor}} en args resuelve outputs anterioresstep_resultsforge_tunix_workflow_runs (dag_spec, step_results, total_steps, succeeded, failed, total_parallel_layers, duration_ms, status)compose_workflow(workflow_name, steps[]) — máx 25 stepssteps: [
{ id:"m", tool:"query_meetings_today", args:{} },
{ id:"t", tool:"query_tasks", args:{status:"open"} },
{ id:"e", tool:"get_emotional_state", args:{} },
{ id:"r", tool:"recall_lessons", args:{category:"user_preference"} },
{ id:"d", tool:"deep_search",
args:{ query:"{{m.next_meeting.contact_name}}" },
depends_on:["m"] }
]
→ Layers: [["m","t","e","r"], ["d"]]
→ 5 tools, 2 layers = ~2× latencia en vez de 5×
compose_workflow [list_memories, recall_lessons] paralelo → ok:true, results map completo, 2 layers en 1 layer real (sin deps) ✓
Endpoint unificado para eventos de TODOS los canales de Patricio. Calcula priority via scoring + reglas, decide routing (push immediate / log silencioso), dedupea.
/api/tunix-event-router (POST ingreso, GET listado)EVENT_ROUTER_SECRET env (si no seteado, abierto — recomendado setear)forge_tunix_channel_events con UNIQUE(source_channel, external_ref) partial indexrequireInteraction:true + persistenciarecent_channel_events(top_k, min_priority) — "¿qué pasó mientras no estaba?"POST /api/tunix-event-router
Authorization: Bearer $EVENT_ROUTER_SECRET
{
"event_type": "wsp_inbound_vip" | "stripe_payment" | "email_critical" | etc,
"source_channel": "wsp" | "stripe" | "email" | "calendar" | "n8n" | "manual",
"title": "string ≤200",
"body": "string ≤1500",
"external_ref": "id externo para dedupe (opcional)",
"payload": { ... extras ... },
"hint_priority": "critical|high|normal|low (opcional, suma score)"
}
· n8n workflow WSP detecta mensaje VIP → POST router → push priority calculado · Stripe webhook cobro exitoso → POST router con hint_priority=high → push 💰 · Calendar event en 5min → POST router con scheduled hint → push high · Ambient agent dispara alertas → futuro: ruteado vía router (unificar)
UI de /talk simplificada a 1 solo toggle override + Wake Word (limpieza final 2026-05-25). El comportamiento "socio operativo" es el default permanente — no es opcional. Lo único toggleable es el override de calidad máxima.
| Toggle | Color | Qué activa |
|---|---|---|
| 🔥 Modo Furia DeepMind | rojo intenso con glow | OVERRIDE OPCIONAL. Fuerza TODO a Opus 4.7 puro (TUNIX/Búnker). Sonnet sidekick DESHABILITADO. Calidad máxima sin compromisos. |
| 👂 Wake Word "Hey TUNIX" | verde | Always-listening on-device. Decí "tunix" y arranca llamada sola. |
Cuando Furia OFF (estado normal), TUNIX Talk actúa siempre como socio operativo completo:
quick_via_sonnet) para queries simples — 3-8s, $0 extradelegate_to_claude_max) para razonamiento — 15-30s, $0 extraFuria OFF → modo socio default (Sonnet rápido + Opus profundo balanceado) Furia ON → todo a Opus 4.7 puro, sin Sonnet (calidad paranoide)
tunix_bridge_queue (from_modo, to_modo, text, urgent, delivered_at, acknowledged_at, status). Modos: code | bunker | talk | tungstenotunix_bridge_pickup(to_modo, limit) atómica con FOR UPDATE SKIP LOCKED/api/tunix-bridge: POST encola, GET ?to=talk pickup, POST ?ack=<id> confirmasend_to_code(text, urgent?). Hook UserPromptSubmit ejecuta scripts/bridge_inject_hook.py que lee cola y la inyecta como additionalContext en mi próximo promptTool quick_via_sonnet(task, context?) ruta a /api/tunix-claude-max con model:"sonnet". Mismo container, mismo OAuth Max — Sonnet 4.6 está incluido en plan Max, $0 extra. 3-8s vs 15-30s de Opus. Deshabilitado cuando 🔥 Furia ON.
| Agente | Tool | Modelo | Especialidad |
|---|---|---|---|
| TUNIX/Búnker | delegate_to_claude_max | Opus 4.7 | Razonamiento profundo, código complejo |
| Sonnet sidekick | quick_via_sonnet | Sonnet 4.6 | Queries rápidas, lookups, estados |
| DEVIX | spawn_agent({agent_id:"devix"}) | Sonnet 4.6 | Código, debug, code review |
| GOJAN | spawn_agent({agent_id:"gojan"}) | Sonnet 4.6 | Copy, marketing, variantes |
| TRUNKS | spawn_agent({agent_id:"trunks"}) | Haiku 4.5 | Investigación, scout, búsquedas |
| TUNIX/Code (yo) | delegate_to_pc | Opus 4.7 | Operaciones con MCPs ricos (Canva/Gmail/Drive/Playwright) |
Aclaración billing 2026-05-25: Todos los sub-agentes (DEVIX/GOJAN/TRUNKS) pasan por /api/agent-run → container Búnker con tu OAuth Max. No cobran API key como afirmé antes — me equivoqué. Lo único con API key Anthropic real son los crons puros (REM, META, ambient) que corren sin humano disparando.
Patricio: "TUNIX, mientras piensas propuesta Nico, poné a DEVIX a revisar bug X"
TUNIX Talk: spawn_agent({agent_id:"devix",task:"revisar bug X"})
+ delegate_to_claude_max({task:"propuesta Nico..."})
(paralelo, dos agentes a la vez, ambos $0 extra)
Sonnet sidekick disponible para preguntas rápidas mientras esperas ambos
Cuando terminen → TUNIX Talk resume ambos resultados
El toggle 🔀 Modo Híbrido fue eliminado. Su lógica (consultar recent_pc_activity proactivamente) ahora vive dentro de 🌉 Conversación Continua. Si activás Continua, ya tienes lo que antes hacía Híbrido + mucho más.
#hybridToggle (al lado del 👂 Wake Word). Persiste tunix.hybridOn en localStorage.hybridMode=true. Reglas: consultar recent_pc_activity ANTES de responder cualquier cosa sustantiva, asumir trabajo cruzado cuando Patricio menciona "lo que cambiamos", updates voz cortos tipo socio (no pasivo).recent_pc_activity(hours, include_diffs) en TUNIX. Combina 4 fuentes:
canquil37/forge-os (últimas N horas)forge_pc_jobs ejecutados por forge_pc_agent.py local (delegate_to_pc completados)forge_memory con pc_updated_at / synced_at recientesforge_tunix_improvements auto-mejoras aplicadasforge_tunix_talk_sessions cuando Patricio me pregunta qué pasó en Talk.Memory Forge (.md en /memory-forge/ + tabla forge_memory) Git (commits ↔ pulls automáticos en container) axis_entities (memoria semántica embeddings Gemini 768) forge_tunix_lessons (consolidaciones REM nocturnas) forge_tunix_talk_sessions (summaries + key_topics + pending_actions) forge_user_context (reminders + notas)
8am desayuno (Talk):
Patricio: "qué tengo hoy?"
TUNIX-Max: "Reunión 11am Nico. 3 tasks vencidas. Pendiente propuesta Equipo Salud."
Patricio: "armá draft propuesta basado en plantilla Kumelemu"
TUNIX-Max: trabaja 90s, push notif "Draft listo en sys_mind/drafts/"
10am PC abierto (yo):
Patricio: "muéstrame draft de Tunix"
Yo: leo archivo + git log → "acá está, observaciones..."
Editamos juntos con diff visual
12pm auto (Talk):
Patricio: "qué cambió Claude conmigo recién?"
TUNIX-Max: recent_pc_activity({hours:3}) → "refinaron 3 secciones..."
3pm vuelta PC (yo):
Yo: leo forge_tunix_talk_sessions → continúo donde quedó la voz
• Híbrido OFF (default): TUNIX responde solo con tools de Talk • Híbrido ON: TUNIX consulta PC activity proactivamente • Ambos canales gastan tu Max plan (humano dispara en los 2 lados) • Memory Forge es el "tercer carril" que persiste TODO entre sesiones
Decisión naming definitiva (2026-05-24): un solo TUNIX con 3 modos visibles + 1 overlay.
| Modo | Es | Modelo | Cuándo |
|---|---|---|---|
| TUNIX/Code | yo en VS Code (Claude Code SDK + MCPs ricos) | Opus 4.7 + Sonnet sidekick | DEFAULT cuando Patricio abre sesión conmigo |
| TUNIX/Búnker | container tunix-claude-agent en VPS Bunker MK4 | Opus 4.7 | Fallback always-on cuando Code no está |
| TUNIX Talk | voz en celu (app /talk, gpt-realtime cedar) | gpt-realtime + delegate | Cuando Patricio habla por audífonos |
| Modo Tungsteno | overlay autónomo | el que corresponda | Cuando Patricio activa explícito ("dale en tungsteno") |
| Capacidad | Code | Búnker |
|---|---|---|
| Bash/Read/Edit/Write/Glob/Grep | ✓ | ✓ |
| MCP Supabase + GitHub | ✓ | ✓ |
| MCP Canva/Gmail/Drive/Calendar/Stripe/Vercel/Playwright | ✓ | ❌ |
| Edita PC directo | ✓ | ❌ (sí el repo clonado) |
| Always-on | ❌ (necesita sesión) | ✓ |
TUNIX/Code, TUNIX/Búnker, TUNIX Talk → OAuth Max ($0 extra) Sonnet sidekick → OAuth Max plan (Sonnet incluido, $0 extra) DEVIX/GOJAN/TRUNKS sub-agentes → OAuth Max via container Búnker ($0 extra) (corregido 2026-05-25: NO usan API key — pasan por /api/agent-run → container Búnker) Crons puros (REM, META, ambient) → API key Anthropic (patrón bot sin humano)
Memoria reference: _core/reference_tunix_identity_split.md + _core/reference_oauth_max_vps_protocol.md
TUNIX Talk al iniciar llamada detecta automáticamente si hay sesión TUNIX/Code activa y avisa al usuario. Soporta múltiples ventanas VS Code abiertas simultáneamente sin confundirse.
tunix_code_sessions (session_token UNIQUE, workspace_path, workspace_label, pid, hostname, started_at, last_seen_at, ended_at, prompt_count, metadata)tunix_code_sessions_active: filtra last_seen_at > now() - 5min AND ended_at IS NULLtunix_code_heartbeat(token, path, label?, pid?, hostname?): upsert atómico por session_token, incrementa prompt_count, refresca last_seen_atscripts/bridge_inject_hook.py escribe heartbeat en cada UserPromptSubmit. Si pasan 5min sin que escribas → la sesión cae fuera de la view activa automáticamente.1. PRIMERA opción: input.session_id de Claude Code hook → token "cc_<sid>" 2. FALLBACK: hash md5(cwd + parent_pid) → token "fb_<hash>" · Cada ventana VS Code tiene PID distinto → ventanas distintas, tokens distintos · Workspaces distintos también generan tokens distintos 3. workspace_label = última carpeta del path (ej: "forge-os")
Llamada al inicio de cada conversación. Devuelve { active_sessions, sessions, mode, saludo_sugerido }:
| Sesiones activas | Mode | Saludo de TUNIX Talk |
|---|---|---|
| 0 | bunker | "Hola Pato, estoy en modo Búnker porque no tengo acceso a TUNIX/Code en este momento." |
| 1 | code | "Hola Pato, estoy en modo Code conectado a {workspace_label}. ¿En qué andamos?" |
| N ≥ 2 | code | "Hola Pato, tengo {N} ventanas TUNIX/Code activas ({labels}). Si quieres mandar mensaje, dime a cuál." |
metadata.target_workspace opcional (para futuro filtrado más estricto)Abrís ventana VS Code → primer prompt tuyo escribe heartbeat → aparece en view activa Trabajás 5min sin escribir → sesión cae fuera de view automático (no necesita cleanup) Cerrás VS Code → eventualmente sale de view por timeout (5min) Reabrís misma carpeta → si Claude Code reusa session_id, sigue mismo token. Si no, token nuevo.
· Vos en VS Code Forge OS + en cama con Talk → check_code_session devuelve 1, saludo "modo Code conectado a forge-os" · Vos sin VS Code, manejando con Talk → 0 sessions, saludo "modo Búnker" · Vos con 3 ventanas (forge-os, tensormed-cl, emabel) → 3 sessions, saludo lista todas + pregunta cuál
TUNIX/Búnker (container VPS Bunker MK4, Opus 4.7 OAuth Max) ahora tiene paridad operativa con TUNIX/Code en lo que respecta a herramientas críticas. Está armado el sistema de approvals via voz y los sub-modos tungsteno audio on/off.
media.tungsteno.tech/tunix-agent/health)| MCP | Env requerida | Estado |
|---|---|---|
| Supabase (full read+write) | SUPABASE_ACCESS_TOKEN | ✓ |
| GitHub (issues, PRs) | FORGE_GH_PAT | ✓ |
| Playwright (browser headless) | PLAYWRIGHT_MCP_ENABLED=1 | ✓ |
| Vercel (deploys, logs) | VERCEL_TOKEN | ✓ |
| Stripe | STRIPE_API_KEY | ⏸️ diferido |
| Gmail/Drive/Calendar | Google OAuth refresh | 📋 Fase 2 |
Decisión 2026-05-25: las aprobaciones destructivas NUNCA se confirman por voz — un "sí" verbal accidental podría disparar git push --force u otra acción irreversible. Patricio toca un botón físico en pantalla (panel rojo con APROBAR/DENEGAR). La voz Cedar SOLO lee informativo el pedido.
Definición canónica destructivos en _core/reference_destructive_actions_canonical.md — 6 categorías: eliminar datos, sobreescribir historia/config, servicios prod, terceros, dinero, multi-user. Aplicable a TUNIX/Code, TUNIX/Búnker y sub-agentes (que escalan al padre, no al user directo).
bash scripts/bunker_request_approval.sh "<acción>" "<por qué>" "<plan>" # Exit code: 0=approved · 1=denied · 2=expired (3min sin respuesta) · 3=error
tunix_approval_requests (request_token UUID, action, why, plan, status, approved, decided_at, expires_at TTL 5min)tunix_approval_decide(token, approved, by, note) y tunix_approval_status(token)/api/tunix-approval con POST action=request|decide y GET ?token=Xrespond_to_approval(request_token, approved, decision_note?)metadata.kind=approval_request y instruye al usuario claramente con el token para que el respond_to_approval funcionebunker_announce.shbash scripts/bunker_announce.sh "<texto>" [--urgent] [--silent] # default: voz Talk Cedar + log a .tunix_live.md # --silent: SOLO log (audio off) # --urgent: push notif + intenta abrir Talk si app cerrada
Cuando Patricio activa modo Tungsteno via Talk, TUNIX Talk PREGUNTA:
"¿lo hago con audio ON (te voy contando todo en vivo) o audio OFF (solo log VS Code, te resumo al final por voz)?"
| Sub-modo | Comportamiento |
|---|---|
| tungsteno + audio=on | Búnker llama bunker_announce.sh sin --silent en cada hito → voz Cedar cuenta progreso en vivo |
| tungsteno + audio=off | Búnker llama con --silent → solo escribe .tunix_live.md en VS Code. Al final SI llama sin --silent para reporte audio cerrando |
TUNIX Talk pasa el flag agregando al task: "...\n\nMODO: tungsteno · TUNGSTENO_AUDIO=on|off". Búnker lee el sufijo y aplica.
El DEFAULT_SYSTEM_PROMPT del container incluye el mismo protocolo de feedback_tungsteno_mode.md:
bunker_request_approval.sh incluso en tungstenoPatricio (Talk celu): "Tunix, refactoreame tunix-ops.html en tungsteno" TUNIX Talk: "¿audio on o off?" Patricio: "on" TUNIX Talk → delegate_to_claude_max(task="refactor tunix-ops.html\n\nMODO: tungsteno · TUNGSTENO_AUDIO=on") Búnker arranca → llama bunker_announce.sh "Voy a leer tunix-ops, son ~600 líneas" ↓ voz Cedar: "Voy a leer tunix-ops, son seiscientas líneas" Búnker edita líneas 45, 120, 350 → bunker_announce.sh "Refactoré 3 bloques principales" ↓ voz Cedar: "Refactoré tres bloques principales" Búnker quiere git push --force (necesita borrar commit anterior por bug grave) → bunker_request_approval.sh "git push --force" "fix bug crítico commit roto" "git reset HEAD~1 + push" ↓ voz Cedar urgente: "Pato, necesito permiso para git push --force, motivo bug..." Patricio: "sí, dale" ↓ TUNIX Talk → respond_to_approval(token, true) ↓ script en Búnker recibe exit 0 Búnker hace el push → bunker_announce.sh "Listo, deploy en curso" ↓ voz Cedar: "Listo, deploy en curso" Búnker cierra → bunker_announce.sh "Cerré: ✅ refactor ok · ⚠️ revisá test X" ↓ voz Cedar: reporte final
/api/tunix-realtime-tools → token efímero OpenAI + tools spec + system prompt /api/tunix-tool-exec → ejecutor tools (30+), 300s timeout /api/tunix-claude-max → proxy JSON al container VPS, 300s timeout /api/tunix-claude-max-stream → proxy SSE al container, streaming token-by-token (ver 6.7) /api/tunix-deploy-watch → post auto-mejora, pollea Vercel READY + push /api/talk-session-end → persistencia sesión + summary Haiku + embedding + reflexión /api/agent-spawn → crea task sub-agente /api/agent-run → worker que ejecuta task vs container Max /api/agent-status → polling estado tasks (anuncia/stuck) /api/push → web push VAPID a forge_push_subscriptions /api/apk-latest → manifest última APK publicada
| Tabla | Para qué |
|---|---|
forge_agents | Definición TUNIX/DEVIX/GOJAN/TRUNKS con system_prompt + model |
forge_agent_tasks | Queue sub-agentes (queued/running/done/failed/stuck) |
forge_pc_jobs | Queue tareas para PC agent (RPC claim_pc_job) |
forge_tunix_talk_sessions | Sesiones Talk con summary + key_topics + pending_actions + embedding |
forge_tunix_tool_audit | Log de cada tool call (compliance + debug) |
forge_tunix_improvements | Auto-mejoras propuestas/aplicadas/falladas |
forge_memory | Memory Forge espejo (canon local sync auto) |
forge_user_context | Reminders + notas (donde viven los reminders REALES, no axis_reminders) |
forge_patricio_emotional_log | Estado emocional para adaptar tono |
forge_push_subscriptions | Endpoints VAPID para push notif al celu |
axis_entities | Personas/empresas/conceptos del holding (768 dims Gemini) |
axis_semantic_memory | Hechos curados del holding |
axis_episodic_memory | Memoria episódica AXIS (input + combined + output embeddings) |
/mnt/forge/projects/tunix-claude-agent//agent :8765 detrás de Traefik (media.tungsteno.tech/tunix-agent)/mnt/forge/data/tunix-claude-agent/home:/root (incluye .credentials.json)/workspace/forge-os clonado con git pull --ff-only automático antes de cada queryscripts/tunix-claude-agent/server.mjs (lección 2026-05-23: docker cp es ephemeral, el código DEBE estar en repo)| Variable | Para qué |
|---|---|
ws | WebSocket OpenAI Realtime activo |
mediaStream | Tracks browser (modo web getUserMedia) |
nativeMicPluginActive | bool del bypass APK (plugin ForgeRecording) |
audioQueue + isPlaying | Buffer PCM TUNIX hablando |
pendingToolCalls | Contador para response.create único (evita freeze con tools paralelas) |
callPaused | Estado pausa (WS abierto, mic muteado) |
transcriptTurns | Array tracking para persistencia + summary post-sesión |
userMsgEl / tunixMsgEl | Placeholders DOM para orden cronológico correcto |
agentPollInterval | setInterval 6s para polling forge_agent_tasks |
Cuando algo falla o quieres extender.
| Síntoma | Causa probable + fix |
|---|---|
| "Mic: NotReadableError" en APK | WebView Android 16 rompe getUserMedia. APK 39+ usa bypass plugin nativo. Si pasa de nuevo, Settings → Apps → Forge OS → Force Stop, reabrir. |
| TUNIX no transcribe lo que decís | Probable session.update con parámetro inválido rechazó toda la config. Revisar consola WebView. Hard reload Talk. |
| TUNIX dice "no tengo info" sin buscar | Bug del system prompt. Debería ejecutar cascada deep_search → forge_global_search antes. Decile "ejecutá deep_search primero". |
| Tool error: "Unexpected token A is not valid JSON" | Vercel timeout (FUNCTION_INVOCATION_TIMEOUT). Subir maxDuration en vercel.json a 300 (max Pro). |
| delegate_to_claude_max falla con "401 Invalid auth" | OAuth Max expirado en container. Re-sync con scp ~/.claude/.credentials.json root@VPS:/tmp/ && ssh root@VPS "docker cp /tmp/creds.json tunix-claude-agent:/root/.claude/.credentials.json && docker restart tunix-claude-agent" |
| Tools paralelas hacen freeze a TUNIX | Verificar pendingToolCalls contador en talk.html. Debe enviarse response.create UNA sola vez cuando todas terminan. |
| Auto-mejora dice "ya aplicado" pero no hubo commit | El cambio ya estaba en sesiones previas. Mirar forge_tunix_improvements.commit_sha. Si NULL → Claude Max no modificó. Probar con observación más específica. |
| search_entities falla con "different vector dimensions" | Pasaste embedding 1536 contra tabla 768. embedQuery debe pedir dims=768 para axis_*. Ver lección 6.3. |
Decisiones técnicas y bugs resueltos quedan en _core/ y forge-os/ del Memory Forge:
feedback_webview_mic_bypass_2026_05_23.md — bypass plugin nativofeedback_container_persistence_2026_05_23.md — docker cp es ephemeralreference_semantic_search_capabilities.md — 25 tablas + cascada 4 nivelesproject_talk_to_tunix.md — diseño full del Talkfeedback_tungsteno_mode.md — protocolo modo autónomofeedback_anti_spaghetti_construction.md — anti-parchesfeedback_priority_80_20.md — clientes vs Forge OSapi/tunix-tool-exec.js dentro del objeto TOOLSapi/tunix-realtime-tools.js dentro de REALTIME_TOOLS (name, description, parameters JSON schema)forge_agents con id slug, name, role, model (haiku/sonnet/opus), system_promptspawn_agent.agent_id en tunix-realtime-tools.jsDecile a TUNIX "mejorate y agregame [capacidad X]". Va a usar tunix_self_improve, te propone plan, vos confirmás, aplica + commit + push + Vercel deploya. Mirá sección 2.5.
Todo lo que cambió en TUNIX Talk durante la sesión maratón del 25-may. Auditable a nivel ingeniería.
Antes: TUNIX no recordaba conversaciones previas a menos que llamaras manualmente search_talk_history. Ahora: arquitectura 3 capas — ver sección 2.3.
tunix_talk_recall(emb, hours, k, min_sim) — vector search Gemini 768d sobre forge_tunix_episodic_memory.combined_embedding con recency boostrecall — realtime + executor. Devuelve top-5 turnos exactos. ~1.5s. Cacheable 30s.memory_librarian — realtime + executor. Sonnet sintetiza top-15 fragmentos. ~6s. Cacheable 60s./api/tunix-realtime-tools ahora inyecta últimas 5 sesiones 72h con tiempo relativo, turnos, summary al system promptSección al tope del system prompt (regla #1 innegociable). Cubre TODOS los cerebros y agentes, no solo Sonnet/mails.
background_result mientras hablas otra cosa → interrumpe-te a vos mismoWatchdog técnico extendido: ahora cubre streamClaudeMax (SSE) y background_result (puente). El watchdog ya NO se desarma al primer audio.delta si el tool result llegó hace menos de 4s (evita filler tipo "dame unos segundos" disarmando el escalado de 5/10/15s).
11 tools de Google registradas en realtime + executors que proxyean a /api/google-tools:
gmail_send, gmail_search, gmail_read, gmail_modifycalendar_today, calendar_list, calendar_create, calendar_update, calendar_deletedrive_search, drive_read, drive_recentFlujo Gmail con regla arquitectónica: Sonnet redacta vía quick_via_sonnet ($0) → TUNIX lee draft completo en voz → vos decís "envíalo" verbal → TUNIX dispara gmail_send (ejecutor liviano). gpt-realtime NUNCA genera contenido — solo orquesta + ejecuta.
histReveal one-shot (sin loop)session_id + precarga últimos 6 turnos como conversation.item.create antes del saludo/api/tunix-talk-auto-rename — respeta renames manuales (title_source='manual')Schema cambios: forge_tunix_talk_sessions ahora tiene columnas title, title_source, manually_renamed_at, deleted_at. Tipo id: uuid → text (para usar talk_<timestamp> directo).
RPCs nuevos: tunix_talk_session_rename, tunix_talk_session_delete, tunix_talk_session_touch.
Cada respuesta de TUNIX muestra chips visibles con los cerebros que contribuyeron:
🎙️ Realtime 🧠 Sonnet 4.6 ⚡ Gmail 14:32:08 · ⚡500ms · 🎙️3.2s
Mapeo via inferToolModel(name, args):
| Tool | Badge |
|---|---|
quick_via_haiku | 🧠 Haiku 4.5 |
quick_via_sonnet | 🧠 Sonnet 4.6 |
delegate_to_claude_max (quick=false) | 🧠 Opus 4.7 |
delegate_to_claude_max (quick=true) | 🧠 Sonnet 4.6 |
run_in_background model=opus | 🧠 Opus 4.7 (bg) |
spawn_agent agent=DEVIX | 🤖 DEVIX |
| Tools Google/WSP | ⚡ Gmail / ⚡ Calendar / ⚡ Drive / ⚡ WhatsApp |
Queries puras (query_*, search_*, list_*) | sin badge (sin LLM, solo SQL) |
Array {name, model, kind} también se persiste en forge_tunix_episodic_memory.tools_used para auditoría posterior.
Ver detalle completo en 6.5b. Resumen:
gpt-4o-mini-transcribe → gpt-4o-transcribe (~10× precisión español, +$1.50/mes)transcription.prompt (hint léxico, $0 extra)Causa real: env var GMAIL_TARGET_ACCOUNT en Vercel apuntaba a pc.scholer@gmail.com (que solo tiene scope gmail.readonly). El refresh token de canquil27tm@gmail.com SÍ tenía gmail.send.
Fix: hard-code DEFAULT_GMAIL_ACCOUNT = 'canquil27tm@gmail.com' en api/google-tools.js, ignorando el env override.
forge_tunix_talk_sessions
Causa: talk-session-end.js hacía session_id.replace(/^talk_/, '') al cerrar, mientras episodic-save.js guardaba con prefijo. Cada llamada generaba 2 filas (una con prefijo durante la sesión, otra sin prefijo al cerrar).
Fix: removido el .replace(). Backfill SQL consolidó 27 sesiones duplicadas mergeando datos en la fila con prefijo y borrando la bare.
Caso 25-may: TUNIX dijo "dame unos segundos", llegó audio.delta → watchdog se desarmó. Después Sonnet devolvió en 5s pero TUNIX quedó mudo 1m 52s hasta que Patricio insistió.
Fix: disarmResponseWatchdog() ahora chequea Date.now() - lastToolResultAt < 4000 — si el audio llega dentro de los 4s post-tool, no desarma (asume filler), mantiene presión hasta contenido real.
api/google-tools.js — hard-code default accountapi/talk-session-end.js — removed cleanId replaceapi/talk-sessions.js (NEW) — CRUD historial (GET list/detail · PATCH rename · DELETE soft)api/tunix-talk-auto-rename.js (NEW) — Haiku auto-rename en checkpointsapi/tunix-episodic-save.js — UPSERT via RPC tunix_talk_session_touchapi/tunix-realtime-tools.js — 13 tools nuevas (Gmail/Calendar/Drive + recall + memory_librarian + recent_talk_sessions), inyección Capa 1 memoria 72hapi/tunix-tool-exec.js — executors nuevos + callGoogleTool helper + embedQuery usado en recall/librarian + cache TTLspublic/talk.html — overlay historial + reanudar + STT upgrade + LEXICON + Ley Proactividad + watchdog tracking + badges modelo + bridge background_result armado watchdogCommits: 9128f09, dc43045, a0de014, 81222e2, f01b9b9, a8d2276, d96cf8b, 576027c, 09bc101, 37d4055, 84c7b54.
project_voice_fingerprint_pendiente.md.gpt-5-realtime: si OpenAI libera versión Realtime de GPT-5, re-evaluar upgrade del modelo principal.Después de probar 4 arquitecturas distintas en un día (gpt-realtime full → mini → híbrido STT+Sonnet+TTS → Gemini 2.5 Flash Live), llegamos a la solución final. Acá la documentación auditable de la arquitectura definitiva.
Modelo: gemini-2.5-flash-native-audio-preview-09-2025. Native audio voice-to-voice end-to-end. Latencia ~600-800ms. Function calling async (no bloquea audio). $0.005/min input + $0.018/min output.
Patricio (audífonos bluetooth APK)
↓ mic capture (ForgeRecording plugin nativo Android bypass WebView)
↓ PCM Int16 resampled a 16kHz
↓ base64
WebSocket directo wss://generativelanguage.googleapis.com (single-user, key en query param)
↓ Gemini 2.5 Flash Live procesa native audio
↓ decide:
├─ Conversación trivial → responde directo (audio PCM 24kHz)
├─ Tool con datos estructurados → llama tool (recall, gmail_search, etc)
├─ Tool con generación → quick_via_sonnet / quick_via_haiku
└─ Razonamiento profundo → delegate_to_claude_max (Opus 4.7, ASYNC bg)
↓ audio output PCM 24kHz por chunks
Frontend: scheduling continuo Web Audio API (sin gaps) → audífonos
El endpoint /api/gemini-live-config exporta el array REALTIME_TOOLS (definido en api/tunix-realtime-tools.js) y lo mapea al formato Gemini ({name, description, parameters}). Mismo schema JSON, solo se quita el wrapper {type:'function'}.
Las 63 tools cubren: Gmail (send/search/read/modify), Calendar (today/list/create/update/delete), Drive (search/read/recent), Memoria (recall, memory_librarian, recent_talk_sessions, search_*, read_memory), WSP (send_wsp_text/audio, wsp_search_history), Tareas/Reminders (create/mark/cancel), Brain delegates (quick_via_haiku, quick_via_sonnet, delegate_to_claude_max), Agentes (spawn_agent, check_agent_done), Puente (send_to_code, check_code_session), Sistema (sync_forge_now, tunix_self_improve, etc).
Tools largas (>5s estimadas) NO bloquean Gemini. Frontend intercepta:
const LONG_TOOLS = new Set([ 'delegate_to_claude_max', // Opus 15-30s 'memory_librarian', // Sonnet 6-10s 'run_in_background', // variable 'spawn_agent', // DEVIX/GOJAN/TRUNKS 'tunix_self_improve', // edit codebase ]);
Cuando Gemini llama una de éstas, frontend:
{ok:true, status:'in_progress', message:'dispatched bg, sigo trabajando en paralelo'}dispatchToolBackground) sin esperarclientContent con instrucción "INTERRUMPÍ amablemente y léele a Patricio este resultado"Esto restaura el patrón multi-agente real: Sonnet/Opus/agentes corren en paralelo mientras vos puedes seguir conversando con Gemini sobre otras cosas.
Problema: cuando Gemini delega un pedido largo a Sonnet, puede parafrasearlo y perder matices. Solución en dos capas:
Regla #2 explícita: "Cuando delegues a quick_via_sonnet/delegate_to_claude_max/quick_via_haiku, el campo 'task' debe contener la cita TEXTUAL de Patricio entre comillas + tu instrucción. NUNCA parafrasees."
Función augmentArgsWithVerbatim(name, args) intercepta toolcalls de tipo FIDELITY_TOOLS y prependea automáticamente:
[Patricio dijo TEXTUAL]: "<transcripción literal>" [Instrucción de Gemini para vos]: <task original> NUNCA ignores el texto literal. Si Gemini contradice algo, priorizá el literal.
Sonnet/Opus siempre reciben los matices exactos aunque Gemini se descuide.
TUNIX Talk SIEMPRE vive en modo Búnker (container always-on VPS). No existe "modo Code" alternativo. TUNIX/Code es OTRO TUNIX (el que vive en VS Code de Patricio cuando programa).
send_to_code = BUZÓN, no ejecución paralelaLo usa SOLO para 4 casos específicos:
Para CUALQUIER otra cosa (Gmail, Calendar, Drive, Supabase, GitHub, código, redacción, análisis), Talk lo hace directo desde Búnker via sus 63 tools. Prohibido decir: "voy a hacerlo en modo Code", "esto desde tu PC", "necesito VS Code".
Hexágono industrial 120px tipo tuerca de tungsteno con:
#fafafa→#71717a con sombra offset abajo = bajorrelieve)| Control | Función | Persiste en |
|---|---|---|
| 🎤 Voz | 8 opciones: Charon/Fenrir/Orus/Puck/Zephyr (masc), Aoede/Kore/Leda (fem) | gem.voice |
| 🔥 Furia DeepMind | Override que fuerza Opus para cualquier tarea no-trivial | gem.furia |
| 👂 Wake Word | Always-listening "Hey TUNIX" (UI lista, lógica pendiente) | gem.wakeWord |
| ✋ Barge-in agresivo | Deshabilita half-duplex en APK para interrumpir libremente | gem.bargeIn |
| 🎭 Tono (0-100) | Calibra empatía: temperature 0.5-1.2 + instrucciones tono en prompt | gem.empathy |
| 🎛️ Tuner | VAD silencio 200-2000ms + sensibilidad inicio/fin LOW/MID/HIGH | gem.silence/start/end |
| Setup | Costo mensual (15 min/día) | Razonamiento | Fluidez |
|---|---|---|---|
| gpt-realtime full (anterior) | $60-150 | GPT-4o tier | ★★★★★ |
| gpt-realtime mini (probado) | $30-50 | GPT-4o-mini (confabula) | ★★★★ |
| Híbrido STT+Sonnet+TTS (probado) | $15 | Sonnet 4.6 (mejor) | ★★ (lento, +1.5s) |
| Gemini 2.5 Live (actual) | $5-15 | Flash Live + delegate Opus on-demand | ★★★★ |
Trade-off real: perdiste razonamiento "in-context puro" de gpt-realtime, ganaste razonamiento "real cuando se necesita" via Opus 4.7 (>>GPT-4o). Para uso operativo (95% tools) el setup nuevo es objetivamente superior. Cost down 80%, calidad delegada up.
Después del rewrite a Gemini Live, agregamos 4 capacidades que cierran el gap real con Claude Code en VS Code. TUNIX Talk pasa de asistente reactivo a socio operativo proactivo multimodal.
Cuando TUNIX ejecuta una tarea multi-paso, Patricio ve los pasos en PANTALLA con checkboxes en vivo (⏳/🔄/✅/❌) mientras escucha la voz.
publish_plan({title, steps:[{label}]}) — TUNIX publica el planupdate_plan_step({step_idx, status, note}) — TUNIX actualiza progresoSet UI_TOOLS en frontend intercepta estos toolcalls antes de llegar al backend. renderPlan() actualiza el panel violeta industrial. Auto-hide 4s después que todos los pasos estén done.
Patricio: "mandá mail a Nico Luna con la propuesta" → Plan visible se muestra:
📋 Mail a Nico Luna 🔄 Redactar borrador con Sonnet ⏳ Leerte el draft completo ⏳ Esperar tu OK verbal ⏳ Enviar via gmail_send
Conforme TUNIX avanza y habla, los checkboxes se marcan. Patricio sabe exactamente en qué va sin tener que preguntar.
Botón 📷 Vision en panel de controles. Click → file picker con capture="environment" (abre cámara directa en APK) o galería. Imagen seleccionada → resize lado largo 1024px JPEG quality 85 → base64 → enviado al WS como realtimeInput.video + system prompt instruyendo a Gemini describir/responder.
Patricio toca 📷 Vision
↓ file picker (cámara o galería)
↓ Canvas resize 1024px + JPEG 85%
↓ base64
ws.send({realtimeInput: {video: {data, mimeType: 'image/jpeg'}}})
ws.send({clientContent: {turns: [{role:'user', parts:[{text:'[Patricio envió imagen, describila/respondé]'}]}], turnComplete:true}})
↓ Gemini 2.5 Flash Live multimodal procesa
↓ audio respuesta describiendo lo que ve
Cierra el gap multimodal vs Claude Code que sí tiene vision nativa.
Hoy si pedís a Opus que analice algo (~20s) tienes que mantener la llamada abierta. Nuevo: puedes colgar inmediato y TUNIX te llama de vuelta al celu cuando tiene el resultado.
run_in_background ahora acepta param notify_via_push: true. Frase típica: "anda pensando esto tranquilo y avísame al celu cuando esté listo" → TUNIX dispara la tool con notify=true → te confirma "dale Pato, te aviso al celu cuando esté listo, puedes colgar".
Gemini llama run_in_background({task, model:'opus', notify_via_push:true})
↓ INSERT forge_tunix_background_tasks (con notify_via_push:true)
↓ fire-and-forget POST /api/tunix-bg-runner
[Patricio cuelga la llamada]
↓ runner trabaja en background (Opus 15-30s)
↓ al terminar:
├─ INSERT tunix_bridge_queue (kind: background_result)
└─ POST /api/push → push notif celu con CTA URL /talk?bg_task=<token>
Patricio recibe notif → tap →
↓ /talk se abre + detecta query param bg_task
↓ auto-startCall() 800ms después
↓ bridge poller entrega resultado al Gemini → leído proactivo
Schema cambio: forge_tunix_background_tasks.notify_via_push boolean (ALTER aplicado).
TUNIX deja de ser reactivo. Triggers configurables que disparan push notif al celu sin que vos abras nada.
forge_tunix_proactive_hooks: id, hook_type, label, enabled, schedule(jsonb), config(jsonb), last_fired_at, next_fire_at/api/tunix-proactive-tick — cron cada 5 min (registrado en vercel.json). Checkea hooks enabled donde se cumple condición + dispara push + actualiza last_fired/api/tunix-proactive-hooks — CRUD (GET list, POST create, PATCH toggle/edit, DELETE)| Tipo | Trigger | Estado |
|---|---|---|
| morning_briefing | Hora fija + días de semana (ej 8:30am lun-vie) → resumen del día | ✅ Implementado (deshabilitado por default, Patricio activa) |
| urgent_contact | Mail/WSP de contacto VIP → llamada inmediata | 🔧 Stub, lógica pendiente |
| meeting_alert | 5 min antes de reunión marcada urgent | 🔧 Stub, lógica pendiente |
| task_done | BG task completa cuando APK cerrada (sin notify_via_push) | 🔧 Stub, lógica pendiente |
forge_internal_meetings (reuniones hoy) + axis_reminders (reminders pendientes 24h) + wsp_audio_inbox (sin responder 12h)tunix_bridge_queue kind=proactive_briefing/talk?proactive=<id> → auto-startCall 800mscurl -X PATCH https://forge.tungsteno.tech/api/tunix-proactive-hooks?id=<HOOK_ID> \
-H "Content-Type: application/json" -d '{"enabled":true}'
O via Talk: "TUNIX, activá el briefing matutino" (cuando agreguemos la tool wrapper).
El 25-may probamos 4 arquitecturas distintas hasta llegar a la final. Lecciones grabadas:
| Hora | Stack | Resultado | Decisión |
|---|---|---|---|
| 09:00 | gpt-realtime full + safeguards | $11 quemados en 2 días por loop bug | Probar mini |
| 14:00 | gpt-realtime-mini | Confabulación grave en saludos | Probar híbrido |
| 15:30 | Híbrido Deepgram+Sonnet+TTS-1 | Funciona pero +1.5s latencia + ruido entrecortado | Probar Gemini Live |
| 17:00 | OpenAI Realtime transcribe (Deepgram WS close 1006 en APK) | Fallback intermedio | Migrar a Gemini |
| 18:30 | Gemini 2.5 Flash Live native audio | Funciona OK pero falta capacidades | Iterar mejoras |
| 19:30 | + Affective dialog (close 1007 unknown field) | Bug Gemini API, remover field | Calibrar via temperature + prompt |
| 20:00 | + Botón cubo isométrico Tungsteno | Patricio no convence | Rediseño hexágono 120px |
| 20:30 | + Identidad Búnker permanente + ruteo tools directas | Funciona | Confirmar arquitectura final |
| 21:00 | + Fidelidad verbatim a Sonnet/Opus | Cierra problema paráfrasis | Done |
| 22:00 | + Parte 9 docs auditables | Patricio aprueba | Continuar mejoras |
| 23:00 | + Fix celu bloqueado (regresión) + Plan Visible + Vision + WhatsApp Async + Hooks proactivos (4 mejoras) | 4/4 desplegadas | Documentar Parte 10 |
Lecciones grabadas en memoria: forge_tunix_lessons + decisión final = Gemini 2.5 Flash Live + delegate Opus on-demand.
| Capacidad | Claude Code (VS Code) | TUNIX Talk (Gemini Live) | Gap |
|---|---|---|---|
| Razonamiento Opus 4.7 | ✅ directo | ✅ via delegate_to_claude_max | — |
| Function calling complejo | ✅ 60+ tools | ✅ 65 tools async | — |
| Memoria persistente | ✅ Memory Forge | ✅ 3 capas + Memory Forge | — |
| Lectura repo/archivos | ✅ directa | ✅ via Búnker container | — |
| Editar código | ✅ Edit tool live | ⚠️ via send_to_code (buzón asíncrono) | Moderado |
| Plan visible (TodoWrite) | ✅ | ✅ NUEVO 25-may | — |
| Vision multimodal | ✅ | ✅ NUEVO 25-may | — |
| Sub-agentes paralelos | ✅ Agent tool | ✅ spawn_agent + dispatchBg | — |
| Ejecución shell | ✅ Bash live | ✅ via Búnker delegate | — |
| Voz natural | ❌ texto only | ✅ NATIVO | Talk gana |
| Always-on 24/7 | ❌ solo activo en VS Code | ✅ Búnker container | Talk gana |
| Hooks proactivos | ❌ no inicia conversación | ✅ NUEVO 25-may | Talk gana |
| WhatsApp async (notif al colgar) | n/a | ✅ NUEVO 25-may | Talk único |
Conclusión auditable: TUNIX Talk al 25-may medianoche tiene paridad total con Claude Code para uso operativo, + 4 capacidades únicas (voz nativa, always-on 24/7, hooks proactivos, WhatsApp async). El único gap real restante: edición de código en vivo (no es uso típico de Talk, se cubre vía send_to_code → VS Code).