Gérer les approbations et les entrées utilisateur

Utilisez les règles allowedTools/disallowedTools et le rappel canUseTool pour contrôler les outils que l’agent peut utiliser et la manière dont les demandes d’autorisation sont traitées.

Par défaut, le SDK applique les autorisations des outils et ne les contourne pas. Le rappel canUseTool vous donne un contrôle granulaire sur les demandes d’autorisation qui ne sont pas déjà résolues par les règles allowedTools/disallowedTools ou par un mode d’autorisation explicite tel que bypassPermissions.

Le SDK ne fournit pas d’invite d’autorisation interactive par défaut. Si une requête avec contrôle d’autorisation atteint le SDK et que canUseTool n’est pas fourni, la requête échoue.

Fonctionnement

Lorsque vous fournissez un rappel canUseTool, le SDK l’appelle avant chaque exécution de l’outil vérifié par l’autorisation. C’est votre fonction de rappel qui détermine ce qui se passe :

  1. L’agent décide d’utiliser un outil dont l’autorisation est vérifiée.

  2. Le SDK appelle votre rappel canUseTool avec le nom de l’outil, l’entrée de la requête et le contexte d’autorisation.

  3. Votre rappel renvoie allow ou deny.

  4. L’exécution de l’outil se poursuit ou est bloquée, selon le cas.

Pour de nombreux contrôles d’autorisation d’outils ordinaires, l’entrée du rappel contient des champs tels que { action, resource }.Les pseudo-outils à routage SDK, tels que AskUserQuestion et ExitPlanMode, contiennent des champs spécifiques à l’outil.

Si canUseTool n’est pas fourni et qu’une requête soumise à une vérification des autorisations parvient au SDK, le SDK renvoie une erreur au lieu d’afficher une invite interactive.

Exemple de base

import { createCortexCodeSession } from "cortex-code-agent-sdk";

const session = await createCortexCodeSession({
  cwd: process.cwd(),
  canUseTool: async (toolName, input, context) => {
    console.log(`Tool requested: ${toolName}`, input);

    // Allow read-only tools, deny destructive ones
    if (["Read", "Glob", "Grep"].includes(toolName)) {
      return { behavior: "allow" };
    }

    return { behavior: "deny", message: "Only read-only tools are allowed" };
  },
});

await session.send("What files are in this directory?");
for await (const event of session.stream()) {
  if (event.type === "assistant") {
    for (const b of event.content) {
      if (b.type === "text") process.stdout.write(b.text);
    }
  }
  if (event.type === "result") break;
}
await session.close();

Modèles de réponse

Autoriser un appel d’outil

Renvoie { behavior: "allow" } pour permettre à l’outil de s’exécuter avec son entrée d’origine :

canUseTool: async (toolName, input, context) => {
  return { behavior: "allow" };
}

Refuser un appel d’outil

Renvoyez { behavior: "deny" } avec un message facultatif. L’agent voit le message de refus et peut adapter son approche :

canUseTool: async (toolName, input, context) => {
  if (toolName === "Bash") {
    return { behavior: "deny", message: "Bash commands are not allowed in this session" };
  }
  return { behavior: "allow" };
}

L’outil AskUserQuestion

Cortex Code dispose d’un outil AskUserQuestion intégré que l’agent utilise lorsqu’il a besoin de précisions de la part de l’utilisateur. Lorsque l’agent appelle AskUserQuestion, le SDK le fait passer par votre rappel canUseTool avec toolName défini sur "AskUserQuestion". L’entrée contient les questions de l’agent sous forme d’options structurées à choix multiples.

Traitement des questions

Cherchez toolName === "AskUserQuestion" dans votre rappel canUseTool. L’input contient un tableau de questions dans lequel chaque question comporte :

Champ

Description

question

Texte complet de la question à afficher

header

Étiquette courte de la question

options

Une série d’options, chacune avec label, description, freeForm (booléen) et isCancel (booléen)

multiSelect

Si true, les utilisateurs peuvent sélectionner plusieurs options

Renvoyez allow avec un updatedInput qui comprend les questions d’origine et une carte answers. Chaque clé est le texte de la question et chaque valeur est l’étiquette de l’option sélectionnée. Pour les questions à choix multiples, reliez les étiquettes avec ", ".

const session = await createCortexCodeSession({
  cwd: process.cwd(),
  canUseTool: async (toolName, input, context) => {
    if (toolName === "AskUserQuestion") {
      const answers: Record<string, string> = {};
      for (const q of input.questions) {
        // Present q.question and q.options to the user, collect their choice
        const selected = await promptUser(q.question, q.options);
        answers[q.question] = selected;
      }
      return {
        behavior: "allow",
        updatedInput: { questions: input.questions, answers },
      };
    }
    return { behavior: "allow" };
  },
});

Pour refuser une question (annuler l’interaction), renvoyez deny :

return { behavior: "deny", message: "User cancelled" };

Astuce

AskUserQuestion est acheminé via canUseTool. Sans fonction de rappel, l’interaction ne peut pas aboutir et la requête génère une erreur au lieu de se poursuivre sans signaler de problème.

L’outil ExitPlanMode

Lorsqu’une session est en mode plan, Cortex Code reste en phase de planification jusqu’à ce que le plan soit approuvé. La demande d’approbation est acheminée par votre rappel canUseTool avec toolName/tool_name défini sur "ExitPlanMode".

L’entrée comprend :

Champ

Description

plan

Texte du plan proposé que l’agent souhaite exécuter

question

Invite d’approbation supplémentaire facultative de l’agent

Approbation ou rejet d’un plan

Renvoyez allow pour approuver le plan. Vous pouvez éventuellement inclure updatedInput.message pour renvoyer le contexte de révision à l’agent avant qu’il ne quitte le mode planification.

Renvoyez deny pour rejeter le plan. Utilisez message lorsque vous souhaitez que l’agent examine le plan avec des commentaires spécifiques. En rejetant ExitPlanMode, le tour en cours reste en mode planification afin que l’agent puisse mettre à jour le plan et poser à nouveau la question.

En approuvant ExitPlanMode, le mode planification prend fin pour le tour en cours. Les tours suivants reprennent le comportement normal d’autorisation hors plan de la session, de sorte que les appels d’outil ultérieurs sont évalués de la même manière qu’en dehors du mode planification.

Exemple de boucle de révision

La boucle de révision la plus simple consiste à rejeter un plan faible une fois avec des commentaires spécifiques, puis à approuver le plan révisé :

let rejectedOnce = false;

const session = await createCortexCodeSession({
  cwd: process.cwd(),
  permissionMode: "plan",
  canUseTool: async (toolName, input) => {
    if (toolName === "ExitPlanMode") {
      const plan = String(input.plan ?? "");
      if (!rejectedOnce) {
        rejectedOnce = true;
        return {
          behavior: "deny",
          message: "Add a verification step and say which file you will edit.",
        };
      }
      return {
        behavior: "allow",
        updatedInput: {
          message: `Approved plan: ${plan}`,
        },
      };
    }
    return { behavior: "allow" };
  },
});

Autorisations basées sur les règles

Pour les modèles d’autorisation courants, vous pouvez utiliser une configuration basée sur des règles au lieu d’écrire une logique de rappel. Les options allowedTools et disallowedTools vous permettent de définir des listes d’outils qui sont automatiquement approuvés ou bloqués :

const session = await createCortexCodeSession({
  cwd: process.cwd(),
  allowedTools: ["Read", "Glob", "Grep", "Bash(npm test:*)"],
  disallowedTools: ["Write"],
});

Les entrées allowedTools peuvent inclure des modèles. Par exemple, Bash(npm test:*) approuve automatiquement toute commande Bash correspondant à ce modèle. Les entrées disallowedTools bloquent entièrement des outils spécifiques.

Ces règles sont évaluées par la CLI avant le rappel canUseTool. Si un outil correspond à une règle autorisée ou non autorisée, le rappel n’est pas invoqué pour cet outil.

Combinaison avec les modes d’autorisation

Le rappel canUseTool fonctionne parallèlement aux modes d’autorisation. Lorsque les deux sont définis, les filtres du mode d’autorisation intégré à la CLI s’exécutent en premier, et votre rappel traite tous les appels d’outil restants qui nécessitent une approbation.

Les modes d’autorisation disponibles sont les suivants :

Mode

Description

default

Utilise les contrôles d’autorisation standard. Dans les sessions SDK, contrôlez les outils soumis à autorisation à l’aide de allowedTools, disallowedTools ou canUseTool.

autoAcceptPlans

Approuve automatiquement les demandes de plan et les confirmations de sortie du plan. Ne contourne pas les autorisations ordinaires des outils.

plan

L’agent planifie les modifications mais ne les exécute pas sans approbation. Avec canUseTool, les approbations en mode planification sont acheminées via ExitPlanMode. Le rejet de cette demande maintient la planification active ; son acceptation met fin au mode planification et rétablit par la suite les autorisations normales.

bypassPermissions

Ignorez tous les contrôles d’autorisation. Nécessite allowDangerouslySkipPermissions: true (TypeScript) ou allow_dangerously_skip_permissions=True (Python) comme indicateur de sécurité.

Modifier le mode d’autorisation pendant une session

Vous pouvez modifier le mode d’autorisation après le début de la session. TypeScript expose setPermissionMode() à la fois sur Query et sur CortexCodeSession. Python expose set_permission_mode() sur CortexCodeSDKClient.

Le mode mis à jour s’applique aux tours ultérieurs après le traitement de la demande de contrôle. Cela ne modifie pas rétroactivement un appel d’outil déjà en cours d’exécution.

const session = await createCortexCodeSession({
  cwd: process.cwd(),
});

await session.setPermissionMode("plan");
await session.send("Review the repo and propose a plan before editing.");

await session.setPermissionMode("default");
await session.send("Now implement the approved change.");

Hooks

Les hooks vous permettent d’intercepter l’exécution d’un outil à différentes étapes de son cycle de vie. Contrairement à canUseTool qui contrôle uniquement l’exécution d’un outil, les hooks peuvent inspecter les résultats de l’outil, injecter le contexte et contrôler le flux d’agent.

import { createCortexCodeSession } from "cortex-code-agent-sdk";

const session = await createCortexCodeSession({
  cwd: process.cwd(),
  hooks: {
    PreToolUse: [
      {
        matcher: "Bash",
        hooks: [
          async (input, toolUseId, context) => {
            console.log(`About to run Bash: ${input.tool_input?.command}`);
            return {};  // Allow execution to proceed
          },
        ],
      },
    ],
    PostToolUse: [
      {
        hooks: [
          async (input, toolUseId, context) => {
            console.log(`Tool ${input.tool_name} completed`);
            return {};
          },
        ],
      },
    ],
  },
});

Événements de hooks

Événement

Quand il se déclenche

PreToolUse

Avant l’exécution d’un outil. Peut bloquer l’exécution ou modifier une entrée.

PostToolUse

Une fois qu’un outil s’est exécuté avec succès.

UserPromptSubmit

Lorsqu’une invite utilisateur est soumise.

Stop

Lorsque l’agent s’arrête.

SubagentStop

Lorsqu’un sous-agent s’arrête.

Notification

Lorsque l’agent émet une notification.

PermissionRequest

Lorsqu’une demande d’autorisation d’outil se produit.

PreCompact

Avant le compactage du contexte.

Le champ matcher sur une entrée de hook est facultatif. Lorsqu’il est fourni, il filtre selon la valeur de correspondance de l’événement : nom de l’outil pour PreToolUse, PostToolUse et PermissionRequest ; type de notification pour Notification ; et déclencheur pour PreCompact. Lorsqu’il est omis, le hook se déclenche pour toutes les valeurs de cet événement.

Voir la référence SDK TypeScript et la référence SDK Python pour l’ensemble des définitions des types d’entrée et de sortie du hook.

Choisir une approche

Approche

Quand utiliser

permissionMode: "bypassPermissions" avec indicateur de sécurité

Environnements sandbox, pipelines CI ou scénarios entièrement fiables où vous n’avez pas besoin de vérifier les appels d’outils. Nécessite allowDangerouslySkipPermissions: true (TypeScript) ou allow_dangerously_skip_permissions=True (Python).

allowedTools / disallowedTools

Lorsque vous pouvez exprimer votre politique d’autorisation sous forme de liste statique d’outils autorisés ou bloqués

Rappel de canUseTool

Systèmes de production dans lesquels vous devez auditer, filtrer ou modifier les appels des outils avant leur exécution

Mode d’autorisation uniquement (pas de rappel)

Lorsque autoAcceptPlans est suffisant et vous n’avez pas besoin d’un traitement de rappel personnalisé

Hooks

Lorsque vous devez observer ou répondre aux résultats des outils, injecter le contexte ou contrôler le flux d’agent au-delà des décisions d’autorisation/de refus

Note

Par défaut, le SDK ne contourne pas les autorisations et n’émet pas de demandes de manière interactive. Si une requête avec contrôle d’autorisation atteint le SDK et que canUseTool n’est pas fourni, la requête échoue. Pour contourner les autorisations, vous devez explicitement définir permissionMode: "bypassPermissions" avec l’indicateur de sécurité approprié.