1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
|
# Référence : fichier de configuration
> Description neutre et exhaustive du fichier YAML lu par `mm`. Chaque clé est optionnelle ; une clé omise garde sa valeur par défaut intégrée. Le fichier est décodé par-dessus les valeurs par défaut, donc les clés inconnues sont ignorées.
## Clés de premier niveau
| Clé | Type | Défaut | Description |
|-----|------|--------|-------------|
| `provider` | chaîne | `dmr` | Clé du registre du serveur LLM : `dmr`, `llamacpp`, `llmman` ou `ollama`. Surchargée par `AGENT_PROVIDER`. |
| `model` | chaîne | `huggingface.co/jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M` | Identifiant du modèle dans la nomenclature du fournisseur. Surchargé par `AGENT_MODEL`. |
| `baseUrl` | chaîne | `""` (défaut du fournisseur) | Point d'accès compatible OpenAI essayé en premier. Surchargé par `AGENT_BASE_URL`. |
| `fallback` | chaîne ou absente | absente (défaut du fournisseur) | Point d'accès utilisé quand `baseUrl` ne répond pas sur `/models` en 2 s. Absente = défaut du fournisseur ; `""` = pas de repli. |
| `apiKeyEnv` | chaîne | `""` (défaut du fournisseur) | Nom de la variable d'environnement qui contient la clé d'API. |
| `contextWindow` | entier | `0` | Tokens servis par le serveur. `0` = demander au serveur, sinon inconnu. Doit être ≥ 0. |
| `maxOutput` | entier | `16000` | Nombre maximal de caractères qu'un outil renvoie au modèle ; au-delà, la sortie garde son début et jusqu'à 2000 caractères de fin. Doit être > 0. |
| `maxTurns` | entier | `10` | Nombre maximal d'allers-retours modèle ↔ outils par question. Doit être > 0. |
| `previewLines` | entier | `20` | Lignes de la sortie d'une commande affichées dans le terminal. `0` désactive l'affichage. Doit être ≥ 0. |
| `displayCommands` | booléen | `false` | Affiche la liste numérotée des commandes et opérations sur fichiers après chaque réponse. |
| `displayHooks` | booléen | `false` | Affiche chaque hook exécuté : événement, commande, outil ou agent, décision, taille du contexte ajouté (jamais son texte). Après chaque réponse en mode ligne, en `-p` et dans la fenêtre ; sous forme d'appel d'outil terminé, un par hook, dans un éditeur. Voir [hooks](hooks.md#affichage). |
| `showThinking` | booléen | `true` | Affiche ce qu'un modèle à raisonnement réfléchit avant de répondre. Seul l'affichage est optionnel : le raisonnement est toujours lu sur le fil, car c'est lui qui maintient le chien de garde en vie pendant que le modèle réfléchit. Voir [Modèles à raisonnement](../explanation/thinking-models.md). |
| `system` | chaîne | prompt intégré | Le prompt système, premier message de chaque historique. |
| `structuredOutput` | chaîne (déclarations Go) | `""` | La forme que doit suivre chaque réponse, en JSON : des déclarations `type` Go et une `var` qui nomme la racine de la réponse. Voir [`structuredOutput`](#structuredoutput) ci-dessous. Vide = réponses en texte libre. Incompatible avec tout outil déclaré. |
| `skillsDir` | chaîne | `skills` | Répertoire des skills, sous forme de fichiers `<nom>.md` ou de répertoires `<nom>/SKILL.md`. Un chemin relatif est résolu depuis le répertoire de lancement de `mm` (mode terminal, ou mode ACP sans fichier de configuration), ou à côté du fichier de configuration (mode ACP avec fichier) ; la bannière avertit avec le chemin absolu quand rien n'y est trouvé. |
| `bashTool` | booléen | `true` | Déclare `bash`. `false` le retire : le modèle peut encore lire et modifier des fichiers (`editTools`) et charger des skills, mais n'exécute jamais de commande. Reformulez un prompt système qui annonce encore un outil bash. |
| `editTools` | booléen | `true` | Déclare `read_file`, `write_file`, `edit_file`. `false` ne laisse que `bash` et `read_skill`. |
| `fetchContentTool` | booléen | `false` | Déclare `fetch_content`, qui récupère des pages web en Markdown. Désactivé par défaut : c'est le seul outil intégré qui accède au réseau. |
| `askUserTool` | booléen | `true` | Déclare `ask_user`, par lequel le modèle pose des questions à l'utilisateur (choisir une option, en choisir plusieurs, taper un texte, oui ou non) et attend les réponses. `false` le retire : le modèle ne peut pas demander. Jamais déclaré à côté d'un `structuredOutput`. Surchargé par `AGENT_ASK_USER` ; pour un agent interrogé par un autre, aussi par le `mmConfigs.<nom>.askUser` de celui qui l'interroge. Voir [outils](tools.md#ask_user). |
| `saveAnswerTool` | bool | `false` | Déclare `save_answer`, qui écrit dans un nouveau fichier une réponse que le modèle a déjà donnée : le modèle envoie un nom de fichier, pas le texte une seconde fois. Désactivé par défaut. La commande `/save` existe dans tous les cas. Voir les [outils](tools.md#save_answer). |
| `unloadModels` | bool | `true` | La commande `/unload [modèle]` (terminal, fenêtre, éditeur) et l'outil `unload_model`, qui libèrent un modèle de la mémoire du serveur : llmman et Ollama, llama-server en mode router. `false` retire les deux. L'outil n'est jamais déclaré à côté d'une `structuredOutput`. Voir les [outils](tools.md#unload_model) et [Libérer la mémoire d'un modèle](../how-to/unload-a-model.md). |
| `sampling` | dictionnaire | `temperature: 0.0`, `top_p: 0.9`, `max_tokens: 4096` | Paramètres de génération, noms de l'API OpenAI, transmis au serveur. Une clé que l'API OpenAI ne déclare pas est envoyée telle quelle, au premier niveau de la requête — par exemple `chat_template_kwargs: {enable_thinking: false}`, qui demande à un serveur llama.cpp (`dmr`, `llamacpp`, `llmman`) de ne pas raisonner ; `reasoning_effort: none` fait de même avec Ollama. `messages`, `tools`, `tool_choice` (et les anciens `functions`, `function_call`) sont ignorées : Genkit les construit à partir de la conversation. L'`agent.yaml` livré ajoute `parallel_tool_calls: false`. `max_tokens` borne aussi un appel d'outil : un `write_file` de long document (ou le raisonnement d'un modèle « thinking » plus l'appel) peut l'atteindre, et l'appel est alors coupé. mm le rend deux fois au modèle en lui demandant de procéder par morceaux, puis s'arrête avec `the model hit max_tokens (N) while writing a tool call` ; augmentez la valeur si cela arrive. En streaming, mm demande aussi au serveur le compte des tokens (`stream_options.include_usage`). |
| `watchdogTimeout` | durée | `20s` | Silence toléré entre deux tokens diffusés avant de déclarer la connexion bloquée. Doublé pour les requêtes de résumé. |
| `firstTokenTimeout` | durée | `3m` | Silence toléré avant le premier token d'un appel au modèle, pendant que le serveur lit le prompt (chaque aller-retour d'un tour en a un). Jamais plus court que `watchdogTimeout` ; doit être >= 0. Avec un serveur llama.cpp (`dmr`, `llamacpp`, `llmman` — dont les modèles GGUF locaux tournent dans llama-server), mm demande `return_progress` : chaque rapport d'avancement relance ce délai, et le spinner affiche `Reading the prompt 12.0k/25.3k tokens`. Ollama n'envoie pas d'avancement : cette seule attente couvre tout le prompt (et le chargement du modèle). À l'expiration, le watchdog dit `no first token from the server after …` (avant le premier token) ou `connection hang detected after …` (après). |
| `bashTimeout` | durée | `30s` | Durée maximale d'une commande `bash` avant qu'elle soit tuée ; l'outil répond alors `[error: <bashTimeout> timeout exceeded]`. Doit être > 0. Le watchdog continue de compter pendant qu'une commande tourne (ticket 57) : une commande plus longue que `watchdogTimeout` termine le tour, donc relevez les deux ensemble. |
| `context` | dictionnaire | voir ci-dessous | Compression automatique de l'historique. |
| `sessions` | dictionnaire | voir ci-dessous | Sessions persistantes, par projet. |
| `agentsMd` | dictionnaire | voir ci-dessous | Ajoute le `AGENTS.md` du projet au prompt système de chaque conversation. Actif par défaut. |
| `trace` | dictionnaire | voir ci-dessous | Écrit chaque requête au modèle et chaque réponse dans des fichiers. |
| `acp` | dictionnaire | voir ci-dessous | La façon dont `mm -acp` se présente à l'éditeur. |
| `lsp` | dictionnaire | voir ci-dessous | Serveurs de langage derrière l'outil `lsp`. Aucun serveur listé = pas d'outil. |
| `hooks` | dictionnaire événement → liste | aucun | Commandes lancées avant qu'une question atteigne le modèle (`preGeneration`), avant et après chaque appel d'outil (`preToolCall`, `postToolCall`) après le tour (`postGeneration`), et avant et après chaque relais vers un autre agent (`preAsk`, `postAsk`) ; chacune lit un objet JSON sur stdin et peut en répondre un sur stdout. Clés d'une entrée : `command` (obligatoire), `matcher` (événements d'outil et d'agent seulement : un nom d'outil, ou un nom d'agent), `timeout` (défaut `10s`), `onError` (`fail` \| `warn` \| `ignore`, défaut `warn`). Voir [les hooks](hooks.md). |
| `tools` | liste | `[]` | Des commandes déclarées comme outils typés : chaque entrée a un `name`, une `description`, ses `parameters` et une `command` exécutée sans shell. Voir les [clés de `tools`](#clés-de-tools) ci-dessous et les [outils déclarés](tools.md#outils-déclarés). |
| `tui` | bool | `false` | Démarre dans le mode fenêtre unique sans l'option `-tui`. Retombe sur le mode ligne quand stdin ou stdout n'est pas un terminal ; ignoré avec `-acp`. |
| `yolo` | booléen | `true` | `true` : les outils s'exécutent sans demander. `false` : chaque appel qui modifie quelque chose — `bash`, `write_file`, `edit_file`, `fetch_content` avec `output`, `rename` de `lsp`, `create`/`update`/`next`/`delete` de `plan` — attend la réponse de l'utilisateur dans toutes les façades (mode ligne, fenêtre, éditeur), de même que chaque outil MCP, quoi que dise `mcp.permissions`. Réponses : autoriser une fois, autoriser cet outil pour la session, refuser cet appel, jamais (cet outil, pour la session). `read_file`, `read_skill`, `fetch_content` sans `output` et les actions de navigation de `lsp` ne demandent jamais. Avec `-p` et `mm --ask`, personne ne peut répondre : ces appels sont refusés. Un agent lancé par `/ask`, `ask_agent` ou `mm --ask` tourne avec `AGENT_YOLO=false` et soumet ses appels à l'agent qui l'interroge : le `yolo` de son entrée `mmConfigs` décide s'il est fixé, sinon le `yolo` de l'agent qui interroge. Surchargée par `AGENT_YOLO`. `bashTool`, `editTools`, `fetchContentTool` décident toujours quels outils existent. Voir [Confirmer les appels d'outils](../how-to/confirm-tool-calls.md). |
| `permissions` | chaîne | absente | Obsolète, remplacée par `yolo`. `ask` = `yolo: false` (et l'emporte sur `yolo: true`) ; `auto` ne change rien. Les deux valeurs ajoutent un avertissement au démarrage. Doit valoir `auto` ou `ask`. |
| `mmConfigs` | dictionnaire nom → chemin, ou nom → `{path, description, yolo, askUser}` | aucun | Les autres agents mm que celui-ci peut interroger. Chaque nom génère `/ask-<nom>` et `/ask-with-context-<nom>` (voir les [commandes slash](slash-commands.md#interroger-un-autre-agent)), et la liste déclare l'outil [`ask_agent`](tools.md#ask_agent), par lequel le modèle les appelle lui-même. La forme longue ajoute `description` (quand appeler cet agent ; affichée dans la description d'`ask_agent`, à défaut l'`acp.title` du fichier de l'agent) et `yolo` (décide des appels d'outils de cet agent quand il est interrogé, par `/ask`, `ask_agent` ou `mm --ask`, à la place du `yolo` de cet agent-ci : `true` ils s'exécutent, `false` chacun est demandé ; absent, le `yolo` de cet agent-ci décide) et `askUser` (si cet agent peut poser des questions à l'utilisateur quand il est interrogé : `true` ses questions `ask_user` remontent à l'utilisateur de cet agent-ci, `false` son outil `ask_user` n'est pas déclaré ; absent, le `askUserTool` de cet agent décide). `path` ne doit pas être vide. Un chemin relatif l'est au dossier du fichier qui le déclare. Noms : lettres minuscules, chiffres et `-`, commençant par une lettre ou un chiffre, ne commençant pas par `with-context`. Un chemin dont le fichier n'existe pas donne un avertissement au démarrage, pas une erreur. |
## Clés de `context`
| Clé | Type | Défaut | Description |
|-----|------|--------|-------------|
| `enabled` | booléen | `false` | Active la compression automatique. `/compact` fonctionne dans tous les cas. |
| `threshold` | entier | `75` | Pourcentage de la fenêtre de contexte au-delà duquel l'historique est compressé avant la question suivante. Entre 1 et 100. |
| `maxMessages` | entier | `80` | Nombre de messages qui déclenche une compression quand la fenêtre est inconnue ou l'estimation trop basse. `0` désactive. Doit être ≥ 0. |
| `keepLastTurns` | entier | `3` | Tours de question conservés bruts en fin d'historique. Doit être ≥ 1. |
| `summaryMaxTokens` | entier | `1200` | `max_tokens` de la requête de résumé. Doit être > 0. Le raisonnement d'un modèle qui réfléchit est décompté de ce budget : sur `dmr`, `llamacpp` et `llmman`, la requête de résumé désactive le raisonnement (`chat_template_kwargs.enable_thinking: false`), sur `ollama` aussi (`reasoning_effort: "none"`, le seul interrupteur qu'Ollama respecte) — quoi que dise `sampling` ; les autres `chat_template_kwargs` sont gardés, et un `reasoning_effort` qui y est fixé est ramené à `low` (gpt-oss n'a pas d'« arrêt ») ; ailleurs, si l'erreur dit que le modèle « reasoned until the limit », augmentez-le. |
| `prompt` | chaîne | `""` | Remplace le prompt de résumé intégré quand non vide. |
| `showStats` | booléen | `true` | Affiche le rapport `🗜️` d'une ligne après chaque compression. |
## Clés de `sessions`
| Clé | Type | Défaut | Description |
|-----|------|--------|-------------|
| `sessions.enabled` | bool | `false` | Enregistre chaque conversation dans un fichier, réécrit après chaque tour, `/compact` et `/new`. Le fichier enregistre aussi, hors de l'historique, chaque erreur affichée (tableau `errors` : génération échouée, compression échouée, `/resume` échoué). Active `/sessions` et `/resume` dans le terminal et `session/load` en ACP. |
| `sessions.dir` | chaîne | `.mm/sessions` | Répertoire des fichiers de session, un `<id>.json` chacun. Relatif au répertoire de lancement de `mm` (terminal) ou au `cwd` de la session (ACP) ; un chemin absolu est utilisé tel quel. Ne doit pas être vide. |
## Clés de `trace`
| Clé | Type | Défaut | Description |
|-----|------|--------|-------------|
| `trace.enabled` | bool | `false` | Écrit une paire de fichiers par appel au modèle : le corps de la requête tel qu'envoyé (après les champs ajoutés par mm : `stream_options`, `return_progress`, l'interrupteur de raisonnement d'un résumé), indenté, et le corps de la réponse tel que reçu. Surchargé par `AGENT_TRACE`. Les en-têtes ne sont jamais écrits. La bannière se termine par ` \| trace: <répertoire absolu>` tant qu'elle est active (` \| trace: <session cwd>/<dir>` en mode ACP). |
| `trace.dir` | chaîne | `.mm/traces` | Répertoire des fichiers. Un chemin relatif est résolu à chaque appel dans le répertoire de travail du tour — le `cwd` de la session en mode ACP, comme `sessions.dir` — sinon dans le répertoire où `mm` tourne ; un chemin absolu est utilisé tel quel. Un répertoire impossible à créer affiche une fois `[trace: cannot write to <dir>: <erreur> — nothing is traced]` (sur stderr en mode ACP) ; les appels continuent. Créé au besoin. Ne doit pas être vide quand la trace est active. |
Noms des fichiers : `<AAAAMMJJ-HHMMSS.mmm>-<acp.name>-<NNNN>.request.json`, puis `….response-<statut>.sse` (une réponse en streaming, les lignes SSE du serveur octet pour octet, écrites à leur arrivée), `….response-<statut>.json` (un corps JSON entier, indenté), ou `….response-error.txt` (pas de réponse : la méthode, l'URL et l'erreur). `NNNN` compte les appels au modèle du processus ; une nouvelle tentative du client HTTP est un appel à part. Un agent lancé par `/ask` ou `ask_agent` hérite de `AGENT_TRACE` et écrit à côté de celui qui l'interroge, sous son propre `acp.name`.
## Clés de `agentsMd`
| Clé | Type | Défaut | Description |
|-----|------|--------|-------------|
| `agentsMd.enabled` | booléen | `true` | Lit le fichier au début de chaque conversation : démarrage, `/new`, `/resume`, une nouvelle session ACP (`session/new`), une session rechargée (`session/load`), `-p`. Jamais au milieu d'une conversation. Un agent démarré par `/ask`, `ask_agent` ou `mm --ask` ne le lit que si son propre fichier l'active ; il cherche dans le répertoire de travail qu'on lui donne (celui de l'agent qui l'interroge). |
| `agentsMd.path` | chaîne | `AGENTS.md` | Le fichier. Un chemin relatif est résolu par rapport au répertoire de travail de la conversation — le répertoire où tourne `mm`, le `cwd` de la session en mode ACP — et non par rapport au fichier de configuration. Un chemin absolu est utilisé tel quel. Ne doit pas être vide quand `enabled` vaut `true`. |
| `agentsMd.maxChars` | entier | `0` | Au-delà de ce nombre de caractères (caractères Unicode, après suppression des blancs aux extrémités), le fichier n'est pas lu du tout, et la bannière le dit. Jamais tronqué. `0` = pas de limite. Doit être ≥ 0. |
Quand le fichier est lu, le message système est le prompt `system`, une ligne vide, puis :
```text
PROJECT INSTRUCTIONS (from <chemin absolu>)
The instructions of the project you work in. Follow them; when the user asks otherwise in the conversation, the user wins.
<le fichier, sans blancs aux extrémités>
```
Non lu : un fichier absent, un fichier qui ne contient que des blancs, un fichier au-delà de `maxChars`, tout fichier quand `structuredOutput` est défini, un fichier illisible (avertissement de bannière `<chemin> cannot be read: …`). Une session reprise (`/resume`, `session/load`) continue sous le fichier tel qu'il est maintenant.
## Clés de `acp`
| Clé | Type | Défaut | Description |
|-----|------|--------|-------------|
| `acp.name` | chaîne | `bob` | `agentInfo.name` dans la réponse à `initialize`. Ne doit pas être vide. |
| `acp.title` | chaîne | `Bob (bash-first agent)` | `agentInfo.title`, le nom que l'éditeur peut afficher. Vide : non envoyé. Affiché aussi par le mode fenêtre (`-tui`) à côté de `mm`, dans son en-tête et au-dessus de chaque réponse — `‣ mm [Homer (coder)]` — sauf s'il est vide ou égal à la valeur intégrée. Pendant un `/ask`, la fenêtre montre à la place l'agent INTERROGÉ : son `acp.title` (ou son nom dans `mmConfigs`) au-dessus de sa réponse, et dans l'en-tête avec son modèle et `asked by <cet agent>`, jusqu'à ce qu'il ait répondu. |
La version n'est pas paramétrable : `agentInfo.version` est toujours celle du binaire, telle qu'affichée par `mm --version`.
## Clés de `lsp`
| Clé | Type | Défaut | Description |
|-----|------|--------|-------------|
| `lsp.servers` | liste | `[]` | Une entrée par serveur de langage. L'extension du fichier choisit le serveur. |
| `lsp.servers[].name` | chaîne | — | Le nom qu'utilisent la bannière, la description de l'outil et les avertissements. Obligatoire. |
| `lsp.servers[].command` | chaîne | — | L'exécutable, cherché sur le `PATH` au démarrage. Obligatoire. Introuvable : avertissement, serveur ignoré. |
| `lsp.servers[].args` | liste de chaînes | `[]` | Ses arguments (beaucoup de serveurs veulent `--stdio`). |
| `lsp.servers[].extensions` | liste de chaînes | — | Les extensions de fichier qu'il sert, avec le point (`.go`, `.ts`). Obligatoire, au moins une ; comparaison insensible à la casse ; une extension ne peut être servie que par un serveur. |
```yaml
lsp:
servers:
- name: gopls
command: gopls
extensions: [".go"]
- name: typescript
command: typescript-language-server
args: ["--stdio"]
extensions: [".ts", ".tsx", ".js", ".jsx"]
```
Le fichier livré `agent.lsp.yaml` est `agent.dmr.yaml` avec ce bloc actif (gopls, les autres serveurs prêts à décommenter) et un prompt système qui dit quand utiliser l'outil.
## Clés de `tools`
| Clé | Type | Défaut | Description |
|-----|------|--------|-------------|
| `tools` | liste | `[]` | Une entrée par outil déclaré. |
| `tools[].name` | chaîne | — | Le nom que le modèle appelle. Obligatoire. Commence par une lettre ; lettres, chiffres, `_`, `-` ; 64 caractères au plus. Unique. Pas le nom d'un outil intégré (`ask_agent`, `ask_user`, `bash`, `edit_file`, `fetch_content`, `lsp`, `plan`, `read_file`, `read_skill`, `save_answer`, `unload_model`, `write_file`), même désactivé dans cet agent. |
| `tools[].description` | chaîne | — | Ce que fait l'outil et quand s'en servir, montré au modèle. Obligatoire. Une ligne `Required parameters: …` y est ajoutée. |
| `tools[].parameters` | map nom → paramètre | `{}` | Les arguments, dans l'ordre écrit. Les noms suivent les règles de `name`. |
| `tools[].parameters.<p>.type` | chaîne | `string` | `string`, `number`, `integer` ou `boolean`. |
| `tools[].parameters.<p>.description` | chaîne | `""` | Ce qu'est la valeur, montré au modèle. |
| `tools[].parameters.<p>.enum` | liste | aucune | Les seules valeurs acceptées, chacune du type du paramètre. |
| `tools[].parameters.<p>.required` | bool | `true` | `false` rend le paramètre facultatif. |
| `tools[].parameters.<p>.default` | valeur | aucune | La valeur d'un paramètre facultatif que l'appel omet. Seulement avec `required: false` ; du type du paramètre ; dans l'`enum` s'il y en a un. |
| `tools[].command` | liste de chaînes | — | Le programme, puis ses arguments, une entrée argv chacun. Obligatoire. `{{p}}` dans une entrée est remplacé par la valeur du paramètre `p`. Le programme (première entrée) ne peut pas contenir de placeholder. Chaque placeholder doit nommer un paramètre déclaré. Un programme relatif (`./scripts/x.sh`) est résolu depuis le répertoire de travail. |
| `tools[].timeout` | durée | `0` (= `bashTimeout`) | Durée maximale d'un appel. Doit être ≥ 0. |
```yaml
tools:
- name: release
description: Prepare a release of the project for a version and a channel.
parameters:
version: { type: string, description: "e.g. 1.4.2" }
channel: { type: string, enum: [stable, beta, nightly] }
dryRun: { type: boolean, required: false, default: false }
command: ["./scripts/release.sh", "{{version}}", "{{channel}}", "--dry-run={{dryRun}}"]
timeout: 60s
```
## Valeurs par défaut des fournisseurs
| Fournisseur | `baseUrl` | `fallback` | `apiKeyEnv` | Clé requise |
|-------------|-----------|------------|-------------|-------------|
| `dmr` | `http://localhost:12434/engines/v1` | `http://host.docker.internal:12434/engines/v1` | aucune | non |
| `llamacpp` | `http://127.0.0.1:8080/v1` | aucun | `LLAMA_API_KEY` | non |
| `llmman` | `http://127.0.0.1:17434/v1` | `http://host.docker.internal:17434/v1` | `LLMMAN_API_KEY` | non |
| `ollama` | `http://localhost:11434/v1` | `http://host.docker.internal:11434/v1` | `OLLAMA_API_KEY` | non |
## `structuredOutput`
Un bloc YAML qui contient des déclarations Go. Chaque `type` est une définition ; l'unique `var` (`var X T` ou `var X = T{}`) nomme la racine de la réponse. Seules les déclarations `type` et `var` sont acceptées.
| Go | JSON Schema |
|----|-------------|
| `string` | `{"type": "string"}` |
| `bool` | `{"type": "boolean"}` |
| `int`, `int8` … `int64`, `uint` … `uint64` | `{"type": "integer"}` |
| `float32`, `float64` | `{"type": "number"}` |
| `[]T` | `{"type": "array", "items": T}` |
| `map[string]T` | `{"type": "object", "additionalProperties": T}` |
| `struct { … }` | `{"type": "object", "properties": …, "required": [toutes les clés], "additionalProperties": false}`, clés dans l'ordre de déclaration |
| un type déclaré `Name` | `{"$ref": "#/$defs/Name"}` ; une racine struct est mise en ligne, `$defs` contient les types qu'atteint la racine |
| champ `*T`, ou `json:",omitempty"` | la clé reste obligatoire, sa valeur est `anyOf: [T, null]` |
| Élément d'un champ | Effet |
|--------------------|-------|
| tag `json:"name"` | la clé ; sans tag, le nom Go du champ tel qu'écrit |
| tag `json:"-"` | champ ignoré |
| commentaire `//` au-dessus ou à côté d'une déclaration ou d'un champ | sa `description` |
Refusés, avec `structuredOutput:<ligne>:<col>: …` compté depuis la première ligne du bloc : `func`, `import`, `const` ; les types génériques ; les alias (`type A = B`) ; les champs embarqués ; les tableaux de taille fixe ; les clés de map autres que `string` ; `interface`, `chan`, `func`, les types qualifiés (`time.Time`) et non définis ; un pointeur hors d'un champ de struct ; un type déclaré deux fois ; une clé utilisée deux fois ; zéro ou plus d'une `var` ; une `var` sans type.
Quand le bloc n'est pas vide :
- les déclarations sont ajoutées à `system`, sous un paragraphe `OUTPUT FORMAT` ;
- chaque appel au modèle porte `response_format: {"type": "json_schema", "json_schema": {"name": "output", "strict": true, "schema": …}}` ;
- la réponse est validée contre le schéma ; une réponse non conforme fait échouer le tour avec `model failed to generate output matching expected schema: …`, précédé de `the answer was cut off by max_tokens before its JSON was complete …` quand le serveur s'est arrêté sur `max_tokens` ;
- `-p` imprime la réponse JSON seule sur stdout (voir [Spécificités du mode invite](cli.md#spécificités-du-mode-invite)) ;
- la bannière affiche `tools: none (structured output: JSON answers)`, sans avertissement « no skills » ni « no tool ».
Exemple :
```yaml
structuredOutput: |
type Language struct {
Name string `json:"name"` // the language's name, in English
Official bool `json:"official"`
}
type Country struct {
Name string `json:"name"`
Capital *string `json:"capital"` // null when unknown
Languages []Language `json:"languages"` // at most 4
}
var Countries = []Country{}
```
## Erreurs de validation
Signalées sur stderr sous la forme `[config error: <chemin>: <message>]`, code de sortie 1 — dans tous les modes, pour que `-p … | jq` ne prenne jamais une erreur pour la réponse. De même pour `[engine error: …]` et `[usage: …]`.
| Message | Cause |
|---------|-------|
| `provider must not be empty` | `provider: ""` |
| `model must not be empty` | `model: ""` |
| `contextWindow must be >= 0` | valeur négative |
| `maxOutput must be > 0` | zéro ou négatif |
| `maxTurns must be > 0` | zéro ou négatif |
| `previewLines must be >= 0` | valeur négative |
| `context.threshold must be between 1 and 100` | hors intervalle |
| `context.keepLastTurns must be >= 1` | zéro ou négatif |
| `context.summaryMaxTokens must be > 0` | zéro ou négatif |
| `context.maxMessages must be >= 0` | valeur négative |
| `sessions.dir must not be empty` | chaîne vide |
| `agentsMd.path must not be empty` | chaîne vide avec `agentsMd.enabled` actif |
| `agentsMd.maxChars must be >= 0` | valeur négative |
| `trace.dir must not be empty` | chaîne vide avec la trace active |
| `acp.name must not be empty` | chaîne vide ou blanche |
| `lsp.servers[N].name must not be empty` | un serveur sans `name` |
| `lsp.servers[N].command must not be empty` | un serveur sans `command` |
| `lsp.servers[N].extensions must list at least one extension` | un serveur sans `extensions` |
| `lsp.servers[N].extensions: "go" must start with a dot, like ".go"` | une extension sans son point |
| `lsp.servers[N]: extension ".ts" is already served by "typescript"` | deux serveurs sur une même extension |
| `tools[N] (<nom>).name "<nom>" must start with a letter and hold only letters, digits, _ or - (64 characters at most)` | un nom d'outil invalide |
| `tools[N] (<nom>).name "<nom>" is the name of a built-in tool` | un outil déclaré qui porte le nom d'un outil intégré |
| `tools[N] (<nom>): another tool is already called "<nom>"` | deux entrées de même nom |
| `tools[N] (<nom>).description must not be empty: …` | pas de `description` |
| `tools[N] (<nom>).command must name a program, …` | pas de `command`, ou une première entrée vide |
| `tools[N] (<nom>).command: the program ("{{p}}") cannot come from a parameter` | un placeholder dans la première entrée |
| `tools[N] (<nom>).command: {{p}} is not a declared parameter` | un placeholder qu'aucun paramètre ne déclare |
| `tools[N] (<nom>).timeout must be >= 0` | un `timeout` négatif |
| `tools[N] (<nom>).parameters.<p>.type "x" must be one of string, number, integer, boolean` | un type inconnu |
| `tools[N] (<nom>).parameters.<p>.enum: …` | une valeur d'`enum` qui n'est pas du type du paramètre |
| `tools[N] (<nom>).parameters.<p>.default only makes sense with required: false` | un `default` sur un paramètre obligatoire |
| `tools[N] (<nom>).parameters.<p>.default: …`, `….default <v> is not one of the enum values` | un `default` du mauvais type, ou hors de l'`enum` |
| `tools[N] (<nom>).parameters.<p>: declared twice`, `…: a parameter name must start with a letter …` | un nom de paramètre répété ou invalide |
| `line N: parameters must be a mapping of name → {type, description, …}` | `parameters:` écrit comme une liste |
| `permissions must be "auto" or "ask"` | toute autre valeur de la clé obsolète |
| `AGENT_YOLO="maybe": must be true or false` | une valeur d'`AGENT_YOLO` que `strconv.ParseBool` refuse (acceptées : `1`, `t`, `true`, `0`, `f`, `false`, sans distinction de casse) |
| `bashTimeout must be > 0` | zéro ou négative |
| `structuredOutput:<ligne>:<col>: …` | une déclaration que la section [`structuredOutput`](#structuredoutput) ne sait pas traduire |
| `structuredOutput: no var declaration: …` | aucune `var` ne nomme la racine |
| `structuredOutput cannot be used with tools — this agent declares: <outils> (…)` | un `structuredOutput` avec au moins un outil déclaré (`bash`, `plan`, les outils de fichiers, `fetch_content`, `read_skill`, `ask_agent`, `lsp`, les outils MCP, les `tools` déclarés). Signalée après l'assemblage des outils, avant le démarrage de toute interface. |
| `mmConfigs: "Bob" is not a valid name (…)` | un nom avec des majuscules, des espaces ou d'autres caractères, ou commençant par `-` |
| `mmConfigs: "with-context-x" is not a valid name (it would read as /ask-with-context-…)` | un nom commençant par `with-context` |
| `mmConfigs.<nom>: the path must not be empty` | `<nom>: ""`, ou une forme longue sans `path` |
| `hooks: unknown event "x" (…)`, `hooks.<événement>[N]: …` | un bloc `hooks:` invalide — voir [les hooks](hooks.md#erreurs-de-validation) |
| `unknown provider "x" (known: dmr, llamacpp, llmman, ollama)` | Signalée par le moteur, sous la forme `[engine error: …]`. |
Un avertissement au démarrage, et non une erreur, est affiché quand `context.enabled` vaut `true`, que la fenêtre est inconnue et que `context.maxMessages` vaut `0`, quand un outil déclaré porte le nom d'un outil MCP (`tools: <nom> skipped — an MCP tool already has that name` ; l'outil n'est pas déclaré), et quand le fichier pose la clé obsolète `permissions` (`permissions: ask is deprecated — use yolo: false instead …`).
## Fichiers livrés
| Fichier | Rôle |
|---------|------|
| `agent.yaml` | Configuration Docker Model Runner : `provider: dmr`, `maxTurns: 40`, `displayCommands: true`, compression désactivée, un prompt système nommé « Bob » qui décrit les skills et les règles d'édition de fichiers. |
| `agent.llamacpp.yaml` | Configuration llama.cpp : `provider: llamacpp`, `baseUrl: http://127.0.0.1:8080/v1`, `fallback: ""`, compression activée. |
### Exemple
```yaml
provider: llamacpp
model: mon-alias
baseUrl: http://127.0.0.1:8080/v1
fallback: ""
maxTurns: 40
context:
enabled: true
```
|