hobots 2.0.0 → 2.2.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/protocol.ts"],"sourcesContent":["/**\n * Tipos do protocolo do Hobots (SDK → servidor).\n *\n * Este arquivo é a fonte de verdade do wire format e só pode reexportar tipos:\n * o app valida os payloads com zod e trava o drift via `satisfies z.ZodType<...>`\n * contra estes tipos (importados de `hobots/protocol`).\n */\n\n// Wire format de tasks (agente ↔ servidor) — reexportado aqui para o app\n// consumir tudo de `hobots/protocol`.\nexport type {\n RunStatus,\n RunUpdateStatus,\n RunLogLevel,\n RunLogEntry,\n InstanceInfo,\n PollRequest,\n RunDescriptor,\n PollResponse,\n RunError,\n RunUpdateRequest,\n RunUpdateResponse,\n RunItemStatus,\n RunItemInput,\n RunItemsCreateRequest,\n RunItemsCreateResponse,\n RunAttachmentContentType,\n RunAttachmentCreateRequest,\n RunAttachmentCreateResponse,\n RunAttachmentConfirmRequest,\n RunAttachmentConfirmResponse,\n} from './tasks/protocol';\n\nexport type Severity = 'fatal' | 'error' | 'warning' | 'info' | 'debug' | 'success';\n\n/** Frames em ordem innermost-first (mesma ordem impressa pelo V8). */\nexport interface StackFrame {\n function?: string;\n filename?: string;\n lineno?: number;\n colno?: number;\n in_app: boolean;\n}\n\nexport interface Breadcrumb {\n /** ISO 8601 */\n timestamp: string;\n type?: string;\n category?: string;\n message?: string;\n level?: Severity;\n data?: Record<string, unknown>;\n}\n\nexport interface ExceptionData {\n type: string;\n value: string;\n stacktrace?: { frames: StackFrame[] };\n}\n\nexport interface EventUser {\n id?: string;\n username?: string;\n email?: string;\n}\n\nexport interface EventPayload {\n /** uuid v4 sem hífens (32 hex), gerado pelo SDK */\n event_id: string;\n /** ISO 8601 */\n timestamp: string;\n level: Severity;\n message?: string;\n exception?: ExceptionData;\n /** Override de agrupamento; quando ausente o servidor calcula pelo stack trace */\n fingerprint?: string[];\n breadcrumbs?: Breadcrumb[];\n tags?: Record<string, string>;\n user?: EventUser;\n contexts?: Record<string, Record<string, unknown>>;\n extra?: Record<string, unknown>;\n release?: string;\n environment?: string;\n}\n\n/** Status de uma step (etapa). `warning` = terminou, mas merece atenção (não é falha). */\nexport type StepStatus = 'ok' | 'warning' | 'error' | 'cancelled';\n\nexport interface StepData {\n /** 16 hex */\n step_id: string;\n parent_step_id?: string;\n op?: string;\n description?: string;\n status?: StepStatus;\n /** ISO 8601 */\n start_timestamp: string;\n /** ISO 8601 (fim da step) */\n timestamp: string;\n data?: Record<string, unknown>;\n}\n\nexport interface TransactionPayload {\n event_id: string;\n name: string;\n op?: string;\n status: string;\n start_timestamp: string;\n timestamp: string;\n steps: StepData[];\n tags?: Record<string, string>;\n release?: string;\n environment?: string;\n /** Correlaciona esta transaction a uma execução de task (ctx.tx dentro de um task handler) */\n run_id?: string;\n}\n\nexport interface HeartbeatPayload {\n instance_id: string;\n name?: string;\n interval_ms: number;\n /** ISO 8601 */\n timestamp: string;\n metadata?: Record<string, unknown>;\n release?: string;\n}\n\nexport type EnvelopeItem =\n | { type: 'event'; payload: EventPayload }\n | { type: 'transaction'; payload: TransactionPayload }\n | { type: 'heartbeat'; payload: HeartbeatPayload };\n\nexport interface Envelope {\n sdk: { name: string; version: string };\n /** ISO 8601 */\n sent_at: string;\n items: EnvelopeItem[];\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
1
+ {"version":3,"sources":["../src/protocol.ts"],"sourcesContent":["/**\n * Tipos do protocolo do Hobots (SDK → servidor).\n *\n * Este arquivo é a fonte de verdade do wire format e só pode reexportar tipos:\n * o app valida os payloads com zod e trava o drift via `satisfies z.ZodType<...>`\n * contra estes tipos (importados de `hobots/protocol`).\n */\n\n// Wire format de tasks (agente ↔ servidor) — reexportado aqui para o app\n// consumir tudo de `hobots/protocol`.\nexport type {\n RunStatus,\n RunUpdateStatus,\n RunLogLevel,\n RunLogEntry,\n InstanceInfo,\n PollRequest,\n RunDescriptor,\n PollResponse,\n RunError,\n RunProgress,\n RunUpdateRequest,\n RunUpdateResponse,\n RunItemStatus,\n RunItemInput,\n RunItemsCreateRequest,\n RunItemsCreateResponse,\n RunAttachmentContentType,\n RunAttachmentCreateRequest,\n RunAttachmentCreateResponse,\n RunAttachmentConfirmRequest,\n RunAttachmentConfirmResponse,\n} from './tasks/protocol';\n\n/**\n * Tipo de um evento — o que ele é, não o quanto ele fala. Só 'fatal', 'error' e\n * 'warning' viram issue; 'success' é ingerido e nunca abre issue.\n * Ruído de log não é evento: para isso existem os logs da execução (RunLogLevel)\n * e as breadcrumbs, que têm níveis próprios com 'debug'/'info'.\n */\nexport type EventType = 'fatal' | 'error' | 'warning' | 'success';\n\n/** Nível de uma breadcrumb — trilha do que aconteceu antes, aqui log é bem-vindo. */\nexport type BreadcrumbLevel = 'debug' | 'info' | 'warning' | 'error' | 'fatal';\n\n/** Frames em ordem innermost-first (mesma ordem impressa pelo V8). */\nexport interface StackFrame {\n function?: string;\n filename?: string;\n lineno?: number;\n colno?: number;\n in_app: boolean;\n}\n\nexport interface Breadcrumb {\n /** ISO 8601 */\n timestamp: string;\n type?: string;\n category?: string;\n message?: string;\n level?: BreadcrumbLevel;\n data?: Record<string, unknown>;\n}\n\nexport interface ExceptionData {\n type: string;\n value: string;\n stacktrace?: { frames: StackFrame[] };\n}\n\nexport interface EventUser {\n id?: string;\n username?: string;\n email?: string;\n}\n\nexport interface EventPayload {\n /** uuid v4 sem hífens (32 hex), gerado pelo SDK */\n event_id: string;\n /** ISO 8601 */\n timestamp: string;\n type: EventType;\n message?: string;\n exception?: ExceptionData;\n /** Override de agrupamento; quando ausente o servidor calcula pelo stack trace */\n fingerprint?: string[];\n breadcrumbs?: Breadcrumb[];\n tags?: Record<string, string>;\n user?: EventUser;\n contexts?: Record<string, Record<string, unknown>>;\n extra?: Record<string, unknown>;\n release?: string;\n environment?: string;\n}\n\n/** Status de uma step (etapa). `warning` = terminou, mas merece atenção (não é falha). */\nexport type StepStatus = 'ok' | 'warning' | 'error' | 'cancelled';\n\nexport interface StepData {\n /** 16 hex */\n step_id: string;\n parent_step_id?: string;\n op?: string;\n description?: string;\n status?: StepStatus;\n /** ISO 8601 */\n start_timestamp: string;\n /** ISO 8601 (fim da step) */\n timestamp: string;\n data?: Record<string, unknown>;\n}\n\nexport interface TransactionPayload {\n event_id: string;\n name: string;\n op?: string;\n status: string;\n start_timestamp: string;\n timestamp: string;\n steps: StepData[];\n tags?: Record<string, string>;\n release?: string;\n environment?: string;\n /** Correlaciona esta transaction a uma execução de task (ctx.tx dentro de um task handler) */\n run_id?: string;\n}\n\nexport interface HeartbeatPayload {\n instance_id: string;\n name?: string;\n interval_ms: number;\n /** ISO 8601 */\n timestamp: string;\n metadata?: Record<string, unknown>;\n release?: string;\n}\n\nexport type EnvelopeItem =\n | { type: 'event'; payload: EventPayload }\n | { type: 'transaction'; payload: TransactionPayload }\n | { type: 'heartbeat'; payload: HeartbeatPayload };\n\nexport interface Envelope {\n sdk: { name: string; version: string };\n /** ISO 8601 */\n sent_at: string;\n items: EnvelopeItem[];\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
@@ -68,6 +68,16 @@ interface RunError {
68
68
  message: string;
69
69
  stack?: string;
70
70
  }
71
+ /**
72
+ * Progresso declarado pelo bot via `ctx.progress` — snapshot absoluto (o servidor
73
+ * sobrescreve, não soma). Opcional: sem ele a plataforma não mostra barra nem ETA.
74
+ */
75
+ interface RunProgress {
76
+ /** itens já processados (0..total) */
77
+ current: number;
78
+ /** total de itens previsto (>= 1) */
79
+ total: number;
80
+ }
71
81
  /** PATCH /v1/tasks/runs/{id} — 'running' pode repetir com logs incrementais */
72
82
  interface RunUpdateRequest {
73
83
  status: RunUpdateStatus;
@@ -75,6 +85,8 @@ interface RunUpdateRequest {
75
85
  error?: RunError;
76
86
  /** Delta desde o último envio — o servidor concatena */
77
87
  logs?: RunLogEntry[];
88
+ /** Snapshot mais recente de `ctx.progress` — pega carona no PATCH que sair (≤ 1/s) */
89
+ progress?: RunProgress;
78
90
  }
79
91
  interface RunUpdateResponse {
80
92
  ok: true;
@@ -155,7 +167,15 @@ interface RunAttachmentConfirmResponse {
155
167
  * contra estes tipos (importados de `hobots/protocol`).
156
168
  */
157
169
 
158
- type Severity = 'fatal' | 'error' | 'warning' | 'info' | 'debug' | 'success';
170
+ /**
171
+ * Tipo de um evento — o que ele é, não o quanto ele fala. Só 'fatal', 'error' e
172
+ * 'warning' viram issue; 'success' é ingerido e nunca abre issue.
173
+ * Ruído de log não é evento: para isso existem os logs da execução (RunLogLevel)
174
+ * e as breadcrumbs, que têm níveis próprios com 'debug'/'info'.
175
+ */
176
+ type EventType = 'fatal' | 'error' | 'warning' | 'success';
177
+ /** Nível de uma breadcrumb — trilha do que aconteceu antes, aqui log é bem-vindo. */
178
+ type BreadcrumbLevel = 'debug' | 'info' | 'warning' | 'error' | 'fatal';
159
179
  /** Frames em ordem innermost-first (mesma ordem impressa pelo V8). */
160
180
  interface StackFrame {
161
181
  function?: string;
@@ -170,7 +190,7 @@ interface Breadcrumb {
170
190
  type?: string;
171
191
  category?: string;
172
192
  message?: string;
173
- level?: Severity;
193
+ level?: BreadcrumbLevel;
174
194
  data?: Record<string, unknown>;
175
195
  }
176
196
  interface ExceptionData {
@@ -190,7 +210,7 @@ interface EventPayload {
190
210
  event_id: string;
191
211
  /** ISO 8601 */
192
212
  timestamp: string;
193
- level: Severity;
213
+ type: EventType;
194
214
  message?: string;
195
215
  exception?: ExceptionData;
196
216
  /** Override de agrupamento; quando ausente o servidor calcula pelo stack trace */
@@ -261,4 +281,4 @@ interface Envelope {
261
281
  items: EnvelopeItem[];
262
282
  }
263
283
 
264
- export type { Breadcrumb, Envelope, EnvelopeItem, EventPayload, EventUser, ExceptionData, HeartbeatPayload, InstanceInfo, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentContentType, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemStatus, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunLogLevel, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, Severity, StackFrame, StepData, StepStatus, TransactionPayload };
284
+ export type { Breadcrumb, BreadcrumbLevel, Envelope, EnvelopeItem, EventPayload, EventType, EventUser, ExceptionData, HeartbeatPayload, InstanceInfo, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentContentType, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemStatus, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunLogLevel, RunProgress, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, StackFrame, StepData, StepStatus, TransactionPayload };
@@ -68,6 +68,16 @@ interface RunError {
68
68
  message: string;
69
69
  stack?: string;
70
70
  }
71
+ /**
72
+ * Progresso declarado pelo bot via `ctx.progress` — snapshot absoluto (o servidor
73
+ * sobrescreve, não soma). Opcional: sem ele a plataforma não mostra barra nem ETA.
74
+ */
75
+ interface RunProgress {
76
+ /** itens já processados (0..total) */
77
+ current: number;
78
+ /** total de itens previsto (>= 1) */
79
+ total: number;
80
+ }
71
81
  /** PATCH /v1/tasks/runs/{id} — 'running' pode repetir com logs incrementais */
72
82
  interface RunUpdateRequest {
73
83
  status: RunUpdateStatus;
@@ -75,6 +85,8 @@ interface RunUpdateRequest {
75
85
  error?: RunError;
76
86
  /** Delta desde o último envio — o servidor concatena */
77
87
  logs?: RunLogEntry[];
88
+ /** Snapshot mais recente de `ctx.progress` — pega carona no PATCH que sair (≤ 1/s) */
89
+ progress?: RunProgress;
78
90
  }
79
91
  interface RunUpdateResponse {
80
92
  ok: true;
@@ -155,7 +167,15 @@ interface RunAttachmentConfirmResponse {
155
167
  * contra estes tipos (importados de `hobots/protocol`).
156
168
  */
157
169
 
158
- type Severity = 'fatal' | 'error' | 'warning' | 'info' | 'debug' | 'success';
170
+ /**
171
+ * Tipo de um evento — o que ele é, não o quanto ele fala. Só 'fatal', 'error' e
172
+ * 'warning' viram issue; 'success' é ingerido e nunca abre issue.
173
+ * Ruído de log não é evento: para isso existem os logs da execução (RunLogLevel)
174
+ * e as breadcrumbs, que têm níveis próprios com 'debug'/'info'.
175
+ */
176
+ type EventType = 'fatal' | 'error' | 'warning' | 'success';
177
+ /** Nível de uma breadcrumb — trilha do que aconteceu antes, aqui log é bem-vindo. */
178
+ type BreadcrumbLevel = 'debug' | 'info' | 'warning' | 'error' | 'fatal';
159
179
  /** Frames em ordem innermost-first (mesma ordem impressa pelo V8). */
160
180
  interface StackFrame {
161
181
  function?: string;
@@ -170,7 +190,7 @@ interface Breadcrumb {
170
190
  type?: string;
171
191
  category?: string;
172
192
  message?: string;
173
- level?: Severity;
193
+ level?: BreadcrumbLevel;
174
194
  data?: Record<string, unknown>;
175
195
  }
176
196
  interface ExceptionData {
@@ -190,7 +210,7 @@ interface EventPayload {
190
210
  event_id: string;
191
211
  /** ISO 8601 */
192
212
  timestamp: string;
193
- level: Severity;
213
+ type: EventType;
194
214
  message?: string;
195
215
  exception?: ExceptionData;
196
216
  /** Override de agrupamento; quando ausente o servidor calcula pelo stack trace */
@@ -261,4 +281,4 @@ interface Envelope {
261
281
  items: EnvelopeItem[];
262
282
  }
263
283
 
264
- export type { Breadcrumb, Envelope, EnvelopeItem, EventPayload, EventUser, ExceptionData, HeartbeatPayload, InstanceInfo, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentContentType, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemStatus, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunLogLevel, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, Severity, StackFrame, StepData, StepStatus, TransactionPayload };
284
+ export type { Breadcrumb, BreadcrumbLevel, Envelope, EnvelopeItem, EventPayload, EventType, EventUser, ExceptionData, HeartbeatPayload, InstanceInfo, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentContentType, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemStatus, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunLogLevel, RunProgress, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, StackFrame, StepData, StepStatus, TransactionPayload };
@@ -16,7 +16,7 @@
16
16
  | `init` | `init(options: InitOptions): void` | — |
17
17
  | `getClient` | `getClient(): Client \| undefined` | cliente atual |
18
18
  | `captureException` | `captureException(error: unknown, hint?: CaptureHint)` | `string \| undefined` (event_id — **não** é confirmação de envio) |
19
- | `captureMessage` | `captureMessage(message: string, level?: Severity, hint?: CaptureHint)` | `string \| undefined` (idem) |
19
+ | `captureMessage` | `captureMessage(message: string, type?: EventType, hint?: CaptureHint)` | `string \| undefined` (idem) |
20
20
  | `addBreadcrumb` | `addBreadcrumb(crumb: BreadcrumbInput): void` | — |
21
21
  | `setTag` / `setTags` | `setTag(key, value)` · `setTags(tags)` | — |
22
22
  | `setUser` | `setUser(user: EventUser \| null): void` | — |
@@ -33,8 +33,8 @@ Os conceitos por trás dessas chamadas estão em [Conceitos](./concepts.md); aqu
33
33
 
34
34
  - **`init(options)`** — cria o cliente, instala os handlers globais de erro (a menos que `captureUnhandled: false`), inicia o heartbeat se configurado e cria o agente de tasks se `tasks` foi passado. Chamar de novo **substitui** o cliente e o agente (os handlers registrados com `register` são preservados; o [scope](./concepts.md#scope) **não** é limpo). Chame uma vez, o mais cedo possível.
35
35
  - **`getClient()`** — devolve o cliente atual ou `undefined` se o `init()` nunca rodou. Útil para checar se o SDK está ativo. A config resolvida (URL base e endpoints) é acessível via `getClient()?.config` (veja abaixo).
36
- - **`captureException(error, hint?)`** — monta um [evento](./concepts.md#evento) a partir da exceção (tipo, mensagem, stack trace), mescla o scope e enfileira. O `level` é `error` a menos que você passe outro no `hint`.
37
- - **`captureMessage(message, level = 'info', hint?)`** — mesma coisa, sem exceção: um evento só com texto. Bom para marcos (`'success'`) e avisos que não são erro.
36
+ - **`captureException(error, hint?)`** — monta um [evento](./concepts.md#evento) a partir da exceção (tipo, mensagem, stack trace), mescla o scope e enfileira. O `type` é `error` a menos que você passe outro no `hint`.
37
+ - **`captureMessage(message, type = 'warning', hint?)`** — mesma coisa, sem exceção: um evento só com texto. Bom para marcos (`'success'`) e avisos que não são erro.
38
38
  > **Atenção:** as duas devolvem um `event_id` **mesmo quando o evento foi descartado** por [amostragem](./concepts.md#amostragem-samplerate-e-tracessamplerate) ou por [`beforeSend`](./concepts.md#beforesend). O retorno não prova que algo foi enviado — só é `undefined` quando o `init()` não foi chamado.
39
39
  - **`addBreadcrumb(crumb)`** — adiciona uma migalha à trilha do [scope](./concepts.md#scope) global. Todos os campos são opcionais menos o `timestamp`, que o SDK preenche. A lista **não** é limpa ao enviar um evento; ela só descarta as mais antigas ao estourar `maxBreadcrumbs`. Conceito: [Breadcrumb](./concepts.md#breadcrumb).
40
40
  - **`setTag(key, value)` / `setTags(tags)`** — grava [tags](./concepts.md#tag) no scope global; valem para todos os eventos seguintes. Só `string` → `string`. Precedência: `init.tags` → scope → evento → `hint`.
@@ -63,8 +63,8 @@ Todos encadeáveis, exceto `finish()` e `toJSON()`.
63
63
 
64
64
  - **`Transaction`** — `startChild(op?, description?) → Step` abre uma [step](./concepts.md#step) direta; `setTag(k, v)` marca a transaction (tags de scope **não** são herdadas); `setStatus(s)` (default `'ok'`); `setName(n)` renomeia antes do envio; `finish()` fecha as etapas abertas, monta o payload e envia — é **idempotente** e não envia nada se a transaction não foi amostrada.
65
65
  - **`Step`** — `startChild(op?, description?) → Step` aninha uma filha (profundidade arbitrária); `setStatus(s)` (default `'ok'`); `setData(k, v)` anexa dados só àquela step; `finish()` marca o fim — **opcional**, porque o `finish()` da transaction fecha o que ficou aberto; `toJSON()` serializa no formato do wire.
66
- - **`Scope`** — `setTag`/`setTags`/`setUser`/`setContext`/`setExtra`/`setExtras`/`setLevel`/`setFingerprint`/`addBreadcrumb`/`clearBreadcrumbs`/`clone`. Alcançável via [`withScope`](./concepts.md#withscope). `setContext(name, null)` apaga o grupo; `setUser(null)` remove o usuário.
67
- > **Atenção:** `setLevel` **não tem efeito** em `captureException` (que já grava `error`) nem em `captureMessage` (que já grava `info`) — os dois definem o level do evento antes de o scope ser consultado. Use o `hint`.
66
+ - **`Scope`** — `setTag`/`setTags`/`setUser`/`setContext`/`setExtra`/`setExtras`/`setType`/`setFingerprint`/`addBreadcrumb`/`clearBreadcrumbs`/`clone`. Alcançável via [`withScope`](./concepts.md#withscope). `setContext(name, null)` apaga o grupo; `setUser(null)` remove o usuário.
67
+ > **Atenção:** `setType` **não tem efeito** em `captureException` (que já grava `error`) nem em `captureMessage` (que já grava `warning`) — os dois definem o tipo do evento antes de o scope ser consultado. Use o `hint`.
68
68
 
69
69
  ---
70
70
 
@@ -124,7 +124,7 @@ O teto da fila do transport (200 itens; ao encher, o **mais antigo** é descarta
124
124
 
125
125
  | Campo | Tipo | Descrição |
126
126
  | --- | --- | --- |
127
- | `level` | `Severity` | nível do evento |
127
+ | `type` | `EventType` | tipo do evento |
128
128
  | `tags` | `Record<string,string>` | tags só deste evento |
129
129
  | `extra` | `Record<string,unknown>` | dados extras |
130
130
  | `fingerprint` | `string[]` | agrupamento manual |
@@ -142,7 +142,7 @@ O teto da fila do transport (200 itens; ao encher, o **mais antigo** é descarta
142
142
 
143
143
  O texto da linha de log é a mensagem capturada; o nível vem do `level` da captura (`'fatal'` vira `'error'`). O upload roda em segundo plano e **nunca bloqueia nem faz a captura lançar**. Limites (todos aplicados também no servidor): PNG/JPEG/WEBP, **5 MB** por imagem, **20** anexos por execução, **4** por linha de log.
144
144
 
145
- `Severity = 'fatal' | 'error' | 'warning' | 'info' | 'debug' | 'success'`. O que cada nível significa (e por que `'success'` existe) em [Level](./concepts.md#level-severity).
145
+ `EventType = 'fatal' | 'error' | 'warning' | 'success'`. O que cada tipo significa (e por que `'success'` existe) em [Type](./concepts.md#type-tipo-do-evento).
146
146
 
147
147
  ### TasksOptions
148
148
 
@@ -169,6 +169,7 @@ interface RunContext {
169
169
  task: string;
170
170
  log: RunLogger; // ctx.log.debug/info/warn/error/success(message)
171
171
  items: RunItems; // ctx.items.add/succeeded/failed/occurrence — a entrega do bot
172
+ progress: RunProgressReporter; // ctx.progress.total/advance/set — barra + tempo restante (opcional)
172
173
  tx: Transaction; // transaction desta run — correlacionada e finalizada pelo SDK
173
174
  signal: AbortSignal;
174
175
  throwIfCanceled(): void; // lança RunCanceled se o signal já disparou
@@ -181,11 +182,20 @@ interface RunItems {
181
182
  failed(message: string, payload?: Record<string, unknown>, options?: { id?: string }): void;
182
183
  occurrence(message: string, payload?: Record<string, unknown>, options?: { id?: string }): void;
183
184
  }
185
+
186
+ interface RunProgressReporter {
187
+ total(total: number): void; // declara/ajusta o total de itens (inteiro >= 1; idempotente)
188
+ advance(count?: number): void; // incrementa os processados (padrão 1; clampa em total)
189
+ set(current: number, total?: number): void; // valor absoluto
190
+ readonly current: number;
191
+ readonly snapshot: RunProgress | null; // { current, total } ou null sem total declarado
192
+ }
184
193
  ```
185
194
 
186
195
  - **`runId` / `task`** — identificam a execução; úteis para correlacionar com os seus próprios logs.
187
196
  - **`log.<level>(message)`** — níveis `debug`, `info`, `warn`, `error` e `success`; enfileira uma linha de log enviada ao app em PATCHes incrementais, e **também imprime no terminal local**. Conceito: [Run](./concepts.md#run).
188
197
  - **`items.<status>(…)`** — registra um item processado/gerado pela execução (síncrono, bufferizado, lotes de até 500). `id` opcional = idempotência dentro da run. Guia: [Itens de execução](./tasks.md#itens-de-execução-ctxitems).
198
+ - **`progress.total(n)` / `progress.advance()` / `progress.set(c, t?)`** — progresso declarado da run (itens processados / total), opcional e independente de `items`; o app mostra barra, percentual e tempo restante. Síncrono; o snapshot pega carona nos PATCHes de log e sai no máximo 1×/s por conta própria. Guia: [Progresso da execução](./tasks.md#progresso-da-execução-ctxprogress).
189
199
  - **`tx`** — a [transaction de performance da run](./concepts.md#transaction-de-run-ctxtx). Use `ctx.tx.startChild(op, description)` para medir as etapas; **não** chame `finish()` nela. `startTransaction()` dentro do handler devolve essa mesma transaction.
190
200
  - **`signal`** — `AbortSignal` que dispara em cancelamento no app, timeout ou shutdown.
191
201
  - **`throwIfCanceled()`** — o ponto de parada de uma linha: lança `RunCanceled` se o `signal` já disparou.
package/docs/concepts.md CHANGED
@@ -11,7 +11,7 @@ Os defaults numéricos **não** estão aqui — eles vivem em [Tempos e interval
11
11
  | Conceito | Em uma frase | Quando você usa |
12
12
  | ------------------------------------------------------- | -------------------------------------------------------- | ----------------------------------------------------------- |
13
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 |
14
+ | [Type](#type-tipo-do-evento) | O que o evento é | Para separar o que acorda alguém do que é só registro |
15
15
  | [Fingerprint](#fingerprint) | A chave de agrupamento dos eventos em issues | Quando o agrupamento automático erra |
16
16
  | [Scope](#scope) | A mochila de contexto que viaja com todo evento | Você raramente mexe direto; mexe pelos `set*` |
17
17
  | [withScope](#withscope) | Um scope temporário e isolado | Contexto que **não** deve vazar para o resto do programa |
@@ -58,28 +58,30 @@ No servidor, eventos parecidos são agrupados numa **issue** — o que você vê
58
58
 
59
59
  ```js
60
60
  Hobots.captureException(error);
61
- Hobots.captureMessage('Lote processado com sucesso', 'info');
61
+ Hobots.captureMessage('Lote processado com sucesso', 'success');
62
62
  ```
63
63
 
64
64
  As duas devolvem o `event_id` (um uuid), útil para você logar junto e depois achar o evento no app.
65
65
 
66
66
  > **Atenção:** o `event_id` devolvido **não é confirmação de envio**. Se o evento foi descartado por [amostragem](#amostragem-samplerate-e-tracessamplerate) ou por [`beforeSend`](#beforesend), você recebe um id normalmente e nada foi enfileirado. Elas devolvem `undefined` só quando o `init()` nunca foi chamado.
67
67
 
68
- ## Level (Severity)
68
+ ## Type (tipo do evento)
69
69
 
70
- O **level** é a gravidade do evento. São seis: `fatal`, `error`, `warning`, `info`, `debug` e `success`.
70
+ O **type** é o que o evento é. São quatro: `fatal`, `error`, `warning` e `success`.
71
71
 
72
- Serve para você separar o que precisa acordar alguém do que é só registro histórico. `error` e `fatal` são problemas; `warning` é algo que ainda funciona mas merece olhar; `info` e `debug` são narração.
72
+ Serve para você separar o que precisa acordar alguém do que é só registro. `error` e `fatal` são problemas (e são os únicos que disparam alerta); `warning` é algo que ainda funciona mas merece olhar — aparece nas issues, sem e-mail.
73
73
 
74
74
  O `success` é o mais incomum e o mais útil no dia a dia de RPA: ele marca uma conclusão **bem-sucedida**. Conta como evento e aparece na timeline do robô, mas **não gera issue nem alerta** — é o jeito de registrar "as 120 notas do dia foram emitidas" sem que isso pareça um problema.
75
75
 
76
- A precedência é: o `level` do `hint` que você passou → o level que a função de captura já definiu → `error`. Na prática:
76
+ Não existe tipo `info` nem `debug`: narração não é problema e não abre issue. Para isso existem o [log da execução](./tasks.md#logs) (`ctx.log.debug/info/...`) e as [breadcrumbs](#breadcrumb), que têm o seu próprio nível.
77
+
78
+ A precedência é: o `type` do `hint` que você passou → o tipo que a função de captura já definiu → `error`. Na prática:
77
79
 
78
80
  - `captureException(error)` sem `hint` é sempre `error`.
79
- - `captureMessage(msg)` sem level explícito é sempre `info`.
80
- - Para qualquer outra coisa, passe o level: `captureException(error, { level: 'warning' })`.
81
+ - `captureMessage(msg)` sem tipo explícito é sempre `warning`.
82
+ - Para qualquer outra coisa, passe o tipo: `captureException(error, { type: 'warning' })`.
81
83
 
82
- > **Atenção:** `scope.setLevel()` (dentro de um [`withScope`](#withscope)) **não afeta** `captureException` nem `captureMessage` — as duas já definem o level do evento antes de o scope ser consultado. Para mudar o level, use o `hint` (`{ level: … }`) ou o segundo argumento do `captureMessage`.
84
+ > **Atenção:** `scope.setType()` (dentro de um [`withScope`](#withscope)) **não afeta** `captureException` nem `captureMessage` — as duas já definem o tipo do evento antes de o scope ser consultado. Para mudar o tipo, use o `hint` (`{ type: … }`) ou o segundo argumento do `captureMessage`.
83
85
 
84
86
  ## Fingerprint
85
87
 
@@ -100,7 +102,7 @@ O fingerprint é um array de strings — o servidor concatena os pedaços. Prece
100
102
 
101
103
  ## Scope
102
104
 
103
- O **scope** é a mochila de contexto do SDK: tudo que ele sabe sobre o estado atual do seu programa e que deve viajar junto do próximo evento. Dentro dela: [tags](#tag), [usuário](#scope), [contexts](#context), [extras](#extra), level, [fingerprint](#fingerprint) e a lista de [breadcrumbs](#breadcrumb).
105
+ O **scope** é a mochila de contexto do SDK: tudo que ele sabe sobre o estado atual do seu programa e que deve viajar junto do próximo evento. Dentro dela: [tags](#tag), [usuário](#scope), [contexts](#context), [extras](#extra), tipo, [fingerprint](#fingerprint) e a lista de [breadcrumbs](#breadcrumb).
104
106
 
105
107
  Você não manipula o scope direto — você usa os `set*`, que escrevem nele:
106
108
 
@@ -203,7 +205,7 @@ O SDK mantém em memória a lista dos últimos breadcrumbs. Quando você captura
203
205
  Hobots.addBreadcrumb({ category: 'nfe', message: 'Baixou XML da nota 123', level: 'info' });
204
206
  ```
205
207
 
206
- Todos os campos são opcionais: `category` agrupa a origem, `message` é o texto lido por humano, `level` colore a linha na timeline, `data` carrega um objeto livre, `type` classifica a migalha. O `timestamp` é preenchido pelo SDK.
208
+ Todos os campos são opcionais: `category` agrupa a origem, `message` é o texto lido por humano, `level` (`debug`, `info`, `warning`, `error`, `fatal`) colore a linha na timeline, `data` carrega um objeto livre, `type` classifica a migalha. O `timestamp` é preenchido pelo SDK.
207
209
 
208
210
  Dois pontos que costumam surpreender:
209
211
 
package/docs/protocol.md CHANGED
@@ -84,11 +84,13 @@ Corpo `RunUpdateRequest`:
84
84
  { "ts": "…", "level": "info", "message": "portal após o login",
85
85
  "attachments": ["<32 hex>"] } // opcional; ids de anexo (máx. 4)
86
86
  ],
87
- "error": { "type": "Error", "message": "…", "stack": "…" } // só com 'failed'
87
+ "error": { "type": "Error", "message": "…", "stack": "…" }, // só com 'failed'
88
+ "progress": { "current": 10, "total": 100 } // opcional (SDK ≥ 2.1): snapshot de ctx.progress
88
89
  }
89
90
  ```
90
91
 
91
92
  - `status: 'running'` pode repetir para enviar **logs incrementais** (throttle por `logFlushIntervalMs`, flush ao acumular 20, chunk de 100, teto de 1000 logs/run, mensagem cap 10k).
93
+ - `progress` é **opcional** e aditivo (SDK ≥ 2.1): snapshot absoluto de `ctx.progress` (`current` 0..total, `total` ≥ 1) — o servidor **sobrescreve** `progress_current`/`progress_total` e grava `progress_started_at` no primeiro PATCH que o traga (relógio do servidor; âncora da ETA no app). `current > total` é clampado; progresso enviado a uma run já terminal é descartado. O SDK anexa o snapshot mais recente a **qualquer** PATCH que saia (logs, heartbeat, terminal) e só dispara um PATCH próprio, no máximo 1/s, quando nada mais está a caminho. Uma API anterior ao recurso descarta o campo (objeto não-strict).
92
94
  - `attachments` é **opcional** e aditivo: SDKs anteriores à 1.9 simplesmente não enviam o campo, e uma API anterior ao recurso descarta o campo na validação (objeto não-strict) sem rejeitar o PATCH. Os ids são gerados pelo agente **antes** do upload, o que permite enviar a linha de log sem esperar o binário subir.
93
95
  - Ordem garantida `running → logs → terminal` (PATCHes serializados numa cadeia de promise).
94
96
  - Retry `[1s, 4s, 16s]`; 4xx exceto 429 é terminal (**409** = run cancelada / transição inválida → marca a run como morta e dispara `ctx.signal`).
package/docs/tasks.md CHANGED
@@ -130,6 +130,44 @@ Diferente de logs e status, **uma run cancelada continua aceitando itens**: item
130
130
 
131
131
  > **Dados sensíveis:** como os logs, `message` e `payload` **são enviados ao servidor** e ficam visíveis na solicitação. Não inclua senhas, tokens ou dados pessoais desnecessários.
132
132
 
133
+ ## Progresso da execução (`ctx.progress`)
134
+
135
+ Itens contam **o que** foi entregue; progresso conta **quanto falta**. Quando a run percorre uma lista de tamanho conhecido, declare o total e avance a cada item — a solicitação no app mostra barra, percentual (`10/100 · 10%`) e **tempo restante estimado** (calculado pela taxa média desde o primeiro reporte).
136
+
137
+ ```js
138
+ Hobots.register('baixar-notas', async (params, ctx) => {
139
+ const notas = await listarNotas(params);
140
+ ctx.progress.total(notas.length); // antes do loop
141
+ for (const nota of notas) {
142
+ await baixar(nota);
143
+ ctx.progress.advance(); // a cada item
144
+ }
145
+ });
146
+ ```
147
+
148
+ ### API
149
+
150
+ ```ts
151
+ interface RunProgressReporter {
152
+ total(total: number): void; // declara/ajusta o total (inteiro >= 1; idempotente)
153
+ advance(count?: number): void; // incrementa os processados (padrão 1; clampa em total)
154
+ set(current: number, total?: number): void; // valor absoluto (ex.: retomada de um lote)
155
+ readonly current: number; // processados até agora
156
+ readonly snapshot: { current: number; total: number } | null; // null sem total declarado
157
+ }
158
+ ```
159
+
160
+ - **Opcional** — sem `total()` nada é enviado e a UI não mostra barra nem estimativa. `advance()` antes do `total()` só guarda o contador.
161
+ - **Independente de `ctx.items`** — use um, outro ou ambos; nenhum avança o outro (um item pode gerar vários registros, ou nenhum).
162
+ - **Total inválido** (`0`, negativo, não-inteiro) é ignorado com um aviso único no terminal.
163
+ - **Semântica de snapshot** — o servidor sobrescreve `current`/`total` com o último valor recebido; não há soma. Ajustar o `total` no meio da run (a lista cresceu) é permitido.
164
+
165
+ ### Como funciona (e por que não gera tráfego)
166
+
167
+ As chamadas são **síncronas e baratas**: o snapshot mais recente pega carona em **qualquer** PATCH que saia — logs incrementais, heartbeat de cancelamento, reporte terminal. Só quando nada mais está a caminho o SDK dispara um PATCH `running` próprio, no máximo **1 por segundo**. `advance()` num loop de 10.000 itens em 5 segundos gera ~5 PATCHes, não 10.000. O PATCH terminal (`succeeded`/`failed`) **sempre** leva o valor final — uma run que falhou aparece como `87/100`.
168
+
169
+ O servidor grava `progress_started_at` no primeiro PATCH com progresso (relógio do servidor) — é a âncora da estimativa de término, calculada no app: `restante = decorrido × (total − current) ÷ current`.
170
+
133
171
  ## Anexos (screenshots)
134
172
 
135
173
  Texto de log conta o que o robô fez; um print conta **como estava a tela**. Os métodos de captura — `Hobots.captureException(error, hint)` e `Hobots.captureMessage(message, level, hint)` — aceitam um **`attachment`** no hint. Dentro de uma execução de task, a imagem vira anexo da linha de log da run que espelha a captura, e aparece como miniatura logo abaixo dela na aba "logs do agente" da execução — clicável para ampliar.
@@ -171,7 +209,7 @@ O `attachment` pode ser a imagem direto, ou um objeto com metadados:
171
209
  | `caption` | `string` | — | legenda curta exibida sob a imagem ampliada (máx. 200 caracteres) |
172
210
  | `contentType` | `'image/png' \| 'image/jpeg' \| 'image/webp'` | detectado | só é usado quando os *magic bytes* não bastam |
173
211
 
174
- O texto da linha de log é a própria mensagem capturada (`message` do `captureMessage`, ou `Nome: mensagem` do erro no `captureException`); o nível vem do `level` da captura (`'fatal'` vira `'error'` no log da run).
212
+ O texto da linha de log é a própria mensagem capturada (`message` do `captureMessage`, ou `Nome: mensagem` do erro no `captureException`); o nível vem do `type` da captura (`'fatal'` vira `'error'` no log da run).
175
213
 
176
214
  ### Limites
177
215
 
package/docs/telemetry.md CHANGED
@@ -29,7 +29,7 @@ Cria o cliente, instala handlers globais de erro (a menos que `captureUnhandled:
29
29
  // exceções
30
30
  Hobots.captureException(error, {
31
31
  tags: { cliente: 'acme' },
32
- level: 'error',
32
+ type: 'error',
33
33
  fingerprint: ['sefaz-timeout'], // agrupa manualmente; sem isso o servidor agrupa pelo stack
34
34
  });
35
35
 
@@ -37,13 +37,13 @@ Hobots.captureException(error, {
37
37
  Hobots.captureMessage('Lote processado com sucesso', 'info');
38
38
  ```
39
39
 
40
- Ambas retornam o `event_id` (ou `undefined` se não inicializado). O segundo argumento é um [`CaptureHint`](./api-reference.md#capturehint) (`level`, `tags`, `extra`, `fingerprint`).
40
+ Ambas retornam o `event_id` (ou `undefined` se não inicializado). O segundo argumento é um [`CaptureHint`](./api-reference.md#capturehint) (`type`, `tags`, `extra`, `fingerprint`).
41
41
 
42
42
  > **Atenção:** o `event_id` retornado **não é confirmação de envio** — se o evento foi descartado por amostragem ou por `beforeSend`, você recebe um id normalmente e nada foi enfileirado.
43
43
 
44
44
  Erros **não tratados** (`uncaughtException` / `unhandledRejection`) são capturados e flushados automaticamente antes do processo sair — desligue com `captureUnhandled: false`.
45
45
 
46
- Conceitos: [Evento](./concepts.md#evento) · [Level](./concepts.md#level-severity) · [Fingerprint](./concepts.md#fingerprint).
46
+ Conceitos: [Evento](./concepts.md#evento) · [Type](./concepts.md#type-tipo-do-evento) · [Fingerprint](./concepts.md#fingerprint).
47
47
 
48
48
  ## Breadcrumbs
49
49
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hobots",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "SDK JavaScript da Hobots — telemetria (erros, transactions e heartbeats) e execução remota de tarefas sob demanda (tasks) para RPAs e integrações. Um único import e um único init().",
5
5
  "type": "module",
6
6
  "sideEffects": false,