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

# Agent loop

> Uitleg over hoe de Copilot CLI alles verwerkt vanaf het ontvangen van een prompt tot en met session.idle.

## Agent loop

Hier leggen we uit hoe de Copilot CLI een gebruikersbericht end-to-end verwerkt (van het versturen van de prompt tot `session.idle`).

## Architectuur

```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
```

De **SDK** is de transportlaag. Deze stuurt prompts via JSON-RPC naar de **Copilot CLI** en geeft events door aan je app. Het is de **CLI** die de agent-achtige tool-gebruiksloop uitvoert en één of meer LLM-API-aanroepen orkestreert totdat de taak is voltooid.

## De tool-gebruiksloop

Wanneer je `session.send({ prompt })` aanroept, gaat de CLI de volgende loop in.

```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
```

Het model kijkt bij elke aanroep naar de **volledige gespreksgeschiedenis** (system prompt / user message / alle eerdere tool-aanroepen en resultaten).

**Belangrijk:** één iteratie van deze loop komt exact overeen met één LLM-API-aanroep en is in de eventlog zichtbaar als één paar `assistant.turn_start` / `assistant.turn_end`. Er zijn geen verborgen aanroepen.

## Wat is een turn?

Een **turn** is één LLM-API-aanroep en het resultaat daarvan.

1. De CLI stuurt de gespreksgeschiedenis naar het LLM
2. Het LLM antwoordt (eventueel met tool-verzoeken)
3. Als er tool-verzoeken zijn, voert de CLI ze uit
4. `assistant.turn_end` wordt uitgestuurd

Eén gebruikersbericht leidt meestal tot **meerdere turns**. Bij een vraag als "Hoe werkt X in deze codebase?" gebeurt bijvoorbeeld het volgende.

| Turn | Wat het model doet                                        | toolRequests?    |
| ---- | --------------------------------------------------------- | ---------------- |
| 1    | De codebase doorzoeken met `grep` en `glob`               | ✅ Yes            |
| 2    | Specifieke bestanden lezen op basis van de zoekresultaten | ✅ Yes            |
| 3    | Extra lezen voor meer diepgaande context                  | ✅ Yes            |
| 4    | Het definitieve tekstantwoord genereren                   | ❌ No → loop ends |

Het model beslist per turn of het "nog meer tools gebruikt" of "doorgaat naar het definitieve antwoord". Elke aanroep ziet de **volledige opgebouwde context** (inclusief eerdere tool-aanroepen en resultaten) en kan dus beoordelen of er genoeg informatie is.

## Eventflow bij meerdere 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
```

## Wie start elke turn?

| Actor           | Responsibility                                                                                           |
| --------------- | -------------------------------------------------------------------------------------------------------- |
| **Your app**    | Stuurt de eerste prompt met `session.send()`                                                             |
| **Copilot CLI** | Voert de tool-gebruiksloop uit en geeft toolresultaten als input voor de volgende turn terug aan het LLM |
| **LLM**         | Beslist of het tools aanvraagt en doorgaat, of een definitief antwoord teruggeeft en stopt               |
| **SDK**         | Geeft alleen events door. Stuurt de loop niet aan                                                        |

Het gedrag van de CLI is mechanisch ("model vraagt tools aan → uitvoeren → model opnieuw aanroepen"). De beslisser over wanneer te stoppen is het **model**.

## Het verschil tussen `session.idle` en `session.task_complete`

Beide zijn voltooiingssignalen, maar de garanties verschillen sterk.

### `session.idle`

* Wordt **altijd uitgestuurd** aan het einde van de tool-gebruiksloop
* **Vluchtig (ephemeral)**. Wordt niet naar disk gepersisteerd en niet opnieuw afgespeeld bij het hervatten van een sessie
* Betekenis: "De agent is gestopt met verwerken en kan het volgende bericht ontvangen"
* Gebruik dit signaal als betrouwbaar "klaar"-signaal

De `sendAndWait()` van de SDK wacht op dit event.

```typescript theme={null}
// Wachten tot session.idle wordt uitgestuurd
const response = await session.sendAndWait({ prompt: "Fix the bug" });
```

### `session.task_complete`

* **Optioneel** (alleen wanneer het model dit expliciet signaleert)
* Wordt **gepersisteerd** (opgeslagen in de sessie-eventlog)
* Betekenis: "De agent heeft zelf geoordeeld dat de hele taak is volbracht"
* Kan een optionele `summary` bevatten

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

### Autopilot-modus: de CLI dringt aan op `task_complete`

In de **autopilot mode** (headless / autonomous) houdt de CLI bij of het model `task_complete` heeft aangeroepen. Eindigt de tool-gebruiksloop zonder `task_complete`, dan voegt de CLI het volgende synthetische gebruikersbericht in om het model aan te sporen.

> *"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."*

Hierdoor wordt de tool-gebruiksloop feitelijk hervat. Het model ontvangt deze aansporing als een nieuw gebruikersbericht en gaat door met werken. De aansporing bevat ook de instructie om `task_complete` niet te vroeg aan te roepen.

* Zijn er onopgeloste vragen, roep het dan niet aan. Neem een beslissing en werk door
* Ben je alleen op een fout gestuit, roep het dan niet aan. Probeer de fout op te lossen
* Zijn er nog resterende taken, roep het dan niet aan. Rond die eerst af

In autopilot ontstaat zo het volgende **tweetrapsvoltooiingsmechanisme**.

1. Het model roept `task_complete` aan met een summary → de CLI stuurt `session.task_complete` uit → klaar
2. Het model stopt zonder aanroep → de CLI spoort aan → het model gaat door of roept `task_complete` aan

### Waarom `task_complete` soms uitblijft

In de **interactive mode** (gewone chat) spoort de CLI niet aan tot `task_complete`. Het model kan het weglaten. De belangrijkste redenen zijn:

* **Conversationele Q\&A**: het beantwoordt een vraag en stopt; er is geen discrete "voltooide taak"
* **Modelbeslissing**: het geeft de definitieve tekst terug zonder `task_complete` aan te roepen
* **Onderbroken sessie**: de sessie eindigt voordat het model het voltooiingspunt bereikt

De CLI stuurt daarentegen altijd `session.idle` uit. Dat is namelijk geen semantisch signaal (het model vindt het klaar), maar een mechanisch signaal (de loop is geëindigd).

### Welke moet je gebruiken?

| Use case                                      | Signal                                |
| --------------------------------------------- | ------------------------------------- |
| "Wachten tot de agent klaar is met verwerken" | `session.idle` ✅                      |
| "Weten dat de codeertaak is voltooid"         | `session.task_complete` (best-effort) |
| "Timeout-/foutafhandeling"                    | `session.idle` + `session.error` ✅    |

## Het aantal LLM-aanroepen tellen

Het aantal `assistant.turn_start`/`assistant.turn_end`-paren in de eventlog komt overeen met het totale aantal LLM-API-aanroepen. Er zijn geen verborgen aanroepen voor plannen, evalueren of voltooiingscontrole.

Voorbeeld om het aantal turns van een sessie te controleren:

```bash theme={null}
# Het aantal turns in de sessie-eventlog tellen
grep -c "assistant.turn_start" ~/.copilot/session-state/<sessionId>/events.jsonl
```

## Gerelateerde documentatie

* [Streaming-events](/nl/packages/laravel-copilot-sdk/streaming-events) — Referentie op veldniveau voor elk eventtype
* [Sessies hervatten](/nl/packages/laravel-copilot-sdk/resume) — Sessies opslaan en hervatten
* [Sessiehooks](/nl/packages/laravel-copilot-sdk/hooks) — Events binnen de loop onderscheppen (permissies en tools)


## Related topics

- [我的包](/zh-CN/packages/index.md)
- [Laravel AI-agents ondersteunen nu MCP-servers](/nl/blog/ai-sdk-mcp-client.md)
- [Blade-templates](/nl/blade.md)
- [Laravel Boost](/nl/boost.md)
- [Custom agents](/nl/packages/laravel-copilot-sdk/custom-agents.md)
