- Inicio
- Documentation
- Sesiones
- Pipeline de generación de `/handoff`
Pipeline de generación de `/handoff`
Este documento describe cómo el agente de codificación implementa /handoff actualmente: ruta de activación, prompt de generación, captura de finalización, cambio de sesión y reinyección de contexto.
Alcance
Sección titulada «Alcance»Cubre:
- Despacho interactivo del comando
/handoff - Ciclo de vida y transiciones de estado de
AgentSession.handoff() - Cómo se captura la salida del handoff desde la salida del asistente
- Cómo las sesiones antiguas/nuevas persisten los datos de handoff de forma diferente
- Comportamiento de la interfaz de usuario para éxito, cancelación y fallo
No cubre:
- Navegación genérica de árbol e internos de ramificación
- Comandos de sesión que no son de handoff (
/new,/fork,/resume)
Archivos de implementación
Sección titulada «Archivos de implementación»../src/modes/controllers/input-controller.ts../src/modes/controllers/command-controller.ts../src/session/agent-session.ts../src/session/session-manager.ts../src/extensibility/slash-commands.ts
Ruta de activación
Sección titulada «Ruta de activación»/handoffse declara en los metadatos de comandos slash integrados (slash-commands.ts) con una sugerencia en línea opcional:[focus instructions].- En el manejo interactivo de entrada (
InputController), el texto enviado que coincide con/handoffo/handoff ...es interceptado antes del envío normal del prompt. - El editor se limpia y se llama a
handleHandoffCommand(customInstructions?). CommandController.handleHandoffCommandrealiza una verificación previa usando las entradas actuales:- Cuenta las entradas con
type === "message". - Si
< 2, advierte:Nothing to hand off (no messages yet)y retorna.
- Cuenta las entradas con
La misma guarda de contenido mínimo existe nuevamente dentro de AgentSession.handoff() y lanza un error si se viola. Esto duplica la seguridad tanto en la capa de interfaz de usuario como en la de sesión.
Ciclo de vida completo
Sección titulada «Ciclo de vida completo»1) Iniciar la generación del handoff
Sección titulada «1) Iniciar la generación del handoff»AgentSession.handoff(customInstructions?):
- Lee las entradas de la rama actual (
sessionManager.getBranch()) - Valida el conteo mínimo de mensajes (
>= 2) - Crea
#handoffAbortController - Construye un prompt fijo en línea que solicita un documento de handoff estructurado (
Goal,Constraints & Preferences,Progress,Key Decisions,Critical Context,Next Steps) - Agrega
Additional focus: ...si se proporcionan instrucciones personalizadas
El prompt se envía mediante:
await this.prompt(handoffPrompt, { expandPromptTemplates: false });expandPromptTemplates: false evita la expansión de slash/plantillas de prompt de esta carga de instrucciones interna.
2) Captura de la finalización
Sección titulada «2) Captura de la finalización»Antes de enviar el prompt, handoff() se suscribe a los eventos de sesión y espera agent_end.
Al recibir agent_end, extrae el texto del handoff del estado del agente buscando hacia atrás el mensaje assistant más reciente, luego concatena todos los bloques content donde type === "text" con \n.
Supuestos importantes de extracción:
- Solo se usan bloques de texto; el contenido que no sea texto se ignora.
- Se asume que el último mensaje del asistente corresponde a la generación del handoff.
- No analiza secciones markdown ni valida el cumplimiento del formato.
- Si la salida del asistente no tiene bloques de texto, el handoff se trata como ausente.
3) Verificaciones de cancelación
Sección titulada «3) Verificaciones de cancelación»handoff() retorna undefined cuando se cumple alguna de las siguientes condiciones:
- no hay texto de handoff capturado, o
#handoffAbortController.signal.abortedes verdadero
Siempre limpia #handoffAbortController en finally.
4) Creación de nueva sesión
Sección titulada «4) Creación de nueva sesión»Si se capturó texto y no fue abortado:
- Vaciar el escritor de la sesión actual (
sessionManager.flush()) - Iniciar una sesión completamente nueva (
sessionManager.newSession()) - Reiniciar el estado del agente en memoria (
agent.reset()) - Reasignar
agent.sessionIdal id de la nueva sesión - Limpiar los arreglos de contexto en cola (
#steeringMessages,#followUpMessages,#pendingNextTurnMessages) - Reiniciar el contador de recordatorio de tareas pendientes
newSession() crea un encabezado nuevo y una lista de entradas vacía (la hoja se reinicia a null). En la ruta de handoff, no se pasa ningún parentSession.
5) Inyección del contexto de handoff
Sección titulada «5) Inyección del contexto de handoff»El documento de handoff generado se envuelve y se agrega a la nueva sesión como una entrada custom_message:
<handoff-context>...handoff text...</handoff-context>
The above is a handoff document from a previous session. Use this context to continue the work seamlessly.Llamada de inserción:
this.sessionManager.appendCustomMessageEntry("handoff", handoffContent, true);Semántica:
customType:"handoff"display:true(visible en la reconstrucción de la TUI)- Tipo de entrada:
custom_message(participa en el contexto del LLM)
6) Reconstrucción del contexto activo del agente
Sección titulada «6) Reconstrucción del contexto activo del agente»Después de la inyección:
sessionManager.buildSessionContext()resuelve la lista de mensajes para la hoja actualagent.replaceMessages(sessionContext.messages)hace que el mensaje de handoff inyectado sea el contexto activo- El método retorna
{ document: handoffText }
En este punto, el contexto activo del LLM en la nueva sesión contiene el mensaje de handoff inyectado, no la transcripción anterior.
Modelo de persistencia: sesión antigua vs sesión nueva
Sección titulada «Modelo de persistencia: sesión antigua vs sesión nueva»Sesión antigua
Sección titulada «Sesión antigua»Durante la generación, la persistencia normal de mensajes permanece activa. La respuesta de handoff del asistente se persiste como una entrada regular message al ocurrir message_end.
Resultado: la sesión original contiene el handoff generado y visible como parte de la transcripción histórica.
Sesión nueva
Sección titulada «Sesión nueva»Después del reinicio de sesión, el handoff se persiste como custom_message con customType: "handoff".
buildSessionContext() convierte esta entrada en un mensaje de contexto personalizado/de usuario en tiempo de ejecución mediante createCustomMessage(...), de modo que se incluye en los futuros prompts de la nueva sesión.
Comportamiento del controlador/interfaz de usuario
Sección titulada «Comportamiento del controlador/interfaz de usuario»Comportamiento de CommandController.handleHandoffCommand:
- Llama a
await session.handoff(customInstructions) - Si el resultado es
undefined:showError("Handoff cancelled") - En caso de éxito:
rebuildChatFromMessages()(carga el nuevo contexto de sesión, incluido el handoff inyectado)- invalida la línea de estado y el borde superior del editor
- recarga las tareas pendientes
- agrega una línea de chat de éxito:
New session started with handoff context
- En caso de excepción:
- si el mensaje es
"Handoff cancelled"o el nombre del error esAbortError:showError("Handoff cancelled") - de lo contrario:
showError("Handoff failed: <message>")
- si el mensaje es
- Solicita renderizado al final
Semántica de cancelación (comportamiento actual)
Sección titulada «Semántica de cancelación (comportamiento actual)»Primitiva de cancelación a nivel de sesión
Sección titulada «Primitiva de cancelación a nivel de sesión»AgentSession expone:
abortHandoff()→ aborta#handoffAbortControllerisGeneratingHandoff→ verdadero mientras existe el controlador
Cuando se usa esta ruta de aborto, el suscriptor del handoff rechaza con Error("Handoff cancelled"), y el controlador de comandos lo mapea a la interfaz de usuario de cancelación.
Limitación de la ruta interactiva de /handoff
Sección titulada «Limitación de la ruta interactiva de /handoff»En el cableado actual del controlador interactivo, /handoff no instala un manejador dedicado de Escape que llame a abortHandoff() (a diferencia de las rutas de compactación/resumen de rama que anulan temporalmente editor.onEscape).
Impacto práctico:
- Existe soporte de cancelación a nivel de sesión, pero no hay un enlace de tecla específico para handoff en la ruta del comando
/handoff. - La interrupción del usuario aún puede ocurrir mediante rutas de aborto de agente más amplias, pero eso no es el mismo canal de cancelación explícito usado por
abortHandoff().
Handoff abortado vs handoff fallido
Sección titulada «Handoff abortado vs handoff fallido»Clasificación actual de la interfaz de usuario:
-
Abortado/cancelado
- La ruta
abortHandoff()activa"Handoff cancelled", o - se lanza
AbortError - La interfaz muestra
Handoff cancelled
- La ruta
-
Fallido
- cualquier otro error lanzado desde
handoff()/ la canalización de prompts (errores de validación de modelo/API, excepciones en tiempo de ejecución, etc.) - La interfaz muestra
Handoff failed: ...
- cualquier otro error lanzado desde
Matiz adicional: si la generación se completa pero no se extrae texto, handoff() retorna undefined y el controlador actualmente reporta cancelado, no fallido.
Salvaguardas de sesión corta y contenido mínimo
Sección titulada «Salvaguardas de sesión corta y contenido mínimo»Dos guardas evitan handoffs con poca información:
- Capa de interfaz de usuario (
handleHandoffCommand): advierte y retorna anticipadamente para< 2entradas de mensajes - Capa de sesión (
handoff()): lanza la misma condición como error
Esto evita crear una nueva sesión con un contexto de handoff vacío o casi vacío.
Resumen de transiciones de estado
Sección titulada «Resumen de transiciones de estado»Flujo de estado de alto nivel:
- Comando slash interactivo interceptado
- Guarda de conteo de mensajes previa
#handoffAbortControllercreado (isGeneratingHandoff = true)- Prompt de handoff interno enviado (visible en el chat como generación normal del asistente)
- Al recibir
agent_end, se extrae el último texto del asistente - Si falta/fue abortado → retornar
undefinedo ruta de error de cancelación - Si está presente:
- vaciar la sesión antigua
- crear una nueva sesión vacía
- reiniciar colas/contadores en tiempo de ejecución
- agregar
custom_message(handoff) - reconstruir y reemplazar los mensajes activos del agente
- El controlador reconstruye la interfaz de chat y anuncia el éxito
#handoffAbortControllerlimpiado (isGeneratingHandoff = false)
Supuestos y limitaciones conocidos
Sección titulada «Supuestos y limitaciones conocidos»- La extracción del handoff es heurística: “últimos bloques de texto del asistente”; sin validación estructural.
- No hay verificación estricta de que el markdown generado siga el formato de sección solicitado.
- El texto extraído faltante se reporta como cancelación en la experiencia de usuario del controlador.
- El flujo interactivo de
/handoffactualmente carece de un enlace dedicado Escape→abortHandoff(). - Los metadatos de linaje de la nueva sesión (
parentSession) no se establecen mediante esta ruta.