Metadata-Version: 2.4
Name: moyter
Version: 0.1.36
Summary: Orquestador local de agentes autónomos: razonamiento ReAct, memoria episódica con utilidad y ejecución sandboxed.
Project-URL: Homepage, https://moyter.com
Project-URL: Repository, https://github.com/rmoya81/moyter
Author: Rubén Moya Morata (moyter)
License-Expression: MIT
License-File: LICENSE
Keywords: agents,autonomous,llm,local-ai,ollama,react
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.7
Requires-Dist: structlog>=24.1
Requires-Dist: tenacity>=8.3
Provides-Extra: browser
Requires-Dist: playwright>=1.40; extra == 'browser'
Provides-Extra: dev
Requires-Dist: chainlit>=1.3; extra == 'dev'
Requires-Dist: chromadb>=0.5; extra == 'dev'
Requires-Dist: docker>=7.0; extra == 'dev'
Requires-Dist: mcp>=1.2; extra == 'dev'
Requires-Dist: playwright>=1.40; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: python-telegram-bot>=21.0; extra == 'dev'
Requires-Dist: rich>=13.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: sqlalchemy>=2.0; extra == 'dev'
Provides-Extra: full
Requires-Dist: chainlit>=1.3; extra == 'full'
Requires-Dist: chromadb>=0.5; extra == 'full'
Requires-Dist: docker>=7.0; extra == 'full'
Requires-Dist: mcp>=1.2; extra == 'full'
Requires-Dist: playwright>=1.40; extra == 'full'
Requires-Dist: python-telegram-bot>=21.0; extra == 'full'
Requires-Dist: rich>=13.0; extra == 'full'
Requires-Dist: sqlalchemy>=2.0; extra == 'full'
Provides-Extra: mcp
Requires-Dist: mcp>=1.2; extra == 'mcp'
Provides-Extra: memory
Requires-Dist: chromadb>=0.5; extra == 'memory'
Provides-Extra: sandbox
Requires-Dist: docker>=7.0; extra == 'sandbox'
Provides-Extra: sql
Requires-Dist: sqlalchemy>=2.0; extra == 'sql'
Provides-Extra: telegram
Requires-Dist: python-telegram-bot>=21.0; extra == 'telegram'
Provides-Extra: tui
Requires-Dist: rich>=13.0; extra == 'tui'
Provides-Extra: ui
Requires-Dist: chainlit>=1.3; extra == 'ui'
Description-Content-Type: text/markdown

<p align="center">
  <img src="docs/moyter-logo.jpg" alt="Moyter" width="180">
</p>

# Moyter

**Asistente autónomo local que no se engaña a sí mismo.**

Moyter ejecuta tareas de análisis, cómputo y construcción mediante un bucle de
razonamiento (plan → acción → observación → crítica) sobre modelos locales
(Ollama) o cloud. Su rasgo distintivo no es lo que sabe hacer: es que **cada
cosa que afirma haber hecho está verificada contra algo que se ejecutó de
verdad**.

La idea de fondo: *mover la inteligencia del modelo al código*. Un modelo puede
equivocarse razonando; el código ejecutado no miente sobre lo que imprime.
Moyter ancla las conclusiones a esa evidencia.

> **Estado:** alpha. Publicado en PyPI, API en evolución.

---

## Por qué Moyter

La mayoría de los frameworks de agentes optimizan por *hacer cosas* —conectar
servicios, ejecutar tareas—. Moyter optimiza por **que el agente no se engañe a
sí mismo**, que es de donde salen los errores que cuestan caro. Eso aparece en
tres capas, y las tres son la misma idea:

1. **No mentirte con los números.** Cuando un agente procesa datos y te da una
   conclusión con cifras, ¿quién garantiza que no confabuló? Es el caso mejor
   resuelto y el que se detalla abajo.
2. **Que las herramientas no mientan sobre por qué fallan.** Una herramienta que
   informa mal de la causa de un fallo hace que el agente arregle lo que no
   está roto — llegó a borrar ficheros correctos porque `verify_web` decía
   "timeout" donde debía decir "ese selector no existe". Misma epistemología,
   una capa por debajo.
3. **Que las herramientas que se escribe a sí mismo estén verificadas.** Un
   agente puede proponer una herramienta nueva (`propose_skill`), y solo se
   activa si sus casos de prueba pasan **de verdad en el sandbox**; las que
   piden privilegio (red, disco) nunca se activan solas. Nada entra por su
   propia palabra.

La primera capa es la más madura, y es la que tiene garantía medida:

- **Síntesis anclada:** la respuesta final solo puede usar cifras que aparezcan
  en la salida real del código ejecutado en el sandbox.
- **Guardián de groundedness:** un verificador sin-LLM extrae los números del
  informe y marca los que no estén respaldados por ninguna ejecución.
- **Autoconsistencia automática (versión dura):** el agente declara sus
  relaciones numéricas (sumas, particiones, porcentajes) y Moyter las verifica
  con aritmética pura; si no cuadran, **regenera** la síntesis con el detalle
  del fallo antes de darla por buena, en vez de solo avisar.
- **Juicio de dominio marcado:** cuando el informe afirma algo que es criterio
  experto y no un hecho verificado por código o datos (una opinión, una
  evaluación de gravedad), Moyter lo señala explícitamente como "no
  verificado, contrástalo" — no evalúa si el juicio es correcto (eso
  necesitaría un oráculo que no existe), pero no deja que se confunda con una
  cifra comprobada.

Estas defensas no hacen listo a un modelo flojo, pero impiden que te engañe sin
avisar — que en tareas donde un número importa, es la diferencia que cuenta.

> **Dónde está cada capa, sin adornos.** La primera está madura y medida (ver
> abajo). La segunda se construyó a base de ver fallar al agente en producción
> y sigue apareciendo cada vez que se prueba en vivo. La tercera funciona
> —lo que se activa, se activó por pasar sus pruebas— pero el agente **rara vez
> elige** proponer una herramienta por su cuenta: en las mediciones prefiere
> resolver la tarea y entregarte el resultado. Que la auto-mejora sea fiable
> está resuelto; que ocurra sola, no.

---

## La garantía, medida

No es una promesa: hay un benchmark que la mide. Sobre el mismo objetivo y los
mismos datos, se sintetiza un informe **desnudo** (prompt neutro, sin defensas —
lo que hace un agente cualquiera) y otro con **Moyter**, y se cuentan las cifras
que el informe afirma sin respaldo en la evidencia — en particular las que
llegarían al usuario **sin marcar**, como si fueran hechos.

El resultado central no es una cifra que dependa del modelo del día, es
estructural: el guardián de groundedness es determinista, así que **marca toda
cifra infundada que el modelo emita**. En las corridas end-to-end, las
confabulaciones que llegan sin aviso caen a **cero** con Moyter, mientras el
informe desnudo —que no marca nada— las deja pasar todas. Como efecto
secundario, anclar la síntesis al stdout suele hacer que el modelo confabule
*menos* de entrada, pero eso sí varía con el modelo y la tarea; la garantía no
descansa en ello, sino en que nada infundado pase sin aviso.

Y no depende de tener un modelo bueno. Se ha corrido la misma comparativa con
tres modelos de calidad muy distinta —desde uno cloud potente hasta un 8B
local flojo— y las confabulaciones que llegan **sin marcar** caen a cero en los
tres. El guardián es determinista: caza toda cifra infundada, la confabule un
modelo bueno o uno malo. (Detalle curioso y honesto: cuanto más flojo el modelo,
menos cifras arriesga de entrada —informe más pobre—, así que no es que sea "más
fiable"; simplemente inventa menos, y lo que inventa queda cazado igual.)

Es una medición estocástica —señal, no prueba estadística—, así que el README no
fija un número: reprodúcelo tú mismo y mira los tuyos (necesita Ollama):

```bash
python benchmarks/reliability/run_e2e.py --model minimax-m3:cloud --repeats 3
python benchmarks/reliability/run_e2e.py --model llama3.1:8b     --repeats 3
```

El **Nivel 1** del benchmark es determinista (sin LLM ni red) y corre en CI, así
que además de medir, detecta regresiones de las defensas. Ver
[`benchmarks/reliability/`](benchmarks/reliability/).

---

## Características

- **Bucle ReAct** con planificación (Plan-and-Solve), ejecución por sub-tareas y
  crítica (LLM-as-judge con rúbrica cerrada).
- **Local-first:** Ollama por defecto (modelos locales y cloud), proveedor
  Anthropic opcional. Capa de proveedores agnóstica.
- **Memoria en dos niveles:** memoria de trabajo compactada + memoria episódica
  vectorial (ChromaDB) con deduplicación, decay y refuerzo por utilidad.
- **Ejecución sandboxed** en Docker; los datos entran por un canal aislado, no
  por disco compartido.
- **Workspace persistente** (`workspace_mode`): `/workspace` conserva lo que el
  agente escribe entre llamadas al sandbox, así que puede construir en varios
  pasos (generar un fichero, leerlo, corregirlo) en vez de empezar cada vez con
  el disco en blanco — y lo escrito antes de un fallo sigue ahí para depurar.
  El contenedor **no gana escritura sobre tu disco**: el estado entra por un
  montaje de solo lectura y sale por copia validada, con la cuota del tmpfs
  (64 MB) intacta. Tres modos: `off`, `run` (default, persiste dentro de un
  objetivo) y `persistent` (también entre objetivos).
- **Agentes como configuración inmutable** (Pydantic frozen). Presets: `coder`,
  `analyst`, `researcher`, `solver`, con herramientas de mínimo privilegio.
- **Defensas anti-confabulación** integradas en el núcleo (ver arriba).
- **Cosecha de módulos reutilizables** (con `workspace_mode=persistent`, ON por
  defecto): el agente ya escribe módulos reutilizables por su cuenta, pero no
  volvía a ellos — reconstruía lo mismo cada vez. Tras cada corrida, Moyter
  ejecuta el autotest (`if __name__ == "__main__"`) de cada módulo nuevo del
  workspace **en el mismo sandbox**; el que pasa entra en un inventario que se
  le da al planificador, que es donde se decide reusar-vs-rehacer.
  **Medido: 3 de 3 corridas reutilizaron, frente a 0 de 8 antes.** El
  manifiesto vive fuera del workspace y atado a hash, así que un módulo editado
  pierde la insignia hasta re-verificarse. Desactivable con `--sin-cosecha`.
- **Auto-mejora verificada** (`propose_skill`, opt-in con `--learn-skills`): el
  agente puede escribir una herramienta nueva, y solo se activa si sus casos de
  prueba pasan de verdad contra el sandbox; las que piden privilegio quedan
  pendientes de revisión humana. Off por defecto, mínimo privilegio.
- **Entrega de artefactos** (`deliver_artifact`): el agente produce un fichero
  (una web autocontenida, un script, un informe) y Moyter te lo hace llegar —
  como documento por Telegram o a disco en la TUI.
- **Verificación en navegador opcional** (`verify_web`, extra `[browser]`):
  ejecuta un artefacto web en chromium headless y comprueba aserciones reales
  sobre el DOM/JS con la red bloqueada — así "la web funciona" es un hecho
  ejecutado, no una afirmación. Fiel al lema: verificar el artefacto.
- **Servidor web** (`serve_web`/`list_servers`/`stop_server`): monta un servidor
  estático efímero que sirve una web en `http://localhost:PUERTO/` (sobrevive al
  turno, se apaga solo por TTL). Con `public=True` abre un túnel cloudflared y da
  una URL pública para el móvil — **fail-closed**, solo si arrancas con
  `MOYTER_ENABLE_TUNNEL=1` (requiere el binario `cloudflared`).
- **Interfaz web opcional** (Chainlit) que muestra el razonamiento paso a paso.
- **TUI opcional** (Rich) para el mismo razonamiento paso a paso, sin
  servidor web — `moyter-tui`.
- **Scheduling opcional** (`moyter-schedule`) para correr un análisis de
  forma recurrente, con log de informes por timestamp.
- **Servidor MCP opcional** que expone esas mismas defensas como servicio para
  otros agentes/harnesses (ver más abajo).

---

## Instalación

Requiere Python 3.11+ y [Ollama](https://ollama.com) para modelos locales.

```bash
pip install moyter              # núcleo
pip install "moyter[full]"      # con memoria, sandbox y SQL
pip install "moyter[ui]"        # con interfaz web Chainlit
pip install "moyter[browser]"   # con verificación en navegador (verify_web)
python -m playwright install chromium   # baja el navegador una vez
```

Para desarrollo:

```bash
git clone https://github.com/rmoya81/moyter.git
cd moyter
pip install -e ".[dev]"
pytest
```

---

## Uso rápido

```python
from moyter import coder_agent

# Construye un agente y ejecuta una tarea verificable
agent = coder_agent(model="minimax-m3:cloud").build()
resultado = agent.run("Calcula 17 * 23 verificándolo con código")
print(resultado)
```

Selección de modelo por variable de entorno:

```bash
MOYTER_MODEL=llama3.1:8b python tu_script.py
```

Pasar datos al sandbox (aislado, sin acceso a tu disco):

```python
csv = open("datos.csv", encoding="utf-8").read()
agent = coder_agent(
    model="minimax-m3:cloud",
    attachments={"datos.csv": csv},
).build()
agent.run("Lee /workspace/datos.csv, límpialo y reporta las métricas clave.")
```

Construir en varios pasos (el sandbox recuerda lo escrito entre llamadas):

```python
# 'run' (default): el estado vive durante el objetivo y se limpia en el siguiente.
# 'persistent': sobrevive entre objetivos ("sigue mejorando la web de antes").
agent = coder_agent(workspace_mode="persistent").build()
agent.run("Genera app.js y index.html, pruébalos y corrige lo que falle.")
```

Los CLIs lo exponen con `--workspace-mode {off,run,persistent}` (`moyter-tui`,
`moyter-telegram`, `moyter-schedule`), y el agente puede consultar qué hay en el
disco con la herramienta `list_workspace`.

### Interfaz web

```bash
pip install "moyter[ui]"
moyter-ui
```

Abre `localhost:8000` y verás el plan, los pensamientos, las herramientas (con
código y salida) y la crítica como tarjetas desplegables; solo la respuesta
final llega al chat.

### Ver la biblioteca que el agente ha construido

Con la cosecha activa, el agente acumula módulos verificados. Para ver cuáles
tiene sin abrir ningún JSON:

```bash
moyter-modules            # los que puede usar: qué son, cuánto los usa
moyter-modules --all      # también los que fallaron, CON EL MOTIVO
moyter-modules --prompt   # el bloque exacto que recibe el planificador
```

Solo lee: no cosecha ni modifica nada, así que se puede mirar con una tarea en
vuelo. Marca además cuáles están a salvo de la poda por antigüedad.

### TUI (terminal)

```bash
pip install "moyter[tui]"
moyter-tui "Calcula los 20 primeros números primos y su suma"
```

Sin objetivo como argumento entra en modo interactivo (pide tareas una a una,
Ctrl-C para salir). Narra el mismo ciclo que la GUI —plan, sub-tareas,
pensamientos, código resaltado, observaciones y la respuesta final— pero
imprimiéndolo directamente en la terminal con Rich, sin servidor web.

```bash
moyter-tui --agent solver --model minimax-m3:cloud "¿Cuántas asignaciones cumplen X?"
moyter-tui --attach datos.csv "Limpia datos.csv y reporta las métricas clave"
```

### Scheduling (análisis recurrente)

```bash
pip install "moyter[tui]"
moyter-schedule --every 1h "Resume las novedades de datos.csv"
```

Proceso persistente (loop interno con `time.sleep`, no depende de cron ni
systemd): ejecuta la tarea, duerme el intervalo, repite — Ctrl-C para parar.
Cada corrida anexa su informe a un log con timestamp (`--log-file`, default
`./moyter_schedule.log`). `--attach` se relee en cada corrida, así que un
archivo que cambie entre corridas llega actualizado sin reiniciar el proceso.

```bash
moyter-schedule --once "..."                       # una corrida, para probar el setup
moyter-schedule --every 30m --agent analyst --attach datos.csv \
    "Detecta anomalías nuevas en datos.csv" --log-file analisis.log
```

Flags: `--agent` (preset: coder/analyst/researcher/solver), `--model`,
`--max-steps`, `--num-ctx`, `--attach RUTA` (repetible).

---

## Servidor MCP — defensas como servicio

Las mismas defensas anti-confabulación del núcleo se pueden exponer por
[MCP](https://modelcontextprotocol.io) para que **cualquier otro agente o
harness** (Claude Code, OpenClaw, Hermes, tu propio bucle...) verifique su
propia salida antes de dártela por buena — sin ceder el control de su bucle de
razonamiento a Moyter. No orquesta nada: solo confirma que las cifras no se
inventaron.

Instalación:

```bash
pip install "moyter[mcp]"
```

Arranque (por stdio):

```bash
moyter-mcp
# equivalente: python -m moyter.mcp.server
```

Las **seis** tools que expone, todas puras y sin Docker/ChromaDB/LLM:

| Tool | Para qué |
|------|----------|
| `check_consistency(sums, partitions, percentages)` | Verifica que sumas, particiones o porcentajes que afirmas cuadran entre sí. |
| `check_grounding(report, evidence)` | Marca cifras de un informe que no aparezcan en la evidencia (stdout, resultados de otras tools) que las respalda. |
| `verify_numeric(expression, claimed_result)` | Evalúa una expresión aritmética suelta (`"17*23"`) y la compara con el resultado que afirmas. |
| `check_units(sums, equations, quantities)` | Análisis dimensional: que no sumes magnitudes de distinta dimensión (`kW`+`V`) ni un producto dé una dimensión que no es (`P=V·I → W`). Con `quantities` verifica la ecuación física completa (valor + unidad), cazando desajustes de prefijo (`0.4 kV·12 A = 4.8 W` falla). |
| `check_scaling(scalings)` | Escalados lineales de instrumentación: la transmisión **4-20 mA** y su familia (0-10 V, 0-20 mA). No es análisis dimensional —mA y bar no tienen relación física— sino el mapeo entre rangos. Comprueba dimensiones de cada lado, escalas (`0.0118 A` y `11.8 mA` son la misma lectura), la aritmética, y que la entrada caiga dentro de su rango: por convención del lazo, una señal bajo el cero vivo es una **avería**, no una medida pequeña. |
| `flag_unverified_judgments(report, judgments)` | Las otras cinco comprueban cifras; esta cubre lo que ninguna puede: las frases de **juicio de dominio** ("esto es una avería de lazo") que suenan igual de firmes que un dato medido y no lo son. Marca las que tú declares, para que quien lea sepa cuáles contrastar. No evalúa si el juicio es correcto —no hay oráculo para eso—, solo impide que se confunda con lo verificado. |

> El escalado 4-20 mA salió de usar Moyter en instrumentación industrial real, y
> está medido: al añadirlo, el agente lo eligió solo como primera herramienta y
> el forcejeo con `check_units` cayó de 6 llamadas a 1 sobre la misma tarea.

### Apuntar un cliente MCP a él

Para Claude Code, añade en `.mcp.json` (raíz del proyecto donde quieras
usarlo):

```json
{
  "mcpServers": {
    "moyter-defenses": {
      "type": "stdio",
      "command": "moyter-mcp"
    }
  }
}
```

Si lo ejecutas desde un checkout con `uv` en vez de un `pip install`, usa
`"command": "uv", "args": ["run", "moyter-mcp"]`.

**¿Integras Moyter en tu propio harness?** La guía
[`docs/mcp-integration.md`](docs/mcp-integration.md) lo cuenta en una pantalla,
agnóstica de cliente (ejemplo con el SDK MCP de Python): contrato exacto de las
tools, dónde enchufar cada una y qué NO hace Moyter.

Verificado con el [MCP Inspector](https://github.com/modelcontextprotocol/inspector)
oficial como cliente independiente:

```bash
npx @modelcontextprotocol/inspector moyter-mcp
```

### Dogfooding real

Este mismo repo se autoverifica: `.mcp.json` en la raíz conecta Claude Code a
`moyter-mcp`, y el propio Claude Code ha usado las tools por decisión propia
(no dirigido a mano) antes de reportar una cifra. Ejemplo real: al contar los
tests del repo desglosados por archivo, verificó la suma antes de darla por
buena —

```
check_consistency(sums=[{
  "label": "tests_totales", "total": 70,
  "parts": [5, 10, 16, 10, 29]   # uno por archivo de test
}])
→ "TODAS COHERENTES\n[OK] tests_totales: suma([5, 10, 16, 10, 29])=70 == 70"
```

— y ancló el informe completo (recuento + passed/skipped) contra la salida
real de `pytest` con `check_grounding` antes de presentarlo. Es la prueba de
que un harness externo puede usar las defensas por su cuenta, dentro de una
tarea normal, sin que Moyter orqueste nada.

---

## Arquitectura

```
Objetivo
   │
   ▼
Planner ──► descompone en sub-tareas
   │
   ▼
Orchestrator ──► bucle ReAct por sub-tarea
   │              (pensamiento → acción → observación)
   │              usa herramientas: execute_python (sandbox), check_consistency…
   ▼
Critic ──► evalúa progreso (rúbrica cerrada), decide continuar o cerrar
   │
   ▼
Síntesis ──► anclada al stdout real del sandbox
   │          + guardián de groundedness numérico
   │          + verificación de autoconsistencia
   ▼
Informe verificado (con avisos si alguna cifra no está fundamentada)
```

**Memoria:** de trabajo (compactada por sub-tarea) + episódica (vectorial, con
refuerzo por utilidad de las lecciones que funcionaron).

---

## Modelos y hardware

Moyter funciona con modelos locales y cloud vía Ollama. La calidad del
resultado sigue a la capacidad del modelo:

| Modelo | Comportamiento típico |
|--------|----------------------|
| Modelos capaces (cloud o grandes) | Resuelven y se auto-verifican bien |
| Modelos medianos | Capaces; las defensas cazan sus errores sutiles |
| Modelos pequeños (~8B) | Limitados en tareas complejas; las defensas impiden que confabulen sin avisar |

Las defensas de Moyter no sustituyen la capacidad del modelo — la
**complementan**, garantizando que un error sea visible en vez de silencioso.

---

## Alcance honesto

Moyter destaca en tareas **computables y de varios pasos**: limpiar datos,
calcular, enumerar, encadenar herramientas, cualquier cosa donde el sandbox
verifique y las defensas anclen las cifras a ejecuciones reales.

En tareas de **puro juicio experto de un solo paso** (interpretar, opinar sin
código que ejecutar), el bucle no añade capacidad sobre el modelo base — el
conocimiento vive en el modelo, no en el andamiaje. Las defensas marcan
cuándo el informe está haciendo un juicio de dominio en vez de reportar un
hecho verificado, pero no pueden comprobar si ese juicio es correcto —eso
necesitaría un oráculo externo—. Conocer este límite es parte de usar la
herramienta bien.

Lo mismo vale para lo que Moyter verifica de sí mismo. Una skill activada o un
módulo cosechado han pasado **sus propios casos declarados contra el sandbox
real** — que es exactamente lo que demuestra cualquier suite de tests, ni más
ni menos: que ESE comportamiento coincide con ESAS expectativas para ESOS
inputs. No demuestra corrección general, no cubre lo no probado, y no garantiza
que la especificación fuera la correcta. No es un límite que Moyter introduzca;
es el de siempre, escrito aquí en vez de escondido.

---

## Contribuir

Moyter es un proyecto joven y las contribuciones son bienvenidas. Abre un issue
para discutir cambios grandes antes de un PR. Todo cambio debe pasar
`ruff check src/` y `pytest`.

## Autor

Moyter fue creado por **Rubén Moya Morata** (moyter) — GitHub
[@rmoya81](https://github.com/rmoya81), [moyter.com](https://moyter.com).

## Licencia

MIT © Rubén Moya Morata (moyter)
