Perseo como Instinct: recados en la web, bóveda, vigilancias, seguimiento y correo con manos - #9
Merged
Merged
Conversation
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>
There was a problem hiding this comment.
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
Open (6)
Validar también el cuerpo aprobado del borrador · New Validar la URL antes de exponer el contenido de la pestaña · New Usar el selector de operación correcto al despachar · New Actualizar el verificador para aceptar el nuevo ámbito · New Usar el directorio de datos configurado en el diagnóstico · New Propagar errores de Gmail al comprobar respuestas · New
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", |
| # 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


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.
agentes/recado.py). Un encargo entero en segundo plano, con su propio Chrome (@playwright/mcpfijado a 0.0.82) y las sesiones que dejes en<datos>/navegadorconperseo navegador. Si tiene que esperar tu sí, al volver sigue desde el mismo botón.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 conperseo boveda.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.enviar_borradorsolo envía si el borrador es exactamente el de la tarjeta;crear_eventoapunta citas, y con invitados espera tu sí.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.composeno 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 comprobaren verde: pytest, ruff, vulture, tsc, vitest, knip ycargo 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.Lo que hay que hacer a mano
calendar.events):python -m perseo_core.servicios.autorizar_google.perseo navegadorpara entrar a mano en tus sitios, yperseo boveda guardar …para las claves.🤖 Generated with Claude Code