Processar aprovações e entrada de usuário

Use as regras allowedTools/disallowedTools e o retorno de chamada canUseTool para controlar quais ferramentas o agente pode usar e como as solicitações de permissão são processadas.

Por padrão, o SDK aplica as permissões da ferramenta, não as ignora. O retorno de chamada canUseTool oferece um controle granular sobre as solicitações de permissão que ainda não foram resolvidas pelas regras allowedTools/disallowedTools ou por um modo de permissão explícita, como bypassPermissions.

O SDK não fornece um prompt de permissão interativo por padrão. Se uma solicitação verificada por permissão chegar ao SDK e``canUseTool`` não for fornecido, a solicitação falhará.

Como funciona

Quando você fornece um retorno de chamada canUseTool, oSDK o chama antes de cada execução de ferramenta verificada por permissão. O retorno de chamada decide o que acontece:

  1. O agente decide usar uma ferramenta verificada por permissão.

  2. O SDK chama o retorno de chamada canUseTool com o nome da ferramenta, a entrada da solicitação e o contexto da permissão.

  3. O retorno de chamada responde allow ou deny.

  4. A execução da ferramenta continua ou é bloqueada de acordo.

Para muitas verificações comuns de permissão de ferramentas, a entrada de retorno de chamada contém campos como { action, resource }. As pseudoferramentas roteadas por SDK, como AskUserQuestion e ExitPlanMode, contêm campos específicos da ferramenta.

Se canUseTool não for fornecido e uma solicitação verificada por permissão chegar ao SDK, o SDK retornará um erro em vez de enviar um prompt interativamente.

Exemplo básico

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();

Padrões de resposta

Permitir chamada de ferramenta

Retornar { behavior: "allow" } para permitir que a ferramenta seja executada com a entrada original:

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

Negar chamada de ferramenta

Retornar { behavior: "deny" } com uma mensagem opcional. O agente vê a mensagem de negação e pode ajustar sua abordagem:

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

A ferramenta AskUserQuestion

O Cortex Code tem uma ferramenta AskUserQuestion integrada que o agente usa quando precisa de esclarecimento do usuário. Quando o agente chama AskUserQuestion, o SDK o roteia por meio do retorno de chamada canUseTool com toolName definido como "AskUserQuestion". A entrada contém as perguntas do agente como opções de múltipla escolha estruturadas.

Lidando com perguntas

Verifique se há toolName === "AskUserQuestion" em seu retorno de chamada canUseTool. input contém um array questions em que cada pergunta tem:

Campo

Descrição

question

O texto completo da pergunta a ser exibido

header

Rótulo abreviado da pergunta

options

Matriz de opções, cada uma com label, description, freeForm (booliano) e isCancel (booliano)

multiSelect

Se true, os usuários podem selecionar várias opções

Retorna allow com updatedInput que inclui questions original e um mapa answers. Cada chave é o texto da pergunta e cada valor é o rótulo da opção selecionada. Para perguntas com várias seleções, une os rótulos com ", ".

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" };
  },
});

Para negar uma pergunta (cancelar a interação), retorna deny:

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

Dica

AskUserQuestion é roteado por meio de canUseTool. Sem um retorno de chamada, a interação não pode ser concluída e a solicitação apresentará erros em vez de prosseguir silenciosamente.

A ferramenta ExitPlanMode

Quando uma sessão está no modo plan, o Cortex Code permanece em planejamento até o plano ser aprovado. A solicitação de aprovação é roteada por meio do retorno de chamada canUseTool com toolName/tool_name definido como "ExitPlanMode".

A entrada contém:

Campo

Descrição

plan

O texto do plano proposto que o agente deseja executar

question

Prompt de aprovação adicional opcional do agente

Aprovando ou rejeitando um plano

Retorna allow para aprovar o plano. Opcionalmente, inclua updatedInput.message para passar o contexto de revisão de volta ao agente antes que ele saia do modo de plano.

Retorna deny para rejeitar o plano. Use message quando você quiser que o agente revise o plano com feedback específico. Rejeitar ExitPlanMode mantém o turno atual no modo de plano para que o agente possa atualizar o plano e perguntar novamente.

Aprovar ExitPlanMode encerra o modo de plano para o turno atual. Os turnos seguintes retomam o comportamento normal de permissão não plano da sessão, assim as chamadas de ferramenta posteriores são avaliadas da mesma forma que seriam fora do modo de plano.

Exemplo de loop de revisão

O loop de revisão mais simples é rejeitar um plano fraco uma vez com feedback específico e, em seguida, aprovar o plano revisado:

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" };
  },
});

Permissões baseadas em regras

Para padrões de permissão comuns, você pode usar uma configuração baseada em regras em vez de escrever uma lógica de retorno de chamada. As opções allowedTools e disallowedTools permitem que você defina listas de ferramentas que são automaticamente aprovadas ou bloqueadas:

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

As entradas allowedTools podem incluir padrões. Por exemplo, Bash(npm test:*) aprova automaticamente qualquer comando Bash que corresponda a esse padrão. Entradas disallowedTools bloqueiam inteiramente ferramentas específicas.

Essas regras são avaliadas pela CLI antes do retorno de chamada canUseTool. Se uma ferramenta corresponder a uma regra permitida ou não permitida, o retorno de chamada não será invocado para essa ferramenta.

Combinando com modos de permissão

O retorno de chamada canUseTool funciona junto com os modos de permissão. Quando ambos são definidos, os filtros internos do modo de permissão da CLI são executados primeiro, e seu retorno de chamada processa todas as chamadas de ferramenta restantes que precisam de aprovação.

Os modos de permissão disponíveis são:

Modo

Descrição

default

Usa verificações de permissão padrão. Nas sessões do SDK, controla as ferramentas verificadas por permissão com allowedTools, disallowedTools ou canUseTool.

autoAcceptPlans

Aprova automaticamente as solicitações de plano e confirmações de saída de plano. Não ignora as permissões de ferramenta comuns.

plan

O agente planeja as alterações, mas não as executa sem aprovação. Com canUseTool, as aprovações do modo de plano são roteadas por meio de ExitPlanMode. Negar essa solicitação mantém o planejamento ativo. Aprová-la sai do modo de plano, e os turnos posteriores retomam as permissões normais.

bypassPermissions

Ignora todas as verificações de permissão. Requer allowDangerouslySkipPermissions: true (TypeScript) ou allow_dangerously_skip_permissions=True (Python) como um sinalizador de segurança.

Alterar o modo de permissão durante uma sessão

Você pode alterar o modo de permissão após o início da sessão. TypeScript expõe setPermissionMode() em ambos Query e CortexCodeSession. O Python expõe set_permission_mode() em CortexCodeSDKClient.

O modo atualizado se aplica aos turnos posteriores depois que a solicitação de controle é processada. Ele não altera retroativamente uma chamada de ferramenta que já está em execução.

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.");

Ganchos

Os ganchos permitem interceptar a execução da ferramenta em diferentes estágios do ciclo de vida. Ao contrário de canUseTool, que só controla se uma ferramenta é ou não executada, os ganchos podem inspecionar os resultados da ferramenta, injetar contexto e controlar o fluxo do agente.

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 {};
          },
        ],
      },
    ],
  },
});

Eventos de gancho

Evento

Quando é acionado

PreToolUse

Antes da execução de uma ferramenta. Pode bloquear a execução ou modificar a entrada.

PostToolUse

Depois que uma ferramenta é concluída com sucesso.

UserPromptSubmit

Quando um prompt de usuário é enviado.

Stop

Quando o agente para.

SubagentStop

Quando um subagente para.

Notification

Quando o agente emite uma notificação.

PermissionRequest

Quando ocorre uma solicitação de permissão de ferramenta.

PreCompact

Antes da compactação do contexto.

O campo matcher em uma entrada de gancho é opcional. Quando fornecido, ele filtra pelo valor de correspondência do evento: nome da ferramenta para PreToolUse, PostToolUse e PermissionRequest; tipo de notificação para Notification e acionador para PreCompact. Quando omitido, o gancho é acionado para todos os valores daquele evento.

Consulte a Referência do SDK TypeScript e a Referência do SDK Python para ver as definições completas dos tipos de entrada e saída de gancho.

Escolhendo uma abordagem

Abordagem

Quando usar

permissionMode: "bypassPermissions" com sinalizador de segurança

Ambientes em sandbox, pipelines de CI ou cenários totalmente confiáveis em que você não precisa revisar as chamadas de ferramentas. Requer allowDangerouslySkipPermissions: true (TypeScript) ou allow_dangerously_skip_permissions=True (Python).

allowedTools / disallowedTools

Quando você pode expressar sua política de permissões como uma lista estática de ferramentas permitidas ou bloqueadas

Retorno de chamada de canUseTool

Sistemas de produção em que você precisa auditar, filtrar ou modificar chamadas de ferramentas antes de serem executadas

Somente modo de permissão (sem retorno de chamada)

Quando autoAcceptPlans é suficiente, e você não precisa de processamento de retorno de chamada personalizado

Ganchos

Quando você precisa observar ou responder aos resultados da ferramenta, injetar contexto ou controlar o fluxo do agente além das decisões de permitir/negar

Nota

Por padrão, o SDK não ignora as permissões ou envia um prompt interativamente. Se uma solicitação verificada por permissão chegar ao SDK e``canUseTool`` não for fornecido, a solicitação falhará. Para ignorar as permissões, você deve definir permissionMode: "bypassPermissions" explicitamente com o sinalizador de segurança apropriado.