# Gatilhos

Um gatilho inicia uma sessão em um horário programado, por meio de um webhook, de um evento de aplicativo ou de um monitor.

Canonical page: https://beeuni.ai/docs/pt/connect/triggers

Um gatilho inicia uma [sessão](/docs/pt/work/sessions) sem a presença de uma pessoa. Use um gatilho para automatizar tarefas recorrentes ou acionadas por eventos.

Para dar continuidade à tarefa de uma sessão mais tarde — "verifique amanhã se responderam" — use um [lembrete](/docs/pt/connect/reminders). Um lembrete é um gatilho vinculado àquela sessão. Ele fica no banco de dados, não em `beeuni.yaml`.

<a id="trigger-types"></a>

## Tipos de gatilho

O Beeuni oferece suporte a quatro tipos de gatilho.

- **cron** — é executado conforme uma programação definida por você.
- **webhook** — é executado quando um serviço externo envia uma solicitação assinada para a URL de webhook do projeto.
- **event** — é executado quando algo acontece em um app conectado, como uma nova solicitação de pull ou um novo e-mail. Consulte [Gatilhos de eventos de app](#app-event-triggers).
- **monitor** — executa um comando do seu repositório 24 horas por dia, 7 dias por semana. Cada linha que o comando imprime em stdout dispara o gatilho. Experimental: consulte [Monitores](#monitors).

Você define os gatilhos no manifesto do projeto, `beeuni.yaml`. Cada gatilho contém um prompt que é exibido como a primeira mensagem da sessão iniciada. O estado de execução — como o horário do último disparo e o status — fica fora do manifesto, no banco de dados. Disparar um gatilho não cria um commit.

Criar, atualizar ou excluir um gatilho pelo dashboard grava diretamente na branch padrão. Isso não passa por uma [solicitação de alteração](/docs/pt/work/change-requests) (CR). Editar `beeuni.yaml` em uma sessão e executar `beeuni ship` segue o fluxo normal de branches e CRs.

<a id="set-up-a-cron-trigger"></a>

## Configurar um gatilho cron

### Adicione o gatilho
```sh
beeuni triggers add daily-digest --type cron \
--cron "0 0 9 * * 1-5" --timezone America/Los_Angeles \
--prompt "Summarize yesterday's activity and save it as a daily note."
```

`cron` é uma expressão de 6 campos: segundo, minuto, hora, dia, mês e dia da semana. Um gatilho é disparado no máximo uma vez por minuto: `0 */30 * * * *` significa a cada 30 minutos, e `*/30 * * * * *` (a cada 30 segundos) é recusado. Este comando edita apenas o `beeuni.yaml` local.

### Publique
```sh
beeuni ship
```

`beeuni ship` faz o commit de `beeuni.yaml` e o envia. A programação entra em vigor quando isso chega à branch padrão do projeto.

### Confirme que está funcionando
```sh
beeuni triggers ls
```

A lista mostra o slug e o estado de cada gatilho, além do horário do último disparo. Para dispará-lo agora, sem esperar pelo horário programado, execute `beeuni triggers fire daily-digest`. O comando aguarda o resultado da execução e retorna um código diferente de zero com o texto do erro quando ela falha; `--wait <dur>` limita o tempo de espera (padrão: `90s`).

<a id="set-up-a-webhook-trigger"></a>

## Configurar um gatilho de webhook

Um gatilho de webhook precisa de um segredo. O Beeuni o usa para verificar a assinatura de cada solicitação recebida.

### Adicione o segredo
```sh
beeuni secrets set WEBHOOK_SECRET=<a-random-value>
```

Consulte [Segredos](/docs/pt/project/secrets) para saber mais sobre segredos.

### Adicione o gatilho
```sh
beeuni triggers add new-lead --type webhook \
--secret-env WEBHOOK_SECRET \
--prompt "A new lead arrived: {{ body.name }} ({{ body.email }}). Add it to the CRM."
```

`--secret-env` indica o segredo que assina as solicitações para este gatilho.

### Publique
```sh
beeuni ship
```

<a id="send-it-a-request"></a>

### Envie uma solicitação

O Beeuni monta a URL do webhook usando o ID do projeto e o slug do gatilho:

```
POST /v1/webhooks/projects/<project-id>/<slug>
```

Envie uma solicitação `POST` assinada para esta URL pelo serviço externo. O Beeuni verifica a assinatura usando `WEBHOOK_SECRET` e inicia uma sessão com o corpo da solicitação disponível no prompt. Consulte [Assinatura de webhook](#webhook-signature) abaixo para ver o cabeçalho e o formato exatos.

Por padrão, cada disparo inicia uma sessão nova em uma nova branch. Em vez disso, um gatilho pode reutilizar ou fixar uma sessão, e um gatilho de webhook pode filtrar quais payloads iniciam uma sessão — consulte [Estratégia de sessão](#session-strategy) e [Modelagem de payload](#payload-templating) abaixo.

<a id="app-event-triggers"></a>

## Gatilhos de eventos de app

Um gatilho de evento executa um agente quando ocorre um evento em um app: uma solicitação de pull é aberta, um e-mail chega, o estado de um problema muda. Você não precisa configurar um webhook, um segredo ou uma assinatura. O Beeuni assina o evento por você, sem que você precise criar uma assinatura manualmente.

Um evento vem de uma **fonte de eventos**: um adaptador que entrega os eventos de um app. IDs como `GITHUB_PULL_REQUEST_CREATED` identificam os eventos compatíveis com essa fonte. Use o catálogo de eventos para selecionar um evento para seu conector.

<a id="set-up-in-the-web-app"></a>

### Configurar no app web

1. Abra o projeto e acesse **Triggers** e **New trigger**.
2. Escolha **App event**.
3. Escolha um app. Abaixo dele, escolha um conector e uma de suas contas compartilhadas, ou conecte outra conta. A conta marcada como **Default** é usada, a menos que você escolha outra.
4. Escolha um evento. Abaixo dele, aparece o formulário das configurações do evento, como o repositório.
5. Escreva o prompt e salve.

O gatilho entra em vigor assim que o selo mostra **Live**. Os outros selos são **Activating**, **Needs connection** e **Error**. Eles correspondem aos status `pending`, `needs_connection` e `error` abaixo.

<a id="see-every-app-event-trigger"></a>

### Veja todos os gatilhos de eventos de app

No app web, abra **Triggers** e escolha **App events**. A linha de filtros mostra **All**, **Schedules**, **App events** e **Webhooks**, cada um com uma contagem. A escolha fica no endereço como `?type=event`, para que você possa criar um link para ela. As linhas são agrupadas por app. Abaixo delas, **Apps with events** lista todos os apps que podem iniciar um agente, com primeiro lugar para os apps que o projeto já usa. Escolha um para abrir o formulário de novo gatilho com esse app selecionado.

A janela de um conector tem uma aba **Triggers** com os eventos de app daquele conector. A aba aparece apenas para apps que têm eventos.

Com a CLI:

```bash
beeuni triggers ls --type event                 # every app event trigger, grouped by app
beeuni triggers ls --connector github-work      # the app events on one connector
beeuni triggers ls --type event --json          # the same list as JSON
```

`--type` também aceita `cron`, `webhook` e `monitor`. `--connector` e `--type` podem ser combinados.

<a id="set-up-with-the-cli"></a>

### Configurar com a CLI

Um agente ou uma pessoa executa estas etapas na ordem. Cada etapa exibe o que a etapa seguinte precisa.

```sh
beeuni triggers events --apps
beeuni triggers events --app github
beeuni connectors add github --provider composio --app github --apply
beeuni connectors connect github --owner project
beeuni triggers events --connector github
beeuni triggers events --connector github --event GITHUB_PULL_REQUEST_CREATED
beeuni triggers add pr-review --type event \
  --connector github --event GITHUB_PULL_REQUEST_CREATED \
  --config repo=hekmat/api \
  --prompt "Review {{ event.data.html_url }}" --apply
beeuni triggers info pr-review
```

1. `triggers events --apps` lista os apps que podem disparar eventos. Os apps são agrupados por fonte de eventos (`source: composio`). Cada app lista seus conectores (perfis) e as contas compartilhadas deles. A coluna `STATE` mostra `connected` ou `needs account`; os apps sem conector aparecem em `No connector yet`.
2. `connectors add` adiciona o conector quando o app não tem nenhum. Ignore esta etapa quando `CONNECTOR` estiver definido.
3. `connectors connect <slug> --owner project` exibe um link. Uma pessoa o abre para conectar a conta compartilhada. Uma conta privada de um membro não é aceita. Execute o comando novamente para adicionar uma segunda conta compartilhada e, em seguida, dê um nome a ela com `connectors rename <id> <label>`.
4. `triggers events --connector <slug>` lista os eventos. Adicione `--event <TYPE>` para ver as configurações de um evento e suas variáveis `{{ event.data.* }}`. Leia a descrição de cada configuração: alguns eventos aceitam o nome completo em uma única configuração, como `repo: hekmat/api`, e recusam o mesmo valor dividido entre `owner` e `repo`.
5. `triggers add --apply` cria o gatilho imediatamente e exibe seu status e a próxima etapa. Se uma configuração estiver ausente ou for inválida, o comando retorna o código 2 e lista todos os problemas. Sem `--apply`, a CLI grava o bloco no `beeuni.yaml` local; execute `beeuni ship` para aplicá-lo.
6. `triggers info <slug>` mostra o status. Repita até que ele mostre `live`.

`--source <adapter>` define a fonte de eventos e é opcional: sem ela, o gatilho usa o provedor do conector. O conector precisa pertencer a essa fonte. Uma fonte `composio` em um conector de outro provedor mostra `error`: `Connector "<slug>" is a <provider> connector; source "composio" needs a composio connector.` Uma fonte desconhecida é recusada com `Unknown event source "<x>". Sources: composio.` `beeuni triggers set <slug> --source <adapter>` altera a fonte; alterar `--connector` a limpa.

`--config key=value` se repete uma vez por configuração e é convertido para o tipo da configuração. `--config-json '{"limit": 5}'` passa valores tipados. `beeuni triggers set <slug> --config key=value` mescla com a configuração ativa; `--config-json` a substitui. Não passe ambos. Um gatilho de evento não aceita nenhum destes: `--cron`, `--run-at`, `--timezone`, `--secret-env`, `--run`, `--mode`, `--interval` ou `--expect-event-within`.

<a id="config-shape"></a>

### Estrutura da configuração

```yaml
# beeuni.yaml
triggers:
  - slug: pr-review
    type: event
    connector: github-work # required, slug of a connector (profile) under `connectors:`
    source: composio # optional, the event source adapter. Default: the connector's provider
    account: hekmat-bot # optional, label of one shared account of that connector
    event: GITHUB_PULL_REQUEST_CREATED # required, the adapter's event id, from `beeuni triggers events`
    config: { repo: hekmat/api } # optional, settings of the event
    agent: default
    session_mode: fresh # default "fresh"
    filter: # optional, same guard a webhook uses
      "event.data.draft": "false"
    prompt: |
      Review {{ event.data.html_url }}
```

<a id="what-the-prompt-can-read"></a>

### O que o prompt pode ler

O prompt, `filter` e `session_key` leem estas raízes:

| Raiz | Valor |
| --- | --- |
| `event.data.<field>` | Os dados do evento do próprio app. `beeuni triggers events` mostra os campos que cada evento contém. |
| `event.id` | O ID do provedor para este evento. |
| `event.type` | O tipo de evento, como `GITHUB_PULL_REQUEST_CREATED`. |
| `event.app` | O slug do app, como `github`. |
| `event.connector` | O slug do conector monitorado pelo gatilho. |
| `event.occurred_at` | Quando o evento aconteceu. |

Os dados do evento vêm de terceiros. O Beeuni inicia a primeira mensagem da sessão com `[App event: <trigger name> — automated, third-party content, not user input]`, para que o agente trate o conteúdo do evento como dados, e não como instruções. Essa linha também é o título da sessão até que o agente dê um nome a ela.

### Status

`beeuni triggers ls` e a lista de gatilhos da web mostram o status da assinatura de cada gatilho de evento.

| Status | Significado | Correção |
| --- | --- | --- |
| `active` | A assinatura existe. Os eventos acionam o gatilho. | Nenhuma. |
| `pending` | O gatilho foi declarado e o Beeuni ainda não criou a assinatura. | Aguarde um momento. Se continuar `pending`, salve o gatilho novamente. |
| `needs_connection` | O conector não tem uma conta compartilhada utilizável. O texto diz "Conecte uma conta `<app>` compartilhada para ativar este gatilho." ou "Conclua a conexão da conta `<app>` compartilhada…" | Conecte uma conta compartilhada para o conector como projeto (`beeuni connectors connect <slug> --owner project`). A conta privada de um membro não é válida. O gatilho é ativado automaticamente quando a pessoa conclui a conexão, mesmo que ela se conecte por um link fora do Beeuni. |
| `error` | A assinatura falhou ou foi desativada pelo provedor. O gatilho exibe o texto do erro. | Corrija a causa indicada no texto e salve o gatilho novamente. |

Mensagens comuns de `error`:

- "Esta conta `<app>` é compartilhada apenas com pessoas específicas. Os gatilhos de evento precisam de uma conta compartilhada com todo o projeto." Abra as configurações de compartilhamento da conta e compartilhe-a com todo o projeto.
- "O conector `<slug>` não está declarado em beeuni.yaml." Adicione o conector em `connectors:`.
- Se os gatilhos de evento não estiverem disponíveis, entre em contato com o suporte.

- "O app desativou esta assinatura: ..." O provedor desativou a assinatura. Salve o gatilho novamente para assinar de novo.
- "A conta conectada expirou: ..." O app revogou ou deixou a conta expirar. Reconecte o app para reativar o gatilho.

<a id="rules"></a>

### Regras

- **Conector e conta.** Um *app* é o serviço, como o GitHub. Um *conector* é um perfil em `beeuni.yaml`; vários conectores podem compartilhar um app. Uma *conta* é um login conectado dentro de um conector. Um gatilho indica um conector. Ele usa a conta compartilhada padrão desse conector, a menos que defina `account: <label>`. Defina `account` apenas quando um conector tiver várias contas compartilhadas. Na CLI, `--account <label>` define esse valor e `--default-account` (em `triggers set`) o remove. Um rótulo que não corresponda a nenhuma conta compartilhada resulta em `needs_connection`, com o texto "Conecte uma conta `<app>` compartilhada com o rótulo "`<label>`" em `<connector>`."
- **Uma conta de origem.** Apenas uma conta compartilhada com o projeto e acessível a todo o projeto pode alimentar um gatilho. Um gatilho é executado sem ninguém presente, e os dados do evento são inseridos em um prompt de sessão. Uma conta compartilhada apenas com pessoas específicas ou uma conta privada nunca pode alimentá-lo.
- **Cada evento é acionado uma vez.** Um gatilho é acionado uma vez por evento do provedor. A reentrega de um evento não inicia uma segunda sessão. Dois gatilhos para o mesmo evento são acionados uma vez cada.
- **Pausar interrompe.** Um gatilho pausado ou um projeto pausado ignora os eventos. Os eventos ignorados não ficam na fila.
- **As edições criam uma nova assinatura.** Alterar `connector`, `source`, `account`, `event` ou `config` substitui a assinatura. Excluir o gatilho o remove.

<a id="monitors"></a>

## Monitores

**Experimental.** Os monitores só são executados quando a flag de recurso `monitors` está ativada. Por padrão, a flag fica desativada. Enquanto estiver desativada, a plataforma não provisiona uma caixa de monitor nem dispara eventos de monitor.

Um monitor observa algo que não envia webhooks nem se encaixa em um agendamento: um log em tempo real, a profundidade de uma fila, uma página que muda, um preço. O Beeuni executa seu comando 24 horas por dia, 7 dias por semana, na caixa de monitor do projeto — uma microVM persistente por projeto, com o mesmo limite de isolamento e os mesmos segredos do projeto que um sandbox de sessão recebe. Código determinístico monitora; o agente é ativado apenas quando o comando emite uma linha.

Quatro regras definem o contrato:

- **Linhas de stdout são eventos. Nada mais é.** Stderr contém diagnósticos: visíveis nos logs do monitor, nunca disparam eventos.
- Cada linha dispara o gatilho exatamente uma vez, pelo caminho já usado por um webhook: `filter` → modelo de prompt → `session_mode`.
- **Um monitor não pode falhar silenciosamente.** A saída do processo, o esgotamento do limite de reinicializações e o silêncio por mais de `expect_event_within` disparam, cada um, um evento de ciclo de vida escrito pela plataforma no mesmo fluxo.
- `session_mode` usa `reuse` por padrão em um monitor, não `fresh`. Um monitor é projetado para disparar repetidamente, então `fresh` criaria uma sessão por evento.

### Adicionar o monitor
```sh
beeuni triggers add checkout-errors --type monitor \
--run "./monitors/checkout-errors.ts" \
--mode poll --interval 60s --expect-event-within 24h \
--prompt "Checkout monitor emitted: {{ line }}"
```

`--mode poll` executa novamente o comando a cada `--interval` e espera que ele seja encerrado. `--mode stream` executa o comando uma vez e o mantém ativo; um fluxo não aceita `--interval`. Ambos os formatos produzem linhas, e nenhum componente posterior consegue distingui-los. `cron`, `run_at`, `timezone` e `secret_env` não são aceitos em um monitor.

### Publique
```sh
beeuni ship
```

A plataforma inicia a caixa do monitor quando isso chega à ramificação padrão.

### Confirme que está em execução
```sh
beeuni triggers ls
beeuni triggers info checkout-errors
```

`ls` mostra o modo e o intervalo de um monitor, enquanto um cron mostra a programação. `info` mostra `run`, `mode`, `interval` e `expect_event_within`.

<a id="monitor-limits"></a>

### Limites dos monitores

Todos os limites abaixo são impostos pela plataforma.

| Limite | Valor |
| --- | --- |
| Monitores por projeto | 10 ativados |
| Intervalo de consulta | pelo menos 30s |
| `expect_event_within` | pelo menos 5m |
| Taxa de eventos por monitor | 60/hora de forma contínua, rajada de 30. O excedente suprime o monitor por 10 minutos; 3 supressões em 24 horas o desativam. |
| Tamanho da linha | 8 KiB, truncada com um marcador `truncated: true` |
| Limite de reinicializações | 5 reinicializações / 10 minutos; depois, um evento de ciclo de vida `restart_budget_exhausted` e uma espera de 15 minutos |
| Retenção de eventos | 30 dias |
| Orçamento mensal da caixa | US$ 75 por padrão. Ao excedê-lo, a caixa é interrompida e um evento de ciclo de vida `budget_exceeded` é disparado. |

A caixa do monitor precisa de um provedor compatível com um sandbox persistente. Se o provedor do projeto não puder manter um, a flag `monitors` indicará que o recurso está indisponível.

<a id="config-shape-1"></a>

## Estrutura da configuração

```yaml
# beeuni.yaml
triggers:
  - slug: daily-digest # required, lowercase + dashes, unique per project
    name: Daily digest # optional, defaults to slug
    type: cron # "cron" | "webhook" | "event" | "monitor", required
    agent: beeuni # optional, defaults to "default"
    model: anthropic/claude-sonnet-4-5 # optional, resolves at fire time if unset
    enabled: true # optional, default true
    cron: "0 0 9 * * 1-5" # 6-field expression, mutually exclusive with run_at
    timezone: America/Los_Angeles # IANA name, default UTC
    session_mode: reuse # "fresh" | "reuse" | "pinned" | "keyed", default "fresh"
    filter: # optional, webhook payload guard
      "body.data.direction": "inbound"
    prompt: "Summarize {{ body.text }}" # required, template string
```

Um monitor substitui os campos de agendamento pelo comando e pelo formato:

```yaml
# beeuni.yaml
triggers:
  - slug: checkout-errors
    type: monitor
    run: ./monitors/checkout-errors.ts # required, repo-relative command
    mode: poll # "poll" | "stream", required
    interval: 60s # required on poll, invalid on stream
    expect_event_within: 24h # optional silence watchdog
    agent: oncall
    session_mode: reuse # the monitor default
    filter: # optional, same guard a webhook uses
      "line.severity": "error"
    prompt: "Checkout monitor emitted: {{ line }}"
```

Um gatilho de evento substitui os campos de agendamento pelo app e pelo evento monitorado. Consulte [Gatilhos de evento de app](#app-event-triggers).

`beeuni.toml` legado usa os mesmos campos em um contêiner diferente; consulte [TOML legado](/docs/pt/project/legacy-toml).

<a id="fields"></a>

## Campos

| Campo | Obrigatório | Padrão | Observações |
| --- | --- | --- | --- |
| `slug` | sim | — | `[a-z0-9][a-z0-9_-]{0,127}`, exclusivo por projeto. |
| `type` | sim | — | `cron`, `webhook` ou `monitor`. |
| `prompt` | sim | — | String de modelo. É exibida como a primeira mensagem da sessão. |
| `name` | não | `slug` | Rótulo legível. |
| `agent` | não | `default_agent` | Deve corresponder a uma chave em `agents:`. Omita para usar `default_agent`; não escreva o literal `default`. |
| `model` | não | resolvido no momento do disparo | Formato de transmissão `provider/model`, por exemplo `anthropic/claude-sonnet-4-5`. |
| `enabled` | não | `true` | Quando `false`, o agendador e o receptor de webhook ignoram a entrada. |
| `session_mode` | não | `fresh` ou `reuse` em um monitor | Consulte [Estratégia de sessão](#session-strategy). |
| `session_id` | obrigatório para `pinned` | — | Sessão exata a ser solicitada novamente. |
| `session_key` | obrigatório para `keyed` | — | String de modelo. Defini-la por si só implica `session_mode: keyed`. |
| `filter` | não | — | Caminho pontuado → string esperada. Uma entrega de webhook que não corresponda retorna `200` e não dispara nenhuma sessão. |
| `cron` | um de `cron`/`run_at`, em `type: cron` | — | Expressão de 6 campos: segundo minuto hora dia mês dia da semana. Dispara no máximo uma vez a cada 60 segundos; uma expressão mais rápida falha em `beeuni validate` e na API. |
| `run_at` | um de `cron`/`run_at`, em `type: cron` | — | Carimbo de data/hora ISO-8601. Dispara uma vez e depois fica inativo. |
| `timezone` | não, somente cron | `UTC` | Nome IANA. |
| `secret_env` | obrigatório, somente webhook | — | Nome de um segredo do projeto que contém a chave de assinatura. O segredo deve usar a entrega `broker` com o consumidor `connector`. |
| `run` | obrigatório, somente monitor | — | Comando relativo ao repositório cujas linhas de stdout são os eventos. Uma linha, no máximo 1024 caracteres. |
| `mode` | obrigatório, somente monitor | — | `poll` executa `run` novamente a cada `interval`; `stream` executa uma vez e o mantém ativo. |
| `interval` | obrigatório para `mode: poll` | — | Duração literal (`30s`, `5m`, `24h`, `7d`), mínimo de `30s`. Inválido em `mode: stream`. |
| `expect_event_within` | não, somente monitor | — | Duração literal, mínimo de `5m`. Um período de silêncio superior a esse valor dispara um evento de ciclo de vida. |

Um gatilho cron precisa de `cron` ou `run_at`, nunca de ambos. Um gatilho de webhook sem `secret_env` é rejeitado — não há webhook sem autenticação. Um monitor precisa de `run` e `mode`, e rejeita `cron`, `run_at`, `timezone` e `secret_env` sem exceção — um manifesto que declara um agendamento que o executor do monitor nunca lê é uma mentira.

Configure o segredo de assinatura antes de criar ou atualizar um gatilho de webhook:

```bash
beeuni secrets set WEBHOOK_SECRET=-
beeuni secrets delivery WEBHOOK_SECRET broker --consumer connector
```

Passe o valor para o primeiro comando pela entrada padrão. Se o segredo usava anteriormente a entrega sandbox, alterne-o após mudar o tipo de entrega, pois uma sandbox existente pode manter o valor anterior.

<a id="session-strategy"></a>

## Estratégia de sessão

`session_mode` controla qual sessão é solicitada novamente quando ocorre um disparo. Beeuni tenta os modos abaixo na ordem indicada e, se cada etapa falhar, passa para a próxima.

1. **`pinned`** — solicita novamente a `session_id` exata. Se essa sessão não existir mais ou tiver falhado, passa para a próxima opção.
2. **`keyed`** — renderiza `session_key` com base no payload e procura a sessão mais recente, sem falhas, identificada anteriormente com essa chave exata. Se a chave renderizada ficar vazia ou nenhuma sessão corresponder, passa para uma nova sessão. Nunca passa para a sessão de outra chave.
3. **`reuse`** — solicita novamente a sessão mais recente, sem falhas, criada anteriormente por esse gatilho. Um gatilho fixado também recorre a essa opção antes de continuar.
4. **`fresh`** — cria uma nova sandbox e ramificação. Esse é o padrão e a última alternativa para todos os modos. A nova sessão passa a ser a sessão do gatilho para futuros disparos de `reuse` e `keyed`.

Uma sessão `reuse` ou `keyed` cujo histórico não caiba mais no modelo, mesmo após a compactação automática, é aposentada. O próximo disparo inicia uma sessão nova. Qualquer outra falha mantém a sessão para o próximo disparo.

Um gatilho cron frequente que não deve reenviar um histórico cada vez maior pode renovar a sessão na virada do calendário: `session_key: "merge-{{ cron.scheduled_date }}"` mantém uma sessão serializada por dia UTC, e `{{ cron.scheduled_hour }}` mantém uma por hora UTC.

<a id="failed-runs"></a>

## Execuções com falha

Quando uma execução iniciada por um gatilho termina com erro, Beeuni registra isso no gatilho:

- `last_status` passa a ser `failed` e `last_error` contém o motivo, por exemplo `Out of credits: Payment Required: Insufficient credits.`
- A página Agendamento marca o gatilho como "A última execução não foi concluída" e exibe o motivo no painel.
- O proprietário da conta recebe uma notificação push no celular quando a primeira execução falha. Uma falha posterior atualiza o motivo sem enviar outra notificação push.
- O gatilho permanece `failed` enquanto continua disparando. Um novo disparo não comprova que a execução foi bem-sucedida.

A próxima execução concluída elimina a falha. Uma execução interrompida por você, um subagente e uma chamada de modelo repetida não contam como resultados de execução. Uma falha antes do início da execução, por exemplo, quando não é possível criar uma sessão, é eliminada no próximo disparo bem-sucedido.

<a id="session-access"></a>

## Acesso à sessão

Por padrão, as sessões criadas por um gatilho usam a política privada. A conta de serviço do agente do gatilho é a proprietária — a identidade de um agente é um principal `service_account`, portanto pode possuir uma sessão e ter atribuições como qualquer outro principal. Um gerente do projeto sempre pode abri-las. O proprietário da conta e um administrador da conta têm acesso equivalente ao de gerente em todos os projetos, portanto o mesmo se aplica a eles. A pessoa que configurou ou acionou manualmente o gatilho não obtém acesso por essa ação, a menos que tenha uma dessas funções.

As configurações do gatilho oferecem três políticas:

- **Agente do gatilho e gerentes do projeto** — nenhum membro comum do projeto pode abrir a sessão.
- **Colegas de equipe selecionados** — o agente do gatilho, os gerentes do projeto e os membros do projeto ou grupos de contas selecionados.
- **Projeto inteiro** — todos os membros do projeto.

Essa política é uma configuração de visibilidade por recurso, aplicada sobre o modelo de funções, não uma função. Ela determina quem pode abrir as sessões de um gatilho. Não concede nenhuma permissão negada pela decisão da função. Consulte [Contas e acesso](/docs/pt/accounts#per-feature-access-settings).

A política de acesso é um estado de execução local à conta. Ela não faz parte do manifesto `beeuni.yaml` portátil porque os IDs dos principais pertencem a uma única conta. Use o painel para configurá-la.
Atualizar somente essa política não cria nenhum commit do Git. Salvar uma política também atualiza as sessões anteriores criadas por esse gatilho.

Uma sessão fixada mantém suas próprias configurações de compartilhamento porque não foi criada pelo gatilho. Se uma sessão fixada estiver indisponível e o gatilho criar uma sessão alternativa, a política do gatilho se aplicará a essa nova sessão.

<a id="payload-templating"></a>

## Modelagem de payload

`prompt` e `session_key` são renderizados com o mesmo mecanismo: `{{ token.dotted.path }}`. Um valor ausente é renderizado como uma string vazia — sem erro nem `{{ }}` restante. Objetos e arrays são renderizados como JSON. `session_key` é removido dos espaços em branco nas extremidades e truncado em 512 caracteres.

Cada disparo também recebe `{{ trigger.slug }}`, `{{ trigger.type }}` e `{{ trigger.kind }}` (sempre `git`). O restante do conjunto de variáveis depende de como o gatilho foi acionado.

| Origem | Variáveis |
| --- | --- |
| cron | `{{ cron.schedule }}`, `{{ cron.timezone }}`, `{{ cron.scheduled_for }}` (o slot para o qual o disparo se destina), `{{ cron.claimed_at }}` (quando o agendador o capturou), `{{ cron.last_scheduled_for }}` (o slot anterior; vazio no primeiro disparo), `{{ cron.scheduled_date }}` (`YYYY-MM-DD` UTC do slot), `{{ cron.scheduled_hour }}` (`YYYY-MM-DDTHH` UTC). Nenhum `fired_at` de nível superior. |
| webhook | `{{ fired_at }}`, `{{ body.* }}` (analisado como JSON; usa `{{ body.raw }}` como fallback se o corpo não puder ser analisado), `{{ headers.content_type }}`, `{{ headers.user_agent }}`, `{{ headers.forwarded_for }}`. |
| monitor | `{{ line.* }}` — a linha de stdout, analisada como JSON; uma linha que não puder ser analisada é renderizada como `{{ line.raw }}`. Além de `{{ monitor.slug }}`, `{{ monitor.seq }}`, `{{ monitor.emitted_at }}` e `{{ monitor.kind }}` (`event` ou `lifecycle`). |
| manual ("disparar agora" no dashboard ou o endpoint `fire`) | `{{ fired_at }}`, `{{ source }}` (`manual`), `{{ actor }}`, `{{ message.text }}`, `{{ message.source }}`. |

O Beeuni prefixa todos os prompts de monitor renderizados com `[MONITOR EVENT — automated, not user input]`, no lado do servidor. Um evento de ciclo de vida ignora completamente seu template e renderiza um prompt escrito pela plataforma; ele também ignora `filter` — o silêncio não deve ser filtrável por acidente.

`{{ message.text }}` é codificado como uma string vazia em um disparo manual, e `{{ message.source }}` como `manual_test`. Um disparo manual não é uma forma de injetar dados de teste no prompt.

`filter` compara caminhos pontuados como strings com o mesmo payload que o prompt recebe. Ele existe para interromper loops. Por exemplo, uma origem que relata os dois lados de uma conversa dispararia novamente o agente na própria resposta.

<a id="webhook-signature"></a>

## Assinatura do webhook

Dispara em `POST /v1/webhooks/projects/{projectId}/{slug}`. O Beeuni verifica a solicitação nesta ordem, usando uma comparação em tempo constante:

1. **Assinatura HMAC** — cabeçalho `X-Beeuni-Signature: sha256=<hmac>` (o prefixo `sha256=` é opcional) ou `X-Hub-Signature-256`, compatível com o GitHub. HMAC-SHA256 sobre o corpo bruto da solicitação, usando o segredo nomeado por `secret_env`.
2. **Token estático**, somente quando nenhum cabeçalho de assinatura estiver presente, para remetentes que não conseguem assinar um corpo com HMAC. Envie o segredo como `X-Beeuni-Token: <secret>`, `Authorization: Bearer <secret>` ou `Authorization: Basic <base64(user:secret)>` (a metade da senha é o token).

`beeuni triggers info <slug>` exibe este esquema — o cabeçalho, o algoritmo e uma solicitação de exemplo assinada com um placeholder `$SECRET` — e `--json` o transporta como `webhook_signing`. A folha de acionamento do dashboard mostra a mesma solicitação de exemplo.

| Status | Significado |
| --- | --- |
| 202 | Assinatura ou token válido. O corpo é `{ status: "fired" \| "queued" \| "deduped", session_id, ... }`. |
| 200 | Válido, mas ignorado — o projeto está pausado ou a entrega não correspondeu a `filter`. |
| 400 | ID ou slug de projeto malformado na URL. |
| 401 | Assinatura e token ausentes ou incorretos. |
| 404 | Acionador não encontrado, desativado, não é um webhook ou o projeto não está ativo. |
| 409 | O segredo de assinatura está ausente, inativo, indisponível ou não autoriza o consumidor `connector`. A resposta inclui um código `webhook_secret_*` e instruções de correção. |
| 500 | A autenticação passou, mas a sessão não conseguiu disparar. |

## Endpoints

| Método + caminho | Necessário | Observações |
| --- | --- | --- |
| `GET /v1/projects/{projectId}/triggers` | `project.trigger.read` | Lista acionadores, estado de execução (`last_fired_at`, `last_status`, `last_error`, `last_attempt_at`) e erros de análise do manifesto. Uma entrada inválida aparece em `errors[]`; ela não interrompe os outros acionadores. |
| `POST /v1/projects/{projectId}/triggers` | `project.trigger.create` | Cria um acionador. Faz commit diretamente no manifesto. |
| `PATCH /v1/projects/{projectId}/triggers/{slug}` | `project.trigger.update` | Atualização parcial, mesclada à entrada atual. |
| `DELETE /v1/projects/{projectId}/triggers/{slug}` | `project.trigger.delete` | Também limpa o estado de execução do acionador. |
| `PATCH /v1/projects/{projectId}/triggers/activation` | `project.trigger.update` | Corpo `{ paused: boolean }`. Consulte [Pausar e retomar](#pause-and-resume). |
| `POST /v1/projects/{projectId}/triggers/{slug}/fire` | `project.trigger.fire` | Disparo manual. A função integrada de projeto `member` detém `project.trigger.fire`, portanto um membro comum pode disparar um acionador. |
| `POST /v1/webhooks/projects/{projectId}/{slug}` | assinatura ou token | URL pública, protegida pelo segredo do webhook. |

<a id="pause-and-resume"></a>

## Pausar e retomar

Uma chave no nível do projeto interrompe todos os acionadores do projeto de uma só vez, independentemente do campo `enabled` de cada acionador. Enquanto pausado, o agendador ignora o projeto e os webhooks recebidos retornam `200` com `{ status: "skipped" }` — nenhuma sessão é disparada. Um disparo manual continua funcionando. Use isso quando o mesmo repositório for executado em dois planos de controle (por exemplo, desenvolvimento e produção), para que o cron não dispare duas vezes. CLI: `beeuni triggers pause` e `beeuni triggers resume`. Consulte [CLI](/docs/pt/cli) para o grupo completo de comandos `beeuni triggers`.

<a id="limits-and-reliability"></a>

## Limites e confiabilidade

- O agendador consulta aproximadamente a cada segundo (1.000 ms por padrão; configurável via `BEEUNI_TRIGGER_SCHEDULER_INTERVAL_MS`). Uma execução do cron não começa exatamente no slot: cada acionador recebe um deslocamento fixo de até um quarto do período, no máximo 30 minutos, para que muitos acionadores no mesmo minuto não comecem ao mesmo tempo. Os detalhes do acionador e `GET /triggers` (`next_fire_at`) mostram o horário em que a próxima execução começa.
- Um disparo que falha define o último status do acionador como failed, com o erro. Isso vale para todas as origens: cron, manual, webhook, evento de aplicativo e monitor. O próximo disparo bem-sucedido limpa um disparo com falha. Uma execução com falha é limpa conforme descrito em [Execuções com falha](#failed-runs).
- Cada projeto permite, por padrão, 3 sessões acionadas em provisionamento simultaneamente. Um disparo que ultrapasse esse limite retorna `queued` (`202`) em vez de falhar e é executado quando uma delas termina o provisionamento. Não há limite para quantas sessões podem ser executadas simultaneamente.
- Um disparo manual ou de webhook tem um tempo limite de 45 segundos. O carregamento do manifesto tem um tempo limite de 30 segundos.
- Um disparo de cron é associado ao slot de agendamento devido, portanto um disparo que atingiu o tempo limite, mas foi efetivamente concluído, não é duplicado na nova tentativa.
- Uma entrega de webhook é executada uma vez por evento. O Beeuni lê o ID do evento de `X-Beeuni-Delivery-Id`, `X-GitHub-Delivery`, `X-Gitlab-Event-UUID`, `Linear-Delivery`, `webhook-id` ou `svix-id`, nessa ordem; uma nova tentativa com o mesmo ID responde `202 { status: "deduped" }` e não executa nada. `X-Request-Id` não é um ID de evento. Sem um ID de evento, um corpo e uma assinatura idênticos dentro de 10 minutos contam como o mesmo evento; depois disso, ele é executado novamente, portanto um job que publica o mesmo corpo estático em cada implantação é executado em cada implantação. Envie `X-Beeuni-Delivery-Id` com um novo valor por evento (a solicitação de exemplo usa `$(uuidgen)`).
- Uma entrega cuja tentativa anterior falhou definitivamente (por exemplo, a conta ficou sem créditos) ou cuja sessão foi excluída é executada novamente quando reenviada com o mesmo ID. Uma duplicata não altera `last_fired_at`.
