8 min de lecture

Modifications de schéma API : des tests de contrat pour des agents plus sûrs

Une modification du schéma API peut changer les décisions d'un agent IA sans provoquer d'erreur. Utilisez des tests de contrat pour vérifier les champs, les valeurs par défaut, le sens des réponses et les nouvelles tentatives.

Modifications de schéma API : des tests de contrat pour des agents plus sûrs

Un agent peut transformer une petite modification d'API en véritable erreur d'action plus vite qu'un client utilisé par une personne. Le renommage d'un champ de réponse peut lui faire sélectionner toutes les ressources au lieu d'une seule. Une nouvelle valeur par défaut peut élargir une requête. Un objet de statut modifié peut ressembler à une autorisation de réessayer, et cette nouvelle tentative peut répéter un débit, un déploiement ou une demande de suppression.

Le plus dangereux, c'est que ces échecs ressemblent souvent à un fonctionnement normal dans les outils de supervision classiques. Le fournisseur renvoie HTTP 200. Le client ne plante pas. L'agent produit une explication plausible. Les tests de contrat doivent vérifier le sens que l'agent attribue aux données de l'API, pas seulement si le JSON est correctement analysé.

Les décisions de l'agent font de la compatibilité une propriété de sécurité

Un client API utilisé par une personne échoue souvent de façon visible quand une réponse change. Un tableau vide s'affiche, une erreur apparaît dans un formulaire et quelqu'un enquête avant de passer à l'action suivante. Les agents transforment souvent directement une réponse en nouvelle requête. Ils peuvent effectuer cette conversion plusieurs fois avant que quiconque ne voie la transcription.

Prenons un agent de nettoyage qui appelle GET /projects?state=inactive, lit le champ owner de chaque élément et demande une approbation avant d'archiver les projets qui ne figurent pas sur une liste autorisée. Le fournisseur remplace ensuite owner par owner_id, tout en conservant l'ancien point de terminaison et le même code de statut. Un analyseur permissif transforme owner absent en chaîne vide. Si la règle de l'agent considère qu'un propriétaire vide signifie « non attribué », il prépare une demande d'archivage beaucoup plus large.

Ce n'est ni un échec d'authentification ni un problème de prompt. C'est un échec d'interprétation à la frontière de l'API. Le contrôle correctif doit se trouver au même endroit.

Traitez tout champ qui influence l'une de ces décisions comme une partie du contrat de sécurité :

  • l'objet sur lequel l'agent peut agir
  • l'éligibilité d'un objet à l'action
  • la portée, le montant ou la destination de l'action
  • le fait qu'une action précédente soit terminée, échouée ou nécessite une nouvelle tentative
  • le fait que l'agent doive demander l'avis d'une personne avant de continuer

Cette distinction compte, car la compatibilité du transport est bien plus faible que la compatibilité comportementale. Un service peut conserver son point de terminaison, sa méthode, son mécanisme d'authentification et sa syntaxe JSON, tout en modifiant la décision qui suit la réponse. Les équipes qualifient souvent ce changement de non bloquant parce que les SDK existants continuent à désérialiser les données. Pour un appelant autonome, cette étiquette peut être dangereusement incomplète.

Dressez une petite carte de toutes les valeurs de réponse qui atteignent un sélecteur d'action, une décision d'autorisation ou une branche de nouvelle tentative. Ne commencez pas par chaque propriété d'une immense spécification d'API. Commencez par les valeurs dont une mauvaise interprétation modifie ce que l'agent peut faire.

Un diff de schéma ne peut pas prouver la compatibilité comportementale

Les outils de comparaison de schémas détectent des changements utiles, mais ils ne peuvent pas dire si un changement est sûr pour un agent donné. Ils comparent des déclarations. Un agent dépend de sémantiques qui se trouvent souvent en dehors de ces déclarations.

Supposons qu'un fournisseur fasse passer limit d'un paramètre facultatif avec une valeur par défaut implicite de 100 à un paramètre facultatif avec une valeur par défaut implicite de 1000. Un diff OpenAPI classique peut ne signaler aucun changement de propriété obligatoire. Pourtant, un agent qui omet limit peut désormais examiner dix fois plus d'objets et envoyer une action groupée sur l'ensemble.

La spécification OpenAPI décrit un Schema Object comme un surensemble du vocabulaire JSON Schema et indique que ses propriétés fournissent des informations sur les charges utiles des requêtes et des réponses. C'est une documentation et une base de validation utiles. Elle ne dit pas que state: "pending" autorise une nouvelle tentative ni qu'un limit omis reste plafonné à 100. Ce sont des assertions de workflow, et vos tests doivent les formuler en termes simples.

De même, l'annotation default de JSON Schema n'oblige ni un validateur ni un client à insérer une valeur. Beaucoup de développeurs le supposent. La documentation JSON Schema présente default comme une donnée d'annotation, pas comme une commande qui modifie une instance. Si votre sécurité dépend d'une valeur, faites-la envoyer explicitement par le client et testez la requête sortante exacte. N'attendez pas d'une annotation de schéma qu'elle corrige une omission.

Utilisez un outil de comparaison comme signal d'alerte. Classez ensuite chaque changement détecté selon le parcours d'action qu'il peut affecter :

  • Un identifiant renommé peut modifier la cible sélectionnée.
  • L'ajout d'une valeur d'énumération peut envoyer l'analyseur vers une branche non testée.
  • La modification d'une valeur par défaut peut changer la portée sans modification du code de requête.
  • La modification d'une représentation peut inverser le sens de la réussite ou de l'échec.

L'inverse compte aussi. Un outil peut signaler l'ajout d'un champ descriptif que l'agent ne lit jamais. Cela mérite une revue, mais pas le gel d'une mise en production. La revue de compatibilité gagne en qualité lorsqu'elle suit les données jusqu'à une décision au lieu de considérer chaque ligne du schéma comme présentant le même risque.

Les tests de contrat doivent figer les requêtes et les décisions

Un test de contrat utile comporte deux parties : il vérifie la requête réellement envoyée par l'agent, puis l'action qu'il propose après avoir lu la réponse du fournisseur. Tester une seule partie laisse un angle mort important.

Pour les valeurs par défaut, capturez une véritable requête HTTP dans un serveur de test local ou une sandbox du fournisseur. L'exemple Python suivant utilise httpx.MockTransport pour examiner une requête sortante. Il évite le problème courant d'un client qui dépend silencieusement d'une valeur par défaut du fournisseur pour une opération destructive.

import httpx

seen = []

def handler(request: httpx.Request) -> httpx.Response:
    seen.append({
        "method": request.method,
        "path": request.url.path,
        "query": dict(request.url.params),
    })
    return httpx.Response(200, json={"items": []})

client = httpx.Client(transport=httpx.MockTransport(handler))

response = client.get(
    "https://api.example.test/projects",
    params={"state": "inactive", "limit": "100"},
)

assert response.status_code == 200
assert seen == [{
    "method": "GET",
    "path": "/projects",
    "query": {"state": "inactive", "limit": "100"},
}]

L'assertion importante n'est pas la réponse 200. C'est limit explicitement transmis. Si une refactorisation supprime ce paramètre, le test échoue avant qu'une nouvelle valeur par défaut du fournisseur puisse élargir la sélection.

Testez ensuite la décision. Séparez la fonction de planification de l'agent du code qui effectue l'appel HTTP afin que le test puisse examiner une action proposée sans en exécuter une.

from dataclasses import dataclass

@dataclass
class ArchivePlan:
    project_ids: list[str]
    requires_approval: bool

def plan_archives(items: list[dict], allowed_owners: set[str]) -> ArchivePlan:
    targets = []
    for item in items:
        owner = item.get("owner")
        if owner is None:
            raise ValueError("provider response lacks owner")
        if item["state"] == "inactive" and owner in allowed_owners:
            targets.append(item["id"])
    return ArchivePlan(targets, requires_approval=bool(targets))

items = [
    {"id": "p17", "state": "inactive", "owner": "team-a"},
    {"id": "p18", "state": "inactive", "owner": "team-b"},
]

plan = plan_archives(items, {"team-a"})
assert plan.project_ids == ["p17"]
assert plan.requires_approval is True

Ce test fait un choix que le code permissif évite souvent : l'absence de owner déclenche une erreur. Pour un champ qui sert à sélectionner une action, il faut échouer par défaut. Renvoyer une chaîne vide, None ou une valeur de repli supposée peut maintenir le processus en marche, mais remplace une panne d'intégration détectable par un plan potentiellement dangereux.

Gardez des jeux de données assez petits pour qu'un relecteur comprenne pourquoi chaque élément apparaît. Un jeu de données de mille objets peut ressembler à la production, mais il masque la condition que vous vouliez protéger.

Les champs renommés exigent un comportement d'échec explicite

Un champ renommé doit interrompre le parcours d'action, sauf si vous prenez délibérément en charge les deux noms pendant une migration définie. La gestion silencieuse d'une solution de repli semble robuste dans une démonstration, mais elle crée des sémantiques non revues en production.

La pire version ressemble à ceci :

owner = item.get("owner", "")
if owner not in blocked_owners:
    archive(item["id"])

Lorsque owner disparaît, chaque élément semble avoir un propriétaire qui n'est pas bloqué. L'analyseur a fait exactement ce que le code lui demandait. L'ingénieur qui l'a écrit voulait probablement éviter une KeyError. Cette petite commodité a transformé l'absence de données en permission d'agir.

Écrivez des tests pour les trois situations : le champ attendu, l'ancien champ si vous avez une promesse temporaire de compatibilité, et l'absence des deux champs. Le troisième test doit préciser si l'agent s'arrête, ignore l'objet ou demande une clarification. Pour l'identité de la cible, l'état d'autorisation et la portée de l'action, l'arrêt est généralement la bonne réponse.

Si vous prenez en charge un alias, rendez sa priorité évidente et temporaire :

def read_owner(item: dict) -> str:
    if "owner" in item:
        return item["owner"]
    if "owner_id" in item:
        return item["owner_id"]
    raise ValueError("owner identity missing")

Ce code nécessite encore un test pour les données contradictoires. Si les deux champs arrivent et ne concordent pas, ne privilégiez pas silencieusement l'un d'eux. Déclenchez une erreur et demandez au fournisseur de lever l'ambiguïté. Une couche de compatibilité doit préserver une ancienne signification connue, pas inventer un critère de départage pour des enregistrements incohérents.

Les équipes soutiennent parfois qu'une analyse permissive protège contre l'évolution du fournisseur. Elle protège contre les ajouts sans danger lorsque vous ignorez les champs inconnus. Elle ne protège pas contre les champs manquants qui gouvernent une action. Dans ces situations, il faut faire l'inverse : accepter par défaut les informations supplémentaires, mais refuser les informations obligatoires absentes.

Les valeurs par défaut et les omissions exigent des tests distincts

Examinez les répétitions incertaines
Utilisez une validation par clé lorsqu'un statut API ambigu pourrait autrement pousser un agent à répéter une action externe.

Une propriété omise, une valeur null explicite et une valeur explicite sont trois requêtes différentes. Les agents les confondent souvent, car les sérialiseurs généralistes les confondent eux aussi.

Un générateur de requêtes peut omettre dry_run lorsque sa valeur interne est None. Le fournisseur peut interpréter cette omission comme false. Une version ultérieure du fournisseur peut changer cette interprétation et utiliser le paramètre du compte, lequel vaut false pour un locataire et true pour un autre. Le code de l'agent n'a pas changé, mais l'action, elle, a changé.

Placez les options qui influencent une décision dans l'une de deux catégories. Lorsque le comportement sûr est connu, envoyez toujours la valeur. Lorsque l'option nécessite un choix de l'opérateur, exigez ce choix avant de construire la requête. Évitez une troisième catégorie « laisser le serveur décider » pour les actions destructives ou visibles de l'extérieur.

Testez la sérialisation avec un tableau de cas exacts. Le but est d'examiner la représentation sur le réseau, pas seulement l'objet du langage avant la sérialisation.

IntentionReprésentation sortanteSignification attendue côté fournisseur
Lire les projets inactifsstate=inactive&limit=100Une sélection limitée
Simuler l'archivage{"dry_run": true}Aucun archivage n'a lieu
Archiver un projet{"project_ids":["p17"],"dry_run": false}Seul p17 peut être modifié
Aucun choix de l'opérateur pour le modeLa requête est refusée localementLe fournisseur ne reçoit rien

Soyez tout aussi rigoureux avec la pagination. Une réponse qui ajoute next_cursor peut pousser un agent à récupérer automatiquement toutes les pages. C'est parfois raisonnable pour un rapport et imprudent pour un planificateur d'actions. Testez à la fois le nombre maximal d'objets que le planificateur peut examiner et la condition qui autorise une deuxième page. Un curseur permet de continuer, il ne donne pas son accord pour une portée illimitée.

Les valeurs par défaut du fournisseur comptent aussi dans les réponses. Si une API commence à omettre archivable lorsqu'il vaut false, un code tel que if item.get("archivable", True) modifie son comportement dans la mauvaise direction. Pour un champ qui accorde une permission, utilisez une comparaison explicite comme item.get("archivable") is True. C'est moins élégant et beaucoup plus facile à auditer.

La validation des réponses doit préserver le sens, pas seulement la forme

La validation des réponses doit distinguer les données mal formées des données inconnues mais inoffensives. Une stricte rigidité généralisée casse les clients lorsque les fournisseurs ajoutent des champs. Une permissivité généralisée transforme les preuves manquantes en suppositions.

Définissez un modèle de réponse limité aux valeurs qui alimentent l'action suivante. Pour chaque valeur, indiquez le type, les états autorisés et la conséquence de son absence sur le workflow. Un identifiant de projet ne doit pas être seulement une string : l'agent a besoin d'un identifiant stable et non vide, identique à celui qu'il enverra ensuite dans la requête d'archivage. Un statut ne doit pas être seulement une string : l'agent a besoin d'un état énuméré et d'une action documentée pour chaque valeur.

Par exemple, cet analyseur gère une représentation de statut modifiée sans s'autoriser une nouvelle tentative :

ALLOWED_STATES = {"queued", "running", "succeeded", "failed"}

def retryable(job: dict) -> bool:
    status = job.get("status")
    if status not in ALLOWED_STATES:
        raise ValueError(f"unknown job status: {status!r}")
    return status == "failed" and job.get("retry_allowed") is True

Si le fournisseur remplace status qui était une chaîne par un objet tel que {"phase":"failed"}, ce code s'arrête. Cette interruption est correcte tant que personne n'a décidé comment la nouvelle représentation correspond à l'ancien workflow. Si le fournisseur ajoute cancelled, l'arrêt est également correct jusqu'à ce que l'équipe décide si l'annulation est définitive, permet une nouvelle tentative ou nécessite l'intervention d'une personne.

Ne transformez pas chaque valeur d'énumération inconnue en urgence pour un affichage en lecture seule. La réaction doit correspondre à l'action. Un agent de reporting peut signaler un état inconnu et continuer. Un agent qui va relancer une tâche de facturation ou supprimer une ressource doit s'arrêter avant d'agir sur un état qu'il ne comprend pas.

Testez aussi les relations entre les champs. Une réponse peut être valide sur le plan structurel tout en contenant une combinaison contradictoire, comme status: "succeeded" et retry_allowed: true. La validation du schéma n'exprime généralement pas toutes les invariantes métier. Un test de contrat doit vérifier qu'une tâche réussie ne produit aucun plan de nouvelle tentative, quel que soit un booléen incohérent.

Testez tout le parcours d'action, pas un mock pratique

Suivez l'appel derrière le plan
Le journal Activity enregistre chaque appel HTTP ou SSH, pas seulement l'explication finale de l'agent.

Les tests unitaires des analyseurs sont nécessaires, mais ils ne prouvent pas que l'agent déployé envoie la requête prévue avec ses véritables identifiants et son véritable parcours d'exécution. Les bibliothèques de sérialisation, les wrappers, les adaptateurs d'outils et les middleware de nouvelle tentative modifient tous le comportement d'une manière qu'un appel direct de fonction ne peut pas révéler.

Exécutez en CI un serveur de contrat local qui enregistre les requêtes et renvoie des jeux de données versionnés. Configurez l'outil de l'agent pour pointer vers ce serveur. Le test doit partir d'une instruction réaliste, attendre le plan proposé ou l'enregistrement de l'action, puis vérifier la séquence enregistrée : méthode, chemin, requête, en-têtes qu'il est possible d'inspecter sans risque, corps et nombre d'actions.

Ne placez pas de secrets réels dans cet environnement. Utilisez un identifiant de test sans aucun pouvoir et vérifiez que l'agent ne le reçoit jamais dans son prompt, le résultat de l'outil, le texte d'une exception ou une trace. Un test qui journalise les en-têtes de requête sans précaution peut recréer l'exposition qu'il était censé empêcher.

Pour les agents qui effectuent des appels HTTP ou SSH externes avec Sallyport, le parcours d'action peut garder les identifiants hors de l'agent tout en conservant un résultat vérifiable. Cette frontière ne corrige pas une mauvaise interprétation de la réponse. Exécutez donc les contrats de schéma et de décision avant d'autoriser l'action externe.

Ajoutez des jeux de données d'échec, pas seulement des réponses de référence. Reproduisez les conditions exactes observées lors des transitions de fournisseur : champ de sélection manquant, nouvelle valeur d'énumération, résultat vide avec curseur de continuation, changement de type de contenu et réponse 200 contenant un objet d'erreur. Un corps d'erreur avec un statut 200 est particulièrement courant dans les anciennes API. Si votre analyseur suppose que chaque réponse 200 contient la forme de réussite, il peut fabriquer un plan vide ou répéter une requête déjà réussie.

Lors des tests de nouvelles tentatives, vérifiez le comportement d'idempotence. Faites en sorte que le serveur de contrat renvoie d'abord un délai d'attente après avoir enregistré la requête, puis une seconde réponse à la nouvelle tentative. Le test doit prouver que le client envoie un jeton d'idempotence lorsque l'API en prend un en charge, ou qu'il s'arrête et demande une confirmation lorsqu'il ne peut pas savoir si la première action est terminée. Répéter une lecture est généralement sans danger. Répéter un transfert, un e-mail, un déploiement ou une suppression ne l'est pas.

Les contrôles de publication doivent bloquer les ruptures sémantiques

Contrôlez les nouvelles tentatives risquées appel par appel
Demandez Touch ID ou un clic pour chaque utilisation d'une clé API susceptible de déclencher une nouvelle tentative destructive.

Exécutez les comparaisons de schémas, les tests de contrat du fournisseur et les tests de décision de l'agent dans le parcours de modification, à la fois pour les producteurs d'API et pour les consommateurs agents. Un processus qui ne les exécute qu'après le déploiement transforme les tests en documentation d'incident.

Pour une modification du fournisseur, exigez une trace de revue qui réponde à quatre questions concrètes : quelles hypothèses des consommateurs changent, quel était l'ancien comportement, combien de temps les deux comportements resteront pris en charge et quel jeu de données illustre le nouveau comportement. Cela demande moins de travail que de reconstituer une régression en production avec des journaux incomplets.

Pour une modification de l'agent, exécutez la suite de jeux de données du fournisseur avant la fusion. Si l'agent utilise désormais un champ nouvellement ajouté, ajoutez des jeux de données où ce champ est absent et où ses valeurs sortent du parcours nominal. Les changements dans la formulation du prompt peuvent aussi modifier les arguments des outils. Testez donc l'appel d'outil produit par l'agent complet au lieu de supposer que le planificateur choisira toujours les mêmes paramètres.

Les jeux de données versionnés rendent la revue concrète. Conservez un identifiant tel que projects-list-v1 avec la paire requête-réponse attendue. Lorsqu'un fournisseur introduit volontairement projects-list-v2, gardez l'ancien jeu de données jusqu'à la fin de la politique de migration. Ne remplacez pas l'ancien JSON en prétendant que les tests sont à jour. Vous perdriez la preuve de la compatibilité abandonnée.

Un contrôle utile signale les échecs dans un langage opérationnel. « Champ owner manquant, planification de l'archivage interrompue » indique au relecteur ce qui s'est passé. « ValidationError at path items.0 » vaut mieux que rien, mais oblige le relecteur à reconstituer le risque pendant la publication.

Les écrans d'approbation ne peuvent pas corriger un plan trompeur

L'approbation humaine reste un bon contrôle pour les actions externes, mais elle arrive trop tard si l'agent a construit le mauvais plan à partir d'un contrat modifié. Une personne qui voit « Archiver 847 projets inactifs » peut le refuser. Une personne qui voit « Archiver le projet p17 » ne peut pas savoir si p17 vient d'un champ owner absent, d'une valeur par défaut élargie ou d'un analyseur qui a confondu cancelled et failed.

Dans les demandes d'approbation, incluez les éléments de décision qui méritent une revue humaine : identifiants des cibles, nombre d'objets, mode demandé et champs de réponse qui ont rendu la cible éligible. Gardez l'enregistrement compact. Afficher le JSON brut à la personne qui approuve lui transfère la tâche d'analyse, alors qu'elle est déjà fatiguée.

Conservez séparément une trace permettant à un ingénieur de reconstituer l'action. Capturez la réponse du fournisseur ou une empreinte protégée de celle-ci, la version de l'analyseur, la version du jeu de données de contrat, la requête générée et la réponse obtenue. Une piste d'audit inviolable aide après coup, mais elle doit renvoyer à la frontière de décision au lieu d'enregistrer seulement qu'un appel HTTP a eu lieu.

La prochaine fois qu'une équipe API affirme qu'une modification de réponse est cosmétique, demandez-lui d'exécuter la suite de contrats de l'agent. Si la suite échoue, le changement a un comportement associé. Corrigez-le avant qu'une réponse 200 bien polie ne devienne une action dangereuse.

FAQ

Le simple renommage d'un champ API peut-il rendre un agent IA dangereux ?

Oui. Le renommage d'un champ peut pousser un agent vers une branche de repli, lui faire considérer une valeur manquante comme sûre ou l'amener à envoyer des requêtes de suivi mal formées. L'API peut continuer à renvoyer 200, ce qui explique pourquoi les contrôles de disponibilité classiques ne détectent pas le problème.

Les snapshots de réponses API suffisent-ils pour assurer la sécurité d'un agent ?

Les tests de snapshots vous aident à repérer les changements, mais ils produisent souvent des différences bruyantes et disent peu de choses sur le comportement. Utilisez-les pour la revue, puis ajoutez des assertions sur les quelques champs et relations qui pilotent une action.

Dois-je utiliser des contrats pilotés par le consommateur ou des tests OpenAPI ?

Un contrat piloté par le consommateur convient souvent mieux lorsqu'un workflow d'agent dépend d'une API fournisseur précise. Il décrit les besoins du consommateur, tandis que le schéma du fournisseur reste utile pour documenter toute la surface publique.

Quelle différence y a-t-il entre un champ JSON manquant et null pour un agent ?

Un champ manquant signifie que le producteur n'a pas envoyé l'information. Un champ null signifie qu'il a envoyé le champ et indiqué qu'aucune valeur n'existe. Les agents ne doivent pas traiter ces états comme équivalents, sauf si le contrat le précise explicitement.

Les tests de contrat doivent-ils refuser les champs de réponse inconnus ?

En général, non. Les champs inconnus apparaissent souvent lorsqu'un fournisseur ajoute des données utiles, et les refuser rend les clients fragiles. Refusez-les seulement si leur acceptation peut modifier une action sensible ou si la réponse doit respecter un vocabulaire fermé de commandes.

Comment tester une modification de la valeur par défaut d'une API ?

Considérez une nouvelle valeur par défaut comme un changement de comportement dès qu'elle touche la portée, la pagination, les permissions, les coûts, la suppression ou l'envoi. Testez la requête avec le champ omis, car c'est alors que la valeur par défaut du fournisseur prend le contrôle.

Comment tester le véritable parcours API utilisé par un agent ?

Testez la séquence réelle la plus courte : requête de l'agent, injection des identifiants ou frontière d'autorisation, requête au fournisseur, réponse du fournisseur, analyse et action proposée. Simuler uniquement l'appel API final ne révèle pas les erreurs de sérialisation ou d'interprétation entre ces étapes.

La validation JSON Schema garantit-elle un comportement sûr de l'agent ?

Non. La validation du schéma confirme qu'une charge utile a une forme autorisée, mais elle ne prouve pas que l'agent a choisi le bon compte, correctement interprété un état ou limité l'action aux objets prévus. Ajoutez des assertions sur ces décisions.

Que doit faire une équipe API avant de supprimer un champ utilisé par des agents ?

Conservez l'ancien comportement pendant la période de migration publiée, envoyez des signaux de dépréciation clairs et exécutez les deux versions du contrat en CI pendant la migration des clients. Ne réutilisez pas silencieusement un nom de champ pour lui donner un autre sens.

Une validation humaine peut-elle compenser des contrats API défaillants ?

La validation peut arrêter un appel, mais elle ne peut pas rendre intelligible à la personne qui l'approuve une requête trompeuse. Effectuez les contrôles de schéma avant que l'appel n'atteigne cette personne, puis affichez des informations d'approbation qui identifient la cible et la portée.

Sallyport

Sallyport exécute les appels d'API et les commandes SSH à la place de votre agent IA. Les clés restent dans un coffre-fort local sur votre Mac ; vous approuvez chaque exécution et chaque action est consignée dans un journal scellé.

© 2026 Sallyport · Open source sous Apache-2.0 · Oleg Sotnikov