Ferramentas de Dev (adk-devtools)

adk-devtools é o conjunto de ferramentas de loop interno que um agente de programação precisa — ler, editar, buscar e executar — com cada operação limitada a um diretório de workspace. É um crate independente e publicável, dependendo apenas de adk-core, então ele se compõe com qualquer LlmAgent (o harness CodingAgent o conecta para você).

As ferramentas

DevToolset é um Toolset que reúne seis ferramentas:

FerramentaParâmetrosComportamento
read_filepath, offset?, limit?Retorna o conteúdo do arquivo, com numeração de linhas
write_filepath, contentCria/sobrescreve um arquivo (cria diretórios pai)
edit_filepath, old_string, new_string, replace_all?Substituição exata de string
globpattern, path?Lista arquivos que correspondem a um glob (por exemplo, src/**/*.rs)
greppattern, path?, glob?, case_insensitive?Busca de conteúdo com regex
bashcommand, timeout_secs?Executa um comando de shell na raiz do workspace

Dois comportamentos de segurança que vale conhecer:

  • edit_file exige um read_file prévio desse arquivo na sessão, e por padrão a string de destino deve ocorrer exatamente uma vez (replace_all para substituir). Isso protege contra sobrescritas cegas.
  • grep ignora diretórios comuns de build/VCS (target, .git, node_modules, …) e arquivos binários/grandes demais.

A ferramenta bash faz streaming de stdout/stderr linha a linha via ToolContext::emit_progress enquanto o comando é executado, para que as UIs possam mostrar um terminal ao vivo. Cada chunk chega como um evento parcial na EventStream do agente (detecção com event.tool_progress_stream()); a saída completa ainda é retornada como resultado final da ferramenta. Veja o exemplo de streaming_bash e Streaming Progress from a Tool.

O Workspace

Um Workspace ancora cada operação em um diretório e impõe uma pequena política:

use adk_devtools::Workspace;
use std::time::Duration;

let ws = Workspace::new("./my-repo");              // read-write, bash enabled
let ws = Workspace::read_only("./my-repo");        // explore/plan: no writes, no bash
let ws = Workspace::new("./my-repo")
    .allow_bash(false)                              // file edits, but no shell
    .bash_timeout(Duration::from_secs(60))
    .max_output_bytes(512 * 1024);
  • Contenção de caminho — qualquer caminho que resolva fora da raiz é rejeitado, então o agente não pode ler ou escrever ../../etc/.... A contenção é aplicada ao caminho resolvido, não apenas ao literal: um symlink apontando para fora da raiz é rejeitado mesmo que esteja lexicalmente dentro. Isso cobre um componente final symlinkado e um diretório pai symlinkado, então a criação por meio de um diretório redirecionado também é recusada. Um symlink cujo destino permaneça dentro do workspace continua funcionando, já que repositórios legitimamente contêm links internos.

    A verificação não é um bloqueio. Um symlink colocado entre a verificação e a abertura subsequente ainda seria seguido; fechar essa janela exige travessia relativa ao descritor com semântica no-follow da plataforma. Trate as ferramentas de arquivo como contenção contra um agente que vagueia, não como isolamento contra um adversário que pode escrever no workspace simultaneamente.

  • Modo somente leituraWorkspace::read_only(..) oculta completamente as ferramentas que modificam (o modelo só vê read_file/glob/grep).

  • Ambiente bash é limpo — o comando recebe apenas PATH, HOME, LANG, LC_ALL, TMPDIR, TERM, USER e SHELL, então as chaves de provedor API mantidas pelo processo do agente não podem ser lidas com env. Workspace::inherit_env(true) restaura o antigo comportamento de passar tudo, e env_allowlist substitui o conjunto.

  • bash timeout + limites de saída — comandos longos ou muito verbosos são limitados. Um comando que estoura o tempo é encerrado como um grupo de processos, então tudo o que ele iniciou também é encerrado; antes apenas o filho direto era sinalizado e os descendentes sobreviviam.

Usando diretamente

Anexe o conjunto de ferramentas a qualquer agente:

use adk_devtools::{DevToolset, Workspace};
use adk_agent::LlmAgentBuilder;
use std::sync::Arc;

let agent = LlmAgentBuilder::new("coder")
    .model(model)
    .toolset(Arc::new(DevToolset::new(Workspace::new("./my-repo"))))
    .build()?;

DevToolset expõe apenas as ferramentas permitidas pelo workspace, então um workspace somente leitura gera automaticamente um agente somente leitura.

Modelo de sandbox

A fase 1 executa bash localmente no host (sh -c, diretório de trabalho fixado na raiz) com um timeout e um ambiente limpo. O que isso oferece e o que não oferece:

AplicadoNão aplicado
As ferramentas de arquivo não podem resolver fora da raiz, inclusive por meio de symlinksbash ainda pode usar caminhos absolutos — o diretório de trabalho não é um limite do sistema operacional
O comando não pode ler as variáveis de ambiente do agenteO comando pode acessar a rede
Um tempo limite encerra o comando e seus descendentesNada limita a memória ou a CPU

Então, ele é contido pelo caminho, isolado do ambiente e delimitado, mas não isolado do SO. O vocabulário da política se alinha com adk-code's SandboxPolicy; para isolamento forte, execute bash atrás de um executor conteinerizado (veja o design doc). Combine com adk-guardrail (listas de अनुमति de comandos, mascaramento de segredos) e adk-auth para ferramentas com tokenização (por exemplo, GitHub).

Próximo: O harness →