Releases: Ander-Labs/ciel-agent-framework
Release list
v0.12.0
[0.12.0] — Fase 18: Evaluación y testing
Nuevo
MockProviderdeterminista (ciel.providers.MockProvider): proveedor
offline-safe (sin red ni API keys) con modosfixed/echo/map; registrado
enauto_providercon prefijomock/. Para tests y eval reproducibles.ciel evaluate(CLI Typer,ciel.cli.evaluate):run(KPIs en tabla
Rich + exit-code por umbral),regression(gate contraresults.json
baseline) yredteam(prompt injection / fuga de tenant con assertions de
aislamiento). Cableado enciel.cli.main.ciel.eval:Evaluator(corre dataset sobre un agente/callable, acumula
KPIs, exportaresults.json),EvalCase,load_dataset(YAML) y métricas
deterministas propias:exact_match,contains,f1_token,faithfulness,
context_relevance(usaRetrieverdeciel.ragsi 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 devuelvenNone). - Extra
evalenpyproject.toml(deepeval,ragas,trulens-eval);
el core no lo requiere. - CI: nuevo job
evalen.github/workflows/ci.yml(offline,MockProvider,
sin red) que corretests/eval+ smoke CLI y hace gate de regression.
Cambios internos
- Corrección de documentación obsoleta:
docs/guide/concepts.mdrefería
MemoryStore(memoria declarativa FTS5) — ahora documentaEpisodicStore
(memoria episódica nativa por(tenant_id, session_id), Fase 17). - API pública aditiva:
ciel.eval(Evaluator,EvalCase, métricas,
load_dataset) yciel.providers.MockProvider. No rompeAgent,
AgentResponse,ToolResultniChatProvider.
v0.11.0
Nuevo
- Memoria episódica nativa (Pilar A, offline-safe):
ciel.runtime.memory_episodic
conEpisodicStoreyMemoryConfig. Persiste user/assistant por
(tenant_id, session_id), conappend/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.ragconKnowledgeBase/Retriever/SemanticCache, índice
vectorialInMemoryVectorStore+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íarag_tools(kb). - API pública aditiva:
ciel.EpisodicStore,ciel.MemoryConfigy
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
ragopcional enpyproject.toml(chromadb,pypdf); el
default corre sin red ni keys.
Cambios internos
state_backendgana métodos de memoria episódica (memory_append,
memory_get,memory_get_recent,memory_search_tenant,
memory_clear_session) sobre SQLite/Postgres, aislados portenant_id.Agent.arun/astreamusansession_idestable 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 aLIKEpor 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 conAgent). Suite total: 434 passed / 7 skipped.
v0.10.0
[0.10.0] — Fase 16: Providers y multimodal
Nuevo
- Multimodal nativo (offline-safe):
ChatMessage.contentaceptastr | list[dict](text/image_url/input_audio). HelperChatMessage.text()degrada multimodal a texto. - LiteLLM meta-provider (extra
litellm):LiteLLMProviderexpone 100+ modelos víaChatProvider, con Router para fallback/balanceo. Import diferido; sin el extra lanzaProviderError. - Nuevos providers:
AzureOpenAIProvider(deployment + api-version), Ollama local y vLLM/TGI comoOpenAICompatibleProvider.auto_providerreconoceazure/,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
[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
OIDCVerifierextendido (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 porkid(TTL +
refresh onkiddesconocido) y validación estricta deiss/aud/exp/alg
(solo RS/ES asimétricos; rechazanone/HS256en modo JWKS).OIDCVerifier.from_config()leeCIEL_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 unAuthContext
(subject/role/claims/via).- Nuevo extra
oidc = ["PyJWT[crypto]", "authlib", "python-multipart"].
F-Vault — Secretos dinámicos y rotación
LeasedSecretdataclass (name/value/lease_id/ttl/expires_at/
renewable) conis_expired().VaultSecretBackend.get_lease()para engines dinámicos (database/aws/...)
víaclient.read, yrevoke_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
SandboxExecutorcon backends seleccionables (SandboxBackend):
INPROCESS(default cross-platform,subprocess+ timeout),LIGHT(Linux,
setrlimitCPU/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) yGVISOR(opt-in, runtimerunsc). Degradación
graceful con log: backend fuerte no disponible ⇒ fallback a inprocess.SandboxLimits(timeout/cpu/mem/pids/network) yExecResultcon
telemetría (backend,duration_ms,limits_applied,timed_out).GuardrailMiddleware: rate-limit por tenant (reusaTenantRateLimiter),
redacción de secretos en salida (reusaredact_string) y truncado.SandboxContextahora ejecuta de verdad:execute/read_file/
write_filereemplazan los antiguos stubs por ejecución real respaldada por
SandboxExecutor(respetando la política de guardrails).
v0.8.0
[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: ABCStateBackend(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 opcionalpg(psycopg[binary]).MemoryStorerefactorizado para heredar deSqliteStateBackend:
retrocompatible (MemoryStore(path)sigue funcionando) y aceptable donde se
espera unStateBackend. Los 4 stores de resume (CheckpointStore,
SessionStore,GraphCheckpointStore,EventLoopCheckpointStore) lo consumen
sin cambios de API.make_appleeCIEL_STATE_BACKEND/CIEL_STATE_DSN(oCIEL_STATE_SQLITE)
y construye el backend; lo inyecta a los stores de checkpoint/session expuestos
enapp.state.
F16 — HA operativa
- Health reales:
GET /healthz(liveness, proceso vivo) yGET /readyz
(readiness:StateBackendconectado + migrado;{"status":"ready"|"not_ready", "backend":"sqlite|postgres",...}).GET /healthqueda como alias. - Resume multi-réplica (
ciel.runtime.resume):claim_run_leaseadquiere un
lease idempotente porrun_idcon TTL para evitar doble ejecución entre
réplicas;release_run_leaselo libera;load_shared_checkpointrehidrata
desde el backend compartido. - El chart Helm apunta las probes de deployment a
/healthz(liveness) y
/readyz(readiness).
F17 — Runbooks + backup
docs/runbooks/backup.mdCORREGIDO: 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íaCIEL_BACKUP_S3).- Chart Helm:
templates/backupjob.yaml(CronJobBackupJob) + bloque
backup:envalues.yamlque vuelca audit/board/state a un PVC (o S3). docs/runbooks/incident.mdactualizado: resume multi-réplica con lease;
/readyznot_readysemántica.
F18 — Cierre de release v0.8.0
- Bump
pyproject.toml0.7.0 → 0.8.0;uv.lockregenerado. - 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;
MemoryStoresigue 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
[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 portenant_id) que
registraSessionRecord(prompt, respuesta, tool-calls, turns) y
LoopRecord;snapshot()devuelve sesiones + loops + counts.install_studio_support(agent): envuelveagent.run/agent.arunpara
registrar cada ejecución en el store sin cambiar la firma ni el retorno
(fachada sobreciel.Agentexistente).- Router FastAPI
create_studio_router()enGET /v1/studio(snapshot),
/sessions,/loops,/health; montado automáticamente porciel serve. - CLI
ciel studio show(src/ciel/cli/studio_cli.py): imprime el
dashboard en consola. Expuesto comociel studio. - Docs:
docs/guide/studio.md,docs/api-reference/studio.md, nav en
mkdocs.yml;roadmap.mdcorregido (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_runcon
steps[]ordenados,replay(run_id)→ estados step a step,snapshot()).attach_trace(checkpointer): envuelvesavesin cambiar firma/retorno.- Router
create_trace_router()enGET /v1/studio/trace(/runs,
/runs/{id},/runs/{id}/replay,/health); montado porciel 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): envuelveCostGovernor.recordpara
espejar gasto en el dashboard sin cambiar firma/retorno.- Router
create_cost_router()enGET /v1/studio/cost(/summary,
/by-tenant,/top,/health); montado porciel serve. - CLI
ciel studio cost(resumen por modelo/tenant).
F22 — Cierre de release v0.7.0
- Integración:
ciel/__init__.pyexponestudio,studio_trace,
studio_cost;ciel servemonta los 3 routers;mkdocs.ymlcon nav de
trace/cost. - Bump
pyproject.toml0.6.0 → 0.7.0;uv.lockregenerado. - 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)
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 envuelveSkillRegistry;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 devuelveSkillVerificationResult.- Skill Versioning + changelog + Evolution Tree (
ciel.runtime.skill_versioning):
SkillVersion(major.minor.patch +changelog+released_at),
set_changelog/changelog(lib, name)yevolution_tree(lib, name)(linaje
conparent/childrennormalizado — 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 nuevaSkill, con detección de callable por AST. - Skill Doc Auto-Generation (
ciel.runtime.skill_doc):generate_doc(skill)
yto_markdown(skill)(frontmatter YAML desde docstring + firma). - Integración con
ciel.Agent(ciel.runtime.skill_agent_integration):
@ciel.skill(decorator →SkillLibraryglobal, valida sintaxis),
Agent(skills=[...])carga skills comoToolFunctionejecutables, y
agent.teach(skill, test_cases=...)registra un skill verificado.
Expuesto comociel.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
/removeoffline-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
[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 nimodel=niprovider=,run()/arun()lanzan
ValueError(comportamiento de Fase 10 preservado). - Loop ReAct multi-turno en
Agent.run()/arun(): el runtime itera
tool_calls → resultadoshastafinish_reason == "stop"omax_turns
(por defecto 10). El single-step se conserva cuandolimit <= 1o no hay
tools, por lo que la API low-level no cambia. AgentResponse.tool_resultsahora es una lista plana con los resultados
de todos los turnos;AgentResponse.tool_callsrecolecta las llamadas de
todos los turnos.finish_reasonreporta"tool_calls"cuando el agente
ejecutó tools en cualquier turno.agent.astream(prompt): async iterator sobreruntime.stream_tokens()
(streaming SSE real con OpenAI/Anthropic/Gemini; el texto final como un chunk
para providers offline).max_turnsmayor a 1 produce el texto final en un
solo chunk tras el loop.@ciel.tool(timeout=, retries=, middleware=): opciones de ejecución
registradas enToolFunction.options.retriesse aplica en el callable del
runtime (reintenta en fallos transitorios);middlewareenvuelve el callable;
timeoutqueda disponible para el dispatcher. La inferencia de schema y el
docstring no se ven afectados.require_tenant=Truepor defecto opcional enAgent: si se activa y no
hay tenant resoluble,run()/arun()lanzanciel.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,
@toolcon opciones,require_tenant). Los 12 tests de fachada de Fase 10
siguen verdes.
v0.3.0 — Fase 9 (Extensibilidad)
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
OpenAICompatibleProvideryAnthropicProvider(ya existentes). Los tres se
registran como builtins endefault_registry(). - Tools de fábrica (
ciel.runtime.tools_builtins): toolsetbuiltinscon
echo,datetime(offline),http_get(inyectable mock client),file_read,
shell(sandboxeados víaciel.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: elToolsetSchema.toolsahora se
mantiene sincronizado (antesget_toolset_schema().toolssalía vacío).
Fixed
ToolProvider.executeno ejecutaba el callable de la tool (bug de raíz). El
provider concretociel.runtime.ToolProvider(usado porDefaultToolDispatcher
y por tanto porDefaultAgentRuntime.run_agent_loop) invocaba el callable con la
firma equivocadacallable_(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 enToolResult.error; acepta
ToolResulto valor crudo). Alineadosexamples/quickstart_agent.py,
tests/gateway_fase4_test.pyytests/test_toolcalls_integration_test.pycon
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.py8,test_fase9_tools_test.py7).- Smoke:
uv run ciel init /tmp/demogenera proyecto que corre offline
(echo: hello).default_registry()expone openai/anthropic/gemini + toolset
builtins.GeminiProvideroffline (sin api_key lanza; con client mock devuelve
texto). Docs DX externas endocs/guide/(subagente).
v0.2.0 — Fase 8 (Deploy HA + observabilidad)
Added
- Helm HA: chart
deploy/helm/cielconreplicaCount: 2,PodDisruptionBudget
(minAvailable: 1),HorizontalPodAutoscaler(2–10 réplicas, target CPU 70%),
podAntiAffinity(topologyKeykubernetes.io/hostname) y
topologySpreadConstraints(maxSkew 1). Templateshpa.yaml+
poddisruptionbudget.yaml. - OTel centralizado (
ciel.observability.otel):init_tracing(*, otlp_endpoint)usaOTLPSpanExportersi hay endpoint oInMemorySpanExporter
por defecto (offline-safe);current_tracer(),span_count()(cuenta spans del
exporter in-memory),OtlpAuditExporter(sink de auditoría como spans). Comando
ciel observey flag--otel/--otel-endpointenciel serve. - Adapters de canal (
ciel.adapters):TeamsAdapter,DiscordAdapter,
WebUIAdapter+FakeAdapter(offline-safe, fakes en tests). Heredan
MessagingAdapter/Messagedeciel.gateway.adapter. - Routers de gateway (
ciel.gateway.messaging):create_teams_webhook_router,
create_discord_webhook_router,create_webui_router, montados enmake_app
(ciel serve) y exportados enciel.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
persistepaused=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-1siempre: (1)init_tracing
no persistía_last_provider(faltabaglobal); (2) accedía a atributos
inexistentes en opentelemetry-sdk 1.x (active_span_processor/span_exporter).
Ahora navegaprovider._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.py8,test_fase8_adapters_test.py14).- Smoke:
uv run ciel observeconfirma exporter;init_tracing()+ span →
span_count() >= 1;ciel servemonta routers Teams/Discord/WebUI
(/v1/messaging/{channel}/health→ 200); grafo conrequire_approvalpausa y
reanuda tras aprobación de roladmin(approve:*), bob (viewer) bloqueado.