CORE-22 — Política de transición de la API pública SymfonicAgent (v11)¶
Estado y autoridad¶
La decisión ya fue tomada en CORE-388: ruptura explícita dentro de la línea
privada 11.x, sin shim de compatibilidad. Desde la versión privada 11.3.0,
SymfonicAgent(...) y AgentBuilder().build() rechazan con
EntryPointRemovedError antes de construir un agente. La metadata actual de
este árbol declara 11.4.0 (pyproject.toml); no es una afirmación sobre la
versión instalada por cada adopter.
Los nombres siguen siendo importables para entregar el error de migración.
En este árbol, el cargador perezoso de symfonic.agent resuelve SymfonicAgent desde
agent/entry_point_removal.py, sin importar engine.py ni agent.cutover;
AgentBuilder.build() aplica el mismo rechazo bajo su propio nombre.
__all__ conserva nombres públicos, pero no los convierte en constructores
que ejecuten el motor legacy. Véanse src/symfonic/agent/__init__.py,
entry_point_removal.py, builder.py y las pruebas
tests/agent/test_core388_public_entry_points.py y
tests/agent/test_core413_refusal_without_engine.py.
La ruta pública es symfonic.Agent, symfonic.platform.AgentHost o
symfonic.migration.agent_from_config, que rechaza por nombre las opciones
activas que no traduce. Para conservar las entradas legacy, el error ofrece
symfonic-core==9.12.*, la última línea pública que las admite. Véase la
guía de transición.
Este rechazo público no acredita paridad completa, cero fallbacks en una
flota ni retiro físico de engine.py. Los cuerpos legacy permanecen en el
árbol; retirarlos requiere la autorización y las catorce condiciones de
CORE-25.
Antecedente histórico — propuesta anterior a CORE-388¶
El diagnóstico, inventario y las opciones siguientes conservan la evaluación previa a aquella decisión. Describen las superficies de esa etapa, no la API admitida por el paquete actual. La decisión y las rutas vigentes son las del apartado anterior.
Estado evaluado entonces¶
Esta era una política propuesta para que el owner decidiera. No aprobaba una
deprecación, no cambiaba contratos ni autorizaba retirar engine.py. La evidencia
de esa evaluación contradecía cualquier anuncio de reemplazo completo:
symfonic.agent.SymfonicAgentseguía publicado en__all__y su cargador perezoso lo resolvía desdesymfonic.agent.engine(src/symfonic/agent/__init__.py:24,__getattr__).- El top-level
symfonic.Agentsí es la fachada canónica nueva (src/symfonic/__init__.py:44,_ORIGINS), pero no es un alias deSymfonicAgenty no se exporta desdesymfonic.agent. - El contrato de top-level de esa etapa afirmaba que
SymfonicAgentera tier 1 y lo orientaba para HMS, scopes, sesiones, checkpoints, resume, subagents y callbacks (src/symfonic/__init__.pydocstring de esa etapa). Esa era la promesa que la propuesta necesitaba resolver. SymfonicAgent.runpuede delegar al kernel, pero una invocación no admitida llega a_legacy_run_impl(src/symfonic/agent/engine.py:SymfonicAgent.run,_legacy_run_impl).FrameworkConfig()queda explícitamente fuera de la admisión porauto_hydrate=True(tests/agent/cutover/test_retrieval_bundle_admission.py:TestTheEnvelopeStaysClosedWithoutEvidence.test_the_stock_defaults_are_still_refused_without_a_bundle).
Conclusión de esa evaluación: no se podía anunciar compatibilidad de la
sustitución por Agent sin decisión y evidencia por contrato. CORE-388 eligió
después una ruptura explícita, no una promesa de paridad completa.
Superficies públicas y mapeo nativo¶
Contrato histórico de SymfonicAgent |
Referencia de implementación | Mapeo Agent evaluado entonces |
Estado de mapeo evaluado |
|---|---|---|---|
Construcción con FrameworkConfig, graph/vector/embedding backends, orchestrator, conversation manager y subagents |
engine.py:SymfonicAgent.__init__ |
Agent(model_provider, instructions, model, tools, capabilities, max_model_rounds) en facade.py:Agent.__init__ |
No equivalente uno-a-uno. Configuración y dependencias HMS requieren capabilities/roots nativos. |
run(query, scope, callbacks, session_id, history, response_model, **state_overrides) → AgentResponse |
engine.py:SymfonicAgent.run; agent/types.py:AgentResponse |
Agent.run(prompt, attachments, history, state, session_id, output_type) → AgentResult (facade.py:Agent.run; facade_types.py:AgentResult) |
Parcial y con ruptura de firma/resultado. scope, callbacks y metadata no tienen sustituto directo. |
stream(...) → StreamChunk |
engine.py:SymfonicAgent.stream; agent/types.py:StreamChunk |
Agent.stream(...) → AgentEvent (facade.py:Agent.stream; facade_types.py:AgentEvent) |
Parcial y con contrato de eventos distinto. |
stream_typed, stream_text |
engine.py:SymfonicAgent.stream_typed, stream_text |
Ningún método homónimo en Agent |
Sin mapeo público demostrado. |
resume, resume_interrupt y tokens de pausa |
engine.py:SymfonicAgent.resume, resume_interrupt |
Agent.continue_recovered(RecoveredContinuation) (facade_continuation.py) |
No equivalente público: exige recuperación/autenticación por host y no acepta el token legacy. |
get_chat_model, load_plugin, validate_action, on_user_correction, flush_background_tasks, lifecycle async |
engine.py métodos públicos de SymfonicAgent |
Ningún método homónimo en Agent |
Sin sustituto público demostrado. |
HTTP legacy /chat, /stream, /stream/typed, /resume |
agent/fastapi/router.py:create_agent_router |
Scaffold/roots kernel-native, no adaptador de firma equivalente probado | Sin mapeo de transporte aprobado. |
Un resultado con texto coincidente no prueba equivalencia: AgentResult usa
text, mensajes tipados y stop_reason; AgentResponse publica, entre otros,
final_response, memoria usada, session_id, delegación y activation_log
(agent/facade_types.py:AgentResult; agent/types.py:AgentResponse).
Compatibilidad que debe conservarse o ser decidida¶
Errores y validación¶
La transición debe especificar por fila si conserva tipo, código, momento de
fallo y representación HTTP. La ruta legacy expone SymfonicAgentError y el
router traduce su .code a HTTP (agent/errors.py:SymfonicAgentError;
agent/fastapi/router.py:_status_from_agent_error). La fachada nativa expone
ConfigurationError y ContractViolationError
(core/contracts/errors.py). Cambiar un error sin un adaptador es una ruptura
de API y del transporte, incluso si la ejecución correcta es kernel-native.
Streaming¶
No se puede ofrecer una equivalencia implícita entre StreamChunk(event_type,
data, timestamp, run_id) y AgentEvent(kind, index, text, tool_call, result,
error, interrupt, stage) (agent/types.py:StreamChunk;
agent/facade_types.py:AgentEvent). La política debe elegir y congelar uno de
estos contratos por versión; un shim tendrá que proyectar explícitamente,
incluidos done, error, cancelación, herramientas e interrupciones.
Pausa, resume y sesión¶
Agent.continue_recovered es intencionalmente host-owned: recibe una
RecoveredContinuation, no un token ni scope elegido por el llamador
(agent/facade_continuation.py:AgentContinuationMixin). Por tanto, ningún
consumidor de resume/resume_interrupt puede migrar por sustitución textual.
El owner debe decidir el protocolo de recuperación, autenticación, expiración
y mapeo de errores antes de anunciar una fecha de deprecación.
Consumidores que la decisión debe cubrir¶
El inventario de aquella evaluación encontró 36 archivos bajo src/symfonic
con construcción/importación de SymfonicAgent, y 233 archivos bajo
examples/tests con referencia. Los números son señales de alcance, no una
prueba de todos los consumidores externos.
Rutas productivas confirmadas que no deben romperse por una deprecación solo de import:
agent/fastapi/router.py:create_agent_routery routers de memoria, privacidad, grafo y mantenimiento aceptanSymfonicAgent.diagnostics/cli.pyconstruye el agente parasymfonic doctor; el runner y sus checks dependen de esa forma.cli/chat.pyconstruyeSymfonicAgentpara los modos HMS/domain/DSN.agent/builder.py:AgentBuildery el grafo de subagents preservan la forma legacy de construcción.
Antes de cualquier fecha, CORE-22 debe pedir además: telemetría de imports y fallbacks cuando esté disponible (CORE-24), búsqueda de distribuidores y adopters soportados, y una lista versionada de ejemplos/tests que representan cada contrato externo. La búsqueda interna no descubre paquetes de terceros.
Opciones de decisión para el owner¶
Opción A — Compatibilidad administrada (recomendada para evaluar)¶
Mantener from symfonic.agent import SymfonicAgent y sus métodos como shim
documentado. Cada método delega solo a un target kernel que haya superado su
contrato diferencial; si no, permanece compatible o rechaza de forma explícita
según una decisión de fila. Añadir advertencia deprecable únicamente cuando la
fila tiene sustituto público, guía de migración, telemetría y periodo de
soporte definidos. La retirada física solo sigue a los gates de CORE-18/25.
Ventaja: no confunde disponibilidad de Agent con equivalencia. Coste: se
mantiene el shim hasta cerrar todos los contratos publicados.
Opción B — Ruptura mayor explícita¶
En una major futura, retirar el import/métodos legacy y publicar un adaptador o guía de migración fuera de la API estable. Exige declarar expresamente que se rompen APIs de HMS, transporte, streaming y resume donde no haya equivalente. No es deprecación silenciosa y no es compatible con la promesa tier-1 actual sin un cambio de contrato aprobado.
Opción C — Mantener dos APIs soportadas¶
Promover Agent para composición nueva y conservar SymfonicAgent como API
de alto nivel con lifecycle propio. Eliminar engine.py seguiría requiriendo
reimplementar internamente todas sus superficies, pero no se prometería una
migración de consumidor que el código aún no soporta.
Ninguna opción estaba aprobada en aquella propuesta. La decisión posterior de CORE-388 y su implementación se describen en «Estado y autoridad».
Secuencia mínima si se elige una transición¶
- Congelar el catálogo de métodos y transportes de
SymfonicAgent, con contrato observable, adopter representativo y owner por fila. - Para cada fila, registrar target nativo, diferencial Agent/legacy, métrica de errores/fallback y resultado: preservar, adaptar, deprecar o romper.
- Implementar y probar adaptadores de resultado, streaming, errores y recuperación antes de emitir advertencias.
- Migrar primero composition roots propios (FastAPI, CLI, diagnostics, builder), manteniendo pruebas de compatibilidad de entrada/salida.
- Publicar una ventana de soporte con versión inicial, versión de advertencia, criterios de adopción y versión de eliminación. Estos cuatro valores deben venir de una decisión de release owner, no de este documento.
- Solo tras cero rutas legacy admitidas/fallback medido, contratos cerrados y
gates de retiro satisfechos, evaluar eliminar el shim y
engine.py.
Decisión exacta solicitada al owner¶
Registrar una sola resolución con:
- opción A, B o C;
- versión objetivo para advertir, duración de soporte y versión objetivo para ruptura/eliminación (si aplica);
- lista de contratos permitidos para adaptador versus contratos que se consideran ruptura explícita, especialmente streaming, resume, callbacks, scope/HMS y respuesta;
- política de tipo/código de error y proyección SSE/HTTP durante la ventana;
- owner y evidencia de paridad para cada fila antes de cambiar su estado.
Esta solicitud histórica dejaba CORE-22 pendiente de decisión. Fue sustituida por la decisión explícita de CORE-388: desde la línea privada 11.3 ambas entradas rechazan; los nombres importables no sirven turnos.
Comandos de localización de aquella evaluación¶
Estos comandos localizan las superficies examinadas; ejecutados sobre el árbol actual, muestran los rechazos vigentes, no reconstruyen el diagnóstico histórico ni certifican una equivalencia de APIs.
rg -n "SymfonicAgent|__all__|def __getattr__" src/symfonic/agent/__init__.py src/symfonic/__init__.py
rg -n "^ async def (run|stream|stream_typed|resume|resume_interrupt)|^ def (get_chat_model|load_plugin)" src/symfonic/agent/engine.py
rg -n "^class Agent|^ async def run|^ def stream|continue_recovered" src/symfonic/agent/facade.py src/symfonic/agent/facade_continuation.py
rg -l "from symfonic\\.agent(?:\\.engine)? import SymfonicAgent|SymfonicAgent\\(" src/symfonic --glob '*.py'