hobots 1.5.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/concepts.md CHANGED
@@ -8,42 +8,55 @@ Os defaults numéricos **não** estão aqui — eles vivem em [Tempos e interval
8
8
 
9
9
  ## Mapa rápido
10
10
 
11
- | Conceito | Em uma frase | Quando você usa |
12
- | --- | --- | --- |
13
- | [Evento](#evento) | Uma foto de algo que aconteceu — um erro ou uma mensagem | `captureException` / `captureMessage` |
14
- | [Level](#level-severity) | A gravidade do evento | Para separar o que acorda alguém do que é só registro |
15
- | [Fingerprint](#fingerprint) | A chave de agrupamento dos eventos em issues | Quando o agrupamento automático erra |
16
- | [Scope](#scope) | A mochila de contexto que viaja com todo evento | Você raramente mexe direto; mexe pelos `set*` |
17
- | [withScope](#withscope) | Um scope temporário e isolado | Contexto que **não** deve vazar para o resto do programa |
18
- | [Tag](#tag) | Rótulo curto e indexável (`string` → `string`) | Você vai **filtrar** ou **agrupar** por isso |
19
- | [Context](#context) | Grupo nomeado de dados estruturados | Você vai **ler** isso ao abrir o evento |
20
- | [Extra](#extra) | Depósito livre, sem estrutura | Não é filtro nem grupo coerente — é anexo |
21
- | [Breadcrumb](#breadcrumb) | Migalha da trilha que levou até o erro | Marcar cada passo relevante do robô |
22
- | [Transaction](#transaction) | Unidade de trabalho nomeada, com duração | Medir quanto uma rotina inteira demorou |
23
- | [Span](#span) | Uma etapa medida dentro da transaction | Descobrir **onde** o tempo foi gasto |
24
- | [ctx.tx](#transaction-de-run-ctxtx) | A transaction que o SDK já abriu para a run | Medir as etapas de um handler do Manager |
25
- | [Amostragem](#amostragem-samplerate-e-tracessamplerate) | Enviar só uma fração do que acontece | Volume alto demais para enviar tudo |
26
- | [Check-in](#check-in) | Par de avisos "comecei" / "terminei" de um job agendado | Cron que precisa ser vigiado |
27
- | [Heartbeat](#heartbeat) | Batimento "estou vivo" | Saber que o robô está de pé, mesmo parado |
28
- | [instanceId](#instanceid) | A identidade da instância do robô | Sempre — é obrigatório no `init()` |
29
- | [Release e environment](#release-e-environment) | Qual versão, em qual ambiente | Separar produção de homologação; achar a versão que quebrou |
30
- | [Run](#run) | Uma execução solicitada no app | O Manager inteiro gira em volta disso |
31
- | [Claim](#claim) | O ato de um agente pegar uma run para si | Entender por que dois agentes nunca colidem |
32
- | [Drain mode](#drain-mode) | O agente acelera o poll enquanto tem trabalho | Entender o ritmo do polling |
33
- | [Fila, flush e close](#fila-flush-e-close) | Nada é enviado na hora — vai para uma fila | Encerrar o processo sem perder telemetria |
34
- | [beforeSend](#beforesend) | O último gancho antes do envio | Censurar ou descartar eventos |
11
+ | Conceito | Em uma frase | Quando você usa |
12
+ | ------------------------------------------------------- | -------------------------------------------------------- | ----------------------------------------------------------- |
13
+ | [Evento](#evento) | Uma foto de algo que aconteceu — um erro ou uma mensagem | `captureException` / `captureMessage` |
14
+ | [Level](#level-severity) | A gravidade do evento | Para separar o que acorda alguém do que é só registro |
15
+ | [Fingerprint](#fingerprint) | A chave de agrupamento dos eventos em issues | Quando o agrupamento automático erra |
16
+ | [Scope](#scope) | A mochila de contexto que viaja com todo evento | Você raramente mexe direto; mexe pelos `set*` |
17
+ | [withScope](#withscope) | Um scope temporário e isolado | Contexto que **não** deve vazar para o resto do programa |
18
+ | [Tag](#tag) | Rótulo curto e indexável (`string` → `string`) | Você vai **filtrar** ou **agrupar** por isso |
19
+ | [Context](#context) | Grupo nomeado de dados estruturados | Você vai **ler** isso ao abrir o evento |
20
+ | [Extra](#extra) | Depósito livre, sem estrutura | Não é filtro nem grupo coerente — é anexo |
21
+ | [Breadcrumb](#breadcrumb) | Migalha da trilha que levou até o erro | Marcar cada passo relevante do robô |
22
+ | [Transaction](#transaction) | Unidade de trabalho nomeada, com duração | Medir quanto uma rotina inteira demorou |
23
+ | [Span](#span) | Uma etapa medida dentro da transaction | Descobrir **onde** o tempo foi gasto |
24
+ | [ctx.tx](#transaction-de-run-ctxtx) | A transaction que o SDK já abriu para a run | Medir as etapas de um handler de task |
25
+ | [Amostragem](#amostragem-samplerate-e-tracessamplerate) | Enviar só uma fração do que acontece | Volume alto demais para enviar tudo |
26
+ | [Check-in](#check-in) | Par de avisos "comecei" / "terminei" de um job agendado | Cron que precisa ser vigiado |
27
+ | [Heartbeat](#heartbeat) | Batimento "estou vivo" | Saber que o robô está de pé, mesmo parado |
28
+ | [instanceId](#instanceid) | A identidade da instância do robô | Sempre — é obrigatório no `init()` |
29
+ | [Release e environment](#release-e-environment) | Qual versão, em qual ambiente | Separar produção de homologação; achar a versão que quebrou |
30
+ | [Run](#run) | Uma execução solicitada no app | As tasks inteiras giram em volta disso |
31
+ | [Claim](#claim) | O ato de um agente pegar uma run para si | Entender por que dois agentes nunca colidem |
32
+ | [Drain mode](#drain-mode) | O agente acelera o poll enquanto tem trabalho | Entender o ritmo do polling |
33
+ | [Fila, flush e close](#fila-flush-e-close) | Nada é enviado na hora — vai para uma fila | Encerrar o processo sem perder telemetria |
34
+ | [beforeSend](#beforesend) | O último gancho antes do envio | Censurar ou descartar eventos |
35
35
 
36
36
  > **Nota de manutenção:** esta página é a fonte canônica das explicações de conceito. As outras páginas da doc, o `README.md` do pacote e os tooltips do app parafraseiam daqui — quando um conceito muda, muda aqui primeiro.
37
37
 
38
+ ## O SDK e os módulos do app
39
+
40
+ Cada API do SDK alimenta um módulo do app da Hobots (app.hobots.app). O mapa:
41
+
42
+ | No SDK | No app |
43
+ | --- | --- |
44
+ | **Projeto** — o "funcionário digital"; dono do Client Secret (`hb_…`) que autentica tudo | **Configurações → Projetos** (é lá que você copia a credencial) |
45
+ | **Agente / instância** — o par `(project_id, instance_id)`; heartbeat e poll de tasks convergem nele | Módulo **Agentes** (uptime, performance) |
46
+ | **[Evento](#evento)** agrupado em **issue** — `captureException` / `captureMessage` | Módulo **Issues** |
47
+ | **Monitor (cron)** — `withMonitor` / `checkIn` com `schedule` | Módulo **Monitores** |
48
+ | **[Transaction](#transaction) / spans** — `startTransaction` (e [`ctx.tx`](#transaction-de-run-ctxtx) dentro de uma run) | Performance no módulo **Agentes**; nas execuções, a aba de performance da solicitação |
49
+ | **Task / [run](#run)** — `register('task-slug', …)` casa com o `task_slug` do formulário | Módulo **Solicitações** (formulários em **Configurações → Formulários**) |
50
+
38
51
  ---
39
52
 
40
53
  ## Evento
41
54
 
42
- Um **evento** é a foto de um instante que vale a pena registrar: uma exceção que estourou ou uma mensagem que você mandou. É a unidade básica do Monitor.
55
+ Um **evento** é a foto de um instante que vale a pena registrar: uma exceção que estourou ou uma mensagem que você mandou. É a unidade básica da telemetria.
43
56
 
44
57
  Quando você chama `captureException` ou `captureMessage`, o SDK monta o evento com o que a exceção traz (tipo, mensagem, stack trace com os frames em ordem) e **anexa tudo que está no [scope](#scope)** naquele momento: tags, usuário, contexts, extras e a trilha de [breadcrumbs](#breadcrumb). Depois disso o evento vai para a [fila](#fila-flush-e-close) e é enviado em lote.
45
58
 
46
- No servidor, eventos parecidos são agrupados numa **issue** — é por isso que 4.000 falhas do mesmo timeout não viram 4.000 linhas na sua tela. Quem decide o que é "parecido" é o [fingerprint](#fingerprint).
59
+ No servidor, eventos parecidos são agrupados numa **issue** — o que você vê no módulo Issues do app. É por isso que 4.000 falhas do mesmo timeout não viram 4.000 linhas na sua tela. Quem decide o que é "parecido" é o [fingerprint](#fingerprint).
47
60
 
48
61
  ```js
49
62
  Hobots.captureException(error);
@@ -112,7 +125,7 @@ Dois pontos importantes:
112
125
  `withScope` executa um callback com uma **cópia isolada** do scope. O que você escrever lá dentro vale para os eventos capturados dentro do callback e **não vaza** para fora.
113
126
 
114
127
  ```js
115
- Hobots.withScope((scope) => {
128
+ Hobots.withScope(scope => {
116
129
  scope.setTag('lote', '128').setFingerprint(['reprocessamento']);
117
130
  Hobots.captureMessage('Reprocessando lote 128');
118
131
  });
@@ -172,11 +185,11 @@ Hobots.setExtra('tentativas', [1, 2, 3]);
172
185
 
173
186
  É deliberadamente o menos estruturado dos três, e por isso o critério de escolha é simples:
174
187
 
175
- | Você vai… | Use |
176
- | --- | --- |
177
- | filtrar, agrupar, contar por isso | [tag](#tag) |
178
- | ler isso como um conjunto coerente | [context](#context) |
179
- | nenhum dos dois — é só um dado a mais | **extra** |
188
+ | Você vai… | Use |
189
+ | ------------------------------------- | ------------------- |
190
+ | filtrar, agrupar, contar por isso | [tag](#tag) |
191
+ | ler isso como um conjunto coerente | [context](#context) |
192
+ | nenhum dos dois — é só um dado a mais | **extra** |
180
193
 
181
194
  Extra não é indexado: não dá para filtrar por ele nem construir gráfico com ele. Se você se pegar querendo isso, o campo era uma tag.
182
195
 
@@ -207,7 +220,7 @@ Tamanho do buffer: [`maxBreadcrumbs`](./api-reference.md#initoptions).
207
220
 
208
221
  Uma **transaction** é uma unidade de trabalho **nomeada** e **medida**: ela começa, termina, e o que fica registrado é quanto tempo levou. É a resposta para "quanto demora minha rotina de emissão?" — e, quando você adiciona [spans](#span), para "onde foram esses minutos?".
209
222
 
210
- Enquanto o [evento](#evento) responde *o que quebrou*, a transaction responde *quanto tempo levou*. São os dois eixos do Monitor.
223
+ Enquanto o [evento](#evento) responde _o que quebrou_, a transaction responde _quanto tempo levou_. São os dois eixos da telemetria.
211
224
 
212
225
  ```js
213
226
  const tx = Hobots.startTransaction('processar-lote', 'rpa.job');
@@ -226,7 +239,7 @@ Três coisas que valem saber:
226
239
 
227
240
  O `status` default é `'ok'`; encadeáveis: `setStatus`, `setName`, `setTag`.
228
241
 
229
- Dentro de um handler do Manager você **não** cria transaction — já existe uma em [`ctx.tx`](#transaction-de-run-ctxtx).
242
+ Dentro de um handler de task você **não** cria transaction — já existe uma em [`ctx.tx`](#transaction-de-run-ctxtx), e o `startTransaction()` chamado lá dentro devolve exatamente ela.
230
243
 
231
244
  ## Span
232
245
 
@@ -249,7 +262,7 @@ O `op` é a categoria (útil para agrupar "todas as queries") e a `description`
249
262
 
250
263
  ## Transaction de run (ctx.tx)
251
264
 
252
- Dentro de um handler do Manager você **não precisa** criar uma transaction: o SDK já abriu uma para aquela execução e entregou em `ctx.tx`. Ela nasce quando o agente captura a run, é **correlacionada** à execução pelo `run_id`, recebe o status conforme o desfecho (`ok`, `error` ou `cancelled`) e é **finalizada automaticamente** quando o handler termina — você nunca chama `ctx.tx.finish()`.
265
+ Dentro de um handler de task você **não precisa** criar uma transaction: o SDK já abriu uma para aquela execução e entregou em `ctx.tx`. Ela nasce quando o agente captura a run, é **correlacionada** à execução pelo `run_id`, recebe o status conforme o desfecho (`ok`, `error` ou `cancelled`) e é **finalizada automaticamente** quando o handler termina — você nunca chama `ctx.tx.finish()`.
253
266
 
254
267
  O que sobra para você é a parte interessante: marcar as **etapas** do trabalho com [spans](#span). Cada `startChild` vira uma barra no waterfall da aba de performance daquela execução no app.
255
268
 
@@ -270,9 +283,9 @@ Hobots.register('processar-nfe', async (params, ctx) => {
270
283
 
271
284
  Sem nenhum `startChild` a transaction ainda existe e ainda mede o tempo total da run — o waterfall só fica com uma barra. Cada span que você abre é uma resposta a mais para "onde foram os 4 minutos".
272
285
 
273
- - **Não chame `Hobots.startTransaction()` dentro de um handler.** Você criaria uma segunda transaction, **sem** correlação com a run, e ela não apareceria na execução.
274
- - `ctx.tx` **não respeita o `tracesSampleRate`** — toda run gera uma transaction. A amostragem vale para as transactions que **você** abre com `startTransaction()`.
275
- - O nome é `run:<task>` e o `op` é `manager.run`, preenchidos pelo SDK.
286
+ - **`Hobots.startTransaction()` dentro de um handler devolve a própria `ctx.tx`.** Cada execução tem exatamente **uma** transaction: o SDK detecta o contexto da run e reutiliza a transaction dela em vez de criar uma segunda — os spans caem no mesmo waterfall. Fora de um handler, `startTransaction()` cria uma transaction nova, sem `run_id`.
287
+ - `ctx.tx` **não respeita o `tracesSampleRate`** — toda run gera uma transaction. A amostragem vale para as transactions que **você** abre com `startTransaction()` fora de uma run.
288
+ - O nome é `run:<task>` e o `op` é `hobots.task`, preenchidos pelo SDK.
276
289
 
277
290
  ## Amostragem (sampleRate e tracesSampleRate)
278
291
 
@@ -280,9 +293,9 @@ Sem nenhum `startChild` a transaction ainda existe e ainda mede o tempo total da
280
293
 
281
294
  São dois controles independentes, os dois de `0` a `1`:
282
295
 
283
- | Opção | Afeta | `0.1` significa |
284
- | --- | --- | --- |
285
- | `sampleRate` | [eventos](#evento) (`captureException`, `captureMessage`) | ~10% dos erros são enviados |
296
+ | Opção | Afeta | `0.1` significa |
297
+ | ------------------ | ------------------------------------------------------------- | ---------------------------------- |
298
+ | `sampleRate` | [eventos](#evento) (`captureException`, `captureMessage`) | ~10% dos erros são enviados |
286
299
  | `tracesSampleRate` | [transactions](#transaction) abertas por `startTransaction()` | ~10% das transactions são enviadas |
287
300
 
288
301
  O sorteio é por item, no momento em que ele é criado — nunca "1 a cada 10", e sim 10% de chance cada.
@@ -319,7 +332,7 @@ Detalhes que importam:
319
332
 
320
333
  O **heartbeat** é um batimento periódico dizendo "estou vivo". O servidor conhece a cadência esperada e marca a instância como **offline** quando os batimentos param.
321
334
 
322
- É o complemento do [check-in](#check-in): check-in vigia *execuções agendadas*, heartbeat vigia *o processo*. Um robô que fica de plantão esperando trabalho não tem cron para monitorar — mas você ainda quer saber quando a VM dele caiu.
335
+ É o complemento do [check-in](#check-in): check-in vigia _execuções agendadas_, heartbeat vigia _o processo_. Um robô que fica de plantão esperando trabalho não tem cron para monitorar — mas você ainda quer saber quando a VM dele caiu.
323
336
 
324
337
  ```js
325
338
  Hobots.init({
@@ -338,11 +351,13 @@ Dois detalhes:
338
351
 
339
352
  O timer do heartbeat é `unref`'d — ele **não** segura o processo vivo.
340
353
 
354
+ Com `tasks: true`, o heartbeat é **opcional**: o próprio poll de tasks já marca a presença do agente no painel. Ele continua essencial para agentes só de telemetria.
355
+
341
356
  ## instanceId
342
357
 
343
358
  O **`instanceId`** é a identidade da instância do robô: qual VM, qual container, qual máquina. É **obrigatório** no `init()` e deve ser **estável entre reinícios** (`vm-cliente-01`, `container-nfe-3`) — se você gerar um valor novo a cada boot, o app vê uma frota de robôs fantasmas em vez de um robô que reiniciou.
344
359
 
345
- O que o torna especial é que ele é a identidade dos **dois** canais ao mesmo tempo: o [heartbeat](#heartbeat) do Monitor e o agente do [Manager](./manager.md) enviam o mesmo `instanceId`. O servidor correlaciona os dois, e o robô aparece como **um agente só** no painel — sem nenhuma configuração extra.
360
+ O que o torna especial é que ele é a identidade dos **dois** canais ao mesmo tempo: o [heartbeat](#heartbeat) e o agente de [tasks](./tasks.md) enviam o mesmo `instanceId`. O servidor correlaciona os dois, e o robô aparece como **um agente só** no módulo Agentes — sem nenhuma configuração extra.
346
361
 
347
362
  ## Release e environment
348
363
 
@@ -350,8 +365,8 @@ O que o torna especial é que ele é a identidade dos **dois** canais ao mesmo t
350
365
 
351
366
  Servem a duas perguntas concretas:
352
367
 
353
- - *"quebrou desde quando?"* — com `release`, você compara o antes e o depois de um deploy e sabe qual versão introduziu o erro.
354
- - *"isso é real ou é teste?"* — com `environment`, o ruído da homologação não polui os alertas de produção.
368
+ - _"quebrou desde quando?"_ — com `release`, você compara o antes e o depois de um deploy e sabe qual versão introduziu o erro.
369
+ - _"isso é real ou é teste?"_ — com `environment`, o ruído da homologação não polui os alertas de produção.
355
370
 
356
371
  `environment` default é `production`. Se você roda homologação sem definir isso, os dois ambientes se misturam na mesma issue — vale configurar no primeiro dia.
357
372
 
@@ -359,7 +374,7 @@ Servem a duas perguntas concretas:
359
374
 
360
375
  Uma **run** (execução) é um pedido concreto de trabalho criado no app: alguém preencheu o formulário da task, ou uma automação disparou, e agora existe uma solicitação esperando um agente pegar.
361
376
 
362
- É a unidade em volta da qual o [Manager](./manager.md) inteiro gira. O ciclo de vida no servidor:
377
+ É a unidade em volta da qual as [tasks](./tasks.md) inteiras giram — no app, cada run é uma solicitação no módulo **Solicitações**. O ciclo de vida no servidor:
363
378
 
364
379
  ```
365
380
  pending → claimed → running → succeeded | failed | canceled
@@ -372,7 +387,7 @@ pending → claimed → running → succeeded | failed | canceled
372
387
 
373
388
  O agente só transiciona para `running`, `succeeded` e `failed`. `pending` e `claimed` são do servidor; `canceled` vem de alguém cancelando no app.
374
389
 
375
- Cada run carrega os **`params`** (o JSON validado do formulário) e chega ao seu código como o primeiro argumento do handler, junto de um [`RunContext`](./api-reference.md#managertaskhandler--runcontext) com logs, sinal de cancelamento e a [transaction](#transaction-de-run-ctxtx).
390
+ Cada run carrega os **`params`** (o JSON validado do formulário) e chega ao seu código como o primeiro argumento do handler, junto de um [`RunContext`](./api-reference.md#taskhandler--runcontext) com logs, sinal de cancelamento e a [transaction](#transaction-de-run-ctxtx).
376
391
 
377
392
  ## Claim
378
393
 
@@ -406,13 +421,13 @@ Devolve `true` se tudo saiu dentro do tempo e `false` se estourou. `false` **nã
406
421
 
407
422
  ### `close(timeoutMs?)` — "acabou, encerra"
408
423
 
409
- Encerra tudo, **nesta ordem**: para o agente do Manager (aguardando as runs em execução) → para o heartbeat → faz o flush final e fecha o transport. É sempre a última linha do seu programa.
424
+ Encerra tudo, **nesta ordem**: para o agente de tasks (aguardando as runs em execução) → para o heartbeat → faz o flush final e fecha o transport. É sempre a última linha do seu programa.
410
425
 
411
426
  > **Atenção:** `close()` é **terminal e sem volta**. Depois dele, todo `captureException`, transaction, check-in ou heartbeat é **descartado** com um aviso no console — não existe `reopen()`. Se o programa precisar voltar a emitir telemetria, chame `Hobots.init()` de novo.
412
427
 
413
- > **Atenção:** o `timeoutMs` do `close()` é aplicado **duas vezes, em sequência** — uma para esperar as runs do Manager, outra para o flush final. `close(15_000)` pode levar até ~30s no pior caso. Se você tem um SIGTERM com prazo curto (Kubernetes, systemd), dimensione por aí.
428
+ > **Atenção:** o `timeoutMs` do `close()` é aplicado **duas vezes, em sequência** — uma para esperar as runs de tasks, outra para o flush final. `close(15_000)` pode levar até ~30s no pior caso. Se você tem um SIGTERM com prazo curto (Kubernetes, systemd), dimensione por aí.
414
429
 
415
- O `flush` cobre a fila do **Monitor**. Os logs de uma run do Manager (`ctx.log`) são um canal separado, com o próprio ritmo — quem espera por eles é o `close()`/`stop()`, ao aguardar a run terminar.
430
+ O `flush` cobre a fila da **telemetria**. Os logs de uma run de task (`ctx.log`) são um canal separado, com o próprio ritmo — quem espera por eles é o `close()`/`stop()`, ao aguardar a run terminar.
416
431
 
417
432
  Defaults e tempos dos dois: [Tempos e intervalos](./api-reference.md#tempos-e-intervalos).
418
433
 
@@ -424,9 +439,9 @@ Defaults e tempos dos dois: [Tempos e intervalos](./api-reference.md#tempos-e-in
424
439
  Hobots.init({
425
440
  clientSecret,
426
441
  instanceId: 'vm-cliente-01',
427
- beforeSend: (event) => {
442
+ beforeSend: event => {
428
443
  if (event.exception?.type === 'AbortError') return null; // não me interessa
429
- delete event.extra?.senha; // censura
444
+ delete event.extra?.senha; // censura
430
445
  return event;
431
446
  },
432
447
  });
@@ -6,7 +6,7 @@
6
6
  pnpm add hobots
7
7
  ```
8
8
 
9
- Sem dependências de runtime. Requer **Node 18+** (usa o `fetch` global). O Monitor também funciona no browser para captura de erros.
9
+ Sem dependências de runtime. Requer **Node 18+** (usa o `fetch` global). A telemetria também funciona no browser para captura de erros.
10
10
 
11
11
  ## Conceitos em 2 minutos
12
12
 
@@ -23,13 +23,13 @@ Os termos que aparecem nesta página, em uma frase cada. Explicação completa d
23
23
 
24
24
  ## O Client Secret
25
25
 
26
- Cada projeto tem **um único Client Secret** — a mesma credencial autentica o Monitor **e** o Manager:
26
+ Cada projeto tem **um único Client Secret** — a mesma credencial autentica a telemetria **e** as tasks:
27
27
 
28
28
  ```
29
29
  hb_<64 hex>
30
30
  ```
31
31
 
32
- (Chaves antigas, 64 hex puros, continuam válidas no servidor.) A credencial é **secreta** e viaja sempre no header `Authorization: Bearer` — nunca na URL. Copie o Client Secret no app, em **Configurações → Projetos** — os snippets de integração já vêm prontos.
32
+ A credencial é **secreta** e viaja sempre no header `Authorization: Bearer` — nunca na URL. Copie o Client Secret no app, em **Configurações → Projetos** — os snippets de integração já vêm prontos.
33
33
 
34
34
  O SDK fala com a API oficial `https://api2.hobots.app` automaticamente — não há configuração de host no `init()`. Em desenvolvimento, aponte para outro host com a env `HOBOTS_API_URL`:
35
35
 
@@ -39,13 +39,13 @@ HOBOTS_API_URL=http://localhost:3001 node meu-robo.js
39
39
 
40
40
  Veja [Protocolo & transporte](./protocol.md) para os endpoints derivados da URL base.
41
41
 
42
- ## Monitor e Manager — qual usar
42
+ ## Telemetria e tasks — qual usar
43
43
 
44
44
  As duas capabilities vivem no mesmo `init()` e você usa **uma, outra ou as duas**:
45
45
 
46
46
  - Quer só saber se o robô quebrou, quanto demora e se está vivo? → `init({ clientSecret, heartbeat: true })`.
47
- - Quer disparar o robô sob demanda a partir de um formulário no app? → `init({ clientSecret, manager: true })` + `register()` + `start()`.
48
- - Ambos? Passe `heartbeat` e `manager` no **mesmo `init()`**. É o cenário do
47
+ - Quer disparar o robô sob demanda a partir de um formulário no app? → `init({ clientSecret, tasks: true })` + `register()` + `start()`.
48
+ - Ambos? Passe `heartbeat` e `tasks` no **mesmo `init()`**. É o cenário do
49
49
  [`full.ts`](../../hobots-sdk-demos/src/full.ts) nos demos do SDK.
50
50
 
51
51
  ## O `init` único
@@ -57,7 +57,7 @@ Hobots.init({
57
57
  clientSecret: 'hb_<64 hex>',
58
58
  environment: 'production',
59
59
  release: 'robo-nfe@1.4.2',
60
- instanceId: 'vm-cliente-01', // identidade única: heartbeat + agente Manager
60
+ instanceId: 'vm-cliente-01', // identidade única: heartbeat + agente de tasks
61
61
  heartbeat: true, // "estou vivo" a cada 30s
62
62
  });
63
63
 
@@ -71,11 +71,11 @@ try {
71
71
  }
72
72
  ```
73
73
 
74
- Chame `Hobots.init` **uma vez**, o mais cedo possível no processo. Detalhes em [Monitor](./monitor.md).
74
+ Chame `Hobots.init` **uma vez**, o mais cedo possível no processo. Detalhes em [Telemetria](./monitor.md).
75
75
 
76
- ## Primeiro agente (Manager)
76
+ ## Primeiro agente (tasks)
77
77
 
78
- Habilite a execução sob demanda com o campo `manager` no mesmo `init()`:
78
+ Habilite a execução sob demanda com o campo `tasks` no mesmo `init()`:
79
79
 
80
80
  ```js
81
81
  import * as Hobots from 'hobots';
@@ -83,7 +83,7 @@ import * as Hobots from 'hobots';
83
83
  Hobots.init({
84
84
  clientSecret: 'hb_<64 hex>',
85
85
  instanceId: 'vm-cliente-01',
86
- manager: true, // ou { pollIntervalMs, concurrency, ... }
86
+ tasks: true, // ou { pollIntervalMs, concurrency, ... }
87
87
  });
88
88
 
89
89
  Hobots.register('processar-nfe', async (params, ctx) => {
@@ -93,21 +93,21 @@ Hobots.register('processar-nfe', async (params, ctx) => {
93
93
  Hobots.start(); // mantém o processo vivo até stop()/close()
94
94
  ```
95
95
 
96
- > O `instanceId` é a identidade dos **dois** canais (heartbeat do Monitor e agente do Manager) — o robô aparece como **um agente só** no painel, sem configuração extra.
96
+ > O `instanceId` é a identidade dos **dois** canais (heartbeat e poll de tasks) — o robô aparece como **um agente só** no módulo Agentes do app, sem configuração extra. Com `tasks: true`, o heartbeat é opcional: o próprio poll já marca a presença do agente.
97
97
 
98
- Detalhes (cancelamento, concorrência, idempotência) em [Manager](./manager.md).
98
+ As execuções aparecem no módulo **Solicitações** do app; o `task` do `register()` é o `task_slug` do formulário em **Configurações → Formulários**. Detalhes (cancelamento, concorrência, idempotência) em [Tasks](./tasks.md).
99
99
 
100
100
  ## Encerramento
101
101
 
102
- `close()` encerra tudo na ordem certa: para o Manager (aguardando a run atual), o heartbeat e faz o flush final do transport.
102
+ `close()` encerra tudo na ordem certa: para o polling de tasks (aguardando a run atual), o heartbeat e faz o flush final do transport.
103
103
 
104
- Sem Manager iniciado, o SDK não segura o processo (timers `unref`'d), mas faça o flush final para não perder eventos em buffer:
104
+ Sem `start()` chamado, o SDK não segura o processo (timers `unref`'d), mas faça o flush final para não perder eventos em buffer:
105
105
 
106
106
  ```js
107
107
  await Hobots.close(2000);
108
108
  ```
109
109
 
110
- Com o Manager, `start()` segura o processo por design. Encerre de forma graciosa aguardando a run atual:
110
+ Com as tasks, `start()` segura o processo por design. Encerre de forma graciosa aguardando a run atual:
111
111
 
112
112
  ```js
113
113
  process.on('SIGINT', () => {
@@ -115,6 +115,6 @@ process.on('SIGINT', () => {
115
115
  });
116
116
  ```
117
117
 
118
- > **Atenção:** o `timeoutMs` do `close()` é gasto **duas vezes, em sequência** — uma esperando as runs do Manager, outra no flush final. O `close(15_000)` acima pode levar até ~30s no pior caso. Se o seu SIGTERM tem prazo curto (Kubernetes, systemd), dimensione por aí. Os defaults, quando você omite o argumento, estão em [Tempos e intervalos](./api-reference.md#tempos-e-intervalos).
118
+ > **Atenção:** o `timeoutMs` do `close()` é gasto **duas vezes, em sequência** — uma esperando as runs de tasks, outra no flush final. O `close(15_000)` acima pode levar até ~30s no pior caso. Se o seu SIGTERM tem prazo curto (Kubernetes, systemd), dimensione por aí. Os defaults, quando você omite o argumento, estão em [Tempos e intervalos](./api-reference.md#tempos-e-intervalos).
119
119
 
120
120
  Para parar de aceitar execuções **sem** derrubar a telemetria, use `Hobots.stop(timeoutMs?)`. Conceito das duas funções em [Fila, flush e close](./concepts.md#fila-flush-e-close).
package/docs/monitor.md CHANGED
@@ -1,6 +1,6 @@
1
- # Monitor
1
+ # Telemetria
2
2
 
3
- Capability de telemetria: erros, performance, crons e uptime. Sempre ativa no `init()` único:
3
+ Capability de telemetria: erros (módulo **Issues**), performance e uptime (módulo **Agentes**), crons (módulo **Monitores**). Sempre ativa no `init()` único:
4
4
 
5
5
  ```js
6
6
  import * as Hobots from 'hobots';
@@ -114,11 +114,11 @@ tx.finish(); // só aqui a transaction é enviada (respeitando tracesSampleRate)
114
114
  - A decisão de amostragem acontece no **`startTransaction()`**, não no `finish()`. Esquecer o `finish()` de um span não perde o dado: ele é fechado no instante em que a transaction fecha.
115
115
  - Uma transaction leva as tags do `init()` e as que você marcar com `tx.setTag()` — ela **não** herda as tags do scope.
116
116
 
117
- Dentro de um handler do Manager não abra transaction: já existe uma em [`ctx.tx`](./concepts.md#transaction-de-run-ctxtx).
117
+ Dentro de um handler de task, `startTransaction()` **retorna a transaction da própria run** ([`ctx.tx`](./concepts.md#transaction-de-run-ctxtx)) em vez de criar uma segunda — cada execução tem exatamente **uma** transaction. Fora de um handler, ela cria uma transaction nova, sem correlação com run nenhuma.
118
118
 
119
119
  ## Crons (check-ins)
120
120
 
121
- Monitora jobs agendados: um check-in `in_progress` na entrada, `ok`/`error` na saída, com duração medida. O jeito recomendado é `withMonitor`, que faz o par e captura exceções:
121
+ Monitora jobs agendados — o módulo **Monitores** do app: um check-in `in_progress` na entrada, `ok`/`error` na saída, com duração medida. O jeito recomendado é `withMonitor`, que faz o par e captura exceções:
122
122
 
123
123
  ```js
124
124
  await Hobots.withMonitor(
@@ -153,7 +153,7 @@ Hobots.init({
153
153
 
154
154
  O primeiro batimento sai **na hora** em que o `init()` roda — não depois do primeiro intervalo. O batimento também carrega o intervalo configurado: é assim que o servidor sabe quanto silêncio é normal antes de declarar o robô offline.
155
155
 
156
- Essa identidade é a mesma que o Manager envia no poll (`agent.instance_id`) — os dois canais derivam do `instanceId` do `init()`, então o robô aparece como **um agente só** em Monitor → Agentes automaticamente.
156
+ Essa identidade é a mesma que o poll de tasks envia (`agent.instance_id`) — os dois canais derivam do `instanceId` do `init()`, então o robô aparece como **um agente só** no módulo Agentes automaticamente. Por isso, com `tasks: true` o heartbeat é **opcional** (o poll já marca a presença do agente); ele é essencial para agentes só de telemetria.
157
157
 
158
158
  ## Flush e close
159
159
 
@@ -161,13 +161,13 @@ Nada é enviado na hora: cada evento, transaction, check-in e heartbeat entra nu
161
161
 
162
162
  ```js
163
163
  await Hobots.flush(2000); // envia o que está em buffer, mantém o SDK vivo
164
- await Hobots.close(2000); // para o Manager (se ativo) + heartbeat + flush final do transport
164
+ await Hobots.close(2000); // para o polling de tasks (se ativo) + heartbeat + flush final do transport
165
165
  ```
166
166
 
167
167
  O `timeoutMs` é opcional nas duas — o default é 5s (veja [Tempos e intervalos](./api-reference.md#tempos-e-intervalos)). As duas devolvem `false` quando estouram o prazo, o que significa que o SDK parou de esperar, **não** que os dados foram perdidos: os envios em voo continuam tentando enquanto o processo existir.
168
168
 
169
- Chame `close` antes do processo sair. Os timers do Monitor são `unref`'d, então a telemetria **não** segura o processo vivo sozinha (só o `start()` do Manager segura).
169
+ Chame `close` antes do processo sair. Os timers da telemetria são `unref`'d, então ela **não** segura o processo vivo sozinha (só o `start()` das tasks segura).
170
170
 
171
171
  > **Atenção:** `close()` é **terminal**. Depois dele, todo evento, transaction, check-in ou heartbeat novo é **descartado** com aviso no console — não existe `reopen()`. Se o programa precisar voltar a emitir telemetria, chame `Hobots.init()` de novo.
172
172
 
173
- > **Atenção:** o `timeoutMs` do `close()` é gasto **duas vezes, em sequência** — uma esperando as runs do Manager, outra no flush final. `close(15_000)` pode levar até ~30s no pior caso. Dimensione por aí se o seu SIGTERM tem prazo curto.
173
+ > **Atenção:** o `timeoutMs` do `close()` é gasto **duas vezes, em sequência** — uma esperando as runs de tasks, outra no flush final. `close(15_000)` pode levar até ~30s no pior caso. Dimensione por aí se o seu SIGTERM tem prazo curto.
package/docs/protocol.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  O SDK é um **cliente HTTP puro**, sem dependências de runtime: ele só fala com o app da Hobots por HTTP. Não abre conexão com banco e **não** roda `SKIP LOCKED` — a reivindicação atômica de runs é server-side (veja o fim desta página).
4
4
 
5
- Os arquivos `src/protocol.ts` e `src/manager/protocol.ts` são a **fonte de verdade do wire format**: o app valida os payloads com zod travados nesses tipos (`satisfies z.ZodType<...>`), então não podem sofrer drift.
5
+ Os arquivos `src/protocol.ts` e `src/tasks/protocol.ts` são a **fonte de verdade do wire format**: o app valida os payloads com zod travados nesses tipos (`satisfies z.ZodType<...>`), então não podem sofrer drift. Todos os tipos são exportados por `hobots/protocol` (e reexportados pelo import raiz `hobots`).
6
6
 
7
7
  ## O Client Secret e os endpoints derivados
8
8
 
@@ -14,20 +14,18 @@ hb_<64 hex>
14
14
 
15
15
  | Canal | Endpoint | Autenticação |
16
16
  | --- | --- | --- |
17
- | Monitor (ingestão) | `POST <baseUrl>/v1/ingest` | `Authorization: Bearer <clientSecret>` |
18
- | Manager (execução) | `<baseUrl>/v1/manager/*` | `Authorization: Bearer <clientSecret>` |
17
+ | Telemetria (ingestão) | `POST <baseUrl>/v1/ingest` | `Authorization: Bearer <clientSecret>` |
18
+ | Tasks (execução) | `<baseUrl>/v1/tasks/*` | `Authorization: Bearer <clientSecret>` |
19
19
 
20
- `resolveConfig({ clientSecret }) → { key, baseUrl, ingestEndpoint, managerBaseUrl }` (em `src/config.ts`, com a constante `DEFAULT_BASE_URL = 'https://api2.hobots.app'`; em desenvolvimento a env `HOBOTS_API_URL` sobrescreve a URL base). A credencial é **secreta** (formato `hb_<64 hex>`; chaves antigas de 64 hex puros continuam válidas no servidor) e viaja **só no header** `Authorization: Bearer` — nunca na URL da requisição.
20
+ A resolução da credencial nos endpoints é interna ao `init()` (em `src/config.ts`, com a constante exportada `DEFAULT_BASE_URL = 'https://api2.hobots.app'`); o resultado fica acessível em `getClient()?.config` → `{ key, baseUrl, ingestEndpoint, tasksBaseUrl }`. Em desenvolvimento a env `HOBOTS_API_URL` sobrescreve a URL base — apenas `http(s)`, e `http` para host fora de loopback gera aviso porque o secret trafega sem TLS. A credencial é **secreta** (formato `hb_` + 64 hex — chaves de outro formato são rejeitadas no `init()`) e viaja **só no header** `Authorization: Bearer` — nunca na URL da requisição.
21
21
 
22
- > As DSNs foram **descontinuadas** em favor do Client Secret — tanto a antiga do Monitor (public key na URL, `POST /v1/ingest/<key>`, que responde **410** e o transport descarta sem retry) quanto a de key-na-URL (`https://<secretKey>@host/<slug>`). Copie o Client Secret em **Configurações → Projetos** no app.
23
-
24
- ## Monitor — ingestão
22
+ ## Telemetria — ingestão
25
23
 
26
24
  Endpoint fixo `POST /v1/ingest` recebe **envelopes** JSON autenticados por Bearer. O transport mantém uma fila em memória, agrupa itens em envelopes e faz POST via `fetch`.
27
25
 
28
26
  ```jsonc
29
27
  {
30
- "sdk": { "name": "hobots", "version": "1.0.0" },
28
+ "sdk": { "name": "hobots", "version": "2.0.0" },
31
29
  "sent_at": "2024-01-01T00:00:00.000Z",
32
30
  "items": [
33
31
  { "type": "event", "payload": { /* EventPayload */ } },
@@ -40,24 +38,24 @@ Endpoint fixo `POST /v1/ingest` recebe **envelopes** JSON autenticados por Beare
40
38
 
41
39
  Tipos de item: `event`, `transaction`, `check_in`, `heartbeat` (definidos em `hobots/protocol`).
42
40
 
43
- Um `transaction` pode carregar `run_id`: é assim que a [transaction de uma run do Manager](./concepts.md#transaction-de-run-ctxtx) (`ctx.tx`) é correlacionada à execução, e o que permite o app montar o waterfall de performance dentro da solicitação. Transactions abertas com `startTransaction()` não têm esse campo.
41
+ Um `transaction` pode carregar `run_id`: é assim que a [transaction de uma run](./concepts.md#transaction-de-run-ctxtx) (`ctx.tx`) é correlacionada à execução, e o que permite o app montar o waterfall de performance dentro da solicitação. Transactions abertas com `startTransaction()` fora de uma run não têm esse campo.
44
42
 
45
43
  **Flush & retries:** o transport flusha por `flushIntervalMs` ou ao atingir `maxBatchSize`. Ladder de retry de 3 tentativas; respostas 4xx (exceto 429) são descartadas sem retry; a fila tem teto fixo e descarta os itens **mais antigos** ao encher. Todos os valores em [Tempos e intervalos](./api-reference.md#tempos-e-intervalos).
46
44
 
47
- ## Manager — polling e reporte
45
+ ## Tasks — polling e reporte
48
46
 
49
- Dois endpoints sob `<baseUrl>/v1/manager`, ambos com `Authorization: Bearer <clientSecret>` (a credencial única do projeto):
47
+ Dois endpoints sob `<baseUrl>/v1/tasks`, ambos com `Authorization: Bearer <clientSecret>` (a credencial única do projeto):
50
48
 
51
- ### `POST /v1/manager/poll`
49
+ ### `POST /v1/tasks/poll`
52
50
 
53
- Corpo `ManagerPollRequest`:
51
+ Corpo `PollRequest`:
54
52
 
55
53
  ```jsonc
56
54
  {
57
55
  "agent": {
58
56
  "instance_id": "vm-01", // identidade canônica do agente (obrigatória)
59
57
  "name": "VM 01",
60
- "version": "1.0.0",
58
+ "version": "2.0.0",
61
59
  "host": "vm-01",
62
60
  "tasks": ["processar-nfe"]
63
61
  },
@@ -66,15 +64,15 @@ Corpo `ManagerPollRequest`:
66
64
  }
67
65
  ```
68
66
 
69
- Resposta `ManagerPollResponse` = `{ runs: ManagerRunDescriptor[] }`, cada `{ id, task, params, form, source, created_at }`.
67
+ Resposta `PollResponse` = `{ runs: RunDescriptor[] }`, cada `{ id, task, params, form, source, created_at }`.
70
68
 
71
- O `agent.instance_id` é a **mesma identidade** do heartbeat do Monitor (`instance_id`): o servidor correlaciona os dois canais numa tabela `agents` por `(project_id, instance_id)`. Com o `init()` único, os dois derivam do `instanceId` — o robô aparece como um agente só no painel automaticamente.
69
+ O `agent.instance_id` é a **mesma identidade** do heartbeat de telemetria (`instance_id`): o servidor correlaciona os dois canais numa tabela `agents` por `(project_id, instance_id)`. Com o `init()` único, os dois derivam do `instanceId` — o robô aparece como um agente só no painel automaticamente. O próprio poll marca a presença do agente (`last_poll_at`), por isso o heartbeat é opcional quando as tasks estão habilitadas.
72
70
 
73
71
  **Backoff:** um request por poll; o loop decide o ritmo. HTTP 401 (key rejeitada) → retry lento a 30s com log throttled; outros erros → ladder `[1s, 4s, 16s]`.
74
72
 
75
- ### `PATCH /v1/manager/runs/{id}`
73
+ ### `PATCH /v1/tasks/runs/{id}`
76
74
 
77
- Corpo `ManagerRunUpdateRequest`:
75
+ Corpo `RunUpdateRequest`:
78
76
 
79
77
  ```jsonc
80
78
  {
@@ -88,17 +86,15 @@ Corpo `ManagerRunUpdateRequest`:
88
86
  - Ordem garantida `running → logs → terminal` (PATCHes serializados numa cadeia de promise).
89
87
  - Retry `[1s, 4s, 16s]`; 4xx exceto 429 é terminal (**409** = run cancelada / transição inválida → marca a run como morta e dispara `ctx.signal`).
90
88
  - Todo PATCH atualiza `last_report_at` no servidor (relógio do servidor) — é o **sinal de vida da run**. O heartbeat de cancelamento (PATCH `running` vazio a cada `cancelCheckIntervalMs`, máx 15s) garante reporte contínuo; um sweep server-side marca como `failed` com erro `AgentTerminated` ("execução encerrada inesperadamente") toda run `running` sem reporte há **60s**, sem re-entrega. No exit path (sinal/erro fatal com runs ativas), o SDK envia um único PATCH `failed`/`AgentTerminated` com timeout curto (~2s), sem retry — o sweep é o backstop.
91
- - O protocolo **não transporta resultado** de execução: o retorno do handler é ignorado pelo SDK e o servidor descarta qualquer campo `result` de SDKs antigos (dados de negócio podem ser sensíveis e não são coletados).
89
+ - O protocolo **não transporta resultado** de execução: o retorno do handler é ignorado pelo SDK e o servidor descarta qualquer campo `result` (dados de negócio podem ser sensíveis e não são coletados).
92
90
 
93
- Ciclo de vida completo da run no servidor: `pending → claimed → running → succeeded | failed | canceled`. O agente só transiciona para `running`/`succeeded`/`failed`.
91
+ Ciclo de vida completo da run no servidor (`RunStatus`): `pending → claimed → running → succeeded | failed | canceled`. O agente só transiciona para `running`/`succeeded`/`failed` (`RunUpdateStatus`).
94
92
 
95
93
  ## Onde vive o claim atômico
96
94
 
97
- O `POST /v1/manager/poll` no app chama `claimPendingRuns()` em
98
- [`apps/api/lib/manager/claim.ts`](../../../apps/api/lib/manager/claim.ts): um CTE que seleciona runs `pending` vencidas com `FOR UPDATE SKIP LOCKED` e as promove a `claimed` num único `UPDATE ... RETURNING`. Isso garante que dois agentes concorrentes nunca peguem a mesma run. **Nada disso está no SDK** — ele só faz o HTTP.
95
+ O `POST /v1/tasks/poll` no app chama `claimPendingRuns()` em
96
+ [`apps/api/lib/tasks/claim.ts`](../../../apps/api/lib/tasks/claim.ts): um CTE que seleciona runs `pending` vencidas com `FOR UPDATE SKIP LOCKED` e as promove a `claimed` num único `UPDATE ... RETURNING`. Isso garante que dois agentes concorrentes nunca peguem a mesma run. **Nada disso está no SDK** — ele só faz o HTTP.
99
97
 
100
98
  ## Identidade do SDK
101
99
 
102
- `SDK_NAME = 'hobots'` e `SDK_VERSION` ficam em `src/utils.ts` e vão em todo envelope (campo `sdk`) e no `agent.version` do poll.
103
-
104
- > **Nota de manutenção:** `SDK_VERSION` é uma constante fixa e precisa ser mantida em sincronia com a `version` do `package.json` manualmente ao publicar.
100
+ `SDK_NAME = 'hobots'` e `SDK_VERSION` ficam em `src/utils.ts` e vão em todo envelope (campo `sdk`) e no `agent.version` do poll. A `SDK_VERSION` é **injetada pelo build** (o `define` do tsup lê a `version` do `package.json`) — não há sincronização manual ao publicar.