AgentSession: capa de orquestación del agent de codificación
AgentSession es la capa más pesada de pi-coding-agent: no ejecuta el LLM directamente ni renderiza UI, sino que se sitúa entre ambos. Arriba recibe la entrada del usuario y los eventos de UI; abajo dirige el Agent de pi-agent-core para que corra el bucle. El registro de modelos, las definiciones de herramientas, la construcción del system prompt, la compactación de contexto, los reintentos de fallo y la cola de mensajes son todos cosa suya. Se puede entender como el "taller de ensamblaje" que envuelve al Agent genérico y lo convierte en un asistente de codificación con estado, recuperable y con skills y herramientas.
Responsabilidades
AgentSession hace cuatro cosas:
- Ensamlar:
AgentSessionConfigrecibe la instancia deAgent, el registro de modelos, las definiciones de herramientas, el runtime de extensiones y el constructor del system prompt; al construir se cuelgan los hooksbeforeToolCall/afterToolCally la suscripción a eventos. Verpackages/coding-agent/src/core/agent-session.ts:313-330. - Recibir entrada:
prompt(text)es la entrada principal; procesa comandos slash/, expande plantillas de prompt tipo archivo, convierte el texto aAgentMessage[]y se lo pasa alAgentsubyacente. Verpackages/coding-agent/src/core/agent-session.ts:967-1000. - Reenvío de eventos: se suscribe al flujo de eventos del
Agenty los reenvuelve comoAgentSessionEventhacia la UI de arriba. Verpackages/coding-agent/src/core/agent-session.ts:330-335. - Cola y reintento: cuando el usuario sigue escribiendo durante el streaming,
streamingBehaviordecide si va porsteer(interrupción) o porfollowUp(cola), en vez de descartar o bloquear. Verpackages/coding-agent/src/core/agent-session.ts:1181-1296.
Motivación de diseño
¿Por qué no dejar que la UI llame directamente a Agent.prompt? Porque el asistente de codificación necesita un montón de lógica transversal que "el Agent genérico no gestiona": comandos slash (/model, /settings), expansión de plantillas de prompt, validación de modelo/API key, compactación automática al exceder el contexto, y auth y telemetría alrededor de las llamadas a herramientas. Meter todo eso en Agent haría el bucle genérico más pesado; ponerlo en la capa de UI lo duplicaría entre los modos print, rpc y las extensiones. AgentSession se abstrae como única entrada; los tres modos de ejecución comparten la misma lógica de orquestación y la UI sólo renderiza eventos.
Archivos clave
packages/coding-agent/src/core/agent-session.ts:121-149— TiposAgentSessionEventyAgentSessionConfig, definen la forma de los eventos y la interfaz de ensamblaje.packages/coding-agent/src/core/agent-session.ts:244-253— Declaración declass AgentSessiony campos de estado internos.packages/coding-agent/src/core/agent-session.ts:313-335— Constructor: cuelga hooksbeforeToolCall/afterToolCally se suscribe a los eventos delAgent.packages/coding-agent/src/core/agent-session.ts:379-435— Implementación de los hooks antes y después de tool calls; hace auth y reemite eventos.packages/coding-agent/src/core/agent-session.ts:967-1109— Flujo principal deprompt: dispatch de comandos, expansión de plantillas,this.agent.prompt(messages).packages/coding-agent/src/core/agent-session.ts:1181-1296— Semántica de colasteer/followUp.packages/coding-agent/src/core/agent-session.ts:1317-1389—sendUserMessage(entrada programática) yabort.packages/coding-agent/src/core/agent-session.ts:713-743—subscribe, la UI obtiene eventos a través de aquí.
El constructor cuelga hooks y se suscribe; es el punto de partida de toda la orquestación:
// packages/coding-agent/src/core/agent-session.ts:313-335
constructor(config: AgentSessionConfig) {
// ... guarda config, inicializa estado ...
this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
// ... etc ...
}prompt convierte el texto del usuario en AgentMessage[] y se lo pasa al Agent subyacente:
// packages/coding-agent/src/core/agent-session.ts:1107-1112
try {
await this.agent.prompt(messages);
} catch (error) {
// ... gestión de fallo ...
}Flujo de datos
El usuario pulsa Enter en el editor de la TUI → InteractiveMode.defaultEditor.onSubmit → session.prompt(text):
Cuando el usuario sigue escribiendo durante el streaming, prompt no empuja forzadamente, sino que distribuye:
Límites y fallos
- Conflicto de comando slash: los comandos registrados por extensiones pueden ejecutarse en streaming (gestionan su propia interacción con el LLM), sin pasar por la cola. Si no se puede encolar, lanza. Ver
packages/coding-agent/src/core/agent-session.ts:1255-1259. - Modelo/key ausente: la ruta no-streaming valida modelo y API key; si faltan, lanza en vez de enviar una petición vacía.
- Compactación abortada:
abortcancela a la vez el bucle principal, el controller de compactación y el controller de resumen de branch. Verpackages/coding-agent/src/core/agent-session.ts:1744-1752. - Reintento:
Agent.emit()llama síncronamente a_handleAgentEvent, mientras queprompt()espera conwaitForRetry(); tras un fallo, la lógica de reintento decide si se vuelve a emitir otro turno.
Resumen
AgentSession es el "taller de ensamblaje + bus de eventos" del asistente de codificación: ensambla el Agent, reenvía eventos, encola interrupciones y gestiona los hooks de herramientas. Arriba están los tres modos de UI InteractiveMode/runPrintMode/runRpcMode; abajo está el bucle genérico de pi-agent-core. Los detalles de ensamblaje en ensamblaje createAgentSession; el bucle subyacente en bucle while doble.