`detect_pii` instanciaba `AnalyzerEngine()` sin configuración, así que Presidio
intentaba cargar su modelo por defecto `en_core_web_lg` (~560 MB), que no está en
la imagen Docker — `core/Dockerfile` instala `en_core_web_sm`. Resultado: el
validador fallaba con `[E050] Can't find model 'en_core_web_lg'` y, al ser la
política fail-closed, *toda* invocación quedaba `blocked_by_guardrail`. Además se
reinstanciaba el engine en cada llamada (caro; probablemente la causa del crash del
proceso al segundo invoke). Ahora:
- `_presidio_analyzer()`: singleton perezoso con `NlpEngineProvider` → `en_core_web_sm`.
- si Presidio no está instalado o el modelo no carga, se hace fallback a la
detección por regex (antes solo se hacía fallback ante ImportError).
Detectado ejecutando el smoke de docker-compose (el venv local no tiene Presidio,
así que los tests usan la rama de regex y no lo veían).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`guardrails-ai<0.6` arrastra `typer 0.12.x`, que es incompatible con click
>=8.2 ("Secondary flag is not valid for non-boolean flag"); el build de
core/Dockerfile fallaba en `python -m spacy download en_core_web_sm`. Detectado
al ejecutar el smoke de docker-compose.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- POST /agents/{name}/invoke, GET /executions, GET /executions/{trace_id},
POST /executions/{trace_id}/{approve,reject}.
- execution_index.json persiste trace_id → (agent_name, version) para reconstruir
el contexto en approve/reject tras un reinicio.
- Refactor del orchestrator: split _snapshot → _build_execution + _snapshot, nuevo
método público snapshot() (lee estado sin avanzar). Sustituye a los helpers
_snapshot_execution/_build_graph_for_snapshot del plan, incompatibles con el
checkpointer por-llamada.
- Aliases Annotated[...] de dependencias movidos a deps.py.
- Fixture mínima tests/fixtures/policies/default/.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Punto único de entrada al runtime. Cada llamada abre su propio AsyncSqliteSaver
sobre data_dir/checkpoints.sqlite, así que un awaiting_approval sobrevive a un
reinicio del proceso (verificado en test_estado_persiste_entre_instancias).
El plan no incluía tests del orchestrator; se añaden 5 (invoke feliz, pausa HITL +
resume aprobar/rechazar, bloqueo por guardrail, persistencia entre instancias).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Los nodos del grafo son async, así que se invoca con `await graph.ainvoke`,
que el SqliteSaver síncrono no soporta (NotImplementedError). Se cambia a
AsyncSqliteSaver expuesto como context manager async. Requiere aiosqlite<0.21
(la 0.21 eliminó Connection.is_alive(), usado por langgraph-checkpoint-sqlite 2.x).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>