Skip to content

Perseo como Instinct: recados en la web, bóveda, vigilancias, seguimiento y correo con manos - #9

Merged
PersusUS merged 11 commits into
mainfrom
mejora/recados-y-boveda
Sep 25, 2026
Merged

PersusUS merged 11 commits into
mainfrom
mejora/recados-y-boveda

Conversation

@PersusUS

Copy link
Copy Markdown
Owner

Qué trae

Lo que hace Instinct (el asistente de Spear Street) llevado a Perseo, sin los incidentes documentados que tuvo: un correo enviado sin permiso y una bandeja de entrada que obedeció instrucciones escondidas en un correo.

  • Recados en la web (agentes/recado.py). Un encargo entero en segundo plano, con su propio Chrome (@playwright/mcp fijado a 0.0.82) y las sesiones que dejes en <datos>/navegador con perseo navegador. Si tiene que esperar tu sí, al volver sigue desde el mismo botón.
  • Bóveda (servicios/boveda.py). Contraseñas y tarjetas cifradas con DPAPI. El modelo solo ve {{boveda:resy.clave}}: el valor se pone en el núcleo justo antes de teclear, y solo en los sitios de esa entrada. Todo lo que vuelve de la página se tapa. Se gestiona con perseo boveda.
  • Nivel exterior (ADR 0007). Pagar, reservar, enviar un correo o invitar a alguien se para aunque las confirmaciones estén apagadas. Ese sí lo das en la tarjeta del panel o del móvil; el modelo no puede darlo, ni por escrito ni por voz.
  • Vigilancias («avísame cuando haya entradas») y seguimiento de los correos que pedían algo y llevan dos días sin respuesta tuya en Gmail. Mientras no hay novedad, no suena nada.
  • Correo y agenda con manos. enviar_borrador solo envía si el borrador es exactamente el de la tarjeta; crear_evento apunta citas, y con invitados espera tu sí.
  • Además entran la rama mejora/biometria-y-salud (recordatorios, parte del día, Perseo llama), el filtro de correos automáticos en el seguimiento, que lo que Perseo abre salga delante, y la detección del Ollama equivocado en el 11434.

Una corrección que importa

El código, las pruebas y la documentación decían que gmail.compose no permite enviar. Es falso: Google lo describe como «Manage drafts and send emails». Nunca salió nada porque ningún código llamaba a enviar. Ahora lo frena la política, y hay pruebas.

Cómo se ha probado

  • perseo comprobar en verde: pytest, ruff, vulture, tsc, vitest, knip y cargo check.
  • verificadores/verificar_recado.py, de punta a punta con Chrome de verdad, la bóveda de verdad (DPAPI) y un Gemini de guion: 13 de 13.
  • Cada commit se validó sobre el árbol exacto que sube, porque la copia la compartían varias sesiones.

Lo que hay que hacer a mano

  • Volver a dar permiso a Google (ahora también se pide calendar.events): python -m perseo_core.servicios.autorizar_google.
  • perseo navegador para entrar a mano en tus sitios, y perseo boveda guardar … para las claves.

🤖 Generated with Claude Code

PersusUS and others added 10 commits September 23, 2026 22:05
Tres cosas que los registros llevaban semanas contando y nadie leía.

La tarea `PerseoRevivir` se creaba con `schtasks /SC MINUTE`, y Windows rellena
lo que no se dice con sus valores de fábrica: con batería no se ejecuta, se para
al desenchufar y pierde la vuelta que toca mientras el portátil duerme. Medido
el 2026-09-23: el núcleo cayó a las 14:21 al suspender y en tres horas, al 23 %
descargando, nadie lo levantó. Ahora `tarea_revivir.py` la escribe entera en
XML —con batería, al despertar y al desbloquear— y `manage_startup status` dice
qué tiene mal la que está registrada.

El permiso de Google llevaba caducado desde el 6 de septiembre: 1.460 trazas
iguales en el registro, una cada cinco minutos por disparador, y ni un aviso.
Diecisiete días sin correo ni agenda. `TestigoCaducado` distingue el
`invalid_grant`; `disparadores.Degradado` lo dice una vez al caer y otra al
volver, espacia las vueltas hasta un cuarto de hora y lleva el aviso a
Telegram con la orden que lo arregla. Y reautorizar ya no pide reiniciar: el
cliente relee `google.json` si cambió.

Dos `POST /confianza` casi a la vez daban un 500 con `WinError 32`: Windows no
deja borrar un fichero que otro hilo está leyendo. Escritura atómica,
reintentos cortos, y no poder leerlo ya no lo borra.

Los fallos de red de una vuelta —el DNS tras despertar— pasan a ser una línea
sin traza.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… aprende sin nombre

Casi siempre llamaba «señor Persus» a quien no era. Los perfiles no tenían la
culpa —se parecen poco entre sí: voz 0,30 como mucho, cara 0,22, medido—. La
tenían tres cosas juntas:

1. La regla 2 del prompt de la llamada: «Mientras nadie diga lo contrario,
   quien te habla es el señor Persus».
2. Casi nunca lo decía nadie, porque el detector de voz de la app recalculaba
   el ruido de fondo también mientras se hablaba: a los ~300 ms el umbral
   quedaba por encima de la voz y solo viajaban picos de sílaba, más cortos de
   lo que el núcleo aceptaba. El perfil del dueño sumaba ocho muestras en un
   mes.
3. Y cuando sí se reconocía a alguien, ese aviso se quedaba puesto aunque
   luego hablara otro.

Ahora la regla es la contraria —solo es él si un aviso lo dice— y sube
`VERSION_PROMPT` para que llegue. La app manda ventanas seguidas de micrófono
(`ventana-voz.ts`) y el núcleo decide qué es voz (`biometria_senal.py`). Una
voz dudosa retira la etiqueta anterior y se avisa de la duda. Dos personas que
puntúan casi igual no se nombran. La cámara ya no decide quién habla: dice
quién está.

Pedido del señor Persus: que no aprenda a nadie sin que alguien diga su
nombre, y que quede anotado. Los desconocidos solo viven en la sesión, con
etiqueta provisional; se guardan al darles nombre, y cada alta, nombre,
muestra nueva o borrado deja una línea en `<datos>/personas.log`. A quien ya
tiene nombre se le siguen sumando muestras claras y de nadie más, hasta 40 de
voz, para que mejore con el uso.

Por el camino: el «refuerzo» de voz era un punto medio que arrastraba medio
perfil hacia el último trozo —ahora es una galería con la media de los tres
mejores, y el vector antiguo se sustituye en vez de heredarse—; el contador de
«Desconocido N» volvía a cero y pisaba perfiles guardados; y una cara movida o
de lado ya no entra en ninguna galería. `biometria_limpieza.py` busca muestras
cruzadas en los perfiles de antes. En Ajustes, una toma mala se rechaza
diciendo por qué, y cada una dice cuánta voz útil tenía.

`verificar_biometria` pasa con ECAPA de verdad contra el núcleo real.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… tira a la primera

1.062 capturas de pantalla en 25 llamadas, una cada dos segundos, casi todas
iguales a la anterior. Cada una ocupa contexto y tiempo del modelo sin decirle
nada nuevo. `pantalla-cambio.ts` compara una miniatura de 32×18 con la última
que se mandó —no con la última capturada, para que un cambio lento acabe
notándose— y reenvía igual cada veinte segundos. El cuaderno de la llamada
apunta cuántas se ahorran.

Tras suspender el portátil, el servidor cierra con `1008 session not found`, y
eso no se reconocía como testigo caducado: se perdían dos intentos antes de
tirarlo. La decisión del cierre sale a `trasElCierre`, pura y probada, y con
ella entra la ventana deslizante de contexto para las llamadas largas, que se
apaga sola si el servidor la rechazara.

`gemini-live.ts` no crece: baja a 1.161 líneas. Lleva también las dos
entradas que usa el commit siguiente: `onSinLlamada` y el turno pedido en
`informarIdentidad`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n la misma llamada

Perseo no sabía recordar nada a una hora. `recordatorios` apunta, lista, quita
y avisa: «en veinte minutos», «mañana a las nueve», «el jueves a las cinco»
—formas que no necesitan que el modelo sepa qué día es hoy, porque ningún
prompt lo dice—, con repetición diaria, laborable o semanal, y avisando tarde,
y diciéndolo, si el núcleo estaba apagado a la hora. `parte` junta el día en
una respuesta: agenda, correos que piden algo, recordatorios, tareas y hábitos;
el de cada mañana viene apagado (`PERSEO_PARTE_HORA`). Y la voz y el chat
pueden pedir por fin un borrador de correo, que el agente `correo` ya sabía
escribir y nadie podía pedirle.

Los recordatorios avisan con el mismo timbre que los encargos, y ese camino
tenía cuatro fallos:

1. un aviso con motivo en plena llamada se tiraba: la regla de «ya estás en
   llamada, no hagas nada» era para la palabra clave;
2. el marcador se sobrescribía, y de dos avisos casi a la vez se perdía uno;
3. los encargos de `dev` pedidos por escrito o desde el móvil terminaban en
   silencio;
4. un resultado que llegaba después de colgar esperaba a la llamada
   siguiente sin timbrar.

`llamada_saliente.py` deja el motivo en el marcador cuando un encargo termina
sin nadie esperándolo —quien espera pregunta, y cada pregunta se apunta—;
`autollamada.ts` decide si timbrar, contarlo en vivo o guardarlo; el marcador
se añade línea a línea y Rust lo toma entero de una vez. Las pruebas y el
arnés lo apartan, para no hacer sonar la app de verdad.

Las herramientas nuevas entran en Rust por una tabla de directas, no como
rama propia: `nucleo.rs` tiene un techo de 900 líneas. `App.tsx` baja a 1.012.

`verificar_recordatorios` pasa de punta a punta contra el núcleo real.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Recordatorios, parte del día, llamada saliente y biometría partida en piezas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… para

El agente `recado` hace un encargo entero en la web, en segundo plano, con su
propio Chrome (`@playwright/mcp` fijado a 0.0.82) y las sesiones que el señor
Persus deje en `<datos>/navegador` con `perseo navegador`. Es lo que Instinct
llama «un ordenador con tus sesiones», en el ordenador de casa.

- La bóveda (`servicios/boveda.py`) guarda claves y tarjetas cifradas con
  DPAPI. El modelo escribe `{{boveda:resy.clave}}`; el valor se pone en el
  núcleo justo antes de teclear y solo si la página es de un sitio de la
  entrada, y todo lo que vuelve de la página se tapa.
- Nivel nuevo, `exterior`: pagar, reservar, enviar o meter una tarjeta se para
  aunque las confirmaciones estén apagadas, y el sí no lo puede dar el modelo
  ni por escrito ni por voz (ADR 0007). El nombre que decide es el del botón en
  la instantánea, no el que escribe el modelo; Enter sobre un botón con el foco
  cuenta como pulsarlo.
- Tras el sí, el recado sigue desde el mismo botón con lo que dejó escrito en
  `<datos>/recados/<id>.json`, sin repetir pasos de modelo.
- Medido: `npx` tardaba 40-50 s en arrancar Playwright en este PC y `node
  cli.js` 1,4 s; el paquete se instala una vez en `<datos>/navegador_mcp`.
  Con `--snapshot-mode none` Playwright deja de escribir instantáneas al disco,
  que enseñaban lo tecleado en claro, también en campos de contraseña.

`verificadores/verificar_recado.py` lo prueba de punta a punta con Chrome de
verdad, la bóveda de verdad y un Gemini de guion: 13 de 13.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…da sin respuesta

Lo que Instinct hace sin que se lo pidan dos veces: mirar cada tanto si pasó
algo, y retomar lo que se cae.

- Vigilancias («avísame cuando haya entradas», «resérvalo si baja de 80 €»).
  Viven en `<datos>/vigilancias.json`; un disparador lanza cada pocas horas un
  `recado` en modo `vigilar`, que contesta con la herramienta `informar`. Sin
  novedad no suena nada; al cumplirse avisa, o hace el recado entero con la
  parada de siempre antes de pagar. Topes por la cuota de Gemini: una vez por
  hora como mucho, cinco a la vez, 24 comprobaciones al día entre todas.
- Seguimiento: un correo «requiere acción» con 2 a 14 días sin respuesta suena
  una vez, entre las 9 y las 21. Antes se mira el hilo en Gmail (solo
  etiquetas): si el último mensaje es suyo, se marca atendido y se calla.
- Por Telegram solo va el tipo de aviso; el detalle va en la llamada, en casa.
- `recado.py` suelta su estado a `recado_estado.py` y el catálogo sus
  herramientas de recados a `catalogo_recados.py`: los dos pasaban de su techo.

También corrige los recuentos del README, que en el commit anterior contaban
18 pruebas que aún no están en la rama.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n lo exterior parado

Lo que Instinct hace con el correo y el calendario, con lo que le salió mal
tapado: un correo enviado sin permiso fue uno de sus incidentes.

- `correo.enviar` envía un borrador ya redactado. Es `exterior`: el trabajador
  lo para aunque las confirmaciones estén apagadas, y el sí no lo da el modelo.
  La tarjeta dice a quién y con qué asunto; antes de enviar se lee el borrador
  de verdad en Gmail y, si no coincide, no sale nada.
- `agenda.crear` apunta una cita (reversible). Con invitados les llega un correo
  de Google, así que el agente pregunta antes (`agenda.invitar`, exterior).
- Herramientas `enviar_borrador` y `crear_evento` en la voz y en el chat.
- Se pide además `calendar.events`: con el permiso de antes, crear una cita
  contesta 403 hasta que se vuelva a pasar por `autorizar_google`.

Y una corrección que importa: el código, las pruebas y la documentación decían
que `gmail.compose` no permite enviar y que Google contestaría 403. Es falso
—Google lo describe como «Manage drafts and send emails», y `drafts.send` lo
acepta—. Nunca se envió nada porque nadie llamaba a enviar; ahora lo que lo
frena está escrito en la política y probado.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…re sale delante

El seguimiento llamaba por cualquier correo «requiere acción» sin respuesta,
y la primera llamada de verdad nombró un evento de Luma, un fallo del CI y un
aviso de Stripe: ninguno tiene a nadie esperando. Ahora cada correo lleva
`automatico`, sacado de las cabeceras de envío (List-Unsubscribe, List-Id,
Auto-Submitted, Precedence) y, en los ya triados, de la dirección; lo
automático no llama ni avisa y se queda en el panel.

Lo que abría el agente pc salía detrás o parpadeando en la barra: el núcleo
es un pythonw sin foco y Windows no deja pasar al frente lo que lanza.
servicios/ventanas.py busca la ventana nueva, o la del programa si ya
estaba abierto, y la trae delante.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ter no vale

En este PC hay dos Ollama: el de Windows, con `qwen3:4b`, y uno en WSL que solo
tiene `qwen2.5-coder:1.5b`. Con el de WSL encendido, `wslrelay.exe` se queda el
11434 y contesta el mismo 200; `perseo on` decía «ya estaba» y el triaje fallaba
pidiendo un modelo que ese Ollama no tiene. `commands/ollama_local.py` pregunta
qué modelos tiene quien contesta y dice qué hacer, y `perseo estado` lo enseña.

Lo escribió otra sesión que compartía esta copia y se cerró sin hacer commit;
se recoge tal cual, con sus pruebas en verde.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 25, 2026 09:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Persisten problemas críticos de seguridad y fiabilidad, junto con fallos de reintento, despacho y verificación OAuth.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 4 High severity · 2 Medium severity

Open (6)
What changed in this PR

Perseo añade recados web, bóveda cifrada, vigilancias, seguimiento de correo, agenda, controles de acciones exteriores y mejoras de voz y biometría.

Changes:

  • Incorpora nuevos agentes, servicios, herramientas y persistencia.
  • Refuerza confirmaciones, seguridad de secretos, Gmail/Calendar y recuperación.
  • Actualiza llamadas, pruebas, verificadores y documentación.

Quedan pendientes problemas críticos de aprobación de borradores, validación de navegación, despacho de vigilancias y verificación OAuth, además de varios fallos de reintento y persistencia.

File Summary
verificadores/​vulture_permitidos.py Permisos de símbolos nuevos.
verificadores/​verificar_recordatorios.py Verificación integral de recordatorios.
verificadores/​verificar_google.py Verificación de correo y ámbitos Google; requiere actualizar la aserción de calendar.events.
verificadores/​verificar_biometria.py Verificación del aprendizaje biométrico.
verificadores/​arnes_pruebas.py Aislamiento de marcadores en pruebas.
RealTime/​src/​lib/​llamada/​screen-manager.ts Evita capturas idénticas.
RealTime/​src/​lib/​llamada/​reconexion.ts Recuperación de sesiones.
RealTime/​src/​lib/​llamada/​pantalla-cambio.ts Comparación de miniaturas.
RealTime/​src/​lib/​llamada/​identidad-llamada.ts Avisos de identidad; debe evitar notificaciones repetidas para hablantes desconocidos.
RealTime/​src/​lib/​llamada/​gemini-live.ts Integración de compresión y avisos.
RealTime/​src/​lib/​llamada/​autollamada.ts Gestión de avisos entrantes.
RealTime/​src/​lib/​identidad/​ventana-voz.ts Agrupación de ventanas de voz.
RealTime/​src/​lib/​identidad/​quien-hay.ts Avisos de identidad.
RealTime/​src/​lib/​datos/​config.ts Prompt de identidad; la nueva condición está incompleta.
RealTime/​src/​components/​Settings.tsx Información de tomas biométricas.
RealTime/​src/​App.tsx Gestión de llamadas y avisos.
RealTime/​src-tauri/​src/​nucleo.rs Herramientas y confirmaciones exteriores; corregir acentos del mensaje de usuario.
RealTime/​src-tauri/​src/​commands.rs Consumo de marcadores; el .tomado fijo puede quedar huérfano.
RealTime/​src-tauri/​src/​autollamada.rs Vigilancia de avisos acumulados.
RealTime/​pruebas/​vigilante.test.ts Pruebas del vigilante de identidad.
RealTime/​pruebas/​ventana-voz.test.ts Pruebas de ventanas de voz.
RealTime/​pruebas/​quien-hay.test.ts Pruebas de identidad.
RealTime/​pruebas/​pantalla-cambio.test.ts Pruebas de capturas.
RealTime/​pruebas/​cierre-llamada.test.ts Pruebas de reconexión.
RealTime/​pruebas/​autollamada.test.ts Pruebas de avisos entrantes.
README.en.md Documentación general actualizada.
pruebas/​test_ventanas.py Pruebas de ventanas Windows.
pruebas/​test_politica.py Pruebas de confianza y bloqueo.
pruebas/​test_parte.py Pruebas del parte diario.
pruebas/​test_navegacion.py Pruebas de navegación segura.
pruebas/​test_llamada_saliente.py Pruebas de llamadas salientes.
pruebas/​test_exterior.py Pruebas del nivel exterior.
pruebas/​test_chat.py Catálogo de herramientas.
pruebas/​test_boveda.py Pruebas de la bóveda.
pruebas/​test_borrador.py Pruebas de permisos Google.
pruebas/​test_biometria_senal.py Pruebas de detección de voz.
pruebas/​test_autorizar_google.py Pruebas de ámbitos OAuth.
pruebas/​test_arranque.py Pruebas de recuperación.
pruebas/​conftest.py Aislamiento global de marcadores.
perseo_core/​servicios/​correo_lectura.py Metadatos de automatización.
perseo_core/​servicios/​catalogo_tipos.py Tipos compartidos del catálogo.
perseo_core/​servicios/​biometria_perfiles.py Persistencia biométrica.
perseo_core/​servicios/​biometria_limpieza.py Limpieza de muestras sospechosas.
perseo_core/​servicios/​biometria_galeria.py Galerías de vectores.
perseo_core/​servicios/​biometria_cara.py Calidad facial.
perseo_core/​servicios/​autorizar_google.py Nuevos ámbitos OAuth.
perseo_core/​servicios/​aplicaciones.py Enfoque de ventanas abiertas.
perseo_core/​infra/​router.py Propagación de niveles.
perseo_core/​infra/​disparadores.py Estados degradados y backoff.
perseo_core/​infra/​configuracion.py Configuración de automatizaciones.
perseo_core/​infra/​almacen.py Persistencia de configuración.
perseo_core/​dominio/​niveles.py Nuevo nivel exterior.
perseo_core/​dominio/​mensaje.py Detección de correos automáticos.
perseo_core/​caras/​telegram.py Avisos operativos.
perseo_core/​caras/​api.py Registro de trabajos esperados.
perseo_core/​agentes/​vigilancias.py Vigilancias; hay problemas de despacho y de confirmación antes de encolar.
perseo_core/​agentes/​recordatorios.py Recordatorios; pueden consumirse antes de un encolado fallido.
perseo_core/​agentes/​recado_estado.py Persistencia y reanudación de recados.
perseo_core/​agentes/​correo.py Borradores y envíos controlados; la aprobación debe incluir el cuerpo exacto.
perseo_core/​agentes/​chat.py Instrucciones de herramientas.
perseo_core/​agentes/​chat_herramientas.py Despacho de herramientas.
perseo_core/​agentes/​agenda.py Creación de eventos e invitaciones.
perseo_core/​__main__.py Registro de agentes y trabajadores.
docs/​PRIVACIDAD.md Datos y secretos protegidos.
docs/​CONFIGURACION.md Configuración de recados y vigilancias.
docs/​adr/​README.md Índice de decisiones.
docs/​adr/​0007-lo-que-sale-de-casa-se-para.md Política de acciones externas.
docs/​adr/​0005-las-confirmaciones-estan-apagadas.md Excepción de confirmaciones.
commands/​tarea_revivir.py Tarea programada de recuperación.
commands/​subagentes_mcp.py Marcadores acumulativos.
commands/​perseo.py Comandos y diagnóstico; debe usar el directorio de datos configurado.
commands/​manage_startup.py Gestión del arranque.
commands/​arquitectura.py Límites de tamaño.
AGENTS.md Mapa de arquitectura actualizado.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

if leer is None or enviar is None:
raise RuntimeError("Este buzón no sabe enviar: hace falta PERSEO_CORREO=gmail.")
real = await leer(borrador)
if _direcciones(real["para"]) != _direcciones(para) or _asunto(real["asunto"]) != _asunto(asunto):
Comment on lines +356 to +359
if nombre == "browser_snapshot":
estado.url = nav.url_de(respuesta) or estado.url
estado.elementos = nav.elementos(respuesta)
return web.envolver(nav.recortar(_tapar(respuesta)))
"titular": f"{len(lista)} vigilancia(s) caducada(s) sin cumplirse" if lista else None,
}

que = str(peticion.get("que") or accion or "").strip().lower()
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/calendar.readonly",
"https://www.googleapis.com/auth/gmail.compose",
"https://www.googleapis.com/auth/calendar.events",
Comment thread commands/perseo.py
# TerminateProcess —el portátil que duerme, un job que se cierra— no deja ni
# una línea, y sin esta el registro solo decía «arrancando», sin fecha de
# la caída con la que cruzar los eventos de energía de Windows.
registro_nucleo = RAIZ / "perseo_core" / "datos" / "nucleo.log"
Comment on lines +124 to +126
except Exception as e: # noqa: BLE001 — sin saberlo, se avisa: es el lado que no calla
logger.warning("Seguimiento: no se pudo mirar el hilo %s: %s", c.get("hilo"), e)
(contestados if respondido else sin_respuesta).append(c)
…dice por qué verificar_recado no va en el CI

La comprobación «ninguno que escriba» era la decisión de antes: desde 38e57de
se pide calendar.events para apuntar citas, y gmail.compose ya enviaba. Lo que
se sigue comprobando es que no se pida nada que borre o comparta.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@PersusUS
PersusUS merged commit 9637be5 into main Sep 25, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants