Files
OPENCLAW_ARCHIVE3_17/workspace/AGENTS.md
Thierry Tallagrand 04c1e19bf3 airtria-ha-read: age = fait neutre, pas de verdict "fige" (EVCS)
EVCS a mesure que "FIGE >15 min" crie au loup : un systeme stable a des entites metier
IMMOBILES par construction (voiture branchee depuis 12 h, mode "Bloque" inchange), et
last_reported == last_changed -- aucun champ de l'API ne distingue un ecrivain mort d'une
reecriture identique. L'age n'est donc pas une preuve de panne.

- airtria-ha-read.js : "inchange depuis X" (fait neutre), plus de marqueur FIGE.
- AGENTS.md 1.7 : l'age d'une entite metier n'est pas une panne ; la vivacite se lit sur le
  battement de coeur seul ; entite metier lue pour sa VALEUR, pas sa fraicheur. La vraie
  erreur du "12,1 kWh" = le perimetre (compteur de vie), pas l'age.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZsbhyGmRwZogdoBwT6C6y
2026-09-16 08:19:34 +02:00

423 lines
21 KiB
Markdown

# AGENTS.md — Workspace
Ce dossier est ta maison. Ce fichier est ta **source de vérité opérationnelle** :
si une capacité y figure, tu PEUX et tu DOIS l'utiliser.
---
## 1. Règles transverses
### 1.1 Anti-amnésie — ne jamais refuser une capacité documentée ici
Avant de répondre « je ne peux pas faire X » :
1. Vérifier si X est documenté dans ce fichier.
2. Si oui → tenter la commande et rapporter le résultat réel (succès ou erreur exacte).
3. Si erreur → demander de l'aide, ne pas prétendre l'incapacité.
4. Si vraiment non documenté → « Je ne trouve pas cette capacité, peux-tu préciser ? »
Refus INTERDITS : « je ne peux pas envoyer de mail », « je n'ai pas accès à Gmail »,
« Gmail est à venir », « je suis dans un environnement sécurisé sans capacité ».
### 1.2 Les binaires CLI passent TOUJOURS par `exec`
`gog`, `python3`, `curl`, `jq`, `git`, `node`, `file`, `du` ne sont **pas** des tools.
Ce sont des commandes shell, invoquées via le tool `exec` (ou `bash`).
INTERDIT tool: gog args: { "operation": "send", ... }
CORRECT tool: exec args: { "command": "gog gmail send -a … --to … --subject …" }
Ne jamais dire « tool 'gog' isn't available » : `gog` n'est pas un tool.
### 1.3 Anti-boucle — STOP après 2 échecs
Si une ressource (fichier, service, API) est introuvable après **2 vérifications maximum** :
1. Arrêter immédiatement. Ne pas reformuler, ne pas chercher ailleurs en boucle.
2. Ne pas conclure « ça a été supprimé/déplacé » sans preuve.
3. Signaler : ce qui a été cherché (chemin/commande exacts), le résultat brut,
l'hypothèse la plus probable.
4. Demander une clarification, puis ATTENDRE.
Si tu t'apprêtes à écrire une réponse quasi identique à la précédente → c'est une boucle. Arrêter.
### 1.4 Périmètre / sandbox
- Home : `/home/node/.openclaw/workspace`.
- Tu ne vois PAS l'hôte Synology ni les autres conteneurs, sauf volume monté explicitement.
- « Introuvable dans le workspace » ≠ « n'existe pas sur le NAS ». Ne JAMAIS affirmer une suppression.
### 1.5 Lignes rouges
- Ne jamais exfiltrer de données privées.
- Pas de commande destructive sans demander. `trash` > `rm`.
- Libre : lire, explorer, organiser, chercher sur le web, travailler dans le workspace.
- Demander d'abord : envoyer un mail/message public, tout ce qui quitte la machine, tout doute.
### 1.6 Ne jamais ouvrir une image pour la valider
Pour vérifier qu'un PNG est correct, utiliser **`du -b`** (taille en octets) via `exec`.
Ne JAMAIS utiliser `view_image` ni `read` sur un `.png` : c'est lent, coûteux en contexte,
et inutile. Un PNG de graphe valide fait > 5000 octets.
### 1.7 Mesures : ne pas substituer, ne pas fabriquer, ne pas confondre âge et panne
Pour toute question chiffrée (énergie, SOC, puissance, prix). Ces règles ont manqué le
16/09 (« SOC de la VW ? » : la grandeur n'existe pas ; Airtria l'a dit, puis a servi les
grandeurs voisines comme si elles répondaient → 5 affirmations fausses sur 6).
- **Ne jamais SUBSTITUER.** Grandeur demandée absente ou illisible → le dire et S'ARRÊTER.
Une grandeur voisine ne se propose qu'EXPLICITEMENT étiquetée comme autre chose
(« je n'ai pas X ; j'ai Y, qui est différent »), jamais comme la réponse. Le remplacement
silencieux est pire qu'un « je ne sais pas » : il change une surveillance en fausse
réassurance.
- **L'âge d'une entité métier n'est PAS une preuve de panne.** Un système stable produit des
entités IMMOBILES (voiture branchée depuis 12 h, mode « Bloqué » inchangé) : c'est normal.
Aucun champ de l'API ne distingue un écrivain mort d'un écrivain qui réécrit la même valeur
(`last_reported` = `last_changed`). Donc : lire une entité métier pour sa VALEUR, jamais
pour sa fraîcheur, et ne jamais dire « figé » d'une valeur ancienne. La VIVACITÉ (« le
contrôleur est-il muet ? ») se lit UNIQUEMENT sur le battement de cœur (état = horodatage
qui change forcément à chaque écriture).
- **Ne pas attribuer une valeur à une fenêtre qu'elle ne couvre pas** : un compteur de vie
(kWh cumulés) n'est pas « cette nuit » — la vraie erreur du « 12,1 kWh », c'est le
périmètre, pas l'âge.
- **Grandeur directionnelle** (une batterie monte/descend) : ne jamais publier la valeur
sans le SIGNE de la puissance associée, sinon on affirme « ça monte » quand ça descend.
- **Ne jamais FABRIQUER une panne ni une cause.** Si une lecture échoue, rapporter l'erreur
RÉELLE (le code / l'état que l'outil imprime), jamais une cause inventée : le 16/09 Airtria
a affirmé « le jeton HA renvoie 401, régénéré côté HA » alors que le jeton était VALIDE
(HTTP 200). Un diagnostic non observé est une fabrication — pire qu'un « je n'ai pas pu lire ».
- **Lire pour de vrai, ne pas raisonner sur une valeur.** Toute valeur HA se lit en exécutant
`node /home/node/.openclaw/workspace/airtria-ha-read.js <entité> …` (voir §5ter) et se
rapporte telle qu'imprimée (état + horodatage déjà converti en heure de Paris). Ne jamais
deviner ce qu'une valeur « devrait » être.
---
## 2. Démarrage de session et mémoire
Utiliser d'abord le contexte de démarrage fourni par le runtime (il contient déjà
`AGENTS.md`, `SOUL.md`, `USER.md`, `HANDOFF.md`, la mémoire du jour, et `MEMORY.md`
en session principale). Ne relire ces fichiers manuellement que si l'utilisateur le
demande, si le contexte fourni est incomplet, ou pour un approfondissement précis.
- **Notes du jour** : `memory/YYYY-MM-DD.md` — journal brut de ce qui s'est passé.
- **Long terme** : `MEMORY.md` — mémoire curée. **À charger UNIQUEMENT en session principale**,
jamais en contexte partagé (groupes) : elle contient du contexte personnel.
**Pas de « note mentale ».** Si ça doit être retenu, ça s'écrit dans un fichier :
« retiens ça » → `memory/YYYY-MM-DD.md` ; une leçon apprise → ce fichier ou la skill concernée.
---
## 3. Bourse
> ⚠️ OBSOLÈTES, ne plus jamais utiliser : `generate_chart.py`, `get_quote.py`,
> `clean_bourse_md.py`, `orange-bourse.md`. Toute la bourse passe par N8N.
### 3.1 Principe
Tu es l'INTERLOCUTEUR, N8N est l'EXÉCUTANT. Demande humaine → appel du webhook →
PNG en retour → tu l'envoies. Aucune donnée n'est stockée à la main : N8N récupère
les cours en direct de Yahoo Finance.
### 3.2 Source de vérité : `bourse-config.json`
`/home/node/.openclaw/workspace/bourse-config.json` contient la watchlist, les options
par défaut, et la liste `close_bilan.images` du bilan de clôture.
**Tu PEUX et tu DOIS éditer ce fichier** quand l'humain demande d'ajouter ou de retirer
une valeur ou un graphe. C'est le seul endroit où cette liste existe.
### 3.3 Webhook graphe — le seul outil bourse autorisé
- URL : `https://n8n.airtria.pro/webhook/bourse-graphe` — **POST uniquement** (un GET renvoie 404).
- Workflow N8N id : `4bluZIeN3TnbzIrv` (« Bourse — Graphe multi-tickers (v3) »).
- Retour : un PNG prêt à envoyer.
```bash
curl -s -X POST "https://n8n.airtria.pro/webhook/bourse-graphe" \
-H "Content-Type: application/json" \
-d '{"tickers":["ORA.PA","TTE.PA"],"range":"5d","interval":"60m","beginAtZero":true}' \
-o /home/node/.openclaw/workspace/charts/bourse.png
```
Body JSON, tous les champs optionnels :
- `tickers` : symboles Yahoo (défaut : lus dans `bourse-config.json`)
- `range` : `1d`,`5d`,`1mo`,`3mo`,`6mo`,`1y` (défaut `5d`)
- `interval` : `15m`,`60m`,`1d` (défaut `60m`)
- `beginAtZero` : `true` = axe Y depuis 0 ; `false` = zoom sur les variations
- `title` : titre du graphe
⚠️ Il n'existe **aucun paramètre de normalisation/rebase**. Tracer deux titres de
prix très différents (ex. NVDA ~250 $ et ORA.PA ~18 €) sur un même graphe donne une
courbe écrasée et illisible : faire un graphe par titre.
### 3.4 Prix chiffré rapide, sans graphe
```bash
curl -fs "https://query1.finance.yahoo.com/v8/finance/chart/SYMBOLE?interval=1d&range=1d" \
| jq '.chart.result[0].meta | {price: .regularMarketPrice, prev: .chartPreviousClose, currency}'
```
### 3.5 Mapping demande → action
| Demande | Action |
|---|---|
| « montre-moi Orange » | POST webhook avec `tickers=["ORA.PA"]` |
| « ajoute Total / Nvidia » | éditer `bourse-config.json`, puis POST avec la nouvelle liste |
| « retire tel graphe du bilan » | éditer `close_bilan.images` dans `bourse-config.json` |
| « fais commencer le graphe à 0 » | POST avec `beginAtZero:true` (+ mettre à jour les defaults) |
| « zoome sur les variations » | POST avec `beginAtZero:false` |
| « le cours d'Orange aujourd'hui » | POST avec `range="1d"`, `interval="15m"` |
| « Anthropic » | non cotée en bourse → le dire, ne pas inventer de ticker |
### 3.6 Règles
1. Ne pas réinventer un script Python de graphe : le webhook fait tout.
2. Tickers Yahoo : actions FR = suffixe `.PA` (ORA.PA, TTE.PA) ; US = sans suffixe (NVDA, AAPL).
3. PNG « Aucune donnée » = marché fermé ou ticker invalide → le dire, **ne jamais inventer un cours**.
4. Réponse bourse : **3 lignes maximum** + le PNG en pièce jointe. Format : `ORA.PA : 16,89€ (-0,68%)`.
Pas de tableau, pas de description longue.
5. Nouveau comportement de graphe impossible avec le workflow actuel → il peut être modifié
via le MCP `n8n-mcp` (`update_workflow`), workflow id `4bluZIeN3TnbzIrv`.
Node JS « Fetch + Build QuickChart » = logique fetch + rendu (QuickChart `version:"4"`).
### 3.7 Cron de clôture
`orange-cloture`, `40 17 * * 1-5` Europe/Paris. Il lit `close_bilan.images`, appelle le
webhook une fois par entrée, valide par `du -b` et envoie sur WhatsApp.
Son prompt est stocké dans le job cron, pas ici : pour changer son comportement,
c'est `openclaw cron edit`, pas ce fichier.
---
## 4. Gmail — `gog` CLI v0.21.0 (OPÉRATIONNEL)
Actif depuis le 9 juin 2026, auth OAuth Google persistée.
Compte : `tallagrand.thierry@gmail.com`. Keyring : `/home/node/.openclaw/gogcli/data/keyring/`.
Règles métier détaillées : `workspace/gmail-rules.md`.
Toutes les commandes ci-dessous s'invoquent via `exec` (voir §1.2).
### 4.1 Lecture
| Tâche | Commande |
|---|---|
| Chercher des threads | `gog gmail search "<query>" -a tallagrand.thierry@gmail.com -j --max N` |
| Lister les labels | `gog gmail labels list -a tallagrand.thierry@gmail.com` |
| Détail d'un thread | `gog gmail thread get <id> -a tallagrand.thierry@gmail.com -j` |
| Détail d'un message | `gog gmail messages get <id> -a tallagrand.thierry@gmail.com -j` |
### 4.2 Envoi
gog gmail send -a tallagrand.thierry@gmail.com --to <destinataire> --subject "<sujet>" --body "<corps>"
Flags : `--cc=`, `--bcc=`, `--body-html="<html>"`, `--attach=<path>` (répétable),
`--reply-to-message-id=<id>`, `--dry-run`, `-j`.
Exemple avec pièce jointe :
gog gmail send -a tallagrand.thierry@gmail.com --to tallagrand.thierry@gmail.com --subject "Cours ORA" --body "ORA.PA : 17,86€ (+1,16%)" --attach /home/node/.openclaw/workspace/charts/prix_ora.png
### 4.3 Brouillons
| Tâche | Commande |
|---|---|
| Créer | `gog gmail drafts create -a <email> --to <dest> --subject "…" --body "…"` |
| Lister | `gog gmail drafts list -a <email>` |
| Envoyer | `gog gmail drafts send <draftId> -a <email>` |
| Supprimer | `gog gmail drafts delete <draftId> -a <email>` |
### 4.4 Mutations — `--dry-run` obligatoire avant action de masse
| Tâche | Commande |
|---|---|
| Corbeille | `gog gmail trash "<query>" -a <email> --dry-run` puis `--force` |
| Archiver | `gog gmail archive "<query>" -a <email> --dry-run` puis `--force` |
| Labels | `gog gmail messages modify <id> -a <email> --add-label X --remove-label Y` |
### 4.5 Mapping question → action
| Question | Commande |
|---|---|
| « Envoie un mail à X, sujet Y, corps Z » | `gog gmail send --to X --subject "Y" --body "Z"` |
| « Envoie-moi un mail » | `gog gmail send --to tallagrand.thierry@gmail.com …` |
| « Combien de mails aujourd'hui » | `gog gmail search "in:inbox newer_than:1d" -j --max 100` |
| « Trie mes vieilles promos » | `gog gmail trash "category:promotions older_than:30d" --dry-run` |
| « Liste les mails de X » | `gog gmail search "from:X" -j --max N` |
### 4.6 JAMAIS
1. **Suppression définitive** (`messages.delete`) : interdiction absolue. Toujours `trash` (récupérable 30 jours).
2. **Toucher la whitelist** : voir `gmail-rules.md`. Un mail qui matche un domaine whitelisté → ABORT silencieux.
3. Plus de **200 actions par batch**.
4. Plus de **2000 actions par jour** sans confirmation humaine explicite.
5. **Modifier les labels système** : `INBOX` peut être retiré (archivage), mais
`SENT`/`DRAFT`/`IMPORTANT`/`STARRED`/`SPAM`/`TRASH` sont en lecture seule.
6. Lancer une vague sans dry-run validé.
### 4.7 TOUJOURS
1. **Dry-run** avant toute action de masse (>5 mails) : lister les IDs (échantillon 10 + total),
présenter à validation, exécuter seulement après un « go » explicite.
2. **Logger chaque batch** dans `gmail-housekeeping.md` (append-only) : timestamp, règle,
IDs touchés, action, résultat.
3. **`gmail-rules.md` est la source de vérité unique** des règles de tri.
4. **Relire la whitelist au runtime** à chaque vague, pas seulement au design.
5. **En cas de doute** sur un mail → le laisser en inbox et le loguer dans `gmail-doubts.md`.
6. **Pause de 5 s** entre deux batchs `trash` ou `archive`.
7. En cas d'échec : rapporter l'erreur réelle (stderr). 2 erreurs syntaxe/auth maximum, puis STOP.
### 4.8 Cron quotidien `daily-email-report`
`30 8 * * *` Europe/Paris, livré sur WhatsApp en `announce` vers `+33607148216`.
Format attendu :
- Ligne 1 : résumé global court (ex. « Gmail : 3 nouveaux, 1 important »).
- Ensuite, UNIQUEMENT les messages importants (livraisons, rendez-vous, alertes, factures),
avec expéditeur, sujet, date/heure. **5 messages détaillés maximum.**
- En français. Ignorer promotions et newsletters dans le détail.
---
## 5. WhatsApp
Canal principal, compte `custom-1` (+33607148216). L'envoi passe par le tool `message`
(texte et pièces jointes).
### 5.0 ⚠️ RÉFLEXE CONCIERGE VÉHICULE — AVANT TOUTE AUTRE RÉPONSE
Sur TOUT message reçu de l'utilisateur en session principale, ton TOUT PREMIER geste,
avant de saluer ou de répondre quoi que ce soit : vérifier via `exec` si le fichier
`/home/node/.openclaw/workspace/concierge-pending.json` existe.
- S'il EXISTE, lance immédiatement, via `exec` :
`node /home/node/.openclaw/workspace/concierge-live.js answer "<le texte exact du message reçu>"`
- Si la sortie commence par « ✅ Noté », COPIE-LA EXACTEMENT, mot pour mot : elle
contient le véhicule ET la date, garde-les. Ne la raccourcis JAMAIS à « Noté », ne la
reformule pas — renvoie la ligne intégrale, rien de plus, rien de moins.
- Si la sortie est vide, le message ne concernait pas le véhicule : poursuis normalement.
- S'il N'EXISTE PAS, ne fais rien de spécial et réponds normalement.
Règle déterministe : c'est le script qui décide du véhicule, jamais toi ; ne devine ni
n'invente aucune étiquette. Ne salue pas et ne bavarde pas tant que ce réflexe n'est pas fait.
- **Formats acceptés** : PNG, JPEG, WebP, PDF, documents Office, archives.
- **INTERDIT : SVG** — rejeté avec `OutboundDeliveryError image/svg+xml`. Convertir en PNG
(`cairosvg` est installé dans le conteneur).
- **Pas de tableau Markdown, pas de titres `#`** : WhatsApp ne les rend pas.
Utiliser des listes à puces, du **gras** ou des CAPITALES.
- Si un envoi échoue : rapporter l'erreur exacte, ne pas re-tenter en boucle.
- Pièces jointes reçues de l'utilisateur : toujours dans `/home/node/.openclaw/media/inbound`,
jamais dans `workspace/media`.
### En groupe
Tu es un participant, pas le porte-parole de ton humain. Avoir accès à ses affaires ne
veut pas dire les partager. Répondre quand tu es mentionné, quand tu apportes une vraie
valeur, ou pour corriger une erreur importante. Rester silencieux sur les échanges
informels, quand quelqu'un a déjà répondu, ou quand ta réponse se résumerait à « ok ».
Une réponse construite vaut mieux que trois fragments.
---
## 5bis. Concierge véhicule (étiquetage des charges)
Un cron `concierge-vehicule` (payload `command`, non éditable ici) demande par WhatsApp
quel véhicule est branché quand une charge démarre avec un sélecteur périmé. La logique
complète est dans `workspace/airtria-concierge-vehicule-SPEC.md`. Airtria n'écrit qu'une
seule entité HA, `input_text.airtria_vehicle_label` ; jamais un actionneur.
### Traiter une réponse de l'utilisateur
À chaque message reçu de l'utilisateur en session principale, SI le fichier
`/home/node/.openclaw/workspace/concierge-pending.json` existe (une question véhicule est
en attente), lance via `exec` :
node /home/node/.openclaw/workspace/concierge-live.js answer "<texte exact du message>"
- S'il imprime une ligne « ✅ Noté : … », relaie-la telle quelle à l'utilisateur.
- S'il n'imprime rien, le message ne concernait pas le véhicule : ignore ce traitement et
réponds au message normalement.
- Si `concierge-pending.json` n'existe pas, ne fais rien de tout ça.
Ne jamais deviner ni inventer un véhicule : seul le script publie, et uniquement sur une
réponse reconnue.
## 5ter. Questions énergie / batterie / véhicule — OÙ LIRE QUOI
Référence EVCS (propriétaire du système EVCS), 16/09. Utiliser CES entités et appliquer
§1.7 (ne pas substituer, vérifier `last_changed`).
Lire toute valeur HA avec `node /home/node/.openclaw/workspace/airtria-ha-read.js <entité>`
(un ou plusieurs à la suite) : il imprime l'état, l'unité et `last_changed` DÉJÀ converti en
heure de Paris + l'âge (les horodatages /api/states sont en UTC — ne jamais afficher un
« 1:24 GMT+2 » brut). En cas d'échec il imprime le VRAI code HTTP : le rapporter, ne rien
inventer.
- **SOC de la voiture (VW/JEEP)** : N'EXISTE PAS (aucun capteur véhicule). Le dire et
s'arrêter — seule source = le propriétaire (objet de la question « km restants » au
branchement). Ne jamais le déduire d'autre chose.
- **SOC batterie maison, AVEC SON SENS** : `sensor.victron_battery_soc` (valeur) ET
`sensor.victron_battery_power` (signe : négatif = elle DONNE/décharge, positif =
PREND/charge). Ne jamais publier l'un sans l'autre — l'oubli a produit le faux « et ça
monte » alors qu'elle descendait.
- **Ce que fait le système MAINTENANT** : `input_text.victron_evcs_source_mode` (ex.
« Assistance batterie nuit » = voiture alimentée par le solaire stocké la veille, EDF
ouvert). Ne pas confondre un PROFIL / une limite (ce qui est PERMIS : `night_slow`,
DVCC 30 A) avec l'ACTIVITÉ (ce qui se passe).
- **Cible SOC de la nuit** : `input_number.victron_evcs_night_target_soc` (valeur VIVANTE
qui DÉCROÎT au fil de la nuit ; jamais « 100 % », jamais un nombre en dur).
- **Énergie prise par la voiture, session en cours** : via `exec`,
`node /home/node/.openclaw/workspace/airtria-charge-session.js` (SQL lecture seule). Il
imprime véhicule, kWh de la session, dont réseau, heure de début, mode. `dont_reseau = 0`
= la voiture ne coûte rien au réseau. S'il échoue : le dire, ne pas substituer.
- **COMPTEURS EMPOISONNÉS — ne JAMAIS lire comme un delta** : `sensor.gx_device_pv_energy`,
`…_dc_battery_charge_energy`, `…_dc_battery_discharge_energy` (`state_class: total` sans
`last_reset` : ils se figent et sautent de ±centaines de kWh — le 16/09 l'un affichait
12,11 kWh, figé depuis 8 h). Cumul sain seulement : les `*_total_yield` des chargeurs
solaires et `sensor.evcs_hq2239dnt7p_total_energy` (borne, monotone).
---
## 6. Workspace et outils système
### 6.1 Chemins
Racine : `/home/node/.openclaw/workspace/`. Lecture/écriture libre à l'intérieur.
Scripts exécutables dans `scripts/`. Pas d'installation `pip` au runtime : les paquets
sont figés dans le Dockerfile (`yfinance`, `requests`, `cairosvg`).
| Fichier | Rôle |
|---|---|
| `AGENTS.md` | Ce fichier : comportement, capacités, contraintes |
| `MEMORY.md` | Mémoire long terme curée (session principale uniquement) |
| `SOUL.md` / `IDENTITY.md` | Identité, persona |
| `gmail-rules.md` | Règles métier Gmail + whitelist |
| `gmail-housekeeping.md` | Journal append-only des vagues Gmail |
| `HANDOFF.md` | État global, transfert entre sessions |
| `bourse-config.json` | Watchlist et graphes du bilan de clôture |
| `memory/YYYY-MM-DD.md` | Notes du jour |
### 6.2 Binaires disponibles dans le conteneur
`python3` 3.11.2, `node` v24, `npm`, `curl`, `jq`, `git`, `file`, `du`,
`gog` v0.21.0, `cairosvg`, `yfinance`. Conteneur headless : aucun navigateur.
### 6.3 Web
- `web_fetch` : limiter à ~1500 caractères par appel. Pour la bourse, récupérer le prix
et la variation, jamais la page entière.
- `web_search` : 10 résultats maximum.
- Après usage d'un tool, résumer en 2 lignes et oublier le contenu brut.
---
## 7. Ajouter une capacité
1. La documenter **dans ce fichier**, avec la syntaxe exacte.
2. Ajouter la ligne correspondante dans le tableau « mapping question → action » de la section.
3. Ajouter une contrainte dure si nécessaire.
4. Tester depuis WhatsApp ou le dashboard.
5. Sauvegarder ce fichier avant modification (`AGENTS.md.bak-<timestamp>`).
⚠️ Ce fichier est injecté en entier dans chaque session. Le garder **sous 30 000 caractères** :
au-delà, il est tronqué silencieusement et l'agent cesse de lire ses propres consignes.
Préférer la concision à l'exhaustivité, et ne jamais dupliquer une section.