@zihin/agent-client 0.2.0 → 0.4.1

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/README.md CHANGED
@@ -118,6 +118,54 @@ await zihin.deleteTrigger({ triggerId }); // remove (requer key admin/owner;
118
118
  > `trigger_config` inteiro). Há uma janela entre o GET e o PATCH em que uma escrita concorrente
119
119
  > pode ser sobrescrita.
120
120
 
121
+ ## Uso — delegação assíncrona durável (tasks)
122
+
123
+ Para runs que estouram o teto do host (o passo do Zapier morre em ~30s) ou que não podem depender
124
+ da conexão viva. A task é persistida no BE, executada fora do request e o resultado notificado.
125
+ Exige key **owner/admin** para delegar e cancelar; editor só lê.
126
+
127
+ ```ts
128
+ const task = await zihin.delegateTask({
129
+ agentId,
130
+ instruction: 'Gere o relatório mensal e envie por e-mail',
131
+ title: 'Relatório de agosto',
132
+ payload: { mes: '2026-08' }, // objeto, ≤ 64KB — validado localmente
133
+ deadlineAt: new Date(Date.now() + 3600e3),
134
+ });
135
+
136
+ // polling: pare quando `terminal` for true — qualquer status desconhecido conta como terminal
137
+ let dossie;
138
+ do {
139
+ await new Promise((r) => setTimeout(r, 2000));
140
+ dossie = await zihin.getTask({ taskId: task.id });
141
+ } while (!dossie.task.terminal);
142
+
143
+ console.log(dossie.task.status, dossie.executions.at(-1)?.totalCostUsd);
144
+ await zihin.cancelTask({ taskId: task.id, reason: 'mudou o plano' }); // 409 se já encerrou
145
+ ```
146
+
147
+ `listTasks({ live: true })` devolve o board (`items` + `total`, este vindo de `meta`).
148
+
149
+ ## O texto que volta — e o que NÃO é resposta
150
+
151
+ `AgentResult.contentSource` diz de onde veio o `content`:
152
+
153
+ - **`'response'`** — o evento final do BE, que no `/stream` é sempre o modo `final` (só as mensagens
154
+ sem `tool_calls`). **É o texto seguro para publicar.**
155
+ - **`'tokens'`** — o stream morreu antes do evento final e sobrou só a concatenação crua dos deltas,
156
+ **com a deliberação interna do modelo**. Foi exatamente isso que vazou para um lead real via
157
+ WhatsApp e que o BE parou de entregar. Trate como parcial de diagnóstico; não mande para fora
158
+ sem revisar.
159
+
160
+ Um `content` vazio com `contentSource: 'response'` é decisão do BE, não lacuna — o núcleo **não**
161
+ preenche com tokens. E quando o BE fecha o stream com `event: error` (turno morto no deadline, por
162
+ exemplo), o núcleo **lança** (`kind: 'timeout'`, com `code` e `requestId`) em vez de devolver o
163
+ parcial. Se foi timeout ou não vem do campo **explícito `error.timed_out`** do evento (BE desde
164
+ 26/09/2026): quando presente ele é autoritativo — `true` é timeout seja qual for o `code`, `false`
165
+ é `server` mesmo com "TIMEOUT" no nome. Sem o campo (backend antigo), cai no fallback por nome
166
+ (`/TIMEOUT/i` no `code`), que será removido numa major futura. Não ramifique no **nome** do código
167
+ de timeout: o BE vai renomeá-los (zihin-agent-builder#561); use `kind`.
168
+
121
169
  ## Dropdown de agentes
122
170
 
123
171
  ```ts
@@ -140,10 +188,33 @@ suporte correlacionar). Erros de rede carregam a causa-raiz (`ECONNREFUSED`/`ENO
140
188
  mensagem. As mensagens redigem **só segredos** (key/Bearer/stack) — `request_id`/`trace_id` são
141
189
  preservados. Cancele com `AbortSignal` via `params.signal`.
142
190
 
191
+ ### Bloqueio comercial não é credencial errada
192
+
193
+ Trial vencido, assinatura inativa e quota esgotada chegam do BE como 403/429 e, mapeados só pelo
194
+ status, virariam "troque a key" ou "tente de novo". Têm `kind` próprio e **nunca são retentados**:
195
+
196
+ | `kind` | HTTP | O que fazer |
197
+ |---|---|---|
198
+ | `trial_expired` | 403 | Fazer upgrade do plano — a key está certa |
199
+ | `no_subscription` | 429 | Ativar uma assinatura — a key está certa |
200
+ | `quota_exceeded` | 429 | Esperar o ciclo ou subir o plano — retentar não ajuda |
201
+
202
+ `isCommercialBlock(body)` é exportado para quem inspeciona respostas por conta própria. O 403 comum
203
+ (`kind: 'auth'`) significa que o **papel** da key não permite a ação — e o papel exigido varia por
204
+ rota (editor cria trigger, mas não delega task), então a mensagem não enumera papéis.
205
+
206
+ `ZihinAgentError.executionId` vem preenchido quando o BE o entrega (sucesso e erro) — é o handle para
207
+ correlacionar com o console.
208
+
143
209
  ## Robustez (timeout / retry / limites)
144
210
 
145
- - **Timeout cobre tudo** — headers e o corpo/stream inteiro. `timeoutMs` (default 120s, igual ao cap
146
- do BE) é o tempo máximo total da chamada; `AbortSignal` do chamador funciona durante todo o stream.
211
+ - **Timeout cobre tudo** — headers e o corpo/stream inteiro. Os defaults são **por operação**
212
+ (`OPERATION_TIMEOUT_MS`): stream 200s, dispatch de trigger 260s, GETs curtos 35s. Cada um fica
213
+ ACIMA do orçamento do BE para aquela rota (SSE 180s, turno async 240s, timeout global 30s), para
214
+ que o desfecho do servidor chegue como erro estruturado em vez de abort cego do cliente.
215
+ `timeoutMs` na config sobrepõe todos; `options.timeoutMs` por chamada vai ao BE como
216
+ `timeout_ms` e o deadline local fica um pouco acima dele. Retentativas NÃO encolhem o deadline
217
+ da tentativa seguinte — o que fica limitado é o tempo gasto com retries.
147
218
  - **Retry sem duplicar efeito** — GET (idempotente) retenta em rede/429/5xx; POST com efeito
148
219
  (`invokeAgent`/`dispatchAgent`/`createTrigger`) retenta **só em 429/503** e **nunca em erro de
149
220
  rede** (o request pode ter sido processado). Backoff exponencial 1s→10s com jitter.
package/dist/index.d.mts CHANGED
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * @module errors
6
6
  */
7
- type ZihinErrorKind = 'config' | 'auth' | 'not_found' | 'rate_limit' | 'timeout' | 'aborted' | 'server' | 'network' | 'protocol' | 'unknown';
7
+ type ZihinErrorKind = 'config' | 'auth' | 'trial_expired' | 'no_subscription' | 'quota_exceeded' | 'not_found' | 'rate_limit' | 'timeout' | 'aborted' | 'server' | 'network' | 'protocol' | 'unknown';
8
8
  /** Contexto opcional anexado a um erro para correlação/depuração. */
9
9
  interface ErrorMeta {
10
10
  /** Ex.: "POST llm.zihin.ai/api/v2/agents/abc/stream". */
@@ -18,6 +18,11 @@ interface ZihinErrorOptions extends ErrorMeta {
18
18
  retryable?: boolean;
19
19
  /** request_id do BE (NÃO é segredo) — handle para o suporte correlacionar nos logs. */
20
20
  requestId?: string;
21
+ /**
22
+ * execution_id do turno. Desde agosto/2026 o BE o entrega no sucesso E no erro (§1.2 do
23
+ * handoff) — é o handle que liga um erro daqui à execução no console/telemetria.
24
+ */
25
+ executionId?: string;
21
26
  }
22
27
  declare class ZihinAgentError extends Error {
23
28
  readonly kind: ZihinErrorKind;
@@ -27,8 +32,14 @@ declare class ZihinAgentError extends Error {
27
32
  readonly requestId?: string;
28
33
  readonly attempts?: number;
29
34
  readonly endpoint?: string;
35
+ readonly executionId?: string;
30
36
  constructor(kind: ZihinErrorKind, message: string, opts?: ZihinErrorOptions);
31
37
  }
38
+ /**
39
+ * true quando o corpo é um bloqueio comercial. Consultado pelo transporte ANTES de retentar:
40
+ * o 429 de quota é indistinguível de rate limit pelo status, e só o corpo separa os dois.
41
+ */
42
+ declare function isCommercialBlock(body: unknown): boolean;
32
43
 
33
44
  /**
34
45
  * Tipos do contrato externo Zihin — fonte de verdade verificada contra o BE
@@ -42,7 +53,13 @@ interface ZihinClientConfig {
42
53
  apiKey: string;
43
54
  /** Base URL. Default: https://llm.zihin.ai */
44
55
  baseUrl?: string;
45
- /** Timeout por request (ms). Default: 120000 (= limite do /stream no BE). */
56
+ /**
57
+ * Timeout por request (ms) — override explícito que vale para TODAS as operações. Deixe vazio
58
+ * para usar o default de cada uma (`OPERATION_TIMEOUT_MS`): stream 200s, dispatch de trigger
59
+ * 260s, GETs curtos 35s. Cada default fica acima do orçamento do BE para aquela rota, para que
60
+ * o desfecho do servidor chegue como erro estruturado em vez de abort do cliente — um valor
61
+ * único menor que isso reintroduz exatamente esse problema.
62
+ */
46
63
  timeoutMs?: number;
47
64
  /** Máximo de retentativas em 5xx/429/erro de rede. Default: 2. */
48
65
  maxRetries?: number;
@@ -137,6 +154,16 @@ interface Source {
137
154
  */
138
155
  interface AgentResult {
139
156
  content: string;
157
+ /**
158
+ * De onde veio o `content`:
159
+ * - `response`: o evento SSE `response` — o texto que o BE decidiu entregar. No `/stream` isso é
160
+ * sempre o modo `final` (só as mensagens sem `tool_calls`). É o texto seguro para publicar.
161
+ * - `tokens`: o stream terminou SEM o evento `response` e sobrou apenas a concatenação crua dos
162
+ * deltas de todas as iterações — **deliberação interna incluída**. Foi exatamente esse texto
163
+ * que vazou para um lead real e que o BE parou de entregar. Trate como parcial de diagnóstico:
164
+ * não publique em canal externo sem revisar.
165
+ */
166
+ contentSource: 'response' | 'tokens';
140
167
  sessionId?: string;
141
168
  model?: string;
142
169
  usage?: Usage;
@@ -194,6 +221,39 @@ type AgentStreamChunk = {
194
221
  type: 'warning';
195
222
  message?: string;
196
223
  data: unknown;
224
+ }
225
+ /**
226
+ * O BE encerra o stream com `event: error` + `{ error: { type, code, message, request_id,
227
+ * timed_out? } }` (`utils/sse.js`) — inclusive quando o TURNO morre no deadline. Antes isto
228
+ * caía em `unknown` e o `invokeAgent` ignorava: se já tinha chegado token, devolvia a
229
+ * deliberação crua como SUCESSO.
230
+ *
231
+ * `timedOut` espelha `error.timed_out` (BE desde 26/09/2026, zihin-agent-builder#605): campo
232
+ * explícito que diz se o turno morreu por timeout, independente do NOME do código — os códigos
233
+ * vão ser renomeados (zihin-agent-builder#561). `undefined` = backend antigo, sem o campo.
234
+ */
235
+ | {
236
+ type: 'error';
237
+ code?: string;
238
+ message?: string;
239
+ requestId?: string;
240
+ timedOut?: boolean;
241
+ data: unknown;
242
+ }
243
+ /**
244
+ * Emitido no meio do turno SÓ quando o BE cunhou uma sessão diferente da pedida
245
+ * (`agents-v2.js:931`). É o id em que a conversa realmente vai continuar — vence o `session_id`
246
+ * do `metadata` e o que o chamador mandou. Visto em produção no E2E de 01/09.
247
+ */
248
+ | {
249
+ type: 'session';
250
+ sessionId?: string;
251
+ data: unknown;
252
+ }
253
+ /** Fim LIMPO do stream (`utils/sse.js:92`). Sem ele, o stream foi cortado no meio. */
254
+ | {
255
+ type: 'done';
256
+ data: unknown;
197
257
  } | {
198
258
  type: 'response';
199
259
  content: string;
@@ -217,6 +277,19 @@ type AgentStreamChunk = {
217
277
  data: unknown;
218
278
  };
219
279
  type TriggerMode = 'sync' | 'async';
280
+ /**
281
+ * Como o BE monta o texto entregue pelo trigger (`#343/#345`):
282
+ * - `final` (default): só as mensagens do agente SEM `tool_calls`. O texto que acompanha uma
283
+ * chamada de tool é deliberação interna — vazou para um lead real via WhatsApp.
284
+ * - `concat` (legado): concatena todas as iterações. Só peça se você realmente depende da
285
+ * narrativa completa e o destino é interno.
286
+ *
287
+ * ⚠️ O BE lê isto de `trigger_config.execution.callback.response_transform.mode`, e o validador dele
288
+ * exige `callback.url` sempre que o objeto `callback` existe. Consequência verificada em 2026-08-31:
289
+ * **um trigger `sync` não consegue declarar o modo sem uma callbackUrl válida**, mesmo que ela nunca
290
+ * seja chamada. `createTrigger` falha alto nesse caso em vez de mandar um config que o BE rejeita.
291
+ */
292
+ type ResponseTransformMode = 'final' | 'concat';
220
293
  /**
221
294
  * Como o BE extrai a mensagem do agente a partir do payload de disparo (webhook):
222
295
  * - `field`: um campo top-level do payload (`payload[field]`).
@@ -254,6 +327,11 @@ interface CreateTriggerParams {
254
327
  queryExtraction?: QueryExtraction;
255
328
  /** Auth de disparo aceita. Default: api_key. */
256
329
  dispatchAuth?: 'api_key' | 'signature' | 'none';
330
+ /**
331
+ * Modo de extração da resposta do agente. Default do BE: `final`. Exige `callbackUrl` — ver
332
+ * {@link ResponseTransformMode}.
333
+ */
334
+ responseTransform?: ResponseTransformMode;
257
335
  signal?: AbortSignal;
258
336
  }
259
337
  /**
@@ -273,6 +351,11 @@ interface UpdateTriggerParams {
273
351
  callbackMethod?: 'POST' | 'PUT';
274
352
  /** Troca o modo de execução (sync/async). */
275
353
  mode?: TriggerMode;
354
+ /**
355
+ * Troca o modo de extração da resposta, preservando `strip_markdown`/`max_length` já
356
+ * configurados. Exige que o trigger já tenha um `callback` (ou que `callbackUrl` venha junto).
357
+ */
358
+ responseTransform?: ResponseTransformMode;
276
359
  signal?: AbortSignal;
277
360
  }
278
361
  interface TriggerRef {
@@ -321,18 +404,42 @@ interface DispatchAgentParams {
321
404
  signal?: AbortSignal;
322
405
  }
323
406
  interface DispatchResult {
324
- /** true quando o BE retorna 202 (async) ou 200 (sync). */
407
+ /**
408
+ * true quando o BE aceitou o disparo. Um turno que FALHOU não chega aqui: o BE responde 200
409
+ * com `{ success:false }` e o núcleo lança — antes isso voltava como disparo aceito com
410
+ * resposta vazia.
411
+ */
325
412
  accepted: boolean;
326
413
  triggerId?: string;
414
+ agentId?: string;
327
415
  executionId?: string;
328
416
  /** Presente quando o trigger é `sync`: resposta imediata do agente. */
329
417
  agentResponse?: string;
330
418
  usage?: Usage;
419
+ /** Iterações do laço de tools no turno (sync). */
420
+ iterations?: number;
421
+ /** Latência medida pelo BE, ponta a ponta (sync). */
422
+ latencyMs?: number;
423
+ /**
424
+ * O texto que o BE extraiu do payload e entregou ao agente. É o campo que denuncia um
425
+ * `query_extraction` mal configurado — mensagem vazia, ou o payload inteiro virando a pergunta.
426
+ */
427
+ queryExtracted?: string;
331
428
  raw: unknown;
332
429
  }
333
430
  interface ExecutionStatus {
334
431
  executionId: string;
432
+ /**
433
+ * Status cru do BE. Além de `completed`/`failed`, o `trigger_executions` passou a registrar
434
+ * `skipped`, `quota_exhausted` e `timeout` de verdade (agosto/2026) — não trate como enum
435
+ * fechado; use `terminal` para decidir quando parar de esperar.
436
+ */
335
437
  status: string;
438
+ /**
439
+ * false só enquanto a execução está em andamento. Qualquer status desconhecido conta como
440
+ * terminal, para que o polling pare em vez de girar para sempre num estado novo do BE.
441
+ */
442
+ terminal: boolean;
336
443
  startedAt?: string;
337
444
  finishedAt?: string;
338
445
  executionTimeMs?: number;
@@ -345,6 +452,145 @@ interface GetExecutionParams {
345
452
  executionId: string;
346
453
  signal?: AbortSignal;
347
454
  }
455
+ interface DelegateTaskParams {
456
+ /** Agente que vai executar. */
457
+ agentId: string;
458
+ /** O que o agente deve fazer (obrigatório; sofre guardrail de tamanho por tier no BE). */
459
+ instruction: string;
460
+ /** Título legível para o board. */
461
+ title?: string;
462
+ /** Payload congelado junto da task. Objeto, **≤ 64KB** (teto do BE). */
463
+ payload?: Record<string, unknown>;
464
+ /**
465
+ * Quando executar. Omita para disparar imediatamente. Não pode estar no passado (o BE tolera
466
+ * 60s de diferença de relógio).
467
+ */
468
+ scheduledFor?: string | Date;
469
+ /** Prazo. Precisa ser futuro e posterior a `scheduledFor`; ao vencer, o sweep aborta o turno. */
470
+ deadlineAt?: string | Date;
471
+ retryPolicy?: Record<string, unknown>;
472
+ notify?: Record<string, unknown>;
473
+ /** Encadeia esta task sob outra (árvore parent/root). */
474
+ parentTaskId?: string;
475
+ signal?: AbortSignal;
476
+ }
477
+ /** Task como o BE a devolve. Campos além dos mapeados ficam em `raw`. */
478
+ interface TaskInfo {
479
+ id: string;
480
+ agentId?: string;
481
+ title?: string;
482
+ instruction?: string;
483
+ /** `pending` → `running` → `completed` | `failed` | `cancelled` | `expired`. Não é enum fechado. */
484
+ status: string;
485
+ /**
486
+ * false só enquanto a task está em andamento. Status desconhecido conta como terminal (parar é
487
+ * melhor que girar para sempre); status ausente conta como não-terminal (não lemos o envelope).
488
+ */
489
+ terminal: boolean;
490
+ scheduledFor?: string;
491
+ deadlineAt?: string;
492
+ parentTaskId?: string;
493
+ rootTaskId?: string;
494
+ sessionId?: string;
495
+ createdAt?: string;
496
+ updatedAt?: string;
497
+ raw: unknown;
498
+ }
499
+ interface ListTasksParams {
500
+ /** Só estados vivos (board do dia a dia). */
501
+ live?: boolean;
502
+ status?: string;
503
+ agentId?: string;
504
+ rootTaskId?: string;
505
+ limit?: number;
506
+ offset?: number;
507
+ signal?: AbortSignal;
508
+ }
509
+ interface TaskListPage {
510
+ items: TaskInfo[];
511
+ /** Total do tenant para a query. Vem de `meta.total` no envelope novo. */
512
+ total?: number;
513
+ raw: unknown;
514
+ }
515
+ /** Uma execução da task (uma task pode ter várias, com retry). */
516
+ interface TaskExecution {
517
+ id: string;
518
+ status: string;
519
+ exitReason?: string;
520
+ modelUsed?: string;
521
+ totalCostUsd?: number;
522
+ executionTimeMs?: number;
523
+ iterations?: number;
524
+ startedAt?: string;
525
+ finishedAt?: string;
526
+ raw: unknown;
527
+ }
528
+ interface TaskDossier {
529
+ task: TaskInfo;
530
+ executions: TaskExecution[];
531
+ children: TaskInfo[];
532
+ approvals: unknown[];
533
+ raw: unknown;
534
+ }
535
+ interface GetTaskParams {
536
+ taskId: string;
537
+ signal?: AbortSignal;
538
+ }
539
+ interface CancelTaskParams {
540
+ taskId: string;
541
+ /** Registrado na trilha. Default do BE: `user_cancel`. */
542
+ reason?: string;
543
+ signal?: AbortSignal;
544
+ }
545
+ interface CancelTaskResult {
546
+ task: TaskInfo;
547
+ /** true quando havia um turno em voo que foi realmente derrubado. */
548
+ aborted: boolean;
549
+ raw: unknown;
550
+ }
551
+
552
+ /**
553
+ * Transporte HTTP do núcleo: resolução/validação de base URL (SSRF), headers de auth,
554
+ * deadline via AbortController que cobre TODO o request (headers + corpo/stream),
555
+ * e retry com exponential backoff + jitter.
556
+ *
557
+ * Política de retry (evita duplicar efeito colateral):
558
+ * - 'idempotent' (GET): retenta em erro de rede, 429 e 5xx.
559
+ * - 'side-effecting' (POST: invoke/dispatch/create): retenta SÓ em 429/503 (servidor disse
560
+ * "tente depois", sem ter processado). NUNCA em erro de rede — o request pode ter sido
561
+ * recebido e processado mesmo com o socket caindo na volta.
562
+ *
563
+ * @module http
564
+ */
565
+
566
+ /**
567
+ * Deadline default POR OPERAÇÃO. Cada valor fica ACIMA do orçamento do BE para aquela rota,
568
+ * porque o BE foi desenhado para entregar o desfecho ao cliente — desistir antes dele troca um
569
+ * erro estruturado (TURN_TIMEOUT com execution_id/session_id) por um abort cego nosso.
570
+ *
571
+ * Verificado contra o BE (`zihin-agent-builder/server-llm`) em 2026-08-31:
572
+ * - `stream` POST /api/v2/agents/:id/stream — o SSE fecha em 180s (`agents-v2.js:690`) e o turno
573
+ * do canal `chat` morre em 150s (`turn-deadline.js`). O stream sobrevive ao turno DE
574
+ * PROPÓSITO, para entregar o desfecho; 200s deixa essa cauda chegar.
575
+ * - `dispatch` POST /api/triggers/webhook/:id — fora do timeout global do Express
576
+ * (`express.js:71`); turno de canal `async` = 240s (`turn-deadline.js`).
577
+ * - `short` GETs de listagem/telemetria e CRUD de trigger — timeout global de 30s
578
+ * (`express.js:40`), que responde 408. Esperar mais que isso é esperar à toa.
579
+ *
580
+ * Mudou o BE? Atualize esta tabela e `docs/CONTRATO-INVOCACAO-AGENTE.md` juntos.
581
+ */
582
+ declare const OPERATION_TIMEOUT_MS: Readonly<{
583
+ stream: 200000;
584
+ dispatch: 260000;
585
+ short: 35000;
586
+ }>;
587
+ type TimeoutOperation = keyof typeof OPERATION_TIMEOUT_MS;
588
+ /**
589
+ * Folga somada ao `options.timeoutMs` do chamador para formar o deadline LOCAL. O mesmo número vai
590
+ * ao BE como `options.timeout_ms` (o teto que ele deve respeitar); sem folga, os dois relógios
591
+ * disparam juntos e o erro do servidor — que tem request_id — perde a corrida para o nosso abort.
592
+ */
593
+ declare const DEADLINE_GRACE_MS = 10000;
348
594
 
349
595
  /**
350
596
  * Listagem de agentes do tenant (GET /api/v2/agents). Alimenta o dropdown do nó n8n (#17)
@@ -404,6 +650,17 @@ declare class ZihinAgentClient {
404
650
  dispatchAgent(params: DispatchAgentParams): Promise<DispatchResult>;
405
651
  /** Consulta o resultado de uma execução disparada por trigger (polling). */
406
652
  getExecution(params: GetExecutionParams): Promise<ExecutionStatus>;
653
+ /**
654
+ * Delega uma tarefa a um agente (requer key owner/admin). Sem `scheduledFor`, o BE dispara na
655
+ * hora fora do request e devolve só o handle — acompanhe com `getTask`.
656
+ */
657
+ delegateTask(params: DelegateTaskParams): Promise<TaskInfo>;
658
+ /** Board de tasks do tenant (requer key owner/admin/editor). */
659
+ listTasks(params?: ListTasksParams): Promise<TaskListPage>;
660
+ /** Dossiê de uma task: task + execuções + filhos + aprovações. */
661
+ getTask(params: GetTaskParams): Promise<TaskDossier>;
662
+ /** Cancela uma task (requer key owner/admin); derruba o turno em voo, se houver. */
663
+ cancelTask(params: CancelTaskParams): Promise<CancelTaskResult>;
407
664
  /** Lista os agentes do tenant (dropdown). */
408
665
  listAgents(opts?: {
409
666
  signal?: AbortSignal;
@@ -412,6 +669,36 @@ declare class ZihinAgentClient {
412
669
  /** Açúcar: cria um cliente a partir da config. */
413
670
  declare function createClient(config: ZihinClientConfig): ZihinAgentClient;
414
671
 
672
+ /**
673
+ * Caminho A — invocação direta de agente (POST /api/v2/agents/:id/stream).
674
+ * `streamAgent` emite deltas ao vivo; `invokeAgent` buffeia o stream e consolida
675
+ * o evento `response` (content/sources/session_id/blocks) com o `metrics` (usage/model).
676
+ *
677
+ * @module invoke
678
+ */
679
+
680
+ /**
681
+ * Decide se o `event: error` terminal do BE foi um timeout do turno (#75).
682
+ *
683
+ * Fonte primária é o campo EXPLÍCITO `error.timed_out` (BE `utils/sse.js`, em prod desde
684
+ * 26/09/2026 — zihin-agent-builder#605): quando presente, é autoritativo nos dois sentidos —
685
+ * `true` classifica como timeout seja qual for o `code`; `false` classifica como `server` mesmo
686
+ * que o nome do código contenha "TIMEOUT". O BE vai renomear esses códigos
687
+ * (zihin-agent-builder#561) e o nome deixa de ser um sinal confiável.
688
+ *
689
+ * O regex no nome só vale como FALLBACK para backends que ainda não emitem `timed_out`
690
+ * (self-hosted/antigos). Some numa major futura, depois do rename no BE.
691
+ */
692
+ declare function classifyStreamErrorKind(code: string, timedOut?: boolean): 'timeout' | 'server';
693
+ /**
694
+ * Converte o chunk `type: 'error'` (evento terminal do BE) num `ZihinAgentError` — a MESMA regra
695
+ * que o `invokeAgent` aplica, exportada para quem consome `streamAgent` direto (o adapter do
696
+ * Paperclip, por exemplo) não reimplementar a classificação por conta e divergir dela.
697
+ */
698
+ declare function errorFromStreamChunk(chunk: Extract<AgentStreamChunk, {
699
+ type: 'error';
700
+ }>): ZihinAgentError;
701
+
415
702
  /**
416
703
  * Security utilities — portado de n8n-nodes-zihin/src/utils/security.ts (zero-dep).
417
704
  * SSRF prevention, payload limits, error sanitization, API-key format.
@@ -507,4 +794,31 @@ interface SignedRequestHeaders {
507
794
  */
508
795
  declare function signRequest(payload: string, secretKey: string, timestamp?: number): SignedRequestHeaders;
509
796
 
510
- export { type AgentResult, type AgentStreamChunk, type AgentSummary, type Attachment, type AttachmentType, type CreateTriggerParams, DEFAULT_LIMITS, type DeleteTriggerParams, type DispatchAgentParams, type DispatchResult, type ExecutionStatus, type FindOrCreateResult, type GetExecutionParams, type GetTriggerParams, type InvokeAgentParams, type InvokeOptions, type ListTriggersParams, type QueryExtraction, type RetryInfo, type SignedRequestHeaders, type Source, type TriggerInfo, type TriggerMode, type TriggerRef, type UpdateTriggerParams, type Usage, ZihinAgentClient, ZihinAgentError, type ZihinClientConfig, type ZihinErrorKind, createClient, sanitizeApiError, signRequest, validateApiKeyFormat, validateBaseUrl, validatePromptSize };
797
+ /**
798
+ * Caminho C — delegação assíncrona durável (`/api/v1/tasks`).
799
+ *
800
+ * Diferente do Caminho B (triggers), aqui a conexão pode morrer sem levar o trabalho junto: a
801
+ * task é persistida (`agent_tasks`), executada fora do request e o resultado é notificado. É o
802
+ * caminho para runs que estouram o teto do host — o passo do Zapier morre em ~30s, e o próprio
803
+ * app marca isso como pendência desde o primeiro commit.
804
+ *
805
+ * Contrato verificado em `routes/tasks.js` (2026-08-31) — ver o Caminho C de
806
+ * `docs/CONTRATO-INVOCACAO-AGENTE.md`. Três coisas que diferem do resto da API e custaram
807
+ * retrabalho quando supostas em vez de lidas:
808
+ * 1. O envelope é o canônico novo: `{ success, data, meta:{request_id} }`. O `request_id` do
809
+ * sucesso vive em `meta`, não no topo.
810
+ * 2. O RBAC é mais estreito do que "admin/editor": delegar e cancelar exigem **owner|admin**;
811
+ * editor só lê o board e o dossiê.
812
+ * 3. Sem `scheduled_for` o BE dispara na hora, fora do request (`setImmediate`), e a resposta
813
+ * devolve só o handle — por isso estas rotas são todas de orçamento curto.
814
+ *
815
+ * @module tasks
816
+ */
817
+
818
+ /**
819
+ * Teto do `payload` congelado na task. Espelha o `MAX_PAYLOAD_BYTES` do BE
820
+ * (`routes/tasks.js:26`) — validar aqui evita gastar um round-trip para receber um 400.
821
+ */
822
+ declare const MAX_TASK_PAYLOAD_BYTES: number;
823
+
824
+ export { type AgentResult, type AgentStreamChunk, type AgentSummary, type Attachment, type AttachmentType, type CancelTaskParams, type CancelTaskResult, type CreateTriggerParams, DEADLINE_GRACE_MS, DEFAULT_LIMITS, type DelegateTaskParams, type DeleteTriggerParams, type DispatchAgentParams, type DispatchResult, type ExecutionStatus, type FindOrCreateResult, type GetExecutionParams, type GetTaskParams, type GetTriggerParams, type InvokeAgentParams, type InvokeOptions, type ListTasksParams, type ListTriggersParams, MAX_TASK_PAYLOAD_BYTES, OPERATION_TIMEOUT_MS, type QueryExtraction, type ResponseTransformMode, type RetryInfo, type SignedRequestHeaders, type Source, type TaskDossier, type TaskExecution, type TaskInfo, type TaskListPage, type TimeoutOperation, type TriggerInfo, type TriggerMode, type TriggerRef, type UpdateTriggerParams, type Usage, ZihinAgentClient, ZihinAgentError, type ZihinClientConfig, type ZihinErrorKind, classifyStreamErrorKind, createClient, errorFromStreamChunk, isCommercialBlock, sanitizeApiError, signRequest, validateApiKeyFormat, validateBaseUrl, validatePromptSize };