# Outils

Un module d’outils fonctionne sur chaque harnais d’agent. beeuni.yaml répertorie les outils Beeuni et ceux propres au projet, et la liste d’outils de chaque agent détermine qui peut les appeler.

Canonical page: https://beeuni.ai/docs/fr/project/tools

Un outil est une fonction que le modèle appelle pendant un tour. Une
[session](/docs/fr/work/sessions) comporte trois types d’outils, dans les deux
[environnements](/docs/fr/work/harnesses), harness standard et harness expérimental :

| Type | Noms | Implémenté par |
| --- | --- | --- |
| Outils de l’environnement | `bash`, `read`, `write`, `edit`, `glob`, `grep`, `task`, `question`, … | L’environnement. |
| Outils Beeuni | `web_search`, `image_search`, `scrape_webpage`, `memory`, `show` | Beeuni. `beeuni.yaml` répertorie chacun sous la forme `<name>: beeuni:<name>`. Un projet sans clé `tools:` obtient les cinq. |
| Outils du projet | Tout nom que vous déclarez | Votre projet : un module déclaré sous `tools:` dans `beeuni.yaml`. |

Les outils Beeuni et ceux du projet sont indépendants de l’environnement. Le démon du bac à sable charge un module par outil et l’exécute dans l’un ou l’autre environnement, avec le même nom, les mêmes arguments et la même sortie. harness expérimental appelle le module dans le processus du démon. harness standard l’appelle au moyen d’un plug-in écrit par le démon, qui transmet chaque appel au démon. Un module n’importe jamais de paquet d’environnement.

<a id="beeuni-tools-in-beeuniyaml"></a>

## Outils Beeuni dans beeuni.yaml

Un nouveau projet répertorie les cinq outils Beeuni sous `tools:` :

```yaml
tools:
  web_search: beeuni:web_search      # maintained by Beeuni, updated automatically
  image_search: beeuni:image_search
  scrape_webpage: beeuni:scrape_webpage
  memory: beeuni:memory
  show: beeuni:show
  # lookup_order: tools/lookup_order.ts   # your own tool: a module in your repo
```

`beeuni:<name>` signifie qu’il s’agit de l’outil maintenu par Beeuni. Un correctif le concernant est appliqué à chaque projet qui le répertorie, sans modification du projet.

<a id="which-beeuni-tools-a-session-gets"></a>

### Outils Beeuni disponibles dans une session

| `beeuni.yaml` | Outils disponibles dans la session |
| --- | --- |
| Aucune clé `tools:`, ni dans le fichier racine ni dans un fichier [importé](/docs/fr/project/manifest#imports) | Les cinq outils Beeuni. Un projet créé avant l’existence de cette liste les conserve tous. |
| Une clé `tools:`, même vide (`tools: {}` ou `tools:` dont toutes les lignes ont été supprimées) | Uniquement les outils Beeuni qu’elle répertorie, sous forme de `beeuni:<name>` ou de module remplaçant l’outil. |

Dans les deux cas, les outils propres au projet sont chargés.

<a id="see-what-a-session-gets"></a>

### Voir les outils disponibles dans une session

`beeuni tools ls` lit `beeuni.yaml` et ses imports, puis affiche chaque outil disponible dans une session, suivi des outils Beeuni supprimés par le projet :

```text
$ beeuni tools ls
NAME            KIND                 SOURCE
web_search      beeuni (overridden)  tools/web_search.ts
scrape_webpage  beeuni               beeuni:scrape_webpage
memory          beeuni               beeuni:memory
show            beeuni               beeuni:show
lookup_order    project              tools/lookup_order.ts

Removed Beeuni tools: image_search
Add `<name>: beeuni:<name>` under `tools:` to get one back.
```

| Type | Signification |
| --- | --- |
| `beeuni` | Un outil Beeuni répertorié sous la forme `beeuni:<name>` (ou tous les outils, en l’absence de clé `tools:`). |
| `beeuni (overridden)` | Un nom d’outil Beeuni accompagné d’un chemin de module : le module du projet est exécuté à la place. |
| `project` | Un outil propre au projet. |

`beeuni tools ls --json` affiche `{ manifest, tools_key, tools: [{ name, kind, source }], removed }`.

<a id="remove-a-beeuni-tool"></a>

### Supprimer un outil Beeuni

Supprimez sa ligne sous `tools:`. Tous les agents du projet perdent l’outil, dans les deux environnements, pour chaque session démarrée après la fusion de la demande de modification. Pour supprimer un outil pour un seul agent, utilisez plutôt [`agents.<name>.tools`](#who-may-use-which-tool).

Dans une session d’un projet qui a supprimé un outil :

- **harness standard** ne propose pas l’outil au modèle.
- **harness expérimental** ne propose pas l’outil. Un appel à celui-ci échoue avec `Tool show not found` (pour `show`).
- **Le démon** répond à `POST /beeuni/tools/<name>` avec `404` et `no tool named <name> is loaded`.
- **Une compétence qui nomme l’outil continue de se charger.** Par exemple, `beeuni-memory` nomme `memory`. Le modèle lit la compétence, constate que l’outil n’existe pas et utilise d’autres outils : sans `memory`, il lit et modifie les fichiers sous `memory/` avec les outils de gestion des fichiers.

<a id="change-a-beeuni-tool-eject"></a>

### Modifier un outil Beeuni : l’extraire

`beeuni tools eject <name>` fournit au projet sa propre copie d’un outil Beeuni :

```text
$ beeuni tools eject web_search
  ✓  Ejected web_search
  wrote   tools/web_search.ts (the Beeuni source, now the project's to change)
  set     beeuni.yaml: tools.web_search: tools/web_search.ts

Next:
  1. Edit tools/web_search.ts. Keep the default export: { description, parameters, execute(args, context) }.
  2. beeuni validate
  3. git add tools/web_search.ts beeuni.yaml && git commit -m "Own the web_search tool"
  4. Open a change request: git push origin HEAD && beeuni cr open --title "Own the web_search tool"
Sessions load the copy after the change request merges.
```

- Il écrit le code source Beeuni, c’est-à-dire les mêmes octets que ceux exécutés par le bac à sable, dans `tools/<name>.ts`. Le fichier importe uniquement des modules `node:*` et lit `BEEUNI_API_URL`, `BEEUNI_TOKEN` et les clés des fournisseurs depuis `context.env` ; il est donc exécutable tel quel.
- Il définit `<name>: tools/<name>.ts` dans `beeuni.yaml`, dans le fichier qui déclare le point d’entrée (le fichier racine ou un fichier importé). Toutes les autres lignes et tous les commentaires sont conservés. La ligne modifiée se termine par `# ejected from beeuni:<name>`.
- En l’absence de clé `tools:`, il en crée une et répertorie les quatre autres outils Beeuni sous la forme `beeuni:<name>`, de sorte que l’extraction ne supprime rien.
- Il refuse de continuer si `tools/<name>.ts` existe ou si l’outil pointe déjà vers un autre module. `--force` écrase le fichier.
- Il refuse tout nom qui ne correspond pas à un outil Beeuni.

La copie vous appartient. Beeuni ne la met plus à jour. Pour revenir à l’outil maintenu, remplacez la ligne par `<name>: beeuni:<name>` et supprimez le fichier.

<a id="override-without-eject"></a>

### Remplacer un outil sans l’extraire

Un nom d’outil Beeuni accompagné d’un chemin de module remplace l’outil Beeuni dans tous les environnements :

```yaml
tools:
  web_search: integrations/search/web_search.ts
```

<a id="the-web-ui-views"></a>

### Vues de l’interface Web

L’interface Web comporte une vue pour chaque outil Beeuni, sélectionnée par son nom. Les vues `show` et `memory` lisent les arguments de l’appel (`type`, `path`, `url`, `content`, `items` ; `command`, `path`). Les vues `web_search`, `image_search` et `scrape_webpage` lisent le JSON de sortie. Une copie modifiée qui conserve le nom doit conserver ces arguments et cette structure de sortie, sinon l’interface affiche la sortie brute.

### Validation

`beeuni validate` vérifie la liste :

- `<name>` dans `beeuni:<name>` doit être un outil Beeuni et la clé doit lui correspondre. `web_search: beeuni:image_search` est une erreur :
  ``must be `beeuni:web_search` (the Beeuni tool) or a repo-relative path to a .ts or .js module (e.g. `tools/web_search.ts`) that replaces it.``
- Une clé `tools:` qui ne répertorie aucun outil Beeuni déclenche un avertissement :
  ``Sessions of this project get no Beeuni tool (web_search, image_search, scrape_webpage, memory, show). Add `<name>: beeuni:<name>` to keep one.``
  Cela permet de repérer un projet qui ajoute son premier outil et supprime accidentellement les cinq autres.
- Un agent dont `tools` nomme un outil Beeuni que le projet ne charge pas déclenche un avertissement : ``"show" is a Beeuni tool this project does not load: add `show: beeuni:show` under the top-level `tools`, or remove it here.``

[La référence du manifeste](/docs/fr/project/manifest#tools) contient toutes les règles.

<a id="write-a-project-tool"></a>

## Écrire un outil de projet

```ts
// tools/lookup_order.ts
export default {
  description: 'Look up an order by id. Returns its status and carrier.',
  parameters: {
    type: 'object',
    properties: {
      id: { type: 'string', description: 'The order id, e.g. "A-1042"' },
      include_items: { type: 'boolean', description: 'Also list the items' },
    },
    required: ['id'],
  },
  async execute(args, context) {
    const response = await fetch(`https://orders.example.com/v1/orders/${args.id}`, {
      headers: { authorization: `Bearer ${context.env.ORDERS_API_KEY}` },
      signal: context.signal,
    })
    if (!response.ok) throw new Error(`orders API answered ${response.status}`)
    return await response.json()
  },
}
```

```yaml
# beeuni.yaml
tools:
  web_search: beeuni:web_search
  # … the other Beeuni tools you keep
  lookup_order: tools/lookup_order.ts
```

L’export par défaut est un objet simple :

| Champ | Règle |
| --- | --- |
| `description` | Chaîne non vide. Le modèle la lit pour décider quand appeler l’outil. |
| `parameters` | [Schéma JSON](https://json-schema.org) des arguments, `type: 'object'`. Le modèle le reçoit tel quel ; indiquez donc les arguments obligatoires dans `required`. Beeuni ne valide pas les arguments par rapport à ce schéma. |
| `execute(args, context)` | Renvoie une chaîne ou une valeur JSON quelconque (transmise sous forme de JSON indenté). Peut être `async`. Toute erreur levée est l’erreur de l’outil, et le modèle en lit le message. |

`context` contient :

| Champ | Description |
| --- | --- |
| `sessionId` | La session Beeuni à laquelle l’appel appartient. |
| `agent` | L’agent qui a effectué l’appel. |
| `directory` | La copie de travail du projet, `/workspace`. Résolvez les chemins relatifs par rapport à celle-ci. |
| `env` | L’environnement du conteneur, ainsi que les [secrets](/docs/fr/project/secrets) reçus par cet agent, dans leur état actuel. Lisez un secret avec `context.env.NAME` : un secret modifié après le démarrage du conteneur se trouve dans `context.env`, et non dans `process.env`. |
| `signal` | Un `AbortSignal` qui annule l’opération si l’utilisateur arrête le tour. Transmettez-le à `fetch`. |

- **Taille de la sortie.** Toute sortie dépassant 50 Ko ou 2 000 lignes est enregistrée intégralement dans un fichier du répertoire temporaire du bac à sable. Le modèle reçoit le début du contenu, la taille et le chemin du fichier. Renvoyez ce dont le modèle a besoin, pas un vidage brut.
- **Erreurs.** Une erreur levée met fin à l’appel, et le modèle lit son message. Le message d’erreur doit indiquer la modification à apporter.
- **Imports.** Un module peut importer des modules `node:*`, des API Bun et des fichiers relatifs. Un paquet est résolu depuis un `node_modules` situé à côté du module ou dans un répertoire parent. Le bac à sable n’installe aucun paquet pour un outil, et une [version de configuration](/docs/fr/work/runtime#config-convergence) ne contient que les fichiers suivis par Git. Utilisez `fetch` pour les API HTTP. TypeScript s’exécute sans étape de compilation.
- **Échecs.** Un module qui ne se charge pas (fichier manquant, absence d’export par défaut, `parameters` qui n’est pas un schéma d’objet, erreur à l’importation ou paquet introuvable) est ignoré, et les autres outils continuent de se charger. Le démon journalise `[tools] project tools that did not load` avec la raison, pour chaque outil.

<a id="parameters-with-zod"></a>

### `parameters` avec zod

zod 4 génère le même schéma JSON :

```ts
import { z } from 'zod'

export default {
  description: 'Look up an order by id.',
  parameters: z.toJSONSchema(z.object({ id: z.string().describe('The order id') })),
  async execute(args) {
    return `order ${args.id}: shipped`
  },
}
```

Ce module ne se charge que si `zod` peut être résolu depuis un `node_modules` dans la copie de travail où s’exécute la session. Sinon, il échoue avec `Cannot find package 'zod'`.
Pour écrire le schéma avec zod et fournir un schéma JSON brut, affichez-le une fois et collez le résultat dans `parameters` :

```bash
bun -e "import { z } from 'zod'; console.log(JSON.stringify(z.toJSONSchema(z.object({ id: z.string() })), null, 2))"
```

<a id="example-call-an-http-api-with-a-project-secret"></a>

### Exemple : appeler une API HTTP avec un secret du projet

Enregistrez la clé comme secret du projet. La commande lit sa valeur depuis l’entrée standard :

```bash
beeuni secrets set WEATHER_API_KEY=-
```

Accordez le secret aux agents qui appellent l’outil. L’agent `beeuni` du modèle de départ dispose de `secrets: all`. Un agent avec des permissions limitées doit le répertorier : `secrets: [WEATHER_API_KEY]`.

Écrivez le module :

```ts
// tools/weather_now.ts
export default {
  description: 'Current weather for a city: temperature in °C and conditions.',
  parameters: {
    type: 'object',
    properties: { city: { type: 'string', description: 'City name, e.g. "Berlin"' } },
    required: ['city'],
  },
  async execute(args, context) {
    const key = context.env.WEATHER_API_KEY
    if (!key) throw new Error('WEATHER_API_KEY is not set: run `beeuni secrets set WEATHER_API_KEY=-` and grant it to this agent.')
    const url = `https://api.weather.example.com/v1/current?city=${encodeURIComponent(args.city)}`
    const response = await fetch(url, { headers: { authorization: `Bearer ${key}` }, signal: context.signal })
    if (!response.ok) throw new Error(`weather API answered ${response.status}: ${await response.text()}`)
    const data = await response.json()
    return { city: args.city, temperature_c: data.temp_c, conditions: data.conditions }
  },
}
```

Déclarez-le :

```yaml
tools:
  # … the Beeuni tools you keep
  weather_now: tools/weather_now.ts
```

<a id="example-override-websearch"></a>

### Exemple : remplacer `web_search`

`beeuni tools eject web_search` écrit `tools/web_search.ts` et définit `web_search: tools/web_search.ts`. Modifiez ensuite la copie. Par exemple, pour ne conserver les résultats que d’un seul site, ajoutez le site à chaque requête dans `execute`, avant la requête :

```ts
// tools/web_search.ts, inside execute()
const queries = String(args.query ?? '').split('|||').map((query) => query.trim()).filter(Boolean)
  .map((query) => `site:docs.example.com ${query}`)
```

Conservez le nom, les arguments et le JSON de sortie pour que les compétences qui nomment `web_search` et la vue de l’interface Web continuent de fonctionner.

<a id="test-a-tool"></a>

### Tester un outil

`beeuni validate` vérifie le nom, le chemin, l’absence d’un fichier et les valeurs `beeuni:`. `beeuni tools ls` affiche les outils disponibles dans une session.

Validez ensuite les modifications, envoyez-les et ouvrez une demande de modification. Après sa fusion, démarrez une session et demandez un appel avec des arguments réels :

```bash
beeuni sessions new --wait --prompt "Call the weather_now tool once with city Berlin and print its output."
```

Un outil qui ne se charge pas n’est pas proposé au modèle. Un outil qui lève une erreur répond en affichant son message d’erreur. `beeuni sessions log <id>` affiche les messages de la session, avec chaque appel d’outil et son état.

<a id="declare-tools"></a>

## Déclarer des outils

`tools:` associe chaque nom d’outil à son module, ou un nom d’outil Beeuni à `beeuni:<name>`. Vous pouvez choisir librement le dossier du module.

```yaml
tools:
  web_search: beeuni:web_search
  memory: beeuni:memory
  lookup_order: tools/lookup_order.ts
  crm_note: integrations/crm/note.ts
```

- Un nom doit être en snake_case et ne pas dépasser 64 caractères : `^[a-z][a-z0-9_]{0,63}$`.
- Un nom d’outil de l’environnement est une erreur. Un nom d’outil Beeuni accepte `beeuni:<name>` ou un chemin de module ; le module remplace l’outil Beeuni dans tous les environnements.
- Un chemin est relatif au dépôt et se termine par `.ts`, `.js`, `.mts`, `.mjs`, `.cts` ou `.cjs`.
- Un fichier répertorié sous [`imports:`](/docs/fr/project/manifest#imports) peut déclarer des outils. Déclarer un nom dans deux fichiers est une erreur. Une clé `tools:` dans un fichier importé compte comme une clé `tools:` pour la règle de sélection.

[La référence du manifeste](/docs/fr/project/manifest#tools) contient toutes les règles.

<a id="who-may-use-which-tool"></a>

## Qui peut utiliser quels outils

`agents.<name>.tools` détermine les outils visibles par le modèle d’un agent. Cette liste englobe les outils de l’environnement, de Beeuni et du projet.

```yaml
agents:
  beeuni:
    tools: all                                    # every tool (the default)
  researcher:
    tools: [read, grep, web_search, lookup_order] # only these
  support:
    tools:
      exclude: [bash, write, edit]                # every tool but these
  greeter:
    tools: none                                    # no tool
```

| Valeur | Outils disponibles pour l’agent |
| --- | --- |
| omise ou `all` | Tous les outils. |
| `none` | Aucun outil. |
| une liste | Uniquement les outils répertoriés. Une liste contenant `*` est `all`. |
| `{ exclude: [...] }` | Tous les outils, sauf ceux répertoriés. |
| `{ bash: false, … }` | L’ancien format de mappage : `false` supprime un outil. |

- `tools` détermine la visibilité. Les règles `permission` de l’agent déterminent toujours si un appel est autorisé, soumis à confirmation ou refusé. Un outil qui ne figure pas dans la liste est masqué pour le modèle et tout appel à cet outil est refusé.
- La liste d’un agent ne peut pas ajouter un outil Beeuni que le projet ne charge pas. Répertoriez-le d’abord sous `tools:`, à la racine.
- Un sous-agent lancé avec l’outil `task` reçoit uniquement, dans harness expérimental, les outils autorisés à la fois par sa propre liste et par celle de l’agent de session. Dans harness standard, il reçoit les outils de sa propre liste : harness standard applique les règles d’un agent à cet agent uniquement. Pour empêcher un agent de déléguer, ne mettez pas `task` dans sa liste.
- Les outils de [connecteur](/docs/fr/connect/connectors) suivent `agents.<name>.connectors`, et non cette liste.
- `beeuni validate` signale tout nom qui ne correspond pas à un outil de l’environnement, de Beeuni ou du projet, ainsi que tout outil Beeuni que le projet ne charge pas. Une faute de frappe dans une liste `exclude` laisse l’outil réel activé.

Dans harness standard, `bash` s’applique également aux outils `pty_*` du plug-in de terminal, et `write`, `edit` et `apply_patch` partagent une même autorisation : en répertorier un affiche les trois, et en exclure un masque les trois. harness expérimental possède des outils `write` et `edit` distincts.

<a id="when-a-change-takes-effect"></a>

## Prise d’effet des modifications

Une session charge ses outils au démarrage de son environnement d’exécution. Une modification prend effet après la fusion de la demande de modification : les nouvelles sessions la chargent et, avec les [versions de configuration](/docs/fr/work/runtime#config-convergence), une session en cours la charge lors de la prochaine convergence. Dans harness expérimental, une modification d’un outil du projet ou de la liste des outils Beeuni redémarre l’environnement d’exécution sur place quand aucun tour n’est en cours ; la conversation se poursuit.

<a id="tools-in-harnessesopencodetools"></a>

## Outils dans `harnesses/opencode/tools/`

harness standard continue de charger les outils personnalisés depuis son répertoire de configuration. harness expérimental ne le fait pas. Les projets créés avant la prise en charge des outils hébergés contiennent des copies des outils Beeuni à cet emplacement (`web_search.ts`, `memory.ts`, …).

- Dans harness standard, un outil de ce dossier remplace l’outil hébergé du même nom. Le projet conserve donc sa copie jusqu’à sa suppression. Supprimez les copies des outils Beeuni : le démon héberge ceux répertoriés dans `beeuni.yaml`, dans les deux environnements.
- Pour exécuter un outil harness standard dans les deux environnements, déplacez-le hors du dossier, remplacez `tool({ description, args, execute })` par un objet simple dont `parameters` est un schéma JSON, lisez les secrets depuis `context.env` et déclarez-le sous `tools:`.
