---
title: "Référence du SDK"
description: "L’ensemble de l’API @beeuni/sdk — méthodes du client, modules, tours et distribution."
---

Cette page présente l’ensemble de l’API `@beeuni/sdk` : toutes les méthodes du client, les
modules indépendants de tout framework, les utilitaires de tours et la distribution du paquet. Consultez
[SDK](/docs/fr/sdk) pour commencer et [Sessions](/docs/fr/sdk/sessions) pour approfondir
le cycle de vie des sessions.

<a id="the-client"></a>

## Le client

`createBeeuni(config)` renvoie un client unique. Chaque méthode est un appel typé à
l’API de la plateforme. Les handles `project(id)` et `session(pid, sid)` associent les identifiants
pour vous éviter de les répéter.

```ts
const beeuni = createBeeuni({ backendUrl, getToken });

beeuni.accounts; // account / team operations
beeuni.accountInvites; // invite accept/decline by token alone
beeuni.projects; // top-level project operations
beeuni.connectors; // Connector calls scoped by an agent-minted token
beeuni.connector(slug); // one connector in the token's scope → see p.connector(slug)
beeuni.project(id); // id-bound project handle
beeuni.session(pid, sid); // id-bound session handle → see Sessions
beeuni.github; // GitHub App install + repo linking (account-scoped)
beeuni.gitBackend; // the instance git backend ("Beeuni managed")
beeuni.billing; // credits, subscription, tier, transactions
beeuni.sandboxShares; // public share links for a sandbox port
beeuni.connectStatus; // easy-connect status
beeuni.marketplace; // public marketplace catalog
beeuni.validateToken; // pasted-API-key check
beeuni.config; // platform config in effect
beeuni.runtime(); // runtime REST compatibility client
```

<a id="accounts--beeuniaccounts"></a>

### Comptes — `beeuni.accounts`

| méthode | fonction |
| --- | --- |
| `list()` · `get(accountId)` | comptes auxquels vous appartenez · un compte |
| `create({ name })` · `updateName(accountId, name)` | créer · renommer un compte |
| `branding.get(accountId)` · `branding.update(accountId, { app_name })` · `branding.uploadAsset(accountId, kind, file)` · `branding.removeAsset(accountId, kind)` · `branding.reset(accountId)` | identité visuelle de l’organisation (Enterprise) : logo / icône / favicon et nom de produit personnalisés ; `kind` vaut `logo` · `icon` · `favicon`, ou `logo_dark` · `icon_dark` · `favicon_dark` pour la variante du thème sombre |
| `members(accountId)` · `invite(accountId, input)` | lister les membres · en inviter un |
| `updateMemberRole(accountId, userId, role)` · `removeMember(accountId, userId)` | attribuer le rôle dans le compte · retirer un membre |
| `invites(accountId)` | invitations en attente |
| `cancelInvite(accountId, inviteId)` · `resendInvite(accountId, inviteId)` | annuler · renvoyer une invitation en attente |
| `leave(accountId)` | quitter le compte |

`accounts.tokens` génère des clés API limitées au compte (`beeuni_pat_...`). Consultez
[Authentification du SDK](/docs/fr/accounts#tokens) pour le modèle complet des jetons.

| méthode | fonction |
| --- | --- |
| `tokens.list(accountId?, options?)` | lister les clés API — celles de tout le compte, ou uniquement les vôtres avec `{ mine: true }` |
| `tokens.create(input)` | en générer une — `{ accountId?, name, expiresAt?, projectId? }` |
| `tokens.revoke(tokenId, accountId?)` | en révoquer une |

`accounts.audit` est le journal de reconstitution pour les entreprises. Il combine les requêtes
API authentifiées avec les événements sémantiques de session, de connecteur, d’approbation et d’ordinateur.

| méthode | fonction |
| --- | --- |
| `audit.log(accountId, filters?)` | lister les événements par projet, session, acteur, source, résultat, requête, corrélation, ressource, action ou date |
| `audit.export(accountId, filters?)` | exporter le même flux d’événements filtré au format CSV ou JSONL |
| `audit.webhooks.list/create/update/remove(...)` | gérer les webhooks SIEM signés pour le flux centralisé |

Chaque événement inclut `project_id`, `session_id`, `actor_type`, `source`, `outcome`,
`request_id`, `trace_id` et `correlation_id` lorsque l’action les fournit.
L’API ne stocke ni corps de requête, ni prompts, ni secrets, ni identifiants d’authentification, ni arguments bruts
de connecteur dans l’événement centralisé. Les événements de connecteur peuvent inclure un
aperçu limité des arguments qui masque les champs ressemblant à des identifiants d’authentification et les données opaques.

<a id="access-assignments"></a>

### Attributions d’accès

Beeuni utilise un seul type d’enregistrement d’octroi : une **attribution**. Elle associe un principal (`user`,
`group`, `service_account` ou `pending`) à un rôle, dans un périmètre (`account`,
ou un `project`), éventuellement limité à un objet (`agent`, `skill`, `secret`,
`app` ou `trigger`) et éventuellement accompagné d’un `expires_at`. Les accès de groupe,
les accès par ressource et les associations à des rôles personnalisés sont tous des attributions. Consultez
[Comptes et accès](/docs/fr/accounts#one-access-model) pour le modèle.

L’interface REST canonique est la suivante :

| Méthode + chemin | Fonction |
| --- | --- |
| `GET /v1/accounts/{accountId}/iam/assignments` | lister les attributions, filtrées par principal, périmètre, objet ou rôle |
| `POST /v1/accounts/{accountId}/iam/assignments` | créer une attribution |
| `DELETE /v1/accounts/{accountId}/iam/assignments/{assignmentId}` | révoquer une attribution |
| `GET /v1/accounts/{accountId}/iam/permissions` | le catalogue des permissions, sous forme de données |
| `GET /v1/accounts/{accountId}/iam/roles` · `…/roles/{roleId}/permissions` | rôles · permissions d’un rôle |

Le SDK les expose via `listAssignments`, `createAssignment`,
`revokeAssignment` et `listPermissions`. Une ligne du catalogue contient `action`,
`scope_type`, `resource_type`, `delegable`, `description`, `area`, `level` et
`implies` — consultez-la plutôt que de coder en dur les chaînes d’action.

L’attribution d’un rôle personnalisé nécessite le droit `rbac` du compte ; sans celui-ci, la route répond
`402` avec `code: "entitlement_required"`.

<a id="account-invites--beeuniaccountinvites"></a>

### Invitations au compte — `beeuni.accountInvites`

Accessible avec le seul jeton d’invitation — la personne invitée peut ne pas encore être membre.

| méthode | fonction |
| --- | --- |
| `listMine()` | toutes les invitations en attente et non expirées adressées à votre e-mail, avec l’espace de travail et les projets auxquels elles donnent accès |
| `describe(inviteId)` · `accept(inviteId)` · `decline(inviteId)` | prévisualiser · accepter · refuser une invitation |

<a id="projects--beeuniprojects"></a>

### Projets — `beeuni.projects`

| méthode | fonction |
| --- | --- |
| `list()` · `listForAccount(accountId)` | vos projets · projets d’un compte |
| `get(id)` · `detail(id)` | résumé · détails complets |
| `create(input)` · `createRepo(input)` | à partir d’un `repo_url` existant · nouveau dépôt GitHub vide |
| `provision(input)` | nouveau projet sur un nouveau dépôt géré par Beeuni, initialisé avec un modèle de démarrage — `{ name, account_id?, seed_starter?, starter_template?, marketplace_items?, source_item_id?, idempotency_key? }` |
| `update(id, input)` · `archive(id)` | mettre à jour les paramètres · archiver |
| `llmCatalog(id)` · `modelPicker(id)` | catalogue de modèles complet · compact pour un sélecteur |
| `sandboxTemplates(id)` · `sandboxHealth(id)` | modèles de build du sandbox · état du build |
| `sessions(id)` · `createSession(id, input?)` | lister les sessions visibles · créer une session |

`provision` crée un nouveau projet ; il ne démarre pas le
sandbox d’un projet existant. Démarrez plutôt une session — voir [Sessions](/docs/fr/sdk/sessions).

Envoyez `idempotency_key` lorsqu’une nouvelle tentative est possible — un rechargement, un deuxième onglet, un
délai d’attente dépassé suivi d’une nouvelle tentative. `provision` génère un tout nouveau dépôt géré à chaque appel ;
sans clé, ces appels créent donc tous de véritables projets en double. Réutilisez une même clé pour chaque
tentative d’une seule création logique : les appels répétés renvoient le projet créé par la première
tentative (201, mêmes `project_id` et `push_token: null`). La clé identifie la
tentative, pas la charge utile — la réutiliser avec un autre `name` renvoie le premier
projet et ignore la nouvelle valeur ; générez donc une nouvelle clé pour chaque création distincte.
Créer un deuxième projet avec le même **nom** et sans clé reste possible.

Un appel répété qui arrive alors que le premier appel est encore en cours de provisionnement reçoit `409` avec
`code: 'provision_in_flight'` plutôt qu’un `project_id` que cet appel pourrait encore
annuler. Réessayez avec la même clé.

`project(id).sessions.list({ scope: 'project' })` est un inventaire du cycle de vie destiné à un
appelant disposant des permissions de gestionnaire de projet. Il ajoute les sessions accessibles indisponibles, préchauffées et supprimées de façon réversible, avec
des métadonnées de propriété et d’état d’exécution. Les deux périmètres de liste excluent toute session que
l’appelant ne peut pas ouvrir.

`sessions.list` et `sessions.listPage` acceptent également les filtres de hiérarchie et
d’attribution. Sans eux, la liste reste plate et inchangée.

| option | fonction |
| --- | --- |
| `parent: 'root'` | Sessions de premier niveau uniquement. Chaque ligne contient `child_count`. |
| `parent: '<sessionId>'` | Les sessions enfants de cette session, les plus récentes d’abord. |
| `startedBy: 'me' \| 'others' \| 'automated'` | Filtrer par `initiator`, relativement à l’appelant. |
| `q` | Recherche insensible à la casse sur le titre, le libellé de démarrage, le slug du déclencheur et le préfixe de l’identifiant de session, parmi toutes les sessions que l’appelant peut ouvrir. Avec `parent: 'root'`, une racine correspondant via un enfant possède `search_match: 'child'`. |

Chaque ligne de session possède `parent_session_id` et `initiator`
(`{ type: 'member' | 'trigger' | 'channel' | 'api' | 'system', id, label }`).
`initiator` indique qui a démarré l’ensemble de l’exécution ; un enfant reprend la valeur de son parent.
`sessionParentId(session)` lit `parent_session_id`, puis l’ancien
`metadata.spawned_by_session`. Un curseur n’est valide que pour l’ensemble de filtres qui
l’a émis.

### GitHub — `beeuni.github`

Installation de l’application GitHub et association de dépôts au niveau du compte, et non du projet.

| méthode | fonction |
| --- | --- |
| `getInstallation(accountId)` · `listInstallations(accountId)` | installation de ce compte · installations accessibles à l'utilisateur |
| `saveInstallation(input)` · `deleteInstallation(accountId, installationId?)` | enregistrer · dissocier une installation |
| `listLinkableInstallations(input)` | installations que l'utilisateur GitHub autorisé peut associer ; chaque entrée contient `linked_to_other_accounts`, le nombre d'autres comptes Beeuni qui la détiennent — jamais leurs noms |
| `listRepositories(accountId, installationId?)` · `listRepositoryBranches(...)` | dépôts visibles par l'installation · branches et branche par défaut de GitHub |
| `linkRepository(input)` | Importer un dépôt en tant que projet à l'aide d'une installation GitHub autorisée. |
| `replaceProjectRepository({ project_id, repo_url, expected_repo_url, github_token })` | remplacer le dépôt GitHub d'un projet existant. Utilisez soit `github_token` avec un accès Contents en lecture/écriture, soit `installation_id` avec un `github_user_token` temporaire pour vérifier l'accès administrateur à l'organisation et autoriser uniquement ce dépôt via la GitHub App. `copy_shared_secrets_from_project_id` avec `copy_shared_secret_identifiers` copie les secrets d'exécution partagés sélectionnés dans la même transaction. Un `expected_repo_url` obsolète renvoie 409. L'ancien dépôt reste sur GitHub. Les nouvelles sessions utilisent le dépôt de remplacement ; les sessions existantes conservent leur copie de travail. |

Cette liste ne contient que de véritables connexions de compte. « Beeuni managed » n'en fait pas
partie — il s'agit du backend de l'instance ci-dessous.

<a id="instance-git-backend--beeunigitbackend"></a>

### Backend git de l'instance — `beeuni.gitBackend`

Un propriétaire unique pour tout le déploiement, accompagné d'un identifiant d'authentification, utilisé pour les dépôts
gérés par Beeuni. Ce n'est jamais une connexion de compte et il n'apparaît jamais dans
`beeuni.github.listInstallations`.

| méthode | fonction |
| --- | --- |
| `get()` | `{ configured, kind: 'app' \| 'pat' \| null, owner }` — lisible par tout utilisateur authentifié ; expose uniquement le nom de connexion du propriétaire déjà présent dans chaque `repo_url` géré |


<a id="billing--beeunibilling"></a>

### Facturation — `beeuni.billing`

Consulte les crédits, l’abonnement, l’offre et l’historique des transactions. Utilisez le paiement et la gestion de facturation Beeuni pour les paiements et les achats de crédits.

| méthode | fonction |
| --- | --- |
| `accountState(accountId?)` · `accountStateMinimal(accountId?)` | état de facturation complet · minimal |
| `transactions(params?)` · `transactionsSummary(params?)` | historique · totaux récapitulatifs |
| `creditBreakdown(accountId?)` · `usageHistory(params?)` | solde de crédits par source · utilisation au fil du temps |
| `sessionCosts.list(options?)` · `sessionCosts.get(sessionId, options?)` | relevés paginés des coûts des sessions · registre détaillé d'une session |
| `tierConfigurations()` | niveaux de forfait disponibles |
| `checkout.createSession(input)` · `checkout.confirmSession(sessionId, accountId?)` | **Obsolètes.** Rejettent avec `ENDPOINT_RETIRED`. Supprimés dans la prochaine version majeure. |
| `subscription.createPortalSession(...)` · `subscription.cancel(...)` · `subscription.reactivate(...)` | ouvrir le portail client · résilier · réactiver |
| `subscription.cancelScheduledChange(...)` · `prorationPreview(...)` | résilier · prévisualiser un changement de forfait |
| `subscription.scheduleDowngrade(...)` | **Obsolète.** Rejette avec `ENDPOINT_RETIRED`. Changez de forfait dans le portail client. Supprimé dans la prochaine version majeure. |
| `credits.purchase(input)` · `credits.autoTopupSettings(...)` · `credits.configureAutoTopup(...)` | achat ponctuel · lire · configurer la recharge automatique |

`sessionCosts.list()` accepte `accountId`, `projectId`, `limit` et `offset`.
Chaque ligne combine le coût LLM finalisé et le coût de calcul facturé du sandbox.
`sessionCosts.get()` ajoute l'utilisation des modèles et le registre distinguant LLM et calcul.
La liste inclut un total de rapprochement pour les coûts sans session.

<a id="sandbox-shares--beeunisandboxshares"></a>

### Partages de sandbox — `beeuni.sandboxShares`

Liens de partage publics pour un port exposé du sandbox. Leur portée est le sandbox, pas
le projet.

| méthode | fonction |
| --- | --- |
| `list(sandboxId)` | liens de partage actifs |
| `create(input)` | en créer un — `{ sandboxId, port, ttl?, label? }` |
| `revoke(sandboxId, token)` | en révoquer un |

<a id="marketplace-catalog--beeunimarketplace"></a>

### Catalogue de la marketplace — `beeuni.marketplace`

Consultation du catalogue public, en lecture seule — distincte de `project(id).marketplace`,
qui installe un élément dans un projet.

| méthode | fonction |
| --- | --- |
| `items(options?)` · `item(id)` · `itemFile(id, path)` | parcourir · un élément · un fichier dans un élément |
| `marketplaces()` · `featured()` | toutes les marketplaces · marketplaces mises en avant |
| `sources.list()` · `sources.add(input)` · `sources.remove(id)` | lister · ajouter · supprimer une source |

### Notifications — `beeuni.notifications`

Les notifications de l'appelant, pour tous les comptes et projets. Chaque personne connectée
dispose d'une boîte de réception, d'un enregistrement de préférences et d'abonnements Web Push pour
ses navigateurs. Chaque méthode nécessite l'identifiant d'authentification d'une personne : un compte de service
reçoit `403`.

:::info[Notifications est un indicateur de fonctionnalité]
Beeuni ne crée des entrées dans la boîte de réception et n'envoie des notifications Web Push et des e-mails que pour les projets dont
l'indicateur `notification_center` est activé. Il est désactivé par défaut. `list()` exclut les entrées
des projets dont l'indicateur est désactivé. `session(pid, sid).watch()` et `.setWatch()`
répondent `403` avec `{ error, code: "feature_disabled", feature: "notification_center" }`
lorsqu'il est désactivé ; utilisez `isFeatureDisabledError(error)` pour adapter le traitement à son état.
Les préférences, les abonnements Web Push et les jetons d'appareil sont propres à chaque personne et fonctionnent
dans les deux cas. Lorsque l'indicateur est désactivé, le téléphone du créateur de la session reçoit tout de même une notification push
lorsqu'un tour se termine, échoue, pose une question ou demande une autorisation. Voir
[Indicateurs de fonctionnalité](/docs/fr/feature-flags) et [Notifications](/docs/fr/work/notifications).
:::

```ts
const page = await beeuni.notifications.list({ limit: 20 });
await beeuni.notifications.markRead({ ids: [page.notifications[0].id] });
await beeuni.notifications.updatePreferences({ kinds: { turn_done: { push: false } } });
```

| méthode | fonction |
| --- | --- |
| `list({ limit?, before? }, { signal? })` | la page la plus récente : `{ notifications, unread_count, next_before }`. `limit` va de 1 à 50 (20 par défaut). Passez `next_before` comme `before` pour la page suivante. Les entrées que l'appelant ne peut plus voir sont omises. Un échec n'appelle pas le gestionnaire d'erreurs de l'hôte. |
| `markRead({ ids })` · `markRead({ all: true })` · `markRead({ sessionId })` | marquer comme lues 1 à 100 entrées, toutes les entrées ou toutes les entrées d'une session. Ne modifie que les entrées de l'appelant. Renvoie `{ updated, unread_count }`. |
| `preferences()` · `updatePreferences({ kinds })` | lire · modifier le choix des notifications push et des e-mails par type. Renvoie `{ kinds, email_available }`. Un patch ne modifie que les types et les canaux qu'il nomme. |
| `webPushPublicKey()` | `{ public_key }` : le `applicationServerKey` pour `pushManager.subscribe` |
| `registerWebPushSubscription({ endpoint, keys })` | envoyer les notifications push à ce navigateur. Nécessite une connexion dans le navigateur : un jeton personnel reçoit `403`. Un point de terminaison sur un service push inconnu reçoit `400`. |
| `unregisterWebPushSubscription(endpoint, { signal? })` | arrêter les notifications push vers ce navigateur. Renvoie `{ deleted }`. |
| `registerDeviceToken(input)` · `unregisterDeviceToken(token, { signal? })` | enregistrer · supprimer le jeton push Expo d'une application native |

Types (`InboxNotificationKind`, répertoriés dans `INBOX_NOTIFICATION_KINDS`) :

| type | envoyé lorsque | push | e-mail (par défaut) |
| --- | --- | --- | --- |
| `turn_done` | un tour que vous avez lancé ou que vous suivez est terminé | activé | désactivé |
| `turn_error` | ce tour s'est terminé par une erreur | activé | activé, dans un récapitulatif |
| `question` | l'agent pose une question | activé | activé, dans un récapitulatif |
| `permission` | l'agent demande une autorisation | activé | désactivé |
| `shared` | quelqu'un a partagé une session avec vous | activé | activé, dans un récapitulatif |
| `automation_failed` | une automatisation que vous suivez a commencé à échouer | activé | activé, immédiatement |
| `automation_recovered` | cette automatisation fonctionne à nouveau | activé | désactivé |

`DEFAULT_NOTIFICATION_PREFERENCES` contient ces valeurs par défaut. L'entrée de la boîte de réception est
toujours créée ; les préférences ne déterminent que les notifications push et les e-mails. Un e-mail récapitulatif
liste les entrées encore non lues 15 minutes après leur arrivée, avec au maximum un récapitulatif
par heure. Masquez la colonne e-mail lorsque `email_available` vaut `false`.

Le `url` de chaque entrée ouvre son sujet. Son paramètre de requête `?notification=<id>` permet à la
page de marquer l'entrée comme lue.

Le handle de session porte le suivi de l'appelant pour une session :

| méthode | fonction |
| --- | --- |
| `session(pid, sid).watch()` | `{ watching }` : l'appelant reçoit-il des notifications pour cette session |
| `session(pid, sid).setWatch(watching)` | `false` désactive les notifications de la session pour l'appelant, `true` rétablit son suivi. Envoyer un prompt à une session active son suivi, mais ne réactive jamais les notifications désactivées. |
| `session(pid, sid).presence({ tab_id, active, alerts? }, { keepalive? })` | le bail de présence de cet onglet. `alerts: true` indique que l'onglet affiche ses propres notifications : lorsque `notification_center` est activé, le serveur n'envoie pas de notifications au téléphone ni via Web Push uniquement pendant qu'un onglet affichant des alertes est utilisé. Lorsqu'il est désactivé, `alerts` est ignoré et tout onglet présent bloque les notifications push au téléphone. `keepalive` permet à la requête de survivre à la fermeture d'une page. Par défaut : activé pour un signalement d'absence, désactivé pour un signalement de présence. `useSession` n'envoie `keepalive` qu'avec `presencePageExit`. |

<a id="the-project-handle--beeuniprojectid"></a>

### Le handle de projet — `beeuni.project(id)`

Lie l’identifiant du projet ; toutes les sous-ressources en dépendent.

```ts
const p = beeuni.project(projectId);
await p.detail();
await p.update({ name });
await p.llmCatalog();
```

| méthode | description |
| --- | --- |
| `get` · `detail` · `update` · `archive` | lire · détails complets · mettre à jour · archiver |
| `llmCatalog` · `modelPicker` · `sandboxHealth` | lectures du modèle et des builds du sandbox |
| `onboardingComplete` | marquer l’intégration du projet comme terminée |
| `validateManifest(raw)` | valider un manifeste `beeuni.yaml` (ou l’ancien `beeuni.toml`) côté serveur |
| `gitToken()` | générer un nouveau jeton git push à portée limitée (`409` pour un dépôt que vous fournissez) |
| `setAgentScope(agentName, scope)` | définir les secrets et connecteurs autorisés d’un agent dans le manifeste — la seconde liaison, pas un rôle |

<a id="ptokens--project-scoped-api-keys"></a>

#### `p.tokens` — clés API limitées au projet

Générées automatiquement à la création d’une session sous la forme `BEEUNI_TOKEN` ; peuvent aussi être générées manuellement.

| méthode | description |
| --- | --- |
| `list()` | clés API du projet |
| `create(input?)` | en générer une nouvelle |
| `revoke(tokenId)` | en révoquer une |

<a id="psetuplinks--agent-minted-setup-links"></a>

#### `p.setupLinks` — liens de configuration générés par l’agent

Un lien qu’une personne ouvre pour saisir un secret ou connecter une application, sans accès complet
au projet.

| méthode | description |
| --- | --- |
| `requestSecret(input)` · `requestConnector(input)` | lien pour recueillir un secret · connecter une application |

<a id="papps--apps-releases-machines-volumes"></a>

#### `p.apps` — Apps (versions, machines, volumes)

Nécessite la fonctionnalité de projet `apps`. Guide : [SDK → Apps](/docs/fr/sdk/apps).

| méthode | description |
| --- | --- |
| `list()` · `get(appId)` · `create(input)` · `update(appId, input)` · `remove(appId)` | inventaire et cycle de vie ; `create` accepte `uses`, `update` accepte aussi `labels` |
| `deployments.*` · `artifacts.*` · `access.*` · `rollback` · `start` · `stop` | déploiements, artefacts, politique d’accès, trafic et environnement d’exécution |
| `machines.list/start/stop/restart/destroy` · `scale(appId, count)` · `exec(appId, input)` | machines par groupe de processus ; `exec` nécessite `project.app.admin` |
| `volumes.list/create/update/destroy/snapshots/snapshot/restore/fork` | volumes persistants, instantanés quotidiens, restauration, duplication |
| `token(appId)` | un jeton de connexion de 15 minutes ; `409 app_capability_unsupported` sans `member_tokens` |

<a id="psecrets--project-secrets"></a>

#### `p.secrets` — secrets du projet

| méthode | description |
| --- | --- |
| `list()` · `upsert(input)` | lister les métadonnées · créer ou mettre à jour une valeur accessible uniquement en écriture et une politique de transmission |
| `setStrategy(identifier, strategy, options?)` | modifier l’exposition et sa liste d’hôtes |
| `broker(identifier, request)` | exécuter une requête HTTPS autorisée par la session et soumise à une politique |
| `remove(identifier)` | supprimer un secret |
| `setPersonal(name, value)` · `removePersonal(name)` | définir · supprimer une substitution par utilisateur |
| `setGitCredential(input)` | définir un identifiant d’authentification git |

`runtime` avec le consommateur `sandbox` correspond à l’exposition **environment** — celle par défaut,
et la seule politique qui place une valeur en clair dans la session. `egress` avec
le consommateur `network` correspond à l’exposition **egress-enforced** : la session détient une référence
et Beeuni substitue la valeur réelle en dehors du sandbox, pour les seuls hôtes HTTPS
énumérés dans la politique. L’exposition egress-enforced est expérimentale ; elle nécessite le
flag de fonctionnalité `secrets_egress` (Settings → Feature flags). Lorsque ce flag est désactivé,
`setStrategy(identifier, 'egress', …)` et `upsert(...)` avec une politique de sortie
renvoient `403` `feature_disabled`. Aucun consommateur `broker` n’a la moindre présence
dans la session. Voir
[Secrets](/docs/fr/project/secrets). La méthode `broker(...)` nécessite un
jeton limité à la session et une référence de session active.

<a id="paccess--project-assignments-invites-requests"></a>

#### `p.access` — attributions, invitations et demandes du projet

Chaque méthode ici lit ou écrit une attribution limitée à ce projet. Les rôles
du projet sont `manager` et `member`.

| méthode | description |
| --- | --- |
| `list()` · `invite(email, role)` | identités disposant d’un accès · inviter un utilisateur |
| `update(userId, role)` · `revoke(userId)` | attribuer un rôle de projet · révoquer l’attribution |
| `pendingInvites()` · `requests()` | invitations en cours · demandes d’accès en attente |
| `resendInvite(inviteId)` · `revokeInvite(inviteId)` · `approveRequest(id)` · `rejectRequest(id)` | renvoyer/révoquer une invitation · approuver/rejeter une demande |
| `groupGrants()` · `attachGroupGrant(...)` · `updateGroupGrant(...)` · `detachGroupGrant(...)` | les mêmes attributions, avec une identité `group` |

`p.access.resourceGrants` est la vue des **attributions d’objet** : elle limite une
identité à un seul objet du projet plutôt qu’à l’ensemble du projet. Beeuni
applique actuellement les attributions d’objet aux agents et aux compétences. Un agent est fermé par
défaut — un membre n’y accède que lorsqu’une attribution le désigne, lui ou l’un de ses
groupes. Une attribution d’objet ne porte aucune permission propre et restreint un
responsable de projet autant qu’un membre.

| méthode | description |
| --- | --- |
| `resourceGrants.list()` | attributions d’objet dans ce projet |
| `resourceGrants.create(input)` | attribuer un objet à un utilisateur ou à un groupe |
| `resourceGrants.remove(grantId)` | révoquer une attribution d’objet |

<a id="pconnectorslug--one-connector"></a>

#### `p.connector(slug)` — un connecteur

| méthode | description |
| --- | --- |
| `run(action, args, options?)` | la sortie de l’action ; lève `ConnectorCallError` ou `ConnectorApprovalPendingError` — voir [Erreurs](/docs/fr/sdk/reference#pconnectorslug--one-connector) |
| `call(action, args, options?)` | le résultat brut de l’appel ; `202` se résout avec `ok: false` |
| `describe()` · `describe(action)` | le connecteur avec les schémas d’entrée et de sortie · une action |
| `accounts()` | les comptes sous lesquels cet appelant peut l’exécuter, celui par défaut en premier |
| `paginate(action, args, { next, maxPages? })` | itérateur asynchrone sur la sortie de chaque page ; lève `ConnectorPageLimitError` avec `nextArgs` lorsqu’il existe plus de `maxPages` pages (100 par défaut) — voir [Pages](/docs/fr/sdk/reference#pconnectorslug--one-connector) |
| `uploadAttachment(content, input)` | `p.connectors.uploadAttachment` avec `connector` défini sur ce slug |

`options` : `account`, `approvalContext`, `timeoutMs` (90 000 par défaut), `signal`. Avec le fichier de `beeuni connectors types`, les arguments et la sortie sont typés.

<a id="pconnectors--tool-connectors"></a>

#### `p.connectors` — connecteurs d’outils

| méthode | description |
| --- | --- |
| `catalog()` · `tools()` | catalogue Connector appelable · outils `<connector>.<action>` aplatis |
| `search(query, options?)` · `describe(tool)` | rechercher · inspecter un outil appelable |
| `call(tool, args?, options?)` | appeler un outil `<connector>.<action>` via la passerelle côté serveur. `options.account` indique le compte sous lequel l’exécuter — voir ci-dessous. |
| `accounts(slug)` | les comptes sous lesquels cet appelant peut appeler `slug`, celui par défaut en premier — les comptes auxquels `options.account` est comparé |
| `uploadAttachment(content, input)` | préparer des octets pour `input.connector` ; renvoie `ref` (`{ $beeuni_attachment }`) à placer dans les arguments d’un appel ultérieur — voir [Joindre des fichiers](/docs/fr/connect/connectors#attach-files) |
| `list()` · `config(connectorId)` | connecteurs configurés · configuration d’un connecteur |
| `create(input)` | ajouter un connecteur |
| `auth.discover(input)` | prévisualiser l’authentification à partir d’une spécification OpenAPI, d’une collection Postman ou d’un point de terminaison |
| `remove(connectorId)` · `sync()` | supprimer un connecteur · resynchroniser les connecteurs |
| `setName(connectorId, name)` · `setSensitive(connectorId, sensitive)` | renommer · le marquer comme sensible (approbation supplémentaire requise) |
| `setAuthorizationStrategy(slug, strategy)` | @deprecated. Appelle toujours la route ; celle-ci est sans effet, répond `{ok:true, deprecated:true}` et ne change rien. `owner_type` sur chaque connexion constitue désormais l’intégralité de la règle d’accès. |
| `setCredentialMode(connectorId, mode)` · `setCredential(connectorId, input)` | changer de source · définir la valeur de l’identifiant d’authentification |
| `policies.get(connectorId)` · `policies.set(connectorId, policies)` | lire · remplacer ses politiques d’outils |
| `connections.list()` · `connections.reconcile(input)` | lister · créer/mettre à jour les comptes connectés |
| `connections.updateCredential(connectionId, input)` | renouveler un identifiant d’authentification de connexion |
| `connections.revoke(connectionId)` · `connections.activate(connectionId)` | bloquer · rétablir une connexion |
| `connections.setDefault(connectionId)` | en faire la connexion par défaut pour le périmètre de son propriétaire |
| `connections.rename(connectionId, label)` | modifier uniquement le libellé ; le compte reste autorisé. Chaque connexion porte `connected_as`, l’identité sous laquelle elle a été autorisée. |

`p.connectors.discover` permet de parcourir le catalogue des connecteurs directs. Cette fonctionnalité est **expérimentale** et désactivée par défaut — activez-la pour chaque projet sous [Settings → Feature flags](/docs/fr/feature-flags) → "Connectors API Discover". Easy Connect reste la place de marché de connecteurs par défaut.

| méthode | description |
| --- | --- |
| `discover.list(query?, cursor?)` · `discover.detail(id)` | rechercher des entrées OpenAPI/MCP/GraphQL/CLI · détails d’une entrée |

`p.connectors.pipedream` est la voie de compatibilité de l’ancien OAuth géré ; `listApps`
ne renvoie que les applications OAuth. Utilisez Easy Connect pour les nouvelles intégrations gérées. Connectez directement les applications à clé API.

| méthode | fonction |
| --- | --- |
| `pipedream.listApps(params?)` · `pipedream.connect(input)` · `pipedream.finalize(input)` | parcourir le catalogue d’applications · lancer un parcours de connexion · le finaliser |

Un connecteur définit l’outil, l’application du fournisseur et les politiques. `connections`
stocke ses **comptes** connectés — un connecteur peut en contenir plusieurs côte à côte.
Une session peut en fixer un pour chaque appel avec
`connector_bindings: { alias: { connection_id } }`, ou un appel individuel peut en désigner
un avec `call(tool, args, { account })`. Les identifiants restent chiffrés et
sont résolus à chaque requête.

`connections` est la seule façade d’autorisation active. Les anciens noms
`authorizations` et `profiles` ne font pas partie de l’interface actuelle du SDK.

<a id="accounts-and-the-call-time-gate"></a>

#### Comptes et contrôle au moment de l’appel

Chaque connexion possède un `owner_type` : `project` (partagé — accessible à toute personne
à qui le connecteur est accordé, humain ou compte de service) ou `member` (privé —
accessible uniquement à son propriétaire, et seulement dans une session privée ; jamais à un
compte de service). L’accès à un compte partagé peut être restreint à des groupes et à des personnes :
`connections.list()` renvoie ses autorisations dans `shared_with`, et
`connectionSharedWithEveryone(connection)` indique si tous les membres du
projet peuvent l’utiliser. Un compte à accès restreint suit les règles des comptes privés pour les
personnes désignées, et `usable: false` signale un compte que l’appelant ne voit que pour le gérer.
Accordez ou révoquez l’accès avec `createAssignment` / `revokeAssignment`
(`object: { type: 'connection', id }`) — voir
[Choisir qui peut utiliser un compte partagé](/docs/fr/connect/connectors#choose-who-can-use-a-shared-account).
`p.connectors.accounts(slug)` répertorie tous les comptes accessibles à cet appelant,
avec celui par défaut en premier :

```ts
const accounts = await beeuni.project(projectId).connectors.accounts('stripe-read');
// [{ connection_id, label, owner_type, is_default }, ...]

await beeuni.project(projectId).connectors.call(
  'stripe-read.get_balance',
  {},
  { account: 'My Stripe' }, // a connection id, a label, or the words `me` / `project`
);
```

Un appel qui ne désigne aucun compte utilise d’abord le compte privé par défaut
de l’appelant, puis le compte partagé par défaut du projet. Un compte désigné
qui ne peut pas être résolu est **refusé** avec `connector_not_connected` — jamais
remplacé silencieusement — et le refus indique `requested_account` et
`available_accounts`. Chaque appel réussi indique le compte sous lequel il a été exécuté :
`result.account = { connection_id, label, owner_type }`.

Il n’y a plus de contrôle des connecteurs lors de la création d’une session. Une session était auparavant
refusée avec `409 CONNECTOR_CONNECTION_REQUIRED` lorsqu’un connecteur requis n’avait aucune
connexion utilisable ; ce refus a été supprimé (`require_connectors` est inactif — voir
[`p.sessions`](#psessions--and-the-session-handle)). Le contrôle s’effectue désormais au moment de
l’appel : un `connector_not_connected` refusé contient `connect_url`, un lien
d’autorisation hébergé, dès qu’il est possible d’en générer un pour l’appelant — la résolution
se fait en un clic, via le bouton de connexion de la transcription.

<a id="ppolicies--project-policies"></a>

#### `p.policies` — politiques du projet

| méthode | fonction |
| --- | --- |
| `list()` · `set(policies)` | les politiques du projet · remplacer l’ensemble |

<a id="ptriggers--cron-webhook-monitor-and-event-automations"></a>

#### `p.triggers` — automatisations cron, webhook, de surveillance et d’événements

Un déclencheur lance une action d’agent selon une planification, un webhook entrant, une commande surveillée ou un événement d’application sur un connecteur. Voir
[Déclencheurs](/docs/fr/connect/triggers) pour la stratégie de session et les modèles de charge utile.

| méthode | fonction |
| --- | --- |
| `list()` | tous les déclencheurs |
| `eventTypes({ connector })` | les événements d’application sur lesquels un connecteur peut se déclencher (`{ provider, app, event_types }`) ; 404 pour un connecteur inconnu, 409 `event_source_unavailable` lorsque son fournisseur n’a aucune source d’événements |
| `create(input)` | en créer un |
| `update(triggerId, input)` | modifier un déclencheur |
| `remove(triggerId)` | supprimer un déclencheur |
| `fire(triggerId)` | l’exécuter maintenant |
| `setActivation(paused)` | suspendre ou reprendre tous les déclencheurs du projet |

`create(input)` accepte `{ name, type: 'cron' | 'webhook' | 'monitor' | 'event', prompt_template,
slug?, agent?, model?, enabled?, session_mode?, session_id?, cron?, run_at?,
timezone?, secret_env?, connector?, event?, event_source?, event_account?, event_config?, session_access? }`. `name` and `prompt_template` sont obligatoires.
`cron`/`run_at` s’excluent mutuellement (`type: 'cron'`) ; `secret_env` (le
secret HMAC du webhook) s’applique à `type: 'webhook'`.

Un déclencheur `event` nécessite `connector` (un slug de connecteur) et `event` (un identifiant de type
issu de `eventTypes`). `event_account` est facultatif : le libellé d’un compte partagé de
ce connecteur ; `null` ou son omission utilise le compte partagé par défaut du connecteur.
`event_source` est facultatif : l’identifiant de l’adaptateur de source d’événements, par exemple `composio`. `null` ou son omission utilise le
fournisseur du connecteur. L’identifiant de type appartient à cet adaptateur. La structure de `event_config` est définie par le `config_schema` de ce type.
`eventTypes` et `eventApps` indiquent l’adaptateur sous `source` ; `provider` est un alias obsolète.
Chaque déclencheur répertorié contient `event: { connector, account, connected_as, type, config, source, provider, app,
status: 'active' | 'needs_connection' | 'error' | 'pending', error,
last_event_at } | null`. Les modèles de prompt lisent l’événement via
`{{ event.data.<field> }}` ainsi que `event.id`, `event.type`, `event.app`,
`event.connector` et `event.occurred_at`.

`session_access` contrôle qui peut ouvrir les sessions créées par le déclencheur. Sa structure est
`{ mode: 'private' | 'members' | 'project', memberIds: string[], groupIds:
string[] }` and defaults to `private`. Cette politique est un état d’exécution
local au compte. Elle n’entre pas dans le manifeste portable `beeuni.yaml`. La modification du seul
`session_access` ne crée aucun commit Git. Une session fixée conserve ses propres paramètres
de partage. Un gestionnaire de projet peut toujours ouvrir les sessions créées par les déclencheurs, y compris
celles qui utilisent `private` ou un accès réservé aux membres sélectionnés.

`session_access` est un paramètre de visibilité par ressource qui s’ajoute au modèle de rôles,
et non un rôle. Il détermine qui peut ouvrir les sessions d’un déclencheur donné. Il n’accorde aucune
permission refusée par la décision du rôle. Voir
[Comptes et accès](/docs/fr/accounts#per-feature-access-settings).

<a id="pmarketplace--pregistry--installed-items"></a>

#### `p.marketplace` / `p.registry` — éléments installés

Installe les fichiers d’un élément du catalogue sur la branche par défaut du projet.
`registry.*` est un alias identique de `marketplace.*`.

| méthode | fonction |
| --- | --- |
| `marketplace.list()` · `marketplace.install(id)` | éléments installés · installer un élément du catalogue |
| `marketplace.updates()` · `marketplace.update(name)` · `marketplace.updateAll()` | mises à jour disponibles · en mettre un à jour · tous les mettre à jour |
| `marketplace.remove(name)` | désinstaller un élément |

<a id="pfiles--repo-files-read"></a>

#### `p.files` — fichiers du dépôt (lecture)

Accès en lecture seule à l’arborescence git du projet. Pour lire et écrire des fichiers dans une
session en cours d’exécution, utilisez les opérations sur les fichiers de la session — voir
[Fichiers](#files) sous Modules ci-dessous.

| méthode | fonction |
| --- | --- |
| `listDirectory({ ref?, path? })` | un niveau de dossier : `{ entries: [{ path, type, size? }], truncated }`. Utilisez-le pour construire une arborescence ; chaque dossier est complet jusqu’à 5 000 entrées. |
| `list(options?)` · `read(path, ref?)` | tous les fichiers sous un chemin, récursivement, limités à 1 000 (la réponse contient `X-Beeuni-Truncated: 1` en cas de troncature) · le contenu d’un fichier à une référence git |
| `search(query, { content?, ref?, limit? })` | rechercher des noms de fichiers dans tout le dépôt, ou du contenu avec `content: true` |
| `archive(options?)` · `history(path)` | télécharger une archive tar · l’historique git d’un fichier |

<a id="pgit--history"></a>

#### `p.git` — historique

| méthode | fonction |
| --- | --- |
| `commits()` | le journal des commits |
| `commit(sha)` · `commitDiff(sha)` | un commit · son diff |
| `branches()` · `versionDiff(from, to)` | les branches · le diff entre deux références |

<a id="pchangerequests--lifecycle-and-merge"></a>

#### `p.changeRequests` — cycle de vie et fusion

Une demande de modification (CR) permet de fusionner le travail d’une session dans la branche par défaut.

| méthode | fonction |
| --- | --- |
| `list()` · `get(crId)` | les CR ouvertes · une CR |
| `diff(crId)` · `mergePreview(crId)` | son diff · prévisualiser le résultat de la fusion |
| `open(input)` · `merge(crId, input?)` | ouvrir · fusionner une CR |
| `close(crId, input?)` · `reopen(crId, input?)` | fermer sans fusionner · rouvrir une CR fermée |
| `requestChanges(crId, input)` | enregistrer des retours, éventuellement transmis à la session d’origine |

<a id="psessions--and-the-session-handle"></a>

#### `p.sessions` — et le handle de session

| méthode | fonction |
| --- | --- |
| `list()` | les sessions du projet |
| `create(input?)` | créer une session |
| `session(sid)` | le handle de session (identique à `beeuni.session(id, sid)`) |

Le handle de session est au cœur du runtime — voir
[Sessions](/docs/fr/sdk/sessions).

`create(input)` accepte `connector_bindings` indexé par slug de connexion de connecteur.
Chaque valeur désigne un `connection_id`. Il accepte aussi `secrets` pour
restreindre les secrets provenant du backend. `require_connectors` est obsolète et
sans effet : le champ reste accepté (un ancien client n'est jamais rejeté pour
l'avoir envoyé), mais rien ne le lit — une session n'est jamais refusée à sa création
pour un connecteur sans compte utilisable. Voir [Comptes et contrôle au moment de
l'appel](#accounts-and-the-call-time-gate).

<a id="papprovals--the-connector-approval-inbox"></a>

#### `p.approvals` — la boîte de réception des approbations de connecteurs

Actions soumises à un contrôle de connecteur en attente d'une décision — alimente
l'interface d'approbation des autorisations (`APPROVE` / `ASK` / `BLOCK`).

| méthode | fonction |
| --- | --- |
| `list(options?)` · `sessionsNeedingInput(options?)` | approbations en attente · sessions bloquées dans l'attente d'une décision |
| `resolve(executionId, decision, scope?)` | approuver ou refuser une action — `decision: 'approve' \| 'deny'`, `scope: 'once' \| 'session' \| 'session_all'` |

<a id="pgateway--llm-observability"></a>

#### `p.gateway` — observabilité des LLM

Journaux de requêtes, agrégats de coût/latence, budgets et clés API de passerelle pour le
trafic des modèles de ce projet.

| méthode | fonction |
| --- | --- |
| `logs(opts?)` · `log(logId)` | entrées du journal de requêtes · une entrée du journal |
| `overview(days?)` · `series(days?)` · `breakdown(days?)` · `sessions(days?)` · `errors(days?)` | agrégats, coût par session et erreurs sur une période |
| `budgets()` · `setBudget(input)` · `deleteBudget(budgetId)` | lire · créer/modifier · supprimer un budget |
| `keys()` · `createKey(name)` · `revokeKey(keyId)` | lister · générer · révoquer une clé API de passerelle |
| `playground(prompt, models)` | exécuter un prompt sur un maximum de 6 modèles |
| `routing.get()` · `routing.set(policy)` · `routing.reset()` | lire · remplacer · hériter de la politique de routage |
| `routing.preview(input)` | résoudre une route sans invoquer de modèle |

Une politique de routage contient un modèle par défaut, un modèle de vision et une chaîne
ordonnée de modèles de repli, chaque modèle étant essayé au plus une fois ; `fallbackOn` vaut
`transient` ou `any-error`.

La passerelle ne passe au modèle de repli suivant que si le modèle actuel
a échoué avant de renvoyer la moindre sortie :

- `transient` : une erreur réseau, un délai d'attente dépassé, tout `5xx`, ou `402`, `403`, `429`.
- `any-error` : tout échec, y compris un `400`.

Un échec après le début de la sortie reste associé à ce modèle. Une requête utilisant votre propre
clé fournisseur (BYOK) ne bascule jamais vers un modèle facturé par Beeuni. Chaque modèle de repli
utilise ses propres paramètres de génération par défaut.

<a id="pchannels--slack--email--voice"></a>

#### `p.channels` — Slack / e-mail / voix

Interfaces de connecteurs permettant à un agent d'agir comme une application Slack, une adresse e-mail,
ou de rejoindre un appel vocal en temps réel.

| méthode | fonction |
| --- | --- |
| `slack.installation()` · `slack.mode()` · `slack.manifest()` | installation actuelle · mode · manifeste de l'application |
| `slack.connect(input)` · `slack.disconnect()` | connecter · déconnecter |
| `slack.getFile(url)` · `slack.uploadFile(input)` | télécharger · téléverser un fichier via le proxy serveur |
| `email.installation(connectorSlug?)` · `email.mode()` | installation actuelle · mode |
| `email.connect(input)` · `email.disconnect(...)` · `email.updatePolicy(input)` | connecter · déconnecter · mettre à jour la politique d'envoi/de réponse |
| `voice.setBotName(name)` | renommer le bot pendant un appel en cours |

<a id="pmodeldefaults--default-model-preferences"></a>

#### `p.modelDefaults` — préférences de modèle par défaut

Modèles par défaut définis au niveau du compte, de l'agent et du projet, résolus par la passerelle.

| méthode | fonction |
| --- | --- |
| `get()` · `set(input)` · `clear(params)` | lire · définir une valeur par défaut · supprimer une surcharge |

<a id="psetdefaultagent--project-default-agent"></a>

#### `p.setDefaultAgent` — agent par défaut du projet

`p.setDefaultAgent(agentName)` vérifie que l'agent est déclaré et activé,
puis le définit comme `default_agent` dans le `beeuni.yaml` du projet. Les nouvelles sessions
privilégient cet agent, sauf si un utilisateur en choisit un autre.

<a id="pupdatefeatureflag--feature-flags"></a>

#### `p.updateFeatureFlag` — indicateurs de fonctionnalités

`p.updateFeatureFlag(feature, enabled)` active ou désactive un indicateur de fonctionnalité pour le
projet. Passez `enabled: null` pour supprimer la surcharge et revenir à la
valeur par défaut de la plateforme. Il appelle le `PATCH /v1/projects/:id/features` canonique.

`feature` est l'un des `FEATURE_FLAG_KEYS` (exportés depuis `@beeuni/sdk`, typé comme
`FeatureFlagKey`). L'appelant doit disposer de l'autorisation `project.settings.write` du projet ;
sinon, la route répond `403`.

Toutes les routes contrôlées par un indicateur rejettent les requêtes de la même manière lorsque celui-ci est désactivé : HTTP `403`
avec `{ error, code: "feature_disabled", feature }`. Utilisez `isFeatureDisabledError(error)`
pour adapter le traitement et `featureDisabledKey(error)` pour lire la clé de l'indicateur — ne vous
basez jamais sur le texte du message.

`p.updateExperimentalFeature(feature, enabled)` est l'alias **obsolète**. Il
continue d'appeler l'alias de route obsolète `PATCH /v1/projects/:id/experimental`
pour que les consommateurs liés à une ancienne API déployée continuent de fonctionner. Utilisez
`p.updateFeatureFlag` dans le nouveau code.

<a id="psandbox--templates-and-snapshot-builds"></a>

#### `p.sandbox` — modèles et builds de snapshots

Configuration de build des sandbox au-delà de `sandboxHealth`/`sandboxTemplates` sur le
handle du projet : modèles Dockerfile/image/pool préchauffé et leurs builds de snapshots.

| méthode | fonction |
| --- | --- |
| `list()` · `snapshots()` | sandbox de ce projet · snapshots construits |
| `rebuildSnapshot(slug?)` · `fixWithAgent()` | reconstruire un snapshot · demander à un agent de corriger un build défaillant |
| `createTemplate(input)` · `updateTemplate(...)` · `removeTemplate(...)` · `buildTemplate(...)` | ajouter · modifier · supprimer · construire un modèle |
| `setProvider(provider)` | demander un changement de fournisseur. `null` (ou le fournisseur par défaut de la plateforme / le fournisseur déjà actif) s'applique immédiatement ; passer à un autre fournisseur activé lance une transition durable préparation→vérification→activation — le fournisseur actuel continue de servir pendant que l'image préchauffée cible est construite et vérifiée, puis activée. La valeur renvoyée est une union discriminée : `kind:'project'` (immédiat) ou `kind:'preparation'` (interroger `getProjectSandboxProviderTransition()` jusqu'à `activated`/`failed`) |

<a id="platform-admin-hooks-beeunisdkreact"></a>

### Hooks d'administration de la plateforme (`@beeuni/sdk/react`)

Ces hooks servent la console d'administration. L'API n'y répond que pour un administrateur
de la plateforme. Chaque mutation invalide les requêtes `['admin', 'accounts']`.

| Hook | Fonction |
| --- | --- |
| `useAdminRole(userId, options?)` | lire le rôle de l'appelant depuis `GET /user-roles` → `{ isAdmin, role }`. `userId` sert de clé de cache. L'absence de `userId` désactive la requête. Recharge au montage et à la prise de focus, afin qu'un rôle révoqué cesse de conditionner l'accès en une navigation. |
| `useAdminGrantCredits()` | mutation `{ accountId, amount, description, isExpiring }` : ajouter du crédit au portefeuille. `POST /admin/api/accounts/:id/credits`. Déplace de l'argent : confirmer d'abord dans l'interface. |
| `useAdminDebitCredits()` | mutation `{ accountId, amount, description }` : retirer du crédit du portefeuille. `POST /admin/api/accounts/:id/credits/debit`. Déplace de l'argent : confirmer d'abord dans l'interface. |
| `useAdminSetTier()` | mutation `{ accountId, tier }` : définir le niveau de l'offre. |
| `useAdminGrantTrial()` | mutation `{ accountId, tierKey, seats, durationDays, creditGrant?, note? }` : accorder ou remplacer un essai. Le compte se comporte comme `tierKey` jusqu'à la fin de la période. `creditGrant` ajoute aussi du crédit au portefeuille. |
| `useAdminRevokeTrial()` | mutation `{ accountId }` : mettre fin immédiatement à un essai actif. Répond `400` si aucun essai n'est actif. |

<a id="escape-hatch"></a>

### Accès direct

`beeuni.runtime()` renvoie le client REST runtime typé pour la sandbox
active.
Sur un client créé par `createScopedBeeuni` (`@beeuni/sdk/server`), il **lève une exception**
— le runtime « actif » global au processus est la sandbox d'une autre requête dans un
serveur multi-tenant, ce qui entraîne une fuite entre tenants. Utilisez plutôt
`beeuni.session(pid, sid).runtime`, limité à la session (appelez d'abord `ensureReady()`), qui
résout la sandbox propre à cette session.

## Modules

Les modules indépendants de tout framework derrière la façade [du client](#the-client) et les
hooks React. Utilisez-les lorsque vous avez besoin d'une opération sans la façade,
d'une fonction utilitaire pure ou d'une couche d'isolation réservée à Node. Chaque module indique un
niveau de stabilité pour vous aider à choisir sur quoi vous appuyer.

| Niveau | Signification |
| --- | --- |
| Canonique | Importez depuis la racine `@beeuni/sdk`. À utiliser pour tout nouveau code. |
| Pris en charge | Un sous-chemin dédié (`@beeuni/sdk/react`, `@beeuni/sdk/server`). Pleinement pris en charge, non obsolète. |
| Alias obsolète | Un ancien sous-chemin qui fonctionne encore. Il réexporte du code déjà exporté par la racine. Importez plutôt depuis la racine. |
| Interne | Hors semver. Ne l’importez pas dans le code hôte. |

Le point d’entrée racine est canonique. Chaque nom indépendant de tout framework ci-dessous peut être importé
directement depuis `@beeuni/sdk` :

```ts
import { files, getSessionHealth, getClient, authenticatedFetch, backendApi } from '@beeuni/sdk';
```

<a id="canonical-modules"></a>

### Modules canoniques

| Module | Fonction |
| --- | --- |
| Fichiers | Opérations sur les fichiers de l’espace de travail : lister, lire, rechercher, écrire |
| Runtime de session | Sonde d’état et générateurs d’URL d’aperçu/proxy |
| Client runtime | Le client REST runtime typé et l’ensemble de ses types |
| Authentification | `authenticatedFetch` et accesseurs de jetons |
| REST des projets | Les fonctions REST brutes encapsulées par la façade |
| Client API | `backendApi`, le client HTTP typé de bas niveau |
| Tours | Regroupement des messages en tours, calcul des coûts et des statuts — voir [Tours](#turns) |
| Transcriptions | `formatTranscript`, un export Markdown côté client |
| Listes de l’éditeur | Les listes d’agents et de modèles de l’éditeur de session : lesquels proposer, lesquels exécuter |

<a id="files"></a>

#### Fichiers

```ts
import { files } from '@beeuni/sdk';

const tree = await files.list('/workspace/src');
const { content } = await files.read('/workspace/README.md');
const hits = await files.findText('TODO');
await files.upload(file, '/workspace/uploads', file.name, {
  onProgress: ({ loadedBytes, totalBytes }) => {
    console.log(`${loadedBytes}/${totalBytes}`);
  },
});
```

Les téléversements de plus de 64 Kio franchissent la frontière du sandbox par blocs vérifiés. Le
callback de progression s’exécute après l’arrivée de chaque bloc dans le système de fichiers de la session.

`uploadNativeFile({ uri, name, type? }, targetPath, baseUrl)` téléverse un fichier de l’appareil
depuis React Native en une seule requête. Le runtime diffuse les octets depuis
`uri`, de sorte qu’ils n’entrent jamais dans le tas JS. Il utilise la même authentification, la même échéance et la même
réexécution `401` que `files.upload`. Il ne découpe pas le fichier en blocs.

`fetchProjectArchive`, `fetchCostExportCsv` et les appels de version du sandbox passent
par le même transport. Un échec lève une exception `ApiError` avec `status`.

`files.write(path, blob)` remplace le fichier à `path` et échoue si `path` est un
dossier. `files.create(path)` crée un fichier vide sans jamais écraser : lorsqu’un
fichier ou dossier existe déjà à `path`, il lève `FileExistsError`
(`status` `409`, `code` `'FILE_EXISTS'`).

```ts
import { FileExistsError, files } from '@beeuni/sdk';

try {
  await files.create('/workspace/notes.md');
} catch (error) {
  if (error instanceof FileExistsError) console.log(`${error.path} already exists`);
  else throw error;
}
```

`files` cible le sandbox actif globalement. Si votre hôte exécute plusieurs
sessions à la fois, appelez plutôt `s.files` sur le handle de session. Il cible toujours
le sandbox propre à cette session. Voir [Sessions](/docs/fr/sdk/sessions).

<a id="session-runtime-helpers"></a>

#### Utilitaires du runtime de session

```ts
import { getSessionHealth, isRuntimeReady } from '@beeuni/sdk';

const result = await getSessionHealth();
if (result.ok && isRuntimeReady(result.health)) {
  // the sandbox daemon is ready
}
```

`getSessionHealth` ne lève jamais d’exception pour un statut non-2xx. Il renvoie
`{ status, ok, health, body }` et vous laisse décider de la signification du statut. Le
même module exporte les utilitaires d’URL qui réécrivent la sortie `localhost` d’un agent
en une URL proxy accessible : `detectLocalhostUrls`, `rewriteLocalhostUrl`,
`proxyLocalhostUrl`, `parseLocalhostUrl` et `buildWebProxyUrl`.

<a id="opencode-client"></a>

#### Client runtime

```ts
import { getClient } from '@beeuni/sdk';

const client = getClient();
const { data } = await client.session.list({ limit: 100 });
```

`getClient()` renvoie le client de compatibilité runtime typé pour le sandbox actif,
avec l’authentification déjà injectée. Préférez `beeuni.session(pid, sid).runtime`, le
même client limité à une session, au client global `getClient()` lorsque votre
hôte exécute plusieurs sessions.

<a id="auth-helpers"></a>

#### Utilitaires d’authentification

```ts
import { authenticatedFetch, getAuthToken } from '@beeuni/sdk';

const res = await authenticatedFetch(`${runtimeUrl}/beeuni/health`);
const token = await getAuthToken();
```

Le jeton provient de la fonction `getToken` que vous avez passée à `createBeeuni`.
La plupart du code applicatif n’a pas besoin de ce module : les couches de fichiers, de session et de façade
assurent déjà l’authentification pour vous.

<a id="api-client"></a>

#### Client API

```ts
import { backendApi } from '@beeuni/sdk';

const data = await backendApi.get('/some/endpoint');
await backendApi.post('/some/endpoint', { name: 'x' });
```

`backendApi` est le client HTTP typé sur lequel repose chaque fonction REST. Utilisez-le
uniquement pour un endpoint qui n’a pas encore de wrapper typé.

<a id="composer-agent-and-model-lists"></a>

#### Listes d’agents et de modèles de l’éditeur

Construisez les sélecteurs d’agent et de modèle d’un éditeur de session avec les mêmes règles
que l’application web Beeuni. Chaque fonction est pure : récupérez les entrées avec le
client, puis passez-les à la fonction.

```ts
import {
  composerSelectableAgents,
  createModelVisibility,
  flattenModels,
  modelInDefaultView,
  pickerProviderList,
  projectConfigAgentsToRuntimeAgents,
  resolveComposerAgent,
  resolveComposerModel,
  resolveModelDefault,
} from '@beeuni/sdk';

// Agents: the roster, the picker list, and the agent the send carries.
const roster = projectConfigAgentsToRuntimeAgents(detail.config);
const pickerAgents = composerSelectableAgents(roster, { enableProjects: false });
const agent = resolveComposerAgent({
  agents: roster,
  boundAgent: session?.agent,
  defaultAgent: detail.config.default_agent,
  selectedAgent: pick,
});
if (agent.disabled) {
  // no accessible agent: refuse the send
}

// Models: gateway projects read /model-picker, native projects merge the
// catalog synthesis with the running sandbox's provider list.
const gatewayEnabled = detail.project.experimental?.llm_gateway === true;
const providers = pickerProviderList({
  gatewayEnabled,
  modelPicker,
  runtimeProviders,
  llmCatalogProviders,
  secretNames,
});
const models = flattenModels(providers, { providerMode: gatewayEnabled ? 'gateway' : 'native' });
const isVisible = createModelVisibility({ catalogModels: models });
const defaultView = models.filter(
  (m) => m.enabled !== false && modelInDefaultView(m, { search: '', isStoreVisible: isVisible, selected }),
);
const { model, explicit } = resolveComposerModel({
  models,
  picks: [sessionPick],
  serverDefault: resolveModelDefault(modelDefaults, agent.selected ?? undefined),
  providers,
});
```

`resolveComposerModel` renvoie `explicit: undefined` lorsque l’éditeur utilise
sa valeur par défaut. `model` est la clé à afficher et à envoyer. Le
`id` de chaque modèle aplati est la référence stockée par une sélection ; `modelRefToKey(ref, gatewayEnabled)` reconvertit une
référence stockée (un épinglage de session, une liaison de canal, le `model` d’un déclencheur) en
clé.

<a id="supported-subpaths"></a>

### Sous-chemins pris en charge

| Sous-chemin | Fonction |
| --- | --- |
| `@beeuni/sdk/react` | Hooks React — voir [Hooks React](/docs/fr/sdk/react) |
| `@beeuni/sdk/server` | Configuration limitée à une requête pour les backends multilocataires |
| `@beeuni/sdk/wire-message-id` | L’horloge des identifiants de messages du protocole runtime seule (un module, aucun import). La racine exporte les mêmes noms |

<a id="wire-message-ids"></a>

#### Identifiants de messages du protocole

Le `messageId` d’un prompt est sa position dans la transcription. runtime ordonne
les messages selon `msg_` + une horloge de 48 bits (`Date.now() * 0x1000`, les 48 bits de poids faible) +
14 caractères base62, et l’horloge revient à zéro toutes les 2^36 ms (environ 2,2 ans).
Ne l’encodez jamais manuellement.

```ts
import { mintWireMessageId } from '@beeuni/sdk';

// `after`: the ids already in this session's transcript.
const messageId = mintWireMessageId({ after: transcriptIds });
```

La création est antidatée de deux minutes, puis avancée d’un tick au-delà de l’identifiant le plus récent
dans `after` qui n’est pas en avance de plus d’une heure. Un appelant qui conserve la position la plus récente
sous forme d’horloge plutôt que d’identifiants utilise `mintWireMessageIdAbove({ nowMs,
newestKnownTime })`, which returns `{ id, time }`. `newestWireIdClock(ids)`
pour trouver cette horloge. Chaque comparaison s’effectue sur l’anneau, de sorte que les identifiants de part et d’autre d’un
retour à zéro conservent leur ordre réel.

L’horloge de 48 bits revient à zéro tous les ~2,2 ans ; ne comparez donc jamais deux horloges avec `>`
ou `<=` : un identifiant créé juste après un retour à zéro est numériquement inférieur à un identifiant créé
juste avant. Utilisez `wireIdClockDelta(clock, reference)`, la distance signée
sur l’anneau (positive lorsque `clock` est en avance), et `maxWireIdClock(clocks)` pour
le plus récent parmi plusieurs :

```ts
import { maxWireIdClock, wireIdClockDelta } from '@beeuni/sdk/wire-message-id';

const floor = maxWireIdClock([transcriptNewest, deliveredNewest]);
const stale = floor !== null && wireIdClockDelta(clientClock, floor) <= 0n;
```

<a id="server-side-isolation"></a>

#### Isolation côté serveur

`createBeeuni` stocke sa configuration, y compris la fonction de jeton, dans une
variable unique à l’échelle du processus. Cela convient à un onglet de navigateur, à une CLI ou à un
serveur monolocataire. Ce n’est pas sûr pour un serveur Node qui traite des requêtes simultanées
pour différents utilisateurs, car le dernier appel à `createBeeuni` prévaut pour
toutes les requêtes en cours. `@beeuni/sdk/server` corrige cela grâce à une isolation par
requête :

```ts
import { createScopedBeeuni } from '@beeuni/sdk/server';

export async function handler(req: Request) {
  const beeuni = createScopedBeeuni({ backendUrl, getToken: () => tokenFor(req) });
  return beeuni.projects.list();
}
```

`createScopedBeeuni` et `runWithBeeuni` isolent la configuration par requête avec
`AsyncLocalStorage` de Node. N’importez jamais `@beeuni/sdk/server` dans un bundle
pour navigateur : il importe statiquement `node:async_hooks`.

Le `runtime()` de premier niveau d’un client à portée limitée lève une exception (il résoudrait le
sandbox d’un autre locataire). Accédez au runtime d’une session précise via
`beeuni.session(pid, sid).runtime` après `await s.ensureReady()`.

<a id="deprecated-aliases"></a>

### Alias obsolètes

Une vingtaine d’anciens sous-chemins fonctionnent encore : `/files`, `/turns`, `/session`, `/auth`,
`/projects-client`, `/api-client`, `/config`, `/event-stream`,
`/opencode-client`, `/platform-client`, entre autres. Chacun réexporte du code que le
point d’entrée racine `@beeuni/sdk` exporte déjà. Ils continuent de fonctionner afin qu’aucun élément existant
import breaks, but new code should import from the root.

<a id="internal-modules"></a>

### Modules internes

`@beeuni/sdk/internal/*` contient les stores zustand utilisés en interne par le runtime propre à apps/web
— état de synchronisation des sessions, suivi du runtime actif et gestion des
reconnexions. Ce sous-chemin est explicitement hors semver. Ne l’importez pas dans
le code hôte ; les [hooks React](/docs/fr/sdk/react) exposent déjà l’état dont vous
avez besoin.

<a id="turns"></a>

## Tours

Des fonctions simples qui regroupent les messages de session en tours et classent chaque
partie de message. Utilisez-les pour construire un moteur de rendu de chat personnalisé plutôt que celui de
référence dans `apps/web`. Ni React ni DOM : chaque export est une simple
fonction ou un type, utilisable sans risque depuis n’importe quel hôte.

```ts
import { classifyPart, classifyTurn, toolInfo, toolViewModel } from '@beeuni/sdk';
```

Importez chaque fonction depuis le point d’entrée racine `@beeuni/sdk`. Le
sous-chemin `@beeuni/sdk/turns` fonctionne encore, mais c’est un alias obsolète. Le nouveau
code doit utiliser le point d’entrée racine — voir [Distribution](#distribution).

<a id="classify-a-part"></a>

### Classer une partie

`classifyPart(part)` normalise l’un des 12 types de parties du format de transcription
Beeuni (`beeuni.transcript.v1`, `BEEUNI_TRANSCRIPT_SCHEMA`) en un
`ClassifiedPart` — une union discriminée par `kind`. Chaque variante résout déjà les
champs nécessaires au rendu : état de l’outil, sortie JSON analysée, détection d’images.

```ts
import { classifyPart, type ClassifiedPart } from '@beeuni/sdk';

for (const part of message.parts) {
  const classified: ClassifiedPart = classifyPart(part);
  switch (classified.kind) {
    case 'text':
      render(classified.text);
      break;
    case 'tool':
      render(classified.tool.title, classified.tool.status);
      break;
  }
}
```

| `kind` | structure |
| --- | --- |
| `text` | `{ id, text, synthetic }` — ignorer les parties `synthetic` ; elles marquent le prompt synthétique du mode shell |
| `reasoning` | `{ id, text }` |
| `tool` | `{ id, tool: ToolView }` |
| `file` | `{ id, filename?, mime, url, isImage, isPdf }` |
| `subtask` | `{ id, description, agent, prompt, model? }` |
| `patch` | `{ id, hash, files, fileCount }` |
| `snapshot` | `{ id, snapshot }` |
| `agent` | `{ id, name }` |
| `retry` | `{ id, attempt, message, createdAt }` |
| `compaction` | `{ id, auto, overflow, tailStartId? }` |
| `step` | `{ id, phase: 'start' \| 'finish', snapshot?, reason?, cost?, tokens? }` |
| `unknown` | `{ raw }` — un type de partie inconnu de cette version du SDK |

Une partie reçue non reconnue devient `unknown` à l’exécution au lieu de
lever une exception. Cela permet à un ancien client de communiquer avec un serveur plus récent.

Une partie d’outil est classée dans `ToolView` :

```ts
interface ToolView {
  name: string;
  title: string;
  status: 'pending' | 'running' | 'done' | 'error';
  input?: Record<string, unknown>;
  output?: string;
  error?: string;
  outputParsed?: unknown; // JSON.parse(output) when it parses, capped at 256KB
  outputText?: string;    // the raw output text, always present
  kind?: ToolKind;        // toolKind(name): the action, whatever the harness calls the tool
  files?: ToolFile[];     // the files a file tool read, wrote or changed: path, patch, additions/deletions
  diff?: string;          // a unified diff of the change
  diagnostics?: Record<string, unknown[]>; // language-server diagnostics, keyed by file path
  answers?: string[][];   // the answers to a `question`, one list per question
  childSessionId?: string; // the child session a delegating call (`task`) runs
}
```

`toToolView(part)` en construit un directement à partir d’une partie d’outil. Lisez ces champs
plutôt que le `state.metadata` propre à un harness : la même carte affiche alors un
`edit` runtime (`filePath`, `filediff`) et un `edit` pi (`path`, `edits`,
un `patch` unifié).

Certains outils (`web_search`, `image_search`, appels de connecteurs) signalent
`state.status: 'completed'` même lorsque leur corps JSON contient
`success: false`. `classifyPart` le détecte et définit aussi `ToolView.status` sur
`'error'` dans ce cas.

`classifyTurn(message)` classe chaque partie d’un message de l’assistant et
renvoie un `ClassifiedTurn` avec trois champs :

- `parts` — chaque partie, classée
- `error` — issu de `message.info.error`, le cas échéant
- `isEmpty` — vrai lorsque le tour ne comporte aucune erreur ni aucune partie au contenu visible

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

### Métadonnées des outils

`toolKind(name)` est la taxonomie unique des outils : elle associe le nom d’outil de n’importe quel harness
(`write` de pi, `oc-apply_patch` d’runtime, `pty_spawn` d’un plugin) à un
`ToolKind` — `read`, `edit`, `write`, `bash`, `web_search`, `task`,
`question`, … ou `other`. Les familles de narration, les modèles de vue, les catégories
`toolInfo` et le groupe d’outils de contexte en dérivent tous. `inputPath(input)`
lit le chemin d’un outil de fichier depuis `filePath`, `file_path` ou `path`.

```ts
toolKind('oc-apply_patch'); // 'apply_patch'
toolKind('pty_spawn');      // 'pty'
inputPath({ path: 'app.py' }); // 'app.py'
```

Deux autres fonctions de recherche décrivent toutes deux un outil. Ne les confondez pas.

- `toolInfo(name)` — sans icône, renvoie `{ label, category }`. `classifyPart`
  l’utilise en interne. `ToolCategory` est `'shell' | 'files' | 'search' |
  'edit' | 'web' | 'task' | 'other'`.
- `getToolInfo(name, input)` — avec prise en charge des icônes, renvoie `{ icon, title, subtitle }`
  pour l’interface de référence des cartes d’outils. Le sous-titre provient de l’entrée de l’outil,
  par exemple un chemin de fichier ou une requête de recherche.

```ts
toolInfo('bash'); // { label: 'Shell', category: 'shell' }
getToolInfo('write', { filePath: '/workspace/main.go' });
// { icon: 'file-pen', title: 'Write', subtitle: 'main.go /workspace' }
```

Les deux fonctions reconnaissent les préfixes des noms d’outils ; les familles d’outils des plugins Beeuni
(`agent_*`, `session_*`, `task_*`, `trigger_*`, `project_*`, `pty_*`) sont donc résolues
sans mise à jour du registre. Un nom d’outil inconnu ne lève jamais d’exception — il se rabat
sur `humanizeToolName(name)` avec la catégorie `'other'`.

<a id="tool-view-models"></a>

### Modèles de vue des outils

`toolViewModel(classifiedTool)` associe une partie d’outil classée à une structure conçue
pour une famille d’outils. Une interface peut alors lui appliquer un rendu spécifique plutôt que de l’afficher comme un
bloc JSON générique.

```ts
const vm = toolViewModel(classifiedTool);
if (vm.kind === 'shell') {
  render(vm.command, vm.stdout, vm.exitCode);
}
```

| `kind` | structure | outils |
| --- | --- | --- |
| `web-search` | `{ query, results?, answer?, error? }` | `web_search`, `image_search` |
| `shell` | `{ command, stdout?, exitCode? }` | `bash` |
| `file-read` | `{ path, preview? }` | `read` |
| `file-write` | `{ path, preview? }` | `write` |
| `file-edit` | `{ path, diff?: DiffLine[] }` | `edit`, `morph_edit` |
| `search` | `{ pattern, matches?: SearchMatch[] }` | `grep`, `glob` |
| `task` | `{ description, agent? }` | `task` |
| `todo` | `{ items: TodoItem[] }` | `todowrite` |
| `question` | `{ questions: QuestionItem[], answers? }` | `question`, `ask` |
| `generic` | `{ label, inputPretty?, outputPretty? }` | tout le reste, toujours affichable sans risque |

`DiffLine` est `{ type: 'added' | 'removed' | 'unchanged', text }`. Les champs de type chaîne
sont plafonnés pour qu’une sortie d’outil volumineuse ne perturbe jamais le rendu : 4000
caractères pour le JSON mis en forme, 256 Ko avant l’exécution d’un diff.

<a id="group-messages-into-turns"></a>

### Regrouper les messages en tours

Un tour associe un message utilisateur aux messages de l’assistant qui y ont répondu.
C’est l’unité qu’une interface de chat affiche comme un échange.

```ts
import { groupMessagesIntoTurns, collectTurnParts, type TurnLike } from '@beeuni/sdk';

const turns: TurnLike[] = groupMessagesIntoTurns(messages);
for (const turn of turns) {
  const parts = collectTurnParts(turn);
}
```

`groupMessagesIntoTurns` relie chaque message de l’assistant à son message utilisateur
parent. Il se rabat sur l’ordre des messages lorsqu’un lien parent manque. Il
rattache aussi un message de l’assistant orphelin — sans parent et précédant
tous les messages utilisateur — au premier tour, et non au dernier.

Fonctions utilitaires associées :

- `findLastTextPart(parts)` — le texte de la réponse finale du tour
- `turnHasSteps(parts)` — vrai si une partie `tool`, `compaction`, `snapshot` ou `patch` existe
- `isShellMode(turn)` / `getShellModePart(turn)` — un tour constitué d’un prompt synthétique déclenchant un appel `bash`

<a id="type-guards"></a>

### Gardes de type

Affinez le type d’une partie par `type` :

```ts
import { isTextPart, isToolPart, getPartText } from '@beeuni/sdk';

if (isTextPart(part)) {
  // part.type narrowed to 'text'
}
const text = getPartText(part); // works for 'text' and 'reasoning' parts
```

Également disponibles : `isReasoningPart`, `isFilePart`, `isAgentPart`,
`isCompactionPart`, `isSnapshotPart`, `isPatchPart`, `isStepPart`
(`step-start` et `step-finish`).

<a id="status-and-errors"></a>

### État et erreurs

```ts
import { getWorkingState, getTurnStatus, formatDuration } from '@beeuni/sdk';

const status = getTurnStatus(parts, childMessages); // "Running commands..."
formatDuration(4300); // "4s" — durations under 1s return ''
```

`getTurnStatus` parcourt les parties d’un tour pour trouver la dernière ligne d’état. Lorsque la dernière
partie est une délégation `task` en cours, passez `childMessages` pour que l’état affiche
l’activité réelle du sous-agent plutôt qu’une ligne générique « Délégation en cours... ».

```ts
import { getTurnError, getChildSessionError, unwrapError } from '@beeuni/sdk';

getTurnError(turn);                    // the first assistant error, unwrapped
getChildSessionError(childMessages);   // newest error in a sub-agent's messages
unwrapError(rawError);                 // normalizes double-JSON and mixed error shapes
```

<a id="cost-and-token-totals"></a>

### Totaux des coûts et des tokens

```ts
import { getTurnCost, getSessionCost, formatCost, formatTokens } from '@beeuni/sdk';

const info = getTurnCost(partsWithMessage, modelPricingLookup);
const sessionCost = getSessionCost(messages, modelPricingLookup);

formatCost(0.0032);  // "$0.003"
formatTokens(12345); // "12k"
```

Les deux fonctions lisent les totaux des coûts et des tokens depuis les parties `step-finish`. Lorsqu’une
partie indique zéro, elles estiment le coût à partir du nombre de tokens à l’aide d’un
`ModelPricingLookup`.

Les messages liés à un abonnement ChatGPT/Codex contribuent à hauteur de `$0.00`, y compris les messages
historiques dont les coûts d’exécution sont non nuls. L’utilisation des tokens et du contexte reste visible.
Les sessions mixtes conservent les frais des messages d’API payants. Les modèles de l’API OpenAI restent
facturables, y compris ceux dont le nom contient `codex`.

<a id="child-sessions-and-pending-requests"></a>

### Sessions enfants et demandes en attente

L’arbre de conversation d’une session de projet (la racine et les conversations des sous-agents
qu’elle a créées) provient de sa liste `runtime_sessions`. Ces
fonctions utilitaires le lisent, en privilégiant les noms actuels plutôt que ceux d’avant W4 :

```ts
import { directSubsessions, projectSessionForRuntimeId, rootRuntimeSession, runtimeSessionsOf } from '@beeuni/sdk';

const root = rootRuntimeSession(projectSession);            // the root conversation id
const children = directSubsessions(projectSession);         // its subagent conversations
const owner = projectSessionForRuntimeId(projectSessions, childId); // the session a conversation belongs to
```

Un appel d’outil `task` délègue à une session enfant. Ces fonctions utilitaires relient un
tour parent aux messages propres à cet enfant.

```ts
import { getChildSessionId, getChildSessionToolParts } from '@beeuni/sdk';

const childId = getChildSessionId(taskToolPart);
const steps = getChildSessionToolParts(childMessages);
```

Associez une demande d’autorisation ou une question en attente à son appel d’outil et déterminez
quelles parties d’outil masquer tant qu’une demande est active :

```ts
import { getPermissionForTool, getHiddenToolParts, isToolPartHidden } from '@beeuni/sdk';

const permission = getPermissionForTool(permissions, callID);
const hidden = getHiddenToolParts(activePermission, activeQuestion);
```

<a id="formatting-and-lists"></a>

### Mise en forme et listes

```ts
import { getFilename, getDirectory, relativizePath, stripAnsi } from '@beeuni/sdk';

getFilename('/workspace/src/main.go');               // "main.go"
getDirectory('/workspace/src/main.go');               // "/workspace/src"
relativizePath('/workspace/src/main.go', '/workspace'); // "src/main.go"
```

Les fonctions utilitaires de liste de sessions opèrent sur les mêmes données de session :

```ts
import { sortSessions, childMapByParent, allDescendantIds } from '@beeuni/sdk';

sessions.sort(sortSessions(Date.now())); // pins sessions updated in the last 60s
const childMap = childMapByParent(sessions);
const descendants = allDescendantIds(childMap, sessionId);
```

État de nouvelle tentative : `getRetryInfo(sessionStatus)` renvoie `{ attempt, message, next,
details? }` when `status.type === 'retry'`, with `message` limité à 60
caractères. `details` préserve une enveloppe structurée de passerelle LLM lorsqu’elle est présente :
le fournisseur final, le code de passerelle, l’identifiant de requête, la suggestion, l’état en amont et
les échecs des candidats dans l’ordre. Chaque échec de candidat contient `attempt`,
`provider`, `routeModel`, `resolvedModel`, `stage`, éventuellement `status`, `code`
et `message`. Les anciennes nouvelles tentatives en texte brut renvoient `details: undefined`.
`getRetryMessage(sessionStatus)` renvoie le message complet extrait de son enveloppe. Lorsque
runtime ne conserve que ce message, le composite de passerelle inclut toujours
l’identifiant de requête ainsi que le fournisseur, le modèle résolu, le statut HTTP, le code
et le message de longueur limitée de chaque candidat.

<a id="structural-types"></a>

### Types structurels

Les fonctions de regroupement et de statut acceptent des types structurels minimaux —
`PartLike`, `MessageInfoLike`, `TurnLike`, `ToolStateLike`,
`SessionStatusLike` — plutôt que les types concrets de transcription.
Vos propres structures de messages et de parties sont transmises sans modification tant qu’elles correspondent
aux champs requis. `classifyPart` et `classifyTurn` font exception.
Leur typage repose sur l’union Beeuni `Part` (`beeuni.transcript.v1`), de sorte que leur
vérification d’exhaustivité détecte tout nouveau type de partie lors de la compilation.

## Distribution

```sh
npm install @beeuni/sdk
```

Le package est distribué en ESM compilé avec des déclarations de types TypeScript complètes.
`react` et `@tanstack/react-query` sont des dépendances homologues facultatives. Si vous
utilisez `@beeuni/sdk/react`, installez les deux.

```ts
import { createBeeuni } from '@beeuni/sdk'; // framework-free core
import { useSession } from '@beeuni/sdk/react'; // optional React layer
import { createScopedBeeuni } from '@beeuni/sdk/server'; // Node and Bun servers
```

<a id="entry-points-and-stability"></a>

### Points d’entrée et stabilité

Le point d’entrée racine, `@beeuni/sdk`, est canonique. Il exporte toute
l’interface indépendante des frameworks et fonctionne dans les navigateurs, Node 18+, Bun et les environnements
d’exécution edge.

| Point d’entrée | Niveau | Contrat |
| --- | --- | --- |
| `@beeuni/sdk` | Canonique | Indépendant des frameworks. N’importe jamais React ni `node:*`. |
| `@beeuni/sdk/react` | Pris en charge | Hooks React. Le seul point d’entrée qui importe React. |
| `@beeuni/sdk/server` | Pris en charge | Isolation de la configuration par requête pour les serveurs Node et Bun, via `node:async_hooks`. Ne jamais l’inclure dans un bundle destiné au navigateur. |
| Anciens sous-chemins (`/projects-client`, `/turns`, `/files`, et d’autres) | Obsolètes | Fonctionnent toujours. Chacun réexporte depuis la racine. Importez plutôt depuis la racine. |
| `@beeuni/sdk/internal/*` | Interne | Utilisé uniquement par l’application web Beeuni. Ce n’est pas une API prise en charge. |

<a id="cdn-bundles"></a>

### Bundles CDN

Le package fournit également deux bundles pour navigateur, générés par `tsup` : un bundle ESM
(`dist/beeuni.esm.min.js`) et un bundle global IIFE (`dist/beeuni.global.js`) qui
définit `window.Beeuni`.

```html
<script src="https://unpkg.com/@beeuni/sdk/dist/beeuni.global.js"></script>
<script>
  const beeuni = Beeuni.createBeeuni({
    backendUrl: 'https://api.beeuni.ai/v1',
    getToken: async () => KEY,
  });
</script>
```

`window.Beeuni` expose le même point d’entrée racine que l’import npm :
`Beeuni.createBeeuni`, `Beeuni.classifyTurn`, `Beeuni.ApiError`.

<a id="see-also"></a>

## Voir aussi

- [SDK](/docs/fr/sdk) — installation et première session.
- [Sessions](/docs/fr/sdk/sessions) — cycle de vie, streaming et gestion des erreurs.
- [Hooks React](/docs/fr/sdk/react) — la couche réactive construite sur ces modules.


<a id="agent-repository-access"></a>

### Accès de l’agent au dépôt

La configuration de l’agent accepte `repository_access?: boolean` (par défaut `true`).
Définissez `false` pour exécuter de nouvelles sessions sans accès au dépôt du projet ni à l’API du dépôt.
Les autorisations Git, de secrets, de connecteurs et d’outils restent distinctes. Les sessions existantes conservent leur politique enregistrée.
`AgentConfigBlock.workspace` est obsolète. Le SDK convertit les anciennes valeurs `branch`/`runtime` vers le champ booléen.
Une écriture avec l’ancienne valeur `read` nécessite un choix explicite `repository_access` ; elle n’active pas l’accès au dépôt en lecture seule.


<a id="provider-and-model-access"></a>

## Accès aux fournisseurs et aux modèles

Consultez `beeuni.projects.modelAccess(projectId)`. Modifiez une cible avec `beeuni.projects.setModelAccess(projectId, { target: 'provider' | 'model', id, enabled })`.

Utilisez l’identifiant de fournisseur `beeuni` pour Beeuni Managed Models, ou un identifiant de fournisseur du catalogue pour BYOK, Codex et les fournisseurs personnalisés. Utilisez l’identifiant complet du modèle dans le protocole de la passerelle. La désactivation d’un fournisseur s’applique aux futurs modèles et préserve les préférences individuelles des modèles ainsi que les identifiants enregistrés.

Le modèle par défaut actuel et son fournisseur nécessitent un remplacement avant leur désactivation (`409 cannot_disable_default`). Les requêtes directes à la passerelle visant des cibles désactivées échouent avec `model_disabled` ou `provider_disabled`. Les requêtes en amont déjà en cours se terminent normalement. L’indicateur `enforced` de la réponse est faux lorsque le projet utilise runtime natif plutôt que la passerelle.

Les utilisateurs de React utilisent `useModelAccess(projectId)` depuis `@beeuni/sdk/react`. Sa mutation `setEnabled(change)` actualise le catalogue de gestion et le sélecteur de session.
