> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Bucle del agente

> Explica cómo Copilot CLI procesa desde la recepción del prompt hasta session.idle.

## Bucle del agente

Explica cómo Copilot CLI procesa un mensaje del usuario de extremo a extremo (desde el envío del prompt hasta `session.idle`).

## Arquitectura

```mermaid theme={null}
graph LR
    App["Your App"] -->|send prompt| SDK["SDK Session"]
    SDK -->|JSON-RPC| CLI["Copilot CLI"]
    CLI -->|API calls| LLM["LLM"]
    LLM -->|response| CLI
    CLI -->|events| SDK
    SDK -->|events| App
```

El **SDK** es la capa de transporte. Envía los prompts al **Copilot CLI** mediante JSON-RPC y retransmite los eventos a tu aplicación. El **CLI** es quien ejecuta el bucle de uso de herramientas al estilo agente, orquestando una o más llamadas a la API del LLM hasta completar la tarea.

## Bucle de uso de herramientas

Cuando llamas a `session.send({ prompt })`, el CLI entra en el siguiente bucle:

```mermaid theme={null}
flowchart TD
    A["User prompt"] --> B["LLM API call<br>(= one turn)"]
    B --> C{"toolRequests<br>in response?"}
    C -->|Yes| D["Execute tools<br>Collect results"]
    D -->|"Results fed back<br>as next turn input"| B
    C -->|No| E["Final text<br>response"]
    E --> F(["session.idle"])

    style B fill:#1a1a2e,stroke:#58a6ff,color:#c9d1d9
    style D fill:#1a1a2e,stroke:#3fb950,color:#c9d1d9
    style F fill:#0d1117,stroke:#f0883e,color:#f0883e
```

En cada llamada, el modelo consulta **todo el historial de la conversación** (system prompt, mensaje del usuario y todas las llamadas a herramientas y sus resultados hasta ese momento).

**Importante:** una iteración de este bucle equivale exactamente a una llamada a la API del LLM y aparece en el log de eventos como un par `assistant.turn_start` / `assistant.turn_end`. No hay llamadas ocultas.

## Qué es un turn

Un **turn** es una llamada a la API del LLM y su resultado.

1. El CLI envía el historial de la conversación al LLM
2. El LLM responde (posiblemente incluyendo solicitudes de herramientas)
3. Si hay solicitudes de herramientas, el CLI las ejecuta
4. Se emite `assistant.turn_end`

En un solo mensaje del usuario suelen producirse **varios turns**. Por ejemplo, ante una pregunta como «¿cómo funciona X en esta base de código?» pasa esto:

| Turn | Comportamiento del modelo                                  | ¿toolRequests?          |
| ---- | ---------------------------------------------------------- | ----------------------- |
| 1    | Busca en la base de código con `grep` y `glob`             | ✅ Sí                    |
| 2    | Lee ficheros concretos según los resultados de la búsqueda | ✅ Sí                    |
| 3    | Lee más para obtener contexto adicional                    | ✅ Sí                    |
| 4    | Genera la respuesta final en texto                         | ❌ No → termina el bucle |

En cada turn, el modelo decide si «usar más herramientas» o «pasar a la respuesta final». Como cada llamada ve **todo el contexto acumulado** (incluyendo las llamadas a herramientas y resultados anteriores), puede evaluar si tiene información suficiente.

## Flujo de eventos con varios turns

```mermaid theme={null}
flowchart TD
    send["session.send({ prompt: &quot;Fix the bug in auth.ts&quot; })"]

    subgraph Turn1 ["Turn 1"]
        t1s["assistant.turn_start"]
        t1m["assistant.message (toolRequests)"]
        t1ts["tool.execution_start (read_file)"]
        t1tc["tool.execution_complete"]
        t1e["assistant.turn_end"]
        t1s --> t1m --> t1ts --> t1tc --> t1e
    end

    subgraph Turn2 ["Turn 2 — auto-triggered by CLI"]
        t2s["assistant.turn_start"]
        t2m["assistant.message (toolRequests)"]
        t2ts["tool.execution_start (edit_file)"]
        t2tc["tool.execution_complete"]
        t2e["assistant.turn_end"]
        t2s --> t2m --> t2ts --> t2tc --> t2e
    end

    subgraph Turn3 ["Turn 3"]
        t3s["assistant.turn_start"]
        t3m["assistant.message (no toolRequests)<br>&quot;Done, here's what I changed&quot;"]
        t3e["assistant.turn_end"]
        t3s --> t3m --> t3e
    end

    idle(["session.idle — ready for next message"])

    send --> Turn1 --> Turn2 --> Turn3 --> idle
```

## Quién inicia cada turn

| Actor           | Responsabilidad                                                                                                          |
| --------------- | ------------------------------------------------------------------------------------------------------------------------ |
| **Tu app**      | Envía el prompt inicial con `session.send()`                                                                             |
| **Copilot CLI** | Ejecuta el bucle de uso de herramientas y devuelve los resultados de la ejecución al LLM como entrada del siguiente turn |
| **LLM**         | Decide si continuar solicitando herramientas o devolver la respuesta final y terminar                                    |
| **SDK**         | Solo retransmite eventos. No controla el bucle                                                                           |

El comportamiento del CLI es mecánico («el modelo pide una herramienta → se ejecuta → se vuelve a llamar al modelo»). Quien toma la decisión de parar es el **modelo**.

## Diferencias entre `session.idle` y `session.task_complete`

Ambas son señales de finalización, pero garantizan cosas muy distintas.

### `session.idle`

* Se emite **siempre** al terminar el bucle de uso de herramientas
* Es **efímera**: no se persiste en disco ni se reproduce al reanudar la sesión
* Significado: «el agente ha dejado de procesar y puede aceptar el siguiente mensaje»
* Úsala como señal fiable de «terminado»

En el SDK, `sendAndWait()` espera este evento.

```typescript theme={null}
// Espera hasta que se emita session.idle
const response = await session.sendAndWait({ prompt: "Fix the bug" });
```

### `session.task_complete`

* Se emite **opcionalmente** (solo cuando el modelo lo señala explícitamente)
* Es **persistente** (se guarda en el log de eventos de la sesión)
* Significado: «el propio agente considera que la tarea global se ha cumplido»
* Puede incluir un `summary` opcional

```typescript theme={null}
session.on("session.task_complete", (event) => {
    console.log("Task done:", event.data.summary);
});
```

### Modo autopilot: el CLI empuja al modelo a llamar a `task_complete`

En el modo **autopilot** (headless / autónomo), el CLI hace un seguimiento de si el modelo ha llamado a `task_complete`. Si el bucle de uso de herramientas termina sin `task_complete`, el CLI inyecta el siguiente mensaje sintético de usuario para instar al modelo:

> *"You have not yet marked the task as complete using the task\_complete tool. If you were planning, stop planning and start implementing. You aren't done until you have fully completed the task."*

Con esto, el bucle de uso de herramientas se reanuda de hecho. El modelo recibe ese aviso como un nuevo mensaje de usuario y continúa el trabajo. El aviso incluye también «no llames a `task_complete` demasiado pronto»:

* No lo llames si quedan dudas sin resolver. Decide y sigue trabajando.
* No lo llames solo por haber encontrado un error. Intenta resolverlo.
* No lo llames si quedan tareas pendientes. Termínalas primero.

En autopilot hay entonces un **mecanismo de finalización en dos fases**:

1. El modelo llama a `task_complete` con un summary → el CLI emite `session.task_complete` → terminado
2. El modelo se detiene sin llamarlo → el CLI le insiste → el modelo continúa o llama a `task_complete`

### Por qué puede no aparecer `task_complete`

En el modo **interactive** (chat convencional), el CLI no empuja hacia `task_complete`. El modelo puede omitirlo. Principales razones:

* **Q\&A conversacional**: responde a la pregunta y termina, sin una «tarea» discreta que completar
* **Decisión del modelo**: devuelve el texto final sin llamar a `task_complete`
* **Sesión interrumpida**: la sesión termina antes de que el modelo llegue a un punto de finalización

En cambio, el CLI siempre emite `session.idle` porque no es una señal semántica (el modelo lo considera completo) sino mecánica (el bucle terminó).

### ¿Cuál deberías usar?

| Caso de uso                                        | Señal                                 |
| -------------------------------------------------- | ------------------------------------- |
| «Esperar a que el agente termine de procesar»      | `session.idle` ✅                      |
| «Saber que una tarea de programación ha terminado» | `session.task_complete` (best-effort) |
| «Gestionar timeouts / errores»                     | `session.idle` + `session.error` ✅    |

## Contar el número de llamadas al LLM

El número de pares `assistant.turn_start` / `assistant.turn_end` del log de eventos coincide con el total de llamadas a la API del LLM. No hay llamadas ocultas de planificación, evaluación o verificación de finalización.

Ejemplo para comprobar el número de turns de una sesión:

```bash theme={null}
# Cuenta los turns en el log de eventos de la sesión
grep -c "assistant.turn_start" ~/.copilot/session-state/<sessionId>/events.jsonl
```

## Documentación relacionada

* [Eventos de streaming](/es/packages/laravel-copilot-sdk/streaming-events) — referencia a nivel de campo de cada tipo de evento
* [Reanudar sesiones](/es/packages/laravel-copilot-sdk/resume) — guardar y reanudar sesiones
* [Hooks de sesión](/es/packages/laravel-copilot-sdk/hooks) — interceptar eventos del bucle (permisos, herramientas)


## Related topics

- [Introducción a Laravel Nightwatch](/es/blog/nightwatch-introduction.md)
- [Laravel Boost](/es/boost.md)
- [Plantillas Blade](/es/blade.md)
- [Agentes personalizados](/es/packages/laravel-copilot-sdk/custom-agents.md)
- [Procesos](/es/processes.md)
