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

# Boucle d'agent

> Décrit comment Copilot CLI traite un prompt depuis sa réception jusqu'à session.idle.

## Boucle d'agent

Cette page décrit de bout en bout comment Copilot CLI traite un message utilisateur (depuis l'envoi du prompt jusqu'à `session.idle`).

## Architecture

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

Le **SDK** est la couche de transport. Il envoie les prompts en JSON-RPC au **Copilot CLI** et relaie les événements vers votre application. C'est le **CLI** qui exécute la boucle agentique d'utilisation d'outils et qui orchestre un ou plusieurs appels à l'API LLM jusqu'à l'achèvement de la tâche.

## Boucle d'utilisation d'outils

Lorsque vous appelez `session.send({ prompt })`, le CLI entre dans la boucle suivante :

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

À chaque appel, le modèle consulte **tout l'historique de la conversation** (system prompt / message utilisateur / l'ensemble des appels d'outils et de leurs résultats jusqu'ici).

**Important :** une itération de cette boucle correspond exactement à un appel à l'API LLM, et se traduit dans le journal d'événements par une paire `assistant.turn_start` / `assistant.turn_end`. Il n'y a pas d'appels cachés.

## Qu'est-ce qu'un Turn (tour) ?

Un **turn** correspond à un appel à l'API LLM et à son résultat.

1. Le CLI envoie l'historique de la conversation au LLM
2. Le LLM répond (avec potentiellement des requêtes d'outils)
3. En cas de requête d'outil, le CLI l'exécute
4. `assistant.turn_end` est émis

Un même message utilisateur déclenche généralement **plusieurs tours**. Par exemple, une question du type « comment fonctionne X dans cette codebase ? » donnera :

| Turn | Action du modèle                                      | toolRequests ?        |
| ---- | ----------------------------------------------------- | --------------------- |
| 1    | Recherche dans la codebase avec `grep` et `glob`      | ✅ Oui                 |
| 2    | Lecture de fichiers spécifiques d'après les résultats | ✅ Oui                 |
| 3    | Lectures supplémentaires pour approfondir le contexte | ✅ Oui                 |
| 4    | Génération de la réponse textuelle finale             | ❌ Non → fin de boucle |

À chaque tour, le modèle décide « continuer à utiliser des outils » ou « passer à la réponse finale ». Chaque appel voit **tout le contexte accumulé** (y compris les appels d'outils et résultats précédents), ce qui lui permet d'évaluer s'il dispose d'assez d'informations.

## Flux d'événements sur plusieurs tours

```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 déclenche chaque tour

| Acteur                | Responsabilité                                                                                   |
| --------------------- | ------------------------------------------------------------------------------------------------ |
| **Votre application** | Envoie le prompt initial via `session.send()`                                                    |
| **Copilot CLI**       | Exécute la boucle d'outils et renvoie les résultats d'outils comme entrée du tour suivant au LLM |
| **LLM**               | Décide de demander des outils et de continuer, ou de renvoyer une réponse finale et de terminer  |
| **SDK**               | Se contente de relayer les événements. Ne contrôle pas la boucle                                 |

Le comportement du CLI est mécanique (« le modèle demande un outil → exécution → nouvel appel au modèle »). C'est le **modèle** qui décide quand s'arrêter.

## Différence entre `session.idle` et `session.task_complete`

Ces deux événements signalent l'achèvement, mais leurs garanties sont très différentes.

### `session.idle`

* **Toujours émis** à la fin de la boucle d'utilisation d'outils
* **Éphémère** : non persisté sur disque, non rejoué lors de la reprise d'une session
* Signification : « l'agent a cessé de traiter et peut recevoir le prochain message »
* À utiliser comme signal fiable de « terminé »

Le `sendAndWait()` du SDK attend cet événement.

```typescript theme={null}
// Attend jusqu'à ce que session.idle soit émis
const response = await session.sendAndWait({ prompt: "Fix the bug" });
```

### `session.task_complete`

* **Émission optionnelle** (uniquement si le modèle le signale explicitement)
* **Persisté** (sauvegardé dans le journal d'événements de la session)
* Signification : « l'agent estime avoir accompli la tâche globale »
* Peut inclure un `summary` optionnel

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

### Mode Autopilot : le CLI incite au `task_complete`

En **mode autopilot** (headless / autonome), le CLI garde trace de l'appel éventuel de `task_complete` par le modèle. Si la boucle d'outils se termine sans `task_complete`, le CLI insère le message utilisateur synthétique suivant pour inciter le modèle :

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

Cela relance de fait la boucle d'utilisation d'outils. Le modèle reçoit cette incitation comme un nouveau message utilisateur et poursuit le travail. L'incitation inclut aussi la consigne « ne pas appeler `task_complete` trop tôt ».

* Ne pas appeler s'il reste des questions en suspens. Décidez et continuez.
* Ne pas appeler si vous ne faites que rencontrer une erreur. Essayez de la résoudre.
* Ne pas appeler s'il reste des tâches. Terminez-les d'abord.

En autopilot, on obtient donc un **mécanisme d'achèvement en deux étapes** :

1. Le modèle appelle `task_complete` avec un résumé → le CLI émet `session.task_complete` → terminé
2. Le modèle s'arrête sans l'appeler → le CLI incite → le modèle continue ou appelle `task_complete`

### Pourquoi `task_complete` n'est parfois pas émis

En **mode interactif** (chat classique), le CLI n'incite pas à `task_complete`. Le modèle peut donc l'omettre. Les principales raisons sont :

* **Q\&A conversationnelle** : le modèle répond à la question et s'arrête, sans « tâche accomplie » discrète
* **Décision du modèle** : il renvoie le texte final sans appeler `task_complete`
* **Session interrompue** : la session se termine avant que le modèle n'atteigne le point d'achèvement

En revanche, le CLI émet toujours `session.idle`. C'est un signal mécanique (fin de boucle) et non un signal sémantique (le modèle juge que c'est fini).

### Lequel utiliser ?

| Cas d'usage                                    | Signal                                |
| ---------------------------------------------- | ------------------------------------- |
| « Attendre la fin du traitement de l'agent »   | `session.idle` ✅                      |
| « Savoir que la tâche de codage est terminée » | `session.task_complete` (best-effort) |
| « Timeout / gestion d'erreurs »                | `session.idle` + `session.error` ✅    |

## Compter les appels au LLM

Le nombre de paires `assistant.turn_start` / `assistant.turn_end` dans le journal d'événements correspond au nombre total d'appels à l'API LLM. Il n'y a pas d'appels cachés pour la planification, l'évaluation ou la vérification d'achèvement.

Exemple pour compter le nombre de tours d'une session :

```bash theme={null}
# Compter les turns dans le journal d'événements de la session
grep -c "assistant.turn_start" ~/.copilot/session-state/<sessionId>/events.jsonl
```

## Documents associés

* [Événements de streaming](/fr/packages/laravel-copilot-sdk/streaming-events) — Référence au niveau des champs pour chaque type d'événement
* [Reprise de session](/fr/packages/laravel-copilot-sdk/resume) — Sauvegarde et reprise d'une session
* [Hooks de session](/fr/packages/laravel-copilot-sdk/hooks) — Intercepter les événements dans la boucle (permissions, outils)


## Related topics

- [Templates Blade](/fr/blade.md)
- [Laravel Pennant](/fr/pennant.md)
- [Laravel Reverb](/fr/reverb.md)
- [Introduction aux relations Eloquent](/fr/eloquent-relationships.md)
- [Mises à jour Laravel — mai 2026](/fr/blog/changelog/202605.md)
