@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 +73 -2
- package/dist/index.d.mts +318 -4
- package/dist/index.d.ts +318 -4
- package/dist/index.js +485 -60
- package/dist/index.mjs +479 -60
- package/package.json +1 -1
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.
|
|
146
|
-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
|
|
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 };
|