Skip to content

Releases: Ander-Labs/ciel-agent-framework

v0.12.0

Choose a tag to compare

@Ander-Labs Ander-Labs released this 21 Jul 22:18

[0.12.0] — Fase 18: Evaluación y testing

Nuevo

  • MockProvider determinista (ciel.providers.MockProvider): proveedor
    offline-safe (sin red ni API keys) con modos fixed/echo/map; registrado
    en auto_provider con prefijo mock/. Para tests y eval reproducibles.
  • ciel evaluate (CLI Typer, ciel.cli.evaluate): run (KPIs en tabla
    Rich + exit-code por umbral), regression (gate contra results.json
    baseline) y redteam (prompt injection / fuga de tenant con assertions de
    aislamiento). Cableado en ciel.cli.main.
  • ciel.eval: Evaluator (corre dataset sobre un agente/callable, acumula
    KPIs, exporta results.json), EvalCase, load_dataset (YAML) y métricas
    deterministas propias: exact_match, contains, f1_token, faithfulness,
    context_relevance (usa Retriever de ciel.rag si se pasa),
    answer_relevance.
  • Integración opt-in con DeepEval/RAGAS/TruLens vía extra eval
    (use_third_party=True); degrada a métricas propias si el extra no está
    instalado (las funciones de terceros devuelven None).
  • Extra eval en pyproject.toml (deepeval, ragas, trulens-eval);
    el core no lo requiere.
  • CI: nuevo job eval en .github/workflows/ci.yml (offline, MockProvider,
    sin red) que corre tests/eval + smoke CLI y hace gate de regression.

Cambios internos

  • Corrección de documentación obsoleta: docs/guide/concepts.md refería
    MemoryStore (memoria declarativa FTS5) — ahora documenta EpisodicStore
    (memoria episódica nativa por (tenant_id, session_id), Fase 17).
  • API pública aditiva: ciel.eval (Evaluator, EvalCase, métricas,
    load_dataset) y ciel.providers.MockProvider. No rompe Agent,
    AgentResponse, ToolResult ni ChatProvider.

v0.11.0

Choose a tag to compare

@Ander-Labs Ander-Labs released this 20 Jul 22:23

Nuevo

  • Memoria episódica nativa (Pilar A, offline-safe): ciel.runtime.memory_episodic
    con EpisodicStore y MemoryConfig. Persiste user/assistant por
    (tenant_id, session_id), con append / get_recent / get_by_id /
    search (filtrada estrictamente por tenant — sin fuga cross-tenant) /
    clear_session. Se recupera e inyecta como contexto en el system prompt del
    agente de forma aditiva (Agent(memory=store)).
  • RAG enterprise (Pilares B/C, offline-safe por defecto): nuevo paquete
    ciel.rag con KnowledgeBase / Retriever / SemanticCache, índice
    vectorial InMemoryVectorStore + DeterministicEmbeddingProvider (sin red),
    búsqueda híbrida BM25 + vector con fusión RRF y rerank, chunking configurable
    (token/paragraph), loaders MD/HTML/TXT (PDF opt-in) y tools RAG
    (retrieve, kb_add) que se enchufan al agente vía rag_tools(kb).
  • API pública aditiva: ciel.EpisodicStore, ciel.MemoryConfig y
    install_agent_memory_support(Agent) (mismo patrón que skills). Degrada
    graceful a "sin memoria" si no se configura. No rompe la API pública.
  • Aislamiento multi-tenant nativo en toda memoria/RAG (requisito k8s/VPS).
  • Extra rag opcional en pyproject.toml (chromadb, pypdf); el
    default corre sin red ni keys.

Cambios internos

  • state_backend gana métodos de memoria episódica (memory_append,
    memory_get, memory_get_recent, memory_search_tenant,
    memory_clear_session) sobre SQLite/Postgres, aislados por tenant_id.
  • Agent.arun/astream usan session_id estable por agente para que la
    memoria episódica persista a lo largo de la conversación (antes se renovaba
    por run, rompiendo la persistencia).
  • memory_search_tenant (SQLite) migrado de FTS5 trigram a LIKE por tenant
    (determinista, offline-safe y sin fuga cross-tenant).
  • Tests offline de F17: 17 nuevos (memoria episódica, RAG end-to-end, tools,
    integración con Agent). Suite total: 434 passed / 7 skipped.

v0.10.0

Choose a tag to compare

@Ander-Labs Ander-Labs released this 18 Jul 15:55

[0.10.0] — Fase 16: Providers y multimodal

Nuevo

  • Multimodal nativo (offline-safe): ChatMessage.content acepta str | list[dict] (text/image_url/input_audio). Helper ChatMessage.text() degrada multimodal a texto.
  • LiteLLM meta-provider (extra litellm): LiteLLMProvider expone 100+ modelos vía ChatProvider, con Router para fallback/balanceo. Import diferido; sin el extra lanza ProviderError.
  • Nuevos providers: AzureOpenAIProvider (deployment + api-version), Ollama local y vLLM/TGI como OpenAICompatibleProvider. auto_provider reconoce azure/, ollama/, vllm/.
  • Serializers OpenAI/Anthropic/Gemini para contenido multimodal.

Tests

  • +27 tests offline (F16-C multimodal, F16-A LiteLLM, F16-B Azure/Ollama/vLLM). Suite: 417 passed / 7 skipped.

Ver CHANGELOG.md para detalle completo.

v0.9.0

Choose a tag to compare

@Ander-Labs Ander-Labs released this 18 Jul 00:47

[0.9.0] — Fase 15 (Enterprise reforzado) — 2026-07-17

Refuerza la capa enterprise con tres pilares de seguridad, todos offline-safe
por defecto
(OIDC real, Vault dinámico y sandbox fuerte son opt-in vía
config/env; sin red/IdP/Vault/Docker el comportamiento previo se mantiene).
Corresponde a v0.9 — Enterprise reforzado del roadmap. Retrocompatibilidad
total: no cambia la API pública ni la verificación local de JWT ni la
abstracción de secretos existente. Baseline de tests: 390 passed / 7 skipped.

F-OIDC — SSO/OIDC con proveedor real

  • OIDCVerifier extendido (ciel.enterprise.rbac): además del modo local
    (clave estática, retrocompat Fase 7), añade modo JWKS opt-in con discovery
    .well-known/openid-configuration, fetch/caché de JWKS por kid (TTL +
    refresh on kid desconocido) y validación estricta de iss/aud/exp/alg
    (solo RS/ES asimétricos; rechaza none/HS256 en modo JWKS).
  • OIDCVerifier.from_config() lee CIEL_OIDC_* (ISSUER, AUDIENCE,
    JWKS_URI, ROLE_CLAIM, ROLE_MAPPING); enabled_from_env() mira
    CIEL_OIDC_ENABLED (default off).
  • map_oidc_claims_to_role(): mapeo configurable de claims → rol RBAC con
    defaults para Keycloak (realm_access.roles), Auth0/genérico (roles),
    Azure AD/Okta (groups). Fail-closed: sin match ⇒ sin rol.
  • make_oidc_dependency() (ciel.gateway.auth): dependency FastAPI que
    delega a la guard api_key cuando OIDC está off (open-mode por defecto) y exige
    Bearer JWT válido cuando está on, devolviendo un AuthContext
    (subject/role/claims/via).
  • Nuevo extra oidc = ["PyJWT[crypto]", "authlib", "python-multipart"].

F-Vault — Secretos dinámicos y rotación

  • LeasedSecret dataclass (name/value/lease_id/ttl/expires_at/
    renewable) con is_expired().
  • VaultSecretBackend.get_lease() para engines dinámicos (database/aws/...)
    vía client.read, y revoke_lease() best-effort. La ruta KV estática
    sigue igual (lease_id=None).
  • RotatingSecretStore: caché lease-aware con refresh proactivo (default al
    75% del TTL), revocación del lease previo al rotar (evita fugas), y
    degradación a last-known-good y a un backend estático (env) ante fallo de red.
    resolve()/require()/invalidate()/revoke() thread-safe (call-time).
  • Nuevo extra vault = ["hvac>=2.0"].

F-Sandbox — Guardrails + sandbox de ejecución

  • SandboxExecutor con backends seleccionables (SandboxBackend):
    INPROCESS (default cross-platform, subprocess + timeout), LIGHT (Linux,
    setrlimit CPU/mem; degrada a inprocess en Windows), DOCKER (opt-in:
    --network=none --read-only --cap-drop=ALL --security-opt=no-new-privileges --memory --pids-limit --rm) y GVISOR (opt-in, runtime runsc). Degradación
    graceful con log: backend fuerte no disponible ⇒ fallback a inprocess.
  • SandboxLimits (timeout/cpu/mem/pids/network) y ExecResult con
    telemetría (backend, duration_ms, limits_applied, timed_out).
  • GuardrailMiddleware: rate-limit por tenant (reusa TenantRateLimiter),
    redacción de secretos en salida (reusa redact_string) y truncado.
  • SandboxContext ahora ejecuta de verdad: execute/read_file/
    write_file reemplazan los antiguos stubs por ejecución real respaldada por
    SandboxExecutor (respetando la política de guardrails).

v0.8.0

Choose a tag to compare

@Ander-Labs Ander-Labs released this 17 Jul 21:41

[0.8.0] — Fase 14 (Escala y HA real) — 2026-07-17

Hace que ciel serve sea multi-réplica real (N>=2 detrás de un balanceador)
para deploy k8s/VPS enterprise. Corresponde a F15–F18 del roadmap
interno (v0.8 — Escala y HA real). Offline-safe primero: el default siempre
corre sin remoto (SQLite); Postgres es opt-in.

F15 — Checkpoint compartido

  • ciel.runtime.state_backend: ABC StateBackend (superficie mínima
    set/get/delete/search/record_tool_execution/close + is_ready() para
    readiness) + SqliteStateBackend (default offline) + PostgresStateBackend
    (SQLAlchemy, upsert idempotente por (tenant_id, session_id, key) para evitar
    races entre réplicas). Extra opcional pg (psycopg[binary]).
  • MemoryStore refactorizado para heredar de SqliteStateBackend:
    retrocompatible (MemoryStore(path) sigue funcionando) y aceptable donde se
    espera un StateBackend. Los 4 stores de resume (CheckpointStore,
    SessionStore, GraphCheckpointStore, EventLoopCheckpointStore) lo consumen
    sin cambios de API.
  • make_app lee CIEL_STATE_BACKEND / CIEL_STATE_DSN (o CIEL_STATE_SQLITE)
    y construye el backend; lo inyecta a los stores de checkpoint/session expuestos
    en app.state.

F16 — HA operativa

  • Health reales: GET /healthz (liveness, proceso vivo) y GET /readyz
    (readiness: StateBackend conectado + migrado; {"status":"ready"|"not_ready", "backend":"sqlite|postgres",...}). GET /health queda como alias.
  • Resume multi-réplica (ciel.runtime.resume): claim_run_lease adquiere un
    lease idempotente por run_id con TTL para evitar doble ejecución entre
    réplicas; release_run_lease lo libera; load_shared_checkpoint rehidrata
    desde el backend compartido.
  • El chart Helm apunta las probes de deployment a /healthz (liveness) y
    /readyz (readiness).

F17 — Runbooks + backup

  • docs/runbooks/backup.md CORREGIDO: el audit (enterprise/audit.py) es
    JSONL append-only particionado por tenant/session, NO SQLite (el runbook
    previo era incorrecto). Board = SQLite; state = SQLite/Postgres.
  • scripts/backup_state.py: backup offline-safe de audit/board/state (JSON
    local + tar.gz; S3 opcional vía CIEL_BACKUP_S3).
  • Chart Helm: templates/backupjob.yaml (CronJob BackupJob) + bloque
    backup: en values.yaml que vuelca audit/board/state a un PVC (o S3).
  • docs/runbooks/incident.md actualizado: resume multi-réplica con lease;
    /readyz not_ready semántica.

F18 — Cierre de release v0.8.0

  • Bump pyproject.toml 0.7.0 → 0.8.0; uv.lock regenerado.
  • Suite completa: 353 passed / 2 skipped (+14 tests Fase 14).
  • Release v0.8.0: tag + push + PyPI mana-ciel + GitHub Release "v0.8.0".

Notas

  • No rompe API: F15 solo cambia type hints + añade un parámetro de backend;
    MemoryStore sigue siendo construible igual.
  • Decisión de diseño: en prod el checkpoint STATE se comparte vía
    StateBackend (Postgres); los dashboards de Studio (costo/trace) son
    best-effort por réplica. No usar SQLite sobre PVC RWX (corrupción bajo
    concurrencia).

v0.7.0

Choose a tag to compare

@Ander-Labs Ander-Labs released this 17 Jul 16:52

[0.7.0] — Fase 13 (Ciel Studio / Observabilidad) — 2026-07-17

Item 1 (F19) — Ciel Studio dashboard mínimo (ciel.studio, offline-safe,
multitenant). La suite completa pasa (325 passed / 2 skipped, +10 tests).

Added

  • ciel.studio: StudioStore (en memoria, aislado por tenant_id) que
    registra SessionRecord (prompt, respuesta, tool-calls, turns) y
    LoopRecord; snapshot() devuelve sesiones + loops + counts.
  • install_studio_support(agent): envuelve agent.run/agent.arun para
    registrar cada ejecución en el store sin cambiar la firma ni el retorno
    (fachada sobre ciel.Agent existente).
  • Router FastAPI create_studio_router() en GET /v1/studio (snapshot),
    /sessions, /loops, /health; montado automáticamente por ciel serve.
  • CLI ciel studio show (src/ciel/cli/studio_cli.py): imprime el
    dashboard en consola. Expuesto como ciel studio.
  • Docs: docs/guide/studio.md, docs/api-reference/studio.md, nav en
    mkdocs.yml; roadmap.md corregido (v0.6 = Autonomía I ya liberada,
    v0.7 = Ciel Studio siguiente, "Escala y HA real" reordenada a v0.8).

F20 — Graph view + replay / time-travel (ciel.studio_trace)

  • GraphTraceStore (en memoria, offline): registra cada checkpoint de
    GraphCheckpointStore (record_checkpoint, list_runs, get_run con
    steps[] ordenados, replay(run_id) → estados step a step, snapshot()).
  • attach_trace(checkpointer): envuelve save sin cambiar firma/retorno.
  • Router create_trace_router() en GET /v1/studio/trace (/runs,
    /runs/{id}, /runs/{id}/replay, /health); montado por ciel serve.
  • CLI ciel studio trace (lista runs / replay de un run).

F21 — Cost dashboard (ciel.studio_cost)

  • CostDashboardStore (en memoria, offline): acumula métricas por tenant
    (record, by_tenant, summary{total_usd, by_model, requests, tenants}, top_tenants).
  • attach_cost_tracking(governor): envuelve CostGovernor.record para
    espejar gasto en el dashboard sin cambiar firma/retorno.
  • Router create_cost_router() en GET /v1/studio/cost (/summary,
    /by-tenant, /top, /health); montado por ciel serve.
  • CLI ciel studio cost (resumen por modelo/tenant).

F22 — Cierre de release v0.7.0

  • Integración: ciel/__init__.py expone studio, studio_trace,
    studio_cost; ciel serve monta los 3 routers; mkdocs.yml con nav de
    trace/cost.
  • Bump pyproject.toml 0.6.0 → 0.7.0; uv.lock regenerado.
  • Suite completa: 339 passed / 2 skipped (+24 tests Fase 13).
  • Release v0.7.0: tag + push + PyPI mana-ciel + GitHub Release "v0.7.0".

Notas

  • WebUIAdapter + su router (Fase 8) ya eran funcionales offline; Fase 13
    los complementa con el panel de observabilidad (sesiones/loops, trace/replay
    y costos). Todos los módulos son fachadas offline-safe sobre el runtime
    existente (sin cambios incompatibles).

Ciel Agent Framework v0.6.0 — Fase 12 (Autonomía I)

Choose a tag to compare

@Ander-Labs Ander-Labs released this 17 Jul 15:14

Fachada de Skill Library dinámica + auto-verificación sobre el runtime
existente (sin cambios incompatibles en el low-level Skill/SkillRegistry).
La suite completa pasa (315 passed / 2 skipped, +61 tests nuevos).

Added

  • SkillLibrary + SkillVerifier (ciel.runtime.skills_lib): store en
    memoria writeable que envuelve SkillRegistry; create_from_code() valida
    sintaxis, register/get/list_skills/remove/update (bump semántico +
    history() con linaje). SkillVerifier.verify(skill, test_cases) ejecuta
    casos offline y devuelve SkillVerificationResult.
  • Skill Versioning + changelog + Evolution Tree (ciel.runtime.skill_versioning):
    SkillVersion (major.minor.patch + changelog + released_at),
    set_changelog/changelog(lib, name) y evolution_tree(lib, name) (linaje
    con parent/children normalizado — base del Skill Evolution Tree único).
  • Skill Composition Engine (ciel.runtime.skill_composition):
    compose(name, skills, combinator) fusiona N skills (sequence/parallel/
    selector) en una nueva Skill, con detección de callable por AST.
  • Skill Doc Auto-Generation (ciel.runtime.skill_doc): generate_doc(skill)
    y to_markdown(skill) (frontmatter YAML desde docstring + firma).
  • Integración con ciel.Agent (ciel.runtime.skill_agent_integration):
    @ciel.skill (decorator → SkillLibrary global, valida sintaxis),
    Agent(skills=[...]) carga skills como ToolFunction ejecutables, y
    agent.teach(skill, test_cases=...) registra un skill verificado.
    Expuesto como ciel.skill / ciel.teach. API de Fase 10/11 intacta.
  • Skill Performance Metrics (ciel.runtime.skill_metrics):
    record_usage + metrics(name) con {calls, successes, failures, success_rate, avg_latency_ms} y aislamiento por tenant.
  • CLI ciel skills (ciel.cli.skills_cli): list / create / verify
    / remove offline-safe.

Tests

  • 7 nuevos módulos tests/test_fase12_*.py (61 tests): skill_library (13),
    versioning (12), composition (7), docgen (6), agent_integration (10),
    metrics (6), cli (7). Todos verdes.

Ciel Agent Framework v0.5.0 — Developer Experience II

Choose a tag to compare

@Ander-Labs Ander-Labs released this 16 Jul 21:16

[0.5.0] — Fase 11 (Developer Experience II) — 2026-07-16

Continúa la fachada de alto nivel de la Fase 10. Mismo principio: fachada
sobre el runtime existente, sin cambios incompatibles en el low-level
. La
suite completa pasa (254 passed / 2 skipped).

Added

  • Auto-provider desde model= (ciel.providers.auto): ciel.Agent(model="gpt-4o-mini")
    infiere el provider y lee la API key del entorno según el prefijo del id
    (gpt-*/o1*/o3* → OpenAI-compatible; claude-* → Anthropic;
    gemini-*/models/* → Gemini). provider= explícito sigue teniendo
    prioridad. Si no se pasa ni model= ni provider=, run()/arun() lanzan
    ValueError (comportamiento de Fase 10 preservado).
  • Loop ReAct multi-turno en Agent.run()/arun(): el runtime itera
    tool_calls → resultados hasta finish_reason == "stop" o max_turns
    (por defecto 10). El single-step se conserva cuando limit <= 1 o no hay
    tools, por lo que la API low-level no cambia.
  • AgentResponse.tool_results ahora es una lista plana con los resultados
    de todos los turnos; AgentResponse.tool_calls recolecta las llamadas de
    todos los turnos. finish_reason reporta "tool_calls" cuando el agente
    ejecutó tools en cualquier turno.
  • agent.astream(prompt): async iterator sobre runtime.stream_tokens()
    (streaming SSE real con OpenAI/Anthropic/Gemini; el texto final como un chunk
    para providers offline). max_turns mayor a 1 produce el texto final en un
    solo chunk tras el loop.
  • @ciel.tool(timeout=, retries=, middleware=): opciones de ejecución
    registradas en ToolFunction.options. retries se aplica en el callable del
    runtime (reintenta en fallos transitorios); middleware envuelve el callable;
    timeout queda disponible para el dispatcher. La inferencia de schema y el
    docstring no se ven afectados.
  • require_tenant=True por defecto opcional en Agent: si se activa y no
    hay tenant resoluble, run()/arun() lanzan ciel.common.TenantRequired
    con un mensaje DX-amigable (enforce tenancy desde día 1).
  • Cookbook offline de Fase 11 (docs/cookbook/auto_provider_multiturn.md).

Changed

  • DefaultAgentRuntime.run_agent_loop (runtime) ahora es multi-turno (gated),
    manteniendo backward-compatibilidad con el single-step histórico.

Tests

  • tests/test_fase11_dx_test.py: 12 tests nuevos (auto-provider por familia de
    modelo, multi-turno con todos los tool_results, max_turns, astream,
    @tool con opciones, require_tenant). Los 12 tests de fachada de Fase 10
    siguen verdes.

v0.3.0 — Fase 9 (Extensibilidad)

Choose a tag to compare

@Ander-Labs Ander-Labs released this 15 Jul 15:14

Publicado en PyPI: pip install mana-ciel==0.3.0 (distribución mana-ciel,
import ciel). Verificado: install limpio + default_registry().list_providers()
expone openai/anthropic/gemini + toolset builtins.

Added

  • Plugin system (ciel.plugins): PluginRegistry + default_registry() que
    auto-registra builtins y descubre plugins de terceros vía entry points
    (ciel.providers, ciel.tools, ciel.agents). Permite extender el framework
    sin tocar el core (pip install mi-plugin-ciel).
  • Providers empaquetados: GeminiProvider (ciel.providers.gemini) se suma a
    OpenAICompatibleProvider y AnthropicProvider (ya existentes). Los tres se
    registran como builtins en default_registry().
  • Tools de fábrica (ciel.runtime.tools_builtins): toolset builtins con
    echo, datetime (offline), http_get (inyectable mock client), file_read,
    shell (sandboxeados vía ciel.sandbox).
  • ciel init: scaffold de proyecto (pyproject + agent + ciel.yaml),
    offline-safe e idempotente. El agente generado corre sin red ni API keys.
  • Bug fix en ToolRegistry.register_tool: el ToolsetSchema.tools ahora se
    mantiene sincronizado (antes get_toolset_schema().tools salía vacío).

Fixed

  • ToolProvider.execute no ejecutaba el callable de la tool (bug de raíz). El
    provider concreto ciel.runtime.ToolProvider (usado por DefaultToolDispatcher
    y por tanto por DefaultAgentRuntime.run_agent_loop) invocaba el callable con la
    firma equivocada callable_(context, **arguments)TypeError/output=None.
    Corregido a la firma OFICIAL documentada:
    callable_(arguments: dict, *, tool_call_id: str, tenant_id: str | None) -> ToolResult | dict | Any
    (await si es corrutina; excepciones se capturan en ToolResult.error; acepta
    ToolResult o valor crudo). Alineados examples/quickstart_agent.py,
    tests/gateway_fase4_test.py y tests/test_toolcalls_integration_test.py con
    la firma oficial. Verificado end-to-end vía dispatcher (no solo llamando el
    callable directo).

Verification

  • uv run pytest tests/230 passed, 2 skipped (215 base + 13 Fase 9 +
    2 regresión dispatch: test_fase9_plugins_test.py 8, test_fase9_tools_test.py 7).
  • Smoke: uv run ciel init /tmp/demo genera proyecto que corre offline
    (echo: hello). default_registry() expone openai/anthropic/gemini + toolset
    builtins. GeminiProvider offline (sin api_key lanza; con client mock devuelve
    texto). Docs DX externas en docs/guide/ (subagente).

v0.2.0 — Fase 8 (Deploy HA + observabilidad)

Choose a tag to compare

@Ander-Labs Ander-Labs released this 15 Jul 15:14

Added

  • Helm HA: chart deploy/helm/ciel con replicaCount: 2, PodDisruptionBudget
    (minAvailable: 1), HorizontalPodAutoscaler (2–10 réplicas, target CPU 70%),
    podAntiAffinity (topologyKey kubernetes.io/hostname) y
    topologySpreadConstraints (maxSkew 1). Templates hpa.yaml +
    poddisruptionbudget.yaml.
  • OTel centralizado (ciel.observability.otel): init_tracing(*, otlp_endpoint) usa OTLPSpanExporter si hay endpoint o InMemorySpanExporter
    por defecto (offline-safe); current_tracer(), span_count() (cuenta spans del
    exporter in-memory), OtlpAuditExporter (sink de auditoría como spans). Comando
    ciel observe y flag --otel/--otel-endpoint en ciel serve.
  • Adapters de canal (ciel.adapters): TeamsAdapter, DiscordAdapter,
    WebUIAdapter + FakeAdapter (offline-safe, fakes en tests). Heredan
    MessagingAdapter/Message de ciel.gateway.adapter.
  • Routers de gateway (ciel.gateway.messaging): create_teams_webhook_router,
    create_discord_webhook_router, create_webui_router, montados en make_app
    (ciel serve) y exportados en ciel.gateway.__init__.
  • Human-in-the-loop (HIL) en ciel.orchestration.graph: GraphNode.require_approval,
    GraphPaused, GraphApprovalDenied, GraphRunner.approve()/deny() con
    chequeo RBAC (enterprise.rbac.check(action="approve:*")). El runner pausa y
    persiste paused=True; reanuda tras aprobación de rol autorizado.
  • Runbooks (docs/runbooks/): deploy HA, incidente, rollback, backup de
    audit/board (SQLite), escalado HPA.

Fixed

  • ciel.observability.otel.span_count() devolvía -1 siempre: (1) init_tracing
    no persistía _last_provider (faltaba global); (2) accedía a atributos
    inexistentes en opentelemetry-sdk 1.x (active_span_processor/span_exporter).
    Ahora navega provider._active_span_processor._span_processors[].span_exporter
    vía _find_in_memory_exporter.

Verification

  • uv run pytest tests/216 passed, 1 skipped (194 base F0–7 + 22 Fase 8:
    test_fase8_hil_otel_test.py 8, test_fase8_adapters_test.py 14).
  • Smoke: uv run ciel observe confirma exporter; init_tracing() + span →
    span_count() >= 1; ciel serve monta routers Teams/Discord/WebUI
    (/v1/messaging/{channel}/health → 200); grafo con require_approval pausa y
    reanuda tras aprobación de rol admin (approve:*), bob (viewer) bloqueado.