> ## Documentation Index
> Fetch the complete documentation index at: https://phaseo.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Phaseo Gateway base URL: https://api.phaseo.app/v1. Send `Authorization: Bearer $PHASEO_API_KEY` with every request.
> Model IDs look like `provider/model`, for example `openai/gpt-6-astra`. Do not guess IDs: list them with `GET /v1/models` or check https://phaseo.app/models. `phaseo/free` routes to an available free model and needs no wallet balance.
> For new text integrations prefer `POST /v1/responses`. `POST /v1/chat/completions` (OpenAI format) and `POST /v1/messages` (Anthropic format) suit existing code.
> The full API contract is https://phaseo.app/docs/openapi/v1/openapi.yaml. Official SDKs: `@phaseo/sdk` (TypeScript) and `phaseo` (Python), plus Go, C#, PHP, Ruby and Rust.
> Search these docs over MCP at https://phaseo.app/docs/mcp, and install Phaseo skills with `npx skills add https://phaseo.app/docs`. If a page is wrong or out of date, report it with the MCP `submit_feedback` tool.
> Manage Phaseo accounts with the `phaseo` CLI (`npm install -g @phaseo/cli`, then `phaseo login` or `phaseo login --device-code`), the `/v1` Management API or the MCP server at `https://mcp.phaseo.app/mcp?access=manage`. Do not send users to the dashboard for account tasks. A person is still needed for sign-up, approving a sign-in, paying the first top-up on the checkout link that `phaseo credits add` returns when no card is saved, confirming each saved-card top-up from the emailed link, and account security (password, MFA, passkeys). Create API keys with `phaseo keys create --name <name> --json`; never guess one. Credit top-ups need `phaseo login --scopes billing:write` first. Task map: https://phaseo.app/docs/v1/developers/manage-from-an-agent.md.

# Ferramentas do Agent SDK

> Defina ferramentas locais, com aprovação, manuais e concorrentes para um agente da Phaseo.

Defina uma ferramenta quando um agente precisa ler dados do aplicativo, chamar um serviço interno ou executar uma ação. O modelo chama a ferramenta pelo ID; o SDK verifica os argumentos, executa seu código no seu processo e envia o resultado de volta no turno seguinte.

## Defina uma ferramenta

```typescript TypeScript theme={null}
import { defineTool } from "@phaseo/agent-sdk";

const fetchTicket = defineTool({
  id: "fetch-ticket",
  description: "Load one internal support ticket.",
  parameters: {
    type: "object",
    properties: {
      ticketId: { type: "string" },
    },
    required: ["ticketId"],
    additionalProperties: false,
  },
  timeoutMs: 3_000,
  async execute(input: { ticketId: string }, context) {
    const response = await fetch(
      `https://internal.example/tickets/${input.ticketId}`,
      { signal: context.signal },
    );

    return response.json();
  },
});
```

```python Python theme={null}
import httpx
from phaseo_agent import AgentTool, define_tool

def fetch_ticket(tool_input, runtime):
    response = httpx.get(f"https://internal.example/tickets/{tool_input['ticketId']}")
    return response.json()

fetch_ticket_tool = define_tool(AgentTool(
    id="fetch-ticket",
    description="Load one internal support ticket.",
    parameters={
        "type": "object",
        "properties": {"ticketId": {"type": "string"}},
        "required": ["ticketId"],
        "additionalProperties": False,
    },
    timeout_ms=3_000,
    execute=fetch_ticket,
))
```

```go Go theme={null}
search := phaseoagent.DefineTool(phaseoagent.Tool{
  ID:          "search_tickets",
  Description: "Search support tickets by keyword",
  Parameters: map[string]any{
    "type":       "object",
    "properties": map[string]any{"query": map[string]any{"type": "string"}},
    "required":   []string{"query"},
  },
  Timeout:     10 * time.Second,
  InputSchema: validateSearchInput, // func(any) (any, error)
  Execute: func(input any, runtime phaseoagent.RuntimeContext) (any, error) {
    return tickets.Search(input.(map[string]any)["query"].(string))
  },
})
```

```csharp C# theme={null}
// O construtor recebe Id, Execute, Description, Parameters e Timeout;
// defina as outras propriedades com um inicializador.
var search = AgentSdk.DefineTool(new Tool(
    "search_tickets",
    async (input, runtime) => await tickets.SearchAsync(input, runtime.CancellationToken),
    "Search support tickets by keyword",
    new Dictionary<string, object?>
    {
        ["type"] = "object",
        ["properties"] = new Dictionary<string, object?> { ["query"] = new { type = "string" } },
        ["required"] = new[] { "query" },
    },
    TimeSpan.FromSeconds(10))
{
    InputSchema = ValidateSearchInput, // Func<object?, object?>
});
```

```php PHP theme={null}
$search = AgentSdk::defineTool(new Tool(
    id: "search_tickets",
    execute: fn (array $input, $runtime) => $tickets->search($input["query"]),
    description: "Search support tickets by keyword",
    parameters: [
        "type" => "object",
        "properties" => ["query" => ["type" => "string"]],
        "required" => ["query"],
    ],
    timeoutMs: 10000,
    inputSchema: fn ($input) => is_string($input["query"] ?? null) ? $input : throw new InvalidArgumentException("query is required"),
));
```

```ruby Ruby theme={null}
search = PhaseoAgentSdk.define_tool(
  PhaseoAgentSdk::Tool.new(
    id: "search_tickets",
    description: "Search support tickets by keyword",
    parameters: {
      type: "object",
      properties: { query: { type: "string" } },
      required: ["query"]
    },
    timeout_ms: 10_000,
    input_schema: ->(input) { input["query"].is_a?(String) ? input : raise("query is required") },
    execute: ->(input, runtime) { tickets.search(input["query"]) }
  )
)
```

```java Java theme={null}
// Tool tem três construtores: (id, execute); (id, execute, description, parameters, timeout);
// e a forma completa abaixo. Validators::requireQuery representa o seu próprio validador.
var search = AgentSdk.defineTool(new AgentSdk.Tool(
    "search_tickets",
    (input, context) -> tickets.search((String) ((Map<?, ?>) input).get("query")),
    "Search support tickets by keyword",
    Map.of("type", "object", "properties", Map.of("query", Map.of("type", "string"))),
    Duration.ofSeconds(10),
    Validators::requireQuery, // inputSchema: AgentSdk.Validator
    null, null,             // outputSchema, eventSchema
    false, null,            // requireApproval, approval
    null, null,             // onToolCalled, onResponseReceived
    "fail-run"              // onError
));
```

```rust Rust theme={null}
use phaseo_agent::Tool;
use serde_json::json;

let get_status = Tool::new(
    "get_status",
    "Get the current service status",
    json!({
        "type": "object",
        "properties": {
            "service": { "type": "string" }
        },
        "required": ["service"]
    }),
    |input, _context| {
        Ok(json!({
            "service": input["service"],
            "status": "healthy"
        }))
    },
);
```

## Campos da ferramenta

| Campo | `camelCase` (TS, PHP, Java) | `snake_case` (Python, Ruby, Rust) | `PascalCase` (Go, C#) | O que faz |
| - | - | - | - | - |
| Parâmetros | `parameters` | `parameters` | `Parameters` | JSON Schema enviado ao modelo |
| Validadores | `inputSchema`, `outputSchema`, `eventSchema` | `input_schema`, `output_schema`, `event_schema` | `InputSchema`, `OutputSchema`, `EventSchema` | Verificam argumentos, resultados e valores de progresso. Uma falha encerra a execução (`AgentSchemaValidationError` no TypeScript, `Invalid tool input: ...` ou `Invalid tool output: ...` nas demais). O Rust não tem. |
| Timeout | `timeoutMs` (TS, PHP), `timeout` (Java) | `timeout_ms` | `Timeout` | Faz a chamada falhar depois desse tempo; veja a tabela abaixo. O Rust não tem. |
| Aprovação | `requireApproval` | `require_approval` | `RequireApproval`, `Approval` | Pausa para uma pessoa antes de a chamada rodar |
| Hooks interativos | `onToolCalled`, `onResponseReceived` | `on_tool_called`, `on_response_received` | `OnToolCalled`, `OnResponseReceived` | Pausam para a resposta de uma pessoa e depois a transformam. O Rust não tem. |
| Substituições do próximo turno | `nextTurnParams` | `next_turn_params` | `NextTurn` (Go), `NextTurnParams` (C#) | Alteram a próxima requisição ao modelo; veja [Turnos dinâmicos](https://phaseo.app/docs/pt-BR/v1/sdk-reference/agent/dynamic-turns.md). O Rust não tem. |
| Política de erro | `onError` | `on_error` | `OnError` | `"fail-run"` (padrão) ou `"return-to-model"`. O Rust não tem. |

Os schemas aceitam o validador nativo de cada linguagem: no TypeScript, uma função ou um objeto com `parse()` ou `safeParse()`; no Python, um callable, um modelo Pydantic ou um objeto com `parse` ou `safe_parse`; nas demais, uma função ou callable que retorna o valor ou lança uma exceção.

### O que um timeout faz

| Linguagem | Quando o `timeout` expira |
| - | - |
| TypeScript | Aborta `context.signal` e trata o timeout como um erro de ferramenta |
| Python | Lança `TimeoutError` e sempre faz a execução falhar, mesmo com `return-to-model`. A thread não é interrompida, então ferramentas demoradas devem verificar o próprio prazo. |
| Go | Faz a chamada falhar com `tool <id> timed out`. `Execute` não tem `context.Context`, então a função continua rodando na sua goroutine. |
| C# | Cancela `runtime.CancellationToken` e faz a chamada falhar com uma `TimeoutException`. Passe o token às suas próprias chamadas assíncronas. |
| PHP | Verificado depois que `execute` retorna: uma chamada mais lenta falha com `Tool <name> timed out after <n>ms`. O PHP não consegue interromper o callback. |
| Ruby | Envolve `execute` em `Timeout.timeout`, que interrompe a chamada e lança `Timeout::Error` |
| Java | Executa o executor na própria thread, faz a chamada falhar com uma `TimeoutException` e interrompe essa thread |
| Rust | Sem timeout de ferramenta |

Uma chamada a um ID de ferramenta que não está na lista atual de ferramentas faz a execução falhar (`unknown tool` no Go, `Unknown tool '<name>'` no C#, PHP, Ruby e Java, `Unknown tool: <name>` como um `AgentError` no Rust). No TypeScript com `return-to-model`, uma ferramenta desconhecida ou argumentos ilegíveis recebem a mesma resposta de um erro de ferramenta.

## Exija aprovação

Defina a flag de aprovação da ferramenta como `true` ou dê a ela uma função que decide a cada chamada. Uma função de aprovação no nível do agente, quando a linguagem tem uma, vale para todas as ferramentas e se sobrepõe às duas. Uma chamada bloqueada pausa a execução com o status `waiting_for_human`; aprove-a ou rejeite-a pelo ID da chamada em [Estado e aprovação](https://phaseo.app/docs/pt-BR/v1/sdk-reference/agent/state-and-approval.md).

```typescript TypeScript theme={null}
const deploy = defineTool({
  id: "deploy",
  description: "Deploy the selected release",
  requireApproval: (input: { environment: string }) => input.environment === "production",
  execute: deployRelease,
});
```

```python Python theme={null}
deploy = define_tool(AgentTool(
    id="deploy",
    description="Deploy the selected release",
    require_approval=lambda tool_input, runtime: tool_input["environment"] == "production",
    execute=deploy_release,
))
```

```go Go theme={null}
deploy := phaseoagent.Tool{
  ID:          "deploy",
  Description: "Deploy the selected release",
  Approval: func(input any, runtime phaseoagent.RuntimeContext) (bool, error) {
    return input.(map[string]any)["environment"] == "production", nil
  },
  Execute: deployRelease,
}
```

```csharp C# theme={null}
var deploy = new Tool("deploy", DeployRelease, "Deploy the selected release")
{
    Approval = async (input, runtime) =>
        ((JsonElement)input!).GetProperty("environment").GetString() == "production",
};
```

```php PHP theme={null}
$deploy = new Tool(
    id: "deploy",
    execute: fn (array $input, $runtime) => $deployments->deploy($input),
    description: "Deploy the selected release",
    requireApproval: fn (array $input, $runtime) => ($input["environment"] ?? null) === "production",
);
```

```ruby Ruby theme={null}
deploy = PhaseoAgentSdk::Tool.new(
  id: "deploy",
  description: "Deploy the selected release",
  require_approval: ->(input, runtime) { input["environment"] == "production" },
  execute: ->(input, runtime) { deployments.deploy(input) }
)
```

```rust Rust theme={null}
let deploy = Tool::new(
    "deploy",
    "Deploy an application environment",
    json!({
        "type": "object",
        "properties": {
            "environment": { "type": "string" }
        },
        "required": ["environment"]
    }),
    |input, _context| {
        Ok(json!({
            "environment": input["environment"],
            "deployed": true
        }))
    },
)
.require_approval();
```

| Linguagem | Por ferramenta | Todas as ferramentas |
| - | - | - |
| TypeScript | `requireApproval: true` ou `(input, context) => boolean` | `requireApproval: (call, context) => boolean` no agente |
| Python | `require_approval=True` ou `(input, runtime) -> bool` | `"require_approval": (call, runtime) -> bool` no agente |
| Go | `RequireApproval: true` ou a função `Approval` | `AgentDefinition.RequireApproval(call, runtime)` |
| C# | `RequireApproval = true` ou a função `Approval` | `AgentDefinition.RequireApproval` |
| PHP | `requireApproval: true` ou `fn ($input, $runtime): bool` | `requireApproval` do `AgentDefinition` (`fn ($toolCall, $runtime): bool`) |
| Ruby | `require_approval: true` ou `->(input, runtime)` | `require_approval:` do agente (`->(tool_call, runtime)`) |
| Java | `requireApproval` `true` ou uma função `approval` no construtor completo | `AgentDefinition.requireApproval` (um `CallApproval`) |
| Rust | `.require_approval()`; sem função por chamada | Nenhuma. O tipo de pausa é `tool_approval`, e não há tipo de rejeição: deixe uma chamada não aprovada pausada ou aplique sua própria política de rejeição antes de retomar. |

## Passe uma chamada para uma pessoa ou outro sistema

Uma ferramenta manual não tem executor: seu aplicativo, uma fila ou outro serviço faz o trabalho. Uma ferramenta interativa executa um hook que pode pausar para uma pessoa. As duas pausam a execução até você continuá-la com uma saída de ferramenta para o ID da chamada; veja [Estado e aprovação](https://phaseo.app/docs/pt-BR/v1/sdk-reference/agent/state-and-approval.md).

| Linguagem | Ferramenta manual | Ferramenta interativa: pausa ao |
| - | - | - |
| TypeScript | `execute: false` | Retornar `null` de `onToolCalled` |
| Python | Deixe `execute` sem definir | Retornar `None` de `on_tool_called` |
| Go | Deixe `Execute` como nil (`Kind` pendente `manual`) | Retornar `completed = false` de `OnToolCalled` (`Kind` pendente `hitl`) |
| C# | Deixe `Execute` como null (`manual`) | Retornar `(Completed: false, Output: null)` de `OnToolCalled` (`hitl`) |
| PHP | `execute: null` (`manual`) | Retornar `null` de `onToolCalled` (`hitl`) |
| Ruby | Deixe `execute` sem definir (`manual`) | Retornar `nil` de `on_tool_called` (`hitl`) |
| Java | Passe `null` como executor (`manual`) | Retornar `new ToolCalledResult(false, null)` de `onToolCalled` (`hitl`) |
| Rust | `Tool::external(...)` (tipo de pausa `external_output`) | Sem suporte; use o `human_review` do agente |

Se o hook interativo retornar um valor, ele vira o resultado da ferramenta imediatamente. Depois que você continua, `onResponseReceived` (ou o equivalente da linguagem) pode validar ou transformar a saída fornecida antes que o validador de saída a verifique.

A ferramenta externa do Rust não recebe executor:

```rust theme={null}
let ticket_lookup = Tool::external(
    "ticket_lookup",
    "Load one support ticket",
    json!({
        "type": "object",
        "properties": {
            "ticket_id": { "type": "string" }
        },
        "required": ["ticket_id"]
    }),
);
```

Se um turno do modelo misturar ferramentas automáticas e pendentes, o runtime executa as automáticas antes de retornar a pausa.

## Relate o progresso e atualize o contexto

Uma ferramenta pode publicar resultados preliminares enquanto trabalha e substituir o contexto do aplicativo para os turnos seguintes. O progresso aparece como eventos `tool.preliminary_result` (veja [Streaming](https://phaseo.app/docs/pt-BR/v1/sdk-reference/agent/streaming.md)), verificados pelo validador de eventos quando você define um.

```typescript TypeScript theme={null}
const indexRepository = defineTool({
  id: "index-repository",
  async *execute(input: { path: string }, context) {
    yield { phase: "scan" };
    yield { phase: "embed" };
    context.setContext({ indexed: true });
    return { indexed: 248 };
  },
});
```

```python Python theme={null}
def index_repository(tool_input, runtime):
    runtime.emit_progress({"stage": "indexing"})
    runtime.set_context({"indexed": True})
    return "done"
```

```go Go theme={null}
Execute: func(input any, runtime phaseoagent.RuntimeContext) (any, error) {
  _ = runtime.EmitProgress(map[string]any{"stage": "indexing"})
  runtime.SetContext(map[string]any{"indexed": true})
  return "done", nil
},
```

```csharp C# theme={null}
async (input, runtime) =>
{
    await runtime.EmitProgress!(new { stage = "indexing" });
    runtime.SetContext!(new { indexed = true });
    return "done";
}
```

```php PHP theme={null}
execute: function (array $input, $runtime) {
    ($runtime->emitProgress)(["stage" => "indexing"]); // evento tool.preliminary_result
    ($runtime->setContext)(["indexed" => true]);       // contexto para os turnos seguintes
    return "done";
},
```

```ruby Ruby theme={null}
execute: ->(input, runtime) {
  runtime.emit_progress.call(stage: "indexing") # evento tool.preliminary_result
  runtime.set_context.call(indexed: true)       # contexto para os turnos seguintes
  "done"
}
```

```java Java theme={null}
(input, context) -> {
    context.emitProgress().receive(Map.of("stage", "indexing"), context); // evento tool.preliminary_result
    context.setContext().accept(Map.of("indexed", true));                 // contexto para os turnos seguintes
    return "done";
}
```

| Linguagem | Progresso | Contexto | Campos do contexto de runtime |
| - | - | - | - |
| TypeScript | Gerador assíncrono: faça `yield` do progresso e `return` do resultado; ou `context.emitProgress(value)`. Também fica guardado em `preliminaryResults` da etapa. | `context.setContext(value)` | `runId`, `agentId`, `stepIndex`, `context`, `signal`, `toolCall` |
| Python | `runtime.emit_progress(value)`, ou um gerador que faz yield do progresso e retorna o resultado | `runtime.set_context(value)` | `run_id`, `agent_id`, `step_index`, `context`, `tool_call` |
| Go | `runtime.EmitProgress(value)` | `runtime.SetContext(value)` | `RunID`, `AgentID`, `StepIndex`, `Context`, `ToolCall` |
| C# | `await runtime.EmitProgress(value)` | `runtime.SetContext(value)` | `RunId`, `AgentId`, `StepIndex`, `Context`, `CancellationToken`, `ToolCall` |
| PHP | `($runtime->emitProgress)(value)` | `($runtime->setContext)(value)` | `$runId`, `$agentId`, `$stepIndex`, `$context`, `$toolCall` |
| Ruby | `runtime.emit_progress.call(value)` | `runtime.set_context.call(value)` | `run_id`, `agent_id`, `step_index`, `context`, `tool_call` |
| Java | `context.emitProgress().receive(value, context)` | `context.setContext().accept(value)` | `runId`, `agentId`, `stepIndex`, `context`, `toolCall` |
| Rust | Sem suporte | Sem suporte; o contexto é definido por execução | `run_id`, ID do agente, índice da etapa, contexto do aplicativo, chamada de ferramenta |

## Recupere-se de erros de ferramenta

Por padrão, um erro de uma ferramenta faz a execução falhar e é relançado. Defina a política de erro da ferramenta como `return-to-model` para enviar o erro de volta como resultado da ferramenta (`{"error": "..."}`), para que o modelo tente outra coisa. Mantenha o padrão para falhas inseguras ou irrecuperáveis.

| Linguagem | Padrão | Defina `return-to-model` com |
| - | - | - |
| TypeScript | Faz a execução falhar | `onError` em uma ferramenta, ou `toolExecution.onError` para todas as ferramentas |
| Python | Faz a execução falhar | `on_error="return-to-model"`. Um timeout ainda faz a execução falhar. |
| Go | Faz a execução falhar | `OnError: "return-to-model"` |
| C# | Faz a execução falhar | `OnError = "return-to-model"` |
| PHP | Faz a execução falhar | `onError: "return-to-model"` |
| Ruby | Faz a execução falhar | `on_error: "return-to-model"` |
| Java | Faz a execução falhar | `"return-to-model"` como argumento `onError` |
| Rust | Devolve ao modelo | Sempre ativo: um `AgentError` de um executor vira uma mensagem estruturada de erro de ferramenta. Uma ferramenta desconhecida ou uma aprovação ou saída ausente faz a continuação falhar com `AgentError`. |

No TypeScript, `toolExecution.maxOutputChars` limita quanto da saída de uma ferramenta o modelo vê; uma saída mais longa mantém o início e o fim em volta de um aviso.

## Execute ferramentas em paralelo

Quando um turno do modelo chama várias ferramentas independentes, defina um limite de concorrência para executá-las ao mesmo tempo. Go, C#, Ruby e Java executam as ferramentas uma de cada vez até você fazer isso. Os resultados são anexados na ordem em que o modelo fez as chamadas, seja qual for a ordem em que terminam. No Go e no C#, se várias ferramentas concorrentes definirem o contexto, vale a última na ordem das chamadas.

| Linguagem | Configuração | Observações |
| - | - | - |
| TypeScript | `toolExecution: { toolConcurrency: 3 }` | Veja a [receita de ferramentas paralelas](https://phaseo.app/docs/pt-BR/v1/cookbook/agent-sdk-parallel-tools.md) |
| Python | `tool_execution=AgentToolExecutionConfig(tool_concurrency=3)` | Threads |
| Go | `ToolExecution: phaseoagent.ToolExecutionConfig{Concurrency: 4}` | No agente, ou em `RunOptions` para uma execução |
| C# | `ToolExecution = new ToolExecutionConfig(Concurrency: 4)` | No agente, ou em `RunOptions` para uma execução |
| PHP | Sem suporte | `toolExecution` é aceito, mas não tem efeito. Inicie trabalho paralelo dentro de uma ferramenta com as requisições concorrentes do seu cliente HTTP, ou coloque-o em uma fila e retorne o ID do job. |
| Ruby | `tool_execution: { tool_concurrency: 4 }` | Threads; em `create_agent`, ou em `run` para uma execução |
| Java | `new AgentSdk.ToolExecutionConfig(4)` | Pool de threads fixo; valores abaixo de 1 são elevados para 1. No agente, ou em `RunOptions` para uma execução. |
| Rust | Sem suporte | As ferramentas rodam de forma síncrona na ordem em que o modelo as chamou |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.