# Referência de API

## Entry points

| Import | Conteúdo |
| --- | --- |
| `hobots` | `hobots` (default e named), `register`, `start`, `close`, `captureException`, `retry`, `image` e os tipos. |
| `hobots/protocol` | Tipos do wire format (telemetria e execuções). |

ESM e CommonJS (`const { hobots } = require('hobots')`), com `.d.ts`. Depois do `register()`/`start()`, `globalThis.hobots` também existe (tipado via `declare global`).

## Funções do processo

| Função | Assinatura | Notas |
| --- | --- | --- |
| `hobots.register` | `<P>(handler: RunHandler<P>, options?: RegisterOptions) => void` | Agente sob demanda. Síncrona: valida handler/secret e lança na hora. O boot roda num microtask. **Uma chamada por processo** (register ou start) — a segunda lança `o agente já foi iniciado`. |
| `hobots.start` | `(handler: RunHandler, options?: StartOptions) => void` | Agente sempre ligado: abre a própria execução e roda o handler uma vez. Mesma regra de uma chamada por processo. |
| `hobots.close` | `(timeoutMs = 30_000) => Promise<boolean>` | Para o polling, espera as execuções em voo (até `timeoutMs`; as restantes viram `AgentTerminated`), para o heartbeat e faz o flush final. `false` se estourou. Um `register`/`start` depois inicia um runtime novo. |
| `hobots.captureException` | `(error: unknown, hint?: CaptureHint) => string \| undefined` | Issue no módulo Issues; devolve o `event_id`. Antes de iniciar o agente: descarta com aviso. |
| `hobots.retry` | `<T>(fn: (attempt: number) => T \| Promise<T>, options?: RetryOptions) => Promise<T>` | async-retry; dentro de uma execução usa o signal dela. |
| `hobots.image` | `(input: AttachInput, options?: ImageOptions) => ImageAttachment` | Embrulha um print para ir junto de um log, item ou step. Puro, nunca lança. |
| `isFileRef` / `hobots.isFileRef` | `(value: unknown) => value is FileRef` | Reconhece um arquivo de campo de upload nos params. Puro. |

## Contexto da execução (`RunContext` — os membros de `hobots` e o `ctx` do handler)

| Membro | Tipo | Notas |
| --- | --- | --- |
| `runId` | `string` | Identidade da execução (`''` fora de uma execução) |
| `params` | `Record<string, unknown>` | Params da solicitação (formulário do agente); `{}` no modo start |
| `log` | `RunLogger` — `debug/info/warn/error/success(message, ...rest)` | Nunca lança. Vários argumentos viram texto unidos por espaço (como `console.log`); um `hobots.image(...)` no **último** argumento vira o print da linha. Eco no terminal: `[hobots] INFO …` |
| `items` | `RunItems` — `add(item)`, `succeeded(payload?, opts?)`, `failed(message, payload?, opts?)`, `occurrence(message, payload?, opts?)`; `opts.id` = identidade de negócio, `opts.image` = print do item. `each(list, (element, item, index) => …, { id?, payload?, op?, description?, progress? }) => Promise<{ total, succeeded, failed, occurrence }>` processa a lista inteira: exceção no handler vira item `failed` + step em erro e o loop segue; `item: RunItemHandle` = `succeeded(payload?, { image }?)/failed(msg, payload?, { image }?)/occurrence(…)`, `id`, `index`, `step`, `reported` | Ponto de parada cooperativo (o `each` para só em cancelamento/timeout) |
| `progress` | `RunProgressReporter` — `total(n)`, `advance(count = 1)`, `set(current, total?)`, `current`, `snapshot` | `total/advance/set` são pontos de parada |
| `files` | `RunFiles` — `list(): FileRef[]`, `get(field): FileRef \| FileRef[] \| undefined`, `download(refOrField: FileRef \| string, { dest? }?): Promise<string>`, `downloadAll(field, { dest? }?): Promise<string[]>`, `read(refOrField): Promise<Buffer>` | Arquivos do formulário; o download consome a origem (S3 confirmado/apagado, pasta do Studio removida). Cópias sem `dest` são apagadas no fim da execução. Respeita o `signal`. Ver [Execuções](./runs.md#arquivos-do-formulário-hobotsfiles) |
| `flush` | `(timeoutMs = 15_000) => Promise<void>` — envia já as steps raiz finalizadas (transactions parciais, op `hobots.run.flush`) e os itens/logs/progresso pendentes | Nunca lança |
| `transaction` | `Transaction` (`run`, op `hobots.run`) — `startChild(op?, description?)`, `flush()`, `setTag`, `setStatus`, `setName`; `Step` — `startChild`, `setStatus('ok' \| 'warning' \| 'error', message?, { image }?)`, `setError(err, message?, { image }?)` (motivo em `data.error`, print em `data.attachments`, exibidos na Central), `setData(k, v)`, `finish()` | Finalizada pelo SDK; `startChild` é ponto de parada |
| `signal` | `AbortSignal` | Cancelamento (app, timeout, shutdown) |
| `throwIfCanceled()` | `() => void` | Lança `RunCanceled` se o signal disparou |
| `sleep(ms)` | `(ms: number) => Promise<void>` | Rejeita com `RunCanceled` no cancelamento |
| `retry(fn, options?)` | como `hobots.retry` | |

`AttachInput = Uint8Array \| ArrayBuffer \| string` (caminho); `ImageOptions = { caption?, contentType? }`; `ImageInput = AttachInput \| ImageAttachment` é o que `log.*` (só o embrulho), `items.*` e `step.setStatus/setError` aceitam. O upload (segundo plano, nunca bloqueia) acontece quando o evento é registrado.

Fora de uma execução, `hobots.*` cai em fallbacks seguros com aviso único por membro (log ecoa no terminal, imagem descartada; items/progress no-op; `signal` nunca aborta).

## Tabelas de opções

Só há uma chamada por processo, então todas as opções são **do agente**. Os tipos se empilham: `HobotsOptions` (telemetria e identidade) → `RunOptions` (a execução) → `RegisterOptions` (só sob demanda); `StartOptions = RunOptions`.

### `HobotsOptions` (telemetria, identidade, handlers globais)

| Opção | Tipo | Default | Descrição |
| --- | --- | --- | --- |
| `clientSecret` | `string` | env `HOBOTS_CLIENT_SECRET` | `hb_<64 hex>`; ausente/inválido lança citando a env (sem ecoar o valor) |
| `instanceId` | `string` | env `HOBOTS_INSTANCE_ID` → `os.hostname()` | identidade estável da instância |
| `environment` | `'development' \| 'testing' \| 'staging' \| 'production' \| string` | `'production'` | vai em eventos, transactions e na instância; filtra alertas |
| `release` | `string` | — | versão do programa |
| `tags` | `Record<string, string>` | — | tags de todos os eventos e transactions |
| `debug` | `boolean` | `false` | loga a atividade do SDK |
| `machineMetrics` | `boolean` | `true` | CPU/memória/disco no heartbeat/poll e em `contexts.machine` |
| `captureUnhandled` | `boolean` | `true` | handlers globais de erro (issues) |
| `installExitHandlers` | `boolean` | `true` | avançado — SIGINT/SIGTERM/uncaughtException reportam `AgentTerminated` |

### `RunOptions extends HobotsOptions` (a execução — `register` e `start`)

| Opção | Tipo | Default | Descrição |
| --- | --- | --- | --- |
| `items` | `ItemsOptions` = `{ columns: Array<string \| { key, label? }> }` | — | colunas da aba de itens no app (chaves do `payload` dos itens); até 12, chave `[a-zA-Z0-9_.-]`. Uma declaração só, do agente; independe de formulário; personalizar no app sobrepõe |
| `retries` | `number` | `1` | tentativas extras do handler; `0` desliga |
| `retryDelayMs` | `number \| (attempt) => number` | `5_000` | `attempt` 1-based (a que falhou) |
| `timeoutMs` | `number` | — | por tentativa; terminal (`TaskTimeout`) |
| `logFlushIntervalMs` | `number` | `2_000` | avançado |
| `cancelCheckIntervalMs` | `number` | `5_000` (1_000..15_000) | avançado — PATCH vazio que detecta cancelamento e é o sinal de vida da execução |
| `cancelGraceMs` | `number` | `10_000` | avançado — graça após o signal antes de liberar o slot |

### `RegisterOptions extends RunOptions` (só sob demanda)

| Opção | Tipo | Default | Descrição |
| --- | --- | --- | --- |
| `concurrency` | `number` | `1` | execuções simultâneas do agente (1..5) |
| `pollIntervalMs` | `number` | `2_000` (mín. 1_000) | intervalo entre polls sem trabalho |

`StartOptions = RunOptions`.

### `RetryOptions`

| Opção | Tipo | Default |
| --- | --- | --- |
| `retries` | `number` | `3` |
| `delayMs` | `number \| (attempt) => number` | `1_000` |
| `shouldRetry` | `(error) => boolean` | tudo, exceto `RunCanceled`/`TaskTimeout`/`AbortError` (nunca re-tentados) |

### `CaptureHint`

| Campo | Tipo | Descrição |
| --- | --- | --- |
| `type` | `'fatal' \| 'error' \| 'warning' \| 'success'` | default `'error'` |
| `tags`, `extra`, `fingerprint` | | metadados do evento (`fingerprint` sobrescreve o agrupamento) |
| `attachment` | `ImageInput \| { image, caption?, contentType? }` | print anexado à linha de log da execução atual |

### `RunHandler`

```ts
type RunHandler<P = Record<string, unknown>> = (params: P, ctx: RunContext) => void | Promise<void>;
```

### Tipos do wire que mudaram na 4.0

| Tipo | O que é |
| --- | --- |
| `InstanceMode` | `'on_demand' \| 'always_on'` — como a instância trabalha (`register` / `start`) |
| `ItemsDeclaration` | `{ attributes: ItemColumn[] }` — as colunas de itens declaradas pela opção `items` |
| `InstanceInfo` | ganhou `mode?: InstanceMode` e `items?: ItemsDeclaration`; perdeu a lista de tasks e as colunas por task da 3.x |
| `PollRequest` | `{ instance?, max? }` — sem lista de tasks |
| `RunDescriptor` / `RunCreateRequest` | sem `task` |

## Tempos e intervalos

| O quê | Valor |
| --- | --- |
| Heartbeat | 30 s (±10 % de jitter), sempre ligado, timer ref'd (segura o processo) |
| Flush da telemetria | 2 s ou 10 itens; envelopes de até 50 itens; fila de 200 |
| Retry HTTP (ingest, PATCH, itens, anexos) | 1 s / 4 s / 16 s com jitter; 4xx ≠ 429 é definitivo |
| Poll sem trabalho | `pollIntervalMs` (2 s); com trabalho, imediato (drain) |
| Poll/criação de run com erro | 1 s / 4 s / 16 s; 401 → 30 s com erro logado na 1ª e a cada 10 falhas |
| Logs da execução | flush a cada `logFlushIntervalMs` (2 s) ou 20 linhas; chunks de 100; 1000 por execução no servidor |
| Cancel-watch / sinal de vida | `cancelCheckIntervalMs` (5 s); o servidor marca `AgentTerminated` após ~120 s sem PATCH |
| Retry do handler | `retries` 1, `retryDelayMs` 5 s |
| Drenos antes do terminal | anexos 15 s, itens 15 s |
| `close()` | 30 s para as execuções em voo; exit path reporta em ≤ 2,5 s |
