@zihin/agent-client 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -2
- package/dist/chunk-E2AJSAPA.mjs +2220 -0
- package/dist/core.d.mts +1150 -0
- package/dist/core.d.ts +1150 -0
- package/dist/core.js +2255 -0
- package/dist/core.mjs +38 -0
- package/dist/index.d.mts +17 -1114
- package/dist/index.d.ts +17 -1114
- package/dist/index.js +98 -50
- package/dist/index.mjs +42 -2173
- package/package.json +20 -3
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,1150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Primitivas de plataforma que o núcleo usa e que NÃO podem ser globais em todo runtime.
|
|
3
|
+
*
|
|
4
|
+
* Por que existe: o scanner de nó verificado do n8n (`@n8n/scan-community-package`) reprova
|
|
5
|
+
* qualquer referência a `setTimeout`/`clearTimeout`/`globalThis`/`process`/`console` no código
|
|
6
|
+
* publicado — e o núcleo vai EMBUTIDO no nó. Com o runtime injetável, o entry `./core` não carrega
|
|
7
|
+
* nenhuma dessas referências; o entry principal (`.`) injeta o runtime Node e segue idêntico.
|
|
8
|
+
*/
|
|
9
|
+
interface ZihinRuntime {
|
|
10
|
+
/**
|
|
11
|
+
* Resolve após `ms`. Rejeita (com qualquer erro) se `signal` abortar antes — o núcleo traduz.
|
|
12
|
+
* Precisa manter o processo vivo enquanto pendente (timer "ref", não `AbortSignal.timeout`):
|
|
13
|
+
* é o backoff entre tentativas e o relógio do deadline.
|
|
14
|
+
*/
|
|
15
|
+
sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
16
|
+
/** Aviso não fatal (descontinuação). Ausente = silencioso. */
|
|
17
|
+
warn?(code: string, message: string): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Erros do @zihin/agent-client — sanitizados (sem vazar key/Bearer/stack), mas COMPLETOS:
|
|
22
|
+
* carregam endpoint, nº de tentativas e request_id do BE para depuração em produção externa.
|
|
23
|
+
*
|
|
24
|
+
* @module errors
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
type ZihinErrorKind = 'config' | 'auth' | 'trial_expired' | 'no_subscription' | 'quota_exceeded' | 'not_found' | 'rate_limit' | 'timeout' | 'aborted' | 'server' | 'network' | 'protocol' | 'blocked' | 'unknown';
|
|
28
|
+
/** Contexto opcional anexado a um erro para correlação/depuração. */
|
|
29
|
+
interface ErrorMeta {
|
|
30
|
+
/** Ex.: "POST llm.zihin.ai/api/v2/agents/abc/stream". */
|
|
31
|
+
endpoint?: string;
|
|
32
|
+
/** Número de tentativas feitas antes de falhar. */
|
|
33
|
+
attempts?: number;
|
|
34
|
+
}
|
|
35
|
+
interface ZihinErrorOptions extends ErrorMeta {
|
|
36
|
+
status?: number;
|
|
37
|
+
code?: string;
|
|
38
|
+
retryable?: boolean;
|
|
39
|
+
/** request_id do BE (NÃO é segredo) — handle para o suporte correlacionar nos logs. */
|
|
40
|
+
requestId?: string;
|
|
41
|
+
/**
|
|
42
|
+
* execution_id do turno. Desde agosto/2026 o BE o entrega no sucesso E no erro (§1.2 do
|
|
43
|
+
* handoff) — é o handle que liga um erro daqui à execução no console/telemetria.
|
|
44
|
+
*/
|
|
45
|
+
executionId?: string;
|
|
46
|
+
/** Sessão do turno — para o chamador retomar/inspecionar a conversa depois de um erro. */
|
|
47
|
+
sessionId?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Só em `kind: 'timeout'` vindo do desfecho do turno (`done.timeout_clock`): qual relógio do BE
|
|
50
|
+
* encerrou o turno — `turn`, `first_signal`, `idle` ou `call`. Ausente num timeout de
|
|
51
|
+
* transporte/local, onde o turno pode nem ter terminado.
|
|
52
|
+
*/
|
|
53
|
+
timeoutClock?: TimeoutClock;
|
|
54
|
+
}
|
|
55
|
+
declare class ZihinAgentError extends Error {
|
|
56
|
+
readonly kind: ZihinErrorKind;
|
|
57
|
+
readonly status?: number;
|
|
58
|
+
readonly code?: string;
|
|
59
|
+
readonly retryable: boolean;
|
|
60
|
+
readonly requestId?: string;
|
|
61
|
+
readonly attempts?: number;
|
|
62
|
+
readonly endpoint?: string;
|
|
63
|
+
readonly executionId?: string;
|
|
64
|
+
readonly sessionId?: string;
|
|
65
|
+
readonly timeoutClock?: TimeoutClock;
|
|
66
|
+
constructor(kind: ZihinErrorKind, message: string, opts?: ZihinErrorOptions);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* true quando o corpo é um bloqueio comercial. Consultado pelo transporte ANTES de retentar:
|
|
70
|
+
* o 429 de quota é indistinguível de rate limit pelo status, e só o corpo separa os dois.
|
|
71
|
+
*/
|
|
72
|
+
declare function isCommercialBlock(body: unknown): boolean;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Tipos do contrato externo Zihin — fonte de verdade verificada contra o BE
|
|
76
|
+
* (zihin-agent-builder/server-llm, 2026-06-28).
|
|
77
|
+
*
|
|
78
|
+
* @module types
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
interface ZihinClientConfig {
|
|
82
|
+
/** API key `zhn_live_*` (tenant embutido). Para triggers/telemetria use role admin/editor. */
|
|
83
|
+
apiKey: string;
|
|
84
|
+
/** Base URL. Default: https://llm.zihin.ai */
|
|
85
|
+
baseUrl?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Timeout por request (ms) — override explícito que vale para TODAS as operações. Deixe vazio
|
|
88
|
+
* para usar o default de cada uma (`OPERATION_TIMEOUT_MS`): stream 200s, dispatch de trigger
|
|
89
|
+
* 260s, GETs curtos 35s. Cada default fica acima do orçamento do BE para aquela rota, para que
|
|
90
|
+
* o desfecho do servidor chegue como erro estruturado em vez de abort do cliente — um valor
|
|
91
|
+
* único menor que isso reintroduz exatamente esse problema.
|
|
92
|
+
*/
|
|
93
|
+
timeoutMs?: number;
|
|
94
|
+
/** Máximo de retentativas em 5xx/429/erro de rede. Default: 2. */
|
|
95
|
+
maxRetries?: number;
|
|
96
|
+
/** Permite base URL fora da whitelist Zihin (default false → SSRF guard). */
|
|
97
|
+
allowCustomBaseUrl?: boolean;
|
|
98
|
+
/** Permite http:// (apenas dev/local). Default false. */
|
|
99
|
+
allowHttp?: boolean;
|
|
100
|
+
/** Teto de bytes por evento/linha SSE (anti-DoS de memória). Default: 1MB. */
|
|
101
|
+
maxStreamEventBytes?: number;
|
|
102
|
+
/** Teto de bytes do stream inteiro (anti-DoS de memória). Default: 64MB. */
|
|
103
|
+
maxStreamTotalBytes?: number;
|
|
104
|
+
/**
|
|
105
|
+
* Implementação de `fetch` a usar. Default: `globalThis.fetch`. Injete em runtimes onde o
|
|
106
|
+
* fetch não é global (Node <18 com polyfill, testes, proxies).
|
|
107
|
+
*/
|
|
108
|
+
fetch?: typeof fetch;
|
|
109
|
+
/**
|
|
110
|
+
* Timers e avisos da plataforma. O entry principal (`@zihin/agent-client`) já injeta o runtime
|
|
111
|
+
* padrão (timers globais, `process.emitWarning`); só o entry `@zihin/agent-client/core` exige
|
|
112
|
+
* este campo — é o que hosts sem timers globais permitidos (nó verificado do n8n) usam.
|
|
113
|
+
*/
|
|
114
|
+
runtime?: ZihinRuntime;
|
|
115
|
+
/**
|
|
116
|
+
* Observabilidade (opt-in, sem console): chamado antes de cada retentativa.
|
|
117
|
+
* Para logar backoff/timeouts/falhas transitórias em produção externa.
|
|
118
|
+
*/
|
|
119
|
+
onRetry?: (info: RetryInfo) => void;
|
|
120
|
+
/**
|
|
121
|
+
* Observabilidade: chamado quando uma operação do cliente falha em definitivo (após retries),
|
|
122
|
+
* inclusive `aborted` — filtre por `error.kind` se quiser ignorar cancelamentos. Uma exceção
|
|
123
|
+
* lançada dentro do callback é engolida (não mascara o erro original). Não cobre erros de
|
|
124
|
+
* configuração lançados pelo construtor (o cliente ainda não existe).
|
|
125
|
+
*/
|
|
126
|
+
onError?: (error: ZihinAgentError) => void;
|
|
127
|
+
}
|
|
128
|
+
interface RetryInfo {
|
|
129
|
+
/** "POST llm.zihin.ai/api/v2/agents/abc/stream". */
|
|
130
|
+
endpoint: string;
|
|
131
|
+
/** Número da tentativa que acabou de falhar (1 = primeira). */
|
|
132
|
+
attempt: number;
|
|
133
|
+
/** Atraso até a próxima tentativa, em ms. */
|
|
134
|
+
delayMs: number;
|
|
135
|
+
/** Motivo do retry. */
|
|
136
|
+
reason: 'status' | 'network';
|
|
137
|
+
/** Status HTTP, quando `reason === 'status'`. */
|
|
138
|
+
status?: number;
|
|
139
|
+
}
|
|
140
|
+
type AttachmentType = 'image' | 'audio' | 'document';
|
|
141
|
+
interface Attachment {
|
|
142
|
+
url: string;
|
|
143
|
+
type: AttachmentType;
|
|
144
|
+
}
|
|
145
|
+
interface InvokeOptions {
|
|
146
|
+
/**
|
|
147
|
+
* @deprecated Sem efeito: o `/stream` usa a temperatura da configuração do agente
|
|
148
|
+
* (`llm_config.temperature`, no console ou via `update_agent`). O servidor ignora o campo e o
|
|
149
|
+
* reporta em `ignoredFields`. Será retirado do SDK na 1.0.
|
|
150
|
+
*/
|
|
151
|
+
temperature?: number;
|
|
152
|
+
/**
|
|
153
|
+
* @deprecated Sem efeito: o teto de saída é a configuração do agente (`llm_config.max_tokens`,
|
|
154
|
+
* no console ou via `update_agent`). O servidor ignora o campo e o reporta em `ignoredFields`.
|
|
155
|
+
* Será retirado do SDK na 1.0.
|
|
156
|
+
*/
|
|
157
|
+
maxTokens?: number;
|
|
158
|
+
/**
|
|
159
|
+
* Teto do TURNO desta chamada (ms); sobrepõe o do cliente. Vai ao BE como `options.timeout_ms`
|
|
160
|
+
* e o BE o honra (desde 30/09/2026, contrato de tempo §4), mas **só para encurtar**: o prazo
|
|
161
|
+
* efetivo é `min(prazo da plataforma, pedido)` — no `/stream` o canal chat dá 150s. Pedir mais
|
|
162
|
+
* não alonga. Um turno cortado por esse prazo sai `TIMEOUT` com `timeoutClock: 'turn'`.
|
|
163
|
+
*
|
|
164
|
+
* O relógio do turno no BE só começa DEPOIS da carga (config, tools, MCP frio). O deadline
|
|
165
|
+
* local soma `DEADLINE_GRACE_MS` a este valor para ainda receber o desfecho do servidor.
|
|
166
|
+
*/
|
|
167
|
+
timeoutMs?: number;
|
|
168
|
+
/**
|
|
169
|
+
* @deprecated Recurso descontinuado no servidor (ago/2026): sem efeito, reportado em
|
|
170
|
+
* `ignoredFields`. Remova-o. Será retirado do SDK na 1.0.
|
|
171
|
+
*/
|
|
172
|
+
jitMode?: boolean;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Blocos estruturados que a sua interface sabe renderizar (`capabilities.blocks`). Valor que o BE
|
|
176
|
+
* não conhece sai em `ignoredFields` como `capabilities.blocks.<v>` + `not_supported` (aviso, não erro).
|
|
177
|
+
*/
|
|
178
|
+
type ClientBlockType = 'quick_replies' | 'html_report' | (string & {});
|
|
179
|
+
/**
|
|
180
|
+
* O que a SUA tela sabe mostrar. O agente só oferece o que você declarar — sem declarar, nada é
|
|
181
|
+
* gerado e nada é cobrado. Não exige privilégio de API key.
|
|
182
|
+
*/
|
|
183
|
+
interface ClientCapabilities {
|
|
184
|
+
/**
|
|
185
|
+
* `quick_replies`: até 4 sugestões curtas de resposta ao fim do turno (o usuário toca e o texto
|
|
186
|
+
* vira a próxima mensagem). Chegam em `AgentResult.suggestions` (e no bloco `quick_replies` de
|
|
187
|
+
* `blocks`). O dono do agente pode desligá-las (`quick_replies_enabled`); o agente decide quando
|
|
188
|
+
* vale sugerir — nem todo turno traz sugestões.
|
|
189
|
+
*
|
|
190
|
+
* `html_report`: a tela renderiza HTML. Não cria bloco novo: o BE orienta o agente a entregar,
|
|
191
|
+
* quando couber, um documento HTML completo num bloco de código ```html dentro do `content`
|
|
192
|
+
* (BE `14d2a43c`, 19/10/2026). Só declare se você realmente renderiza HTML — senão o usuário
|
|
193
|
+
* recebe markup cru.
|
|
194
|
+
*/
|
|
195
|
+
blocks?: ClientBlockType[];
|
|
196
|
+
}
|
|
197
|
+
/** A pessoa do outro lado da conversa. Só vale sob API key **admin/owner**. */
|
|
198
|
+
interface ConsumerIdentity {
|
|
199
|
+
/**
|
|
200
|
+
* Identificador estável da pessoa no SEU sistema (memória por pessoa, denylist, gate de
|
|
201
|
+
* engajamento). Para continuar conversas que um webhook já gravava, use o MESMO valor que o
|
|
202
|
+
* webhook usava. Enviado como `consumer_key`.
|
|
203
|
+
*/
|
|
204
|
+
key: string;
|
|
205
|
+
/**
|
|
206
|
+
* Nome para exibição no console da Zihin (lista de contatos). Enviado como
|
|
207
|
+
* `consumer_display_name`. Dado pessoal: mande só o que a operação precisa ver.
|
|
208
|
+
*/
|
|
209
|
+
name?: string;
|
|
210
|
+
}
|
|
211
|
+
interface InvokeAgentParams {
|
|
212
|
+
/** UUID do agente (path :agent_id). */
|
|
213
|
+
agentId: string;
|
|
214
|
+
/** Mensagem ao agente. `message` OU `attachments` é obrigatório. */
|
|
215
|
+
message?: string;
|
|
216
|
+
/** Anexos multimodais. `message` OU `attachments` é obrigatório. */
|
|
217
|
+
attachments?: Attachment[];
|
|
218
|
+
/** Continuidade de conversa (Supabase agent_sessions). Não combina com `sessionFrom`. */
|
|
219
|
+
sessionId?: string;
|
|
220
|
+
/**
|
|
221
|
+
* Chave de idempotência do turno (UUID, qualquer versão; BE normaliza para minúsculas).
|
|
222
|
+
* Reenviar a MESMA mensagem com o mesmo id não roda o agente de novo: enquanto o turno original
|
|
223
|
+
* está em voo o BE responde `SESSION_BUSY` (`blocked`, retentável); depois de concluído com
|
|
224
|
+
* `COMPLETED`/`LENGTH` o BE **repete a resposta gravada** (`AgentResult.idempotentReplay: true`,
|
|
225
|
+
* sem `usage`, por 24 h). Mesmo id com OUTRA mensagem → `IDEMPOTENCY_CONFLICT` (`config`).
|
|
226
|
+
* Turno que terminou em erro/timeout/gate libera o id: o reenvio roda de novo.
|
|
227
|
+
*
|
|
228
|
+
* Com ele, o núcleo passa a **retentar o POST** do `/stream` em erro de rede e 5xx antes do
|
|
229
|
+
* stream abrir (sem ele, POST nunca retenta em rede — poderia duplicar o turno).
|
|
230
|
+
*
|
|
231
|
+
* ⚠️ **O que a garantia cobre:** só não repetir um turno **concluído** (`COMPLETED`/`LENGTH`).
|
|
232
|
+
* Turno cortado no meio do stream, cancelado, com erro ou timeout **libera o id** e o reenvio
|
|
233
|
+
* roda de novo — inclusive tools de efeito colateral que já rodaram. É best-effort (Redis do BE
|
|
234
|
+
* fora → sem dedupe, campo ainda aceito). O escopo é API key + `consumerKey` (não a sessão) e a
|
|
235
|
+
* comparação é só mensagem+anexos: **nunca use um valor fixo** — um workflow agendado com a
|
|
236
|
+
* mesma mensagem receberia a mesma resposta por 24 h; reusar o id noutra sessão dá erro
|
|
237
|
+
* `config` (`IDEMPOTENT_REPLAY_OTHER_SESSION`). O BE devolve o id em minúsculas. Vale sob
|
|
238
|
+
* qualquer papel de API key. Enviado como `client_message_id`; contra um BE que não o aceite
|
|
239
|
+
* (`accepted_fields` sem ele) o núcleo recusa o turno (`config`).
|
|
240
|
+
*/
|
|
241
|
+
clientMessageId?: string;
|
|
242
|
+
/**
|
|
243
|
+
* Identidade da pessoa do outro lado (memória por pessoa, denylist, gate de engajamento). Só vale
|
|
244
|
+
* sob API key **admin/owner**. Para continuar conversas que um webhook já gravava, use o MESMO
|
|
245
|
+
* valor que o webhook usava. Enviado como `consumer_key`.
|
|
246
|
+
*/
|
|
247
|
+
consumerKey?: string;
|
|
248
|
+
/**
|
|
249
|
+
* A pessoa do outro lado: `{ key, name? }`. `consumer.key` é o mesmo que `consumerKey` (use um
|
|
250
|
+
* dos dois; se vierem os dois, têm de ser iguais). Só vale sob API key admin/owner.
|
|
251
|
+
*/
|
|
252
|
+
consumer?: ConsumerIdentity;
|
|
253
|
+
/** O que a sua interface sabe renderizar — ex.: `{ blocks: ['quick_replies'] }`. */
|
|
254
|
+
capabilities?: ClientCapabilities;
|
|
255
|
+
/**
|
|
256
|
+
* Contexto estruturado que o **modelo lê** — bloco de plataforma junto da mensagem do usuário
|
|
257
|
+
* (não no system prompt). **Fica gravado** com a mensagem (`declared_context`) e se repete em
|
|
258
|
+
* todo turno; o BE redige segredos por nome de chave E por formato de valor (ex.: `sk-…`), o
|
|
259
|
+
* que pode alterar valores legítimos — ids que vão a tools ou ao `sessionFrom` ficam melhor em
|
|
260
|
+
* `bindings`. Plano, só escalares. Tools também o recebem. Só vale sob key admin/owner.
|
|
261
|
+
* ⚠️ Credencial não vai aqui nem em `bindings`: use um secret do tenant.
|
|
262
|
+
*/
|
|
263
|
+
context?: Record<string, ContextValue>;
|
|
264
|
+
/**
|
|
265
|
+
* Valores que **só as tools** recebem — nunca vão ao prompt (ex.: id interno de fornecedor).
|
|
266
|
+
* Plano, só escalares; **nunca gravado** nem redigido (vive só no contexto das tools durante o
|
|
267
|
+
* turno). Só vale sob key admin/owner.
|
|
268
|
+
*/
|
|
269
|
+
bindings?: Record<string, ContextValue>;
|
|
270
|
+
/**
|
|
271
|
+
* Deriva o `session_id` dos valores destes campos de `context ∪ bindings`, com a MESMA função
|
|
272
|
+
* do webhook (`derive: [campo]`) — o mesmo valor cai na mesma sessão pelos dois caminhos. Não
|
|
273
|
+
* combina com `sessionId`. O id derivado volta em `AgentResult.sessionId`.
|
|
274
|
+
*/
|
|
275
|
+
sessionFrom?: string[];
|
|
276
|
+
options?: InvokeOptions;
|
|
277
|
+
metadata?: {
|
|
278
|
+
traceId?: string;
|
|
279
|
+
correlationId?: string;
|
|
280
|
+
[k: string]: unknown;
|
|
281
|
+
};
|
|
282
|
+
/** Cancelamento externo. */
|
|
283
|
+
signal?: AbortSignal;
|
|
284
|
+
/**
|
|
285
|
+
* Retém todos os eventos SSE brutos em `AgentResult.raw`. Default: false (economiza memória
|
|
286
|
+
* em runs longos). `streamAgent` sempre entrega os chunks ao vivo, independente disto.
|
|
287
|
+
*/
|
|
288
|
+
includeRaw?: boolean;
|
|
289
|
+
}
|
|
290
|
+
/** Valor aceito em `context`/`bindings`: plano e escalar (regra 8 do contrato de entrada). */
|
|
291
|
+
type ContextValue = string | number | boolean | null;
|
|
292
|
+
/** Campo que o BE recebeu e NÃO aplicou (evento `metadata.ignored_fields`). */
|
|
293
|
+
interface IgnoredField {
|
|
294
|
+
/** Nome no fio (`context`, `bindings`, `session_from`, `consumer_key`, `options.temperature`…). */
|
|
295
|
+
field: string;
|
|
296
|
+
/** Motivo reportado pelo BE (ex.: papel da key insuficiente), quando houver. */
|
|
297
|
+
reason?: string;
|
|
298
|
+
}
|
|
299
|
+
interface Usage {
|
|
300
|
+
inputTokens?: number;
|
|
301
|
+
outputTokens?: number;
|
|
302
|
+
totalTokens?: number;
|
|
303
|
+
costUsd?: number;
|
|
304
|
+
}
|
|
305
|
+
interface Source {
|
|
306
|
+
title?: string;
|
|
307
|
+
url?: string;
|
|
308
|
+
[k: string]: unknown;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Resultado final de `invokeAgent`: consolida o evento SSE `response`
|
|
312
|
+
* (content/sources/session_id/blocks) com o evento `metrics` (usage/model).
|
|
313
|
+
*/
|
|
314
|
+
interface AgentResult {
|
|
315
|
+
content: string;
|
|
316
|
+
/**
|
|
317
|
+
* De onde veio o `content`:
|
|
318
|
+
* - `response`: o evento SSE `response` — o texto que o BE decidiu entregar. No `/stream` isso é
|
|
319
|
+
* sempre o modo `final` (só as mensagens sem `tool_calls`). É o texto seguro para publicar.
|
|
320
|
+
* - `tokens`: o stream terminou SEM o evento `response` e sobrou apenas a concatenação crua dos
|
|
321
|
+
* deltas de todas as iterações — **deliberação interna incluída**. Foi exatamente esse texto
|
|
322
|
+
* que vazou para um lead real e que o BE parou de entregar. Trate como parcial de diagnóstico:
|
|
323
|
+
* não publique em canal externo sem revisar.
|
|
324
|
+
*/
|
|
325
|
+
contentSource: 'response' | 'tokens';
|
|
326
|
+
sessionId?: string;
|
|
327
|
+
model?: string;
|
|
328
|
+
usage?: Usage;
|
|
329
|
+
sources?: Source[];
|
|
330
|
+
/** Cards/blocos estruturados (SPEC B). */
|
|
331
|
+
blocks?: Array<{
|
|
332
|
+
type: string;
|
|
333
|
+
[k: string]: unknown;
|
|
334
|
+
}>;
|
|
335
|
+
/**
|
|
336
|
+
* Sugestões de resposta do turno (bloco `quick_replies`), já extraídas de `blocks`. Só vêm se
|
|
337
|
+
* você declarou `capabilities: { blocks: ['quick_replies'] }`. O texto de cada uma é o que o
|
|
338
|
+
* usuário "diz" ao tocá-la. Num turno só com sugestões, `content` vem vazio.
|
|
339
|
+
*/
|
|
340
|
+
suggestions?: string[];
|
|
341
|
+
iterations?: number;
|
|
342
|
+
executionTimeMs?: number;
|
|
343
|
+
/**
|
|
344
|
+
* `execution_id` do turno — handle de correlação no console/telemetria. Lido do `metadata`
|
|
345
|
+
* (BE desde 17/10/2026) com fallback no `metrics`; ausente num turno barrado por gate.
|
|
346
|
+
*/
|
|
347
|
+
executionId?: string;
|
|
348
|
+
/** `client_message_id` que o BE aplicou ao turno (eco do que você mandou, em **minúsculas**). */
|
|
349
|
+
clientMessageId?: string;
|
|
350
|
+
/**
|
|
351
|
+
* `true` = esta é a resposta GRAVADA de um turno anterior com o mesmo `clientMessageId`
|
|
352
|
+
* (reenvio idempotente). O agente não rodou de novo: sem `usage`/`model`/`iterations`, e
|
|
353
|
+
* `truncated`/`finishReason` não vêm mesmo que o original fosse `LENGTH`.
|
|
354
|
+
*/
|
|
355
|
+
idempotentReplay?: boolean;
|
|
356
|
+
/** Id da mensagem do usuário gravada na sessão (quando o BE o deu). */
|
|
357
|
+
userMessageId?: string;
|
|
358
|
+
/** Id da mensagem do agente gravada na sessão (quando o BE o deu). */
|
|
359
|
+
assistantMessageId?: string;
|
|
360
|
+
/** Campos do pedido que o BE aplicou (`metadata.accepted_fields`). Ausente = backend antigo. */
|
|
361
|
+
acceptedFields?: string[];
|
|
362
|
+
/** Campos do pedido que o BE recebeu e ignorou (`metadata.ignored_fields`). */
|
|
363
|
+
ignoredFields?: IgnoredField[];
|
|
364
|
+
/**
|
|
365
|
+
* `true` = o `content` é a resposta ENLATADA de um gate da plataforma (conversa em atendimento
|
|
366
|
+
* humano, consumidor bloqueado, sessão suspensa), não do agente: nenhum modelo rodou. Em canal
|
|
367
|
+
* conversacional ela pode ser entregue à pessoa; numa automação, trate como "não respondido".
|
|
368
|
+
*/
|
|
369
|
+
gated?: boolean;
|
|
370
|
+
/**
|
|
371
|
+
* Desfecho do turno, lido do `done` (`exitReason` do motor). `undefined` = backend antigo ou
|
|
372
|
+
* stream cortado antes do `done`. Só chegam aqui os desfechos cujo `content` é resposta
|
|
373
|
+
* (`COMPLETED`, `LENGTH`), o `PAUSED` (aprovação pendente ou agente pausado) e desfechos novos.
|
|
374
|
+
* `TIMEOUT`, `ERROR`, `CANCELLED`, `SUSPENDED`, `REFUSED`, `CONTEXT_LIMIT` e os de bloqueio
|
|
375
|
+
* viram `ZihinAgentError` — o `content` desses turnos é a mensagem de falha/recusa do motor
|
|
376
|
+
* (`errorFromDoneChunk`).
|
|
377
|
+
*/
|
|
378
|
+
outcome?: AgentOutcome;
|
|
379
|
+
/** `true` quando a resposta foi cortada pelo teto de saída do modelo (`done.truncated`). */
|
|
380
|
+
truncated?: boolean;
|
|
381
|
+
/** Motivo do corte quando `truncated` (ex.: `length`). */
|
|
382
|
+
finishReason?: string;
|
|
383
|
+
/** Eventos SSE brutos coletados (para depuração / casos avançados). */
|
|
384
|
+
raw: AgentStreamChunk[];
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Desfecho do turno (`done.outcome` = `resolveTurnOutcome` do motor). `COMPLETED` e `LENGTH`
|
|
388
|
+
* contam como sucesso no BE (`isSuccessfulOutcome`); os demais valores são repassados como vêm.
|
|
389
|
+
* Aberto (`string & {}`) porque o BE pode acrescentar desfechos (ex.: os de bloqueio).
|
|
390
|
+
*/
|
|
391
|
+
type AgentOutcome = 'COMPLETED' | 'LENGTH' | 'TIMEOUT' | 'ERROR' | 'CANCELLED' | 'PAUSED' | 'SUSPENDED' | 'BUDGET_EXCEEDED' | 'QUOTA_EXCEEDED' | 'SESSION_BUSY' | 'CONFLICT'
|
|
392
|
+
/** O provedor do modelo recusou por política (desde 08/10/2026). O `content` é um texto neutro fixo. */
|
|
393
|
+
| 'REFUSED'
|
|
394
|
+
/** A conversa reuniu mais contexto do que o modelo processa (desde 14/10/2026). No `/stream` chega como `event: error`. */
|
|
395
|
+
| 'CONTEXT_LIMIT' | (string & {});
|
|
396
|
+
/**
|
|
397
|
+
* Qual relógio encerrou o turno (contrato de tempo §2): `turn` (prazo do turno, inclusive o
|
|
398
|
+
* `options.timeoutMs` pedido), `first_signal` (o provedor não começou a responder), `idle`
|
|
399
|
+
* (silêncio entre pedaços do stream do provedor) ou `call` (chamada ao modelo sem stream).
|
|
400
|
+
*/
|
|
401
|
+
type TimeoutClock = 'turn' | 'first_signal' | 'idle' | 'call' | (string & {});
|
|
402
|
+
/**
|
|
403
|
+
* Chunk normalizado emitido por `streamAgent`. Mapeia os tipos de evento SSE do BE:
|
|
404
|
+
* metadata, status, attachments, phase, thinking, tool, action_proposed, token,
|
|
405
|
+
* warning, response, metrics.
|
|
406
|
+
*/
|
|
407
|
+
type AgentStreamChunk =
|
|
408
|
+
/**
|
|
409
|
+
* Abertura do stream. `acceptedFields`/`ignoredFields` dizem o que o BE fez com cada campo do
|
|
410
|
+
* pedido (ausentes = backend anterior ao contrato de entrada). Se o chamador mandou
|
|
411
|
+
* `context`/`bindings`/`sessionFrom` e o BE não os aceitou, o `streamAgent` lança
|
|
412
|
+
* `ZihinAgentError('config')` ANTES de entregar este chunk — nunca segue em silêncio.
|
|
413
|
+
*/
|
|
414
|
+
{
|
|
415
|
+
type: 'metadata';
|
|
416
|
+
requestId?: string;
|
|
417
|
+
agentId?: string;
|
|
418
|
+
sessionId?: string;
|
|
419
|
+
/** `execution_id` do turno (BE desde 17/10/2026). Ausente num turno barrado por gate. */
|
|
420
|
+
executionId?: string;
|
|
421
|
+
clientMessageId?: string;
|
|
422
|
+
/** `true` = o BE vai repetir a resposta gravada (mesmo `clientMessageId`), sem rodar o agente. */
|
|
423
|
+
idempotentReplay?: boolean;
|
|
424
|
+
acceptedFields?: string[];
|
|
425
|
+
ignoredFields?: IgnoredField[];
|
|
426
|
+
data: unknown;
|
|
427
|
+
}
|
|
428
|
+
/** `blocked` = motivo do bloqueio do turno (`agent_paused`, `budget_exceeded`…), quando houver. */
|
|
429
|
+
| {
|
|
430
|
+
type: 'status';
|
|
431
|
+
phase?: string;
|
|
432
|
+
blocked?: string;
|
|
433
|
+
data: unknown;
|
|
434
|
+
} | {
|
|
435
|
+
type: 'attachments';
|
|
436
|
+
data: unknown;
|
|
437
|
+
} | {
|
|
438
|
+
type: 'phase';
|
|
439
|
+
phase?: string;
|
|
440
|
+
data: unknown;
|
|
441
|
+
} | {
|
|
442
|
+
type: 'thinking';
|
|
443
|
+
thought?: string;
|
|
444
|
+
data: unknown;
|
|
445
|
+
} | {
|
|
446
|
+
type: 'tool';
|
|
447
|
+
tool?: string;
|
|
448
|
+
callId?: string;
|
|
449
|
+
success?: boolean;
|
|
450
|
+
data: unknown;
|
|
451
|
+
} | {
|
|
452
|
+
type: 'action_proposed';
|
|
453
|
+
data: unknown;
|
|
454
|
+
}
|
|
455
|
+
/** Aprovação humana encaminhada ao aprovador (`{ action_id, kind, title, expires_at }`); o turno fica `PAUSED`. */
|
|
456
|
+
| {
|
|
457
|
+
type: 'action_forwarded';
|
|
458
|
+
actionId?: string;
|
|
459
|
+
data: unknown;
|
|
460
|
+
} | {
|
|
461
|
+
type: 'token';
|
|
462
|
+
content: string;
|
|
463
|
+
data: unknown;
|
|
464
|
+
} | {
|
|
465
|
+
type: 'warning';
|
|
466
|
+
message?: string;
|
|
467
|
+
data: unknown;
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* O BE encerra o stream com `event: error` + `{ error: { type, code, message, request_id,
|
|
471
|
+
* timed_out? } }` (`utils/sse.js`) — inclusive quando o TURNO morre no deadline. O BE NÃO põe
|
|
472
|
+
* `status` nem `details` no fio (ficam no servidor): `SESSION_BUSY`, `IDEMPOTENCY_CONFLICT`,
|
|
473
|
+
* `CONTEXT_LIMIT`, `CONFLICT` e os de quota chegam só com `code`+`message`. Antes isto
|
|
474
|
+
* caía em `unknown` e o `invokeAgent` ignorava: se já tinha chegado token, devolvia a
|
|
475
|
+
* deliberação crua como SUCESSO.
|
|
476
|
+
*
|
|
477
|
+
* `timedOut` espelha `error.timed_out` (BE desde 26/09/2026, zihin-agent-builder#605): campo
|
|
478
|
+
* explícito que diz se o turno morreu por timeout, independente do NOME do código — os códigos
|
|
479
|
+
* vão ser renomeados (zihin-agent-builder#561). `undefined` = backend antigo, sem o campo.
|
|
480
|
+
*/
|
|
481
|
+
| {
|
|
482
|
+
type: 'error';
|
|
483
|
+
code?: string;
|
|
484
|
+
message?: string;
|
|
485
|
+
requestId?: string;
|
|
486
|
+
timedOut?: boolean;
|
|
487
|
+
data: unknown;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Emitido no meio do turno SÓ quando o BE cunhou uma sessão diferente da pedida
|
|
491
|
+
* (`agents-v2.js:931`). É o id em que a conversa realmente vai continuar — vence o `session_id`
|
|
492
|
+
* do `metadata` e o que o chamador mandou. Visto em produção no E2E de 01/09.
|
|
493
|
+
*/
|
|
494
|
+
| {
|
|
495
|
+
type: 'session';
|
|
496
|
+
sessionId?: string;
|
|
497
|
+
data: unknown;
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Fim LIMPO do stream (`utils/sse.js`). Sem ele, o stream foi cortado no meio. Carrega o
|
|
501
|
+
* desfecho do turno: `{ outcome, timed_out?, timeout_clock?, truncated?, finish_reason? }`
|
|
502
|
+
* (aditivo — backend antigo manda `{}`). Um turno que o motor encerrou por tempo chega aqui
|
|
503
|
+
* com `outcome: 'TIMEOUT'` (idem ERROR e os bloqueios), DEPOIS de um `response` cujo `content`
|
|
504
|
+
* é a mensagem de falha — use `errorFromDoneChunk` para não publicá-lo como resposta.
|
|
505
|
+
*/
|
|
506
|
+
| {
|
|
507
|
+
type: 'done';
|
|
508
|
+
outcome?: AgentOutcome;
|
|
509
|
+
timedOut?: boolean;
|
|
510
|
+
timeoutClock?: TimeoutClock;
|
|
511
|
+
truncated?: boolean;
|
|
512
|
+
finishReason?: string;
|
|
513
|
+
executionId?: string;
|
|
514
|
+
clientMessageId?: string;
|
|
515
|
+
idempotentReplay?: boolean;
|
|
516
|
+
userMessageId?: string;
|
|
517
|
+
assistantMessageId?: string;
|
|
518
|
+
data: unknown;
|
|
519
|
+
} | {
|
|
520
|
+
type: 'response';
|
|
521
|
+
content: string;
|
|
522
|
+
sessionId?: string;
|
|
523
|
+
sources?: Source[];
|
|
524
|
+
blocks?: Array<{
|
|
525
|
+
type: string;
|
|
526
|
+
[k: string]: unknown;
|
|
527
|
+
}>;
|
|
528
|
+
/** Sugestões de resposta (bloco `quick_replies`), extraídas de `blocks`. */
|
|
529
|
+
suggestions?: string[];
|
|
530
|
+
/** Resposta ENLATADA de um gate (handoff humano, denylist, sessão suspensa) — não do agente. */
|
|
531
|
+
gated?: boolean;
|
|
532
|
+
executionId?: string;
|
|
533
|
+
clientMessageId?: string;
|
|
534
|
+
idempotentReplay?: boolean;
|
|
535
|
+
userMessageId?: string;
|
|
536
|
+
assistantMessageId?: string;
|
|
537
|
+
data: unknown;
|
|
538
|
+
} | {
|
|
539
|
+
type: 'metrics';
|
|
540
|
+
usage?: Usage;
|
|
541
|
+
model?: string;
|
|
542
|
+
iterations?: number;
|
|
543
|
+
executionTimeMs?: number;
|
|
544
|
+
executionId?: string;
|
|
545
|
+
data: unknown;
|
|
546
|
+
} | {
|
|
547
|
+
type: 'unknown';
|
|
548
|
+
event: string;
|
|
549
|
+
data: unknown;
|
|
550
|
+
};
|
|
551
|
+
type TriggerMode = 'sync' | 'async';
|
|
552
|
+
/**
|
|
553
|
+
* Como o BE monta o texto entregue pelo trigger (`#343/#345`):
|
|
554
|
+
* - `final` (default): só as mensagens do agente SEM `tool_calls`. O texto que acompanha uma
|
|
555
|
+
* chamada de tool é deliberação interna — vazou para um lead real via WhatsApp.
|
|
556
|
+
* - `concat` (legado): concatena todas as iterações. Só peça se você realmente depende da
|
|
557
|
+
* narrativa completa e o destino é interno.
|
|
558
|
+
*
|
|
559
|
+
* ⚠️ O BE lê isto de `trigger_config.execution.callback.response_transform.mode`, e o validador dele
|
|
560
|
+
* exige `callback.url` sempre que o objeto `callback` existe. Consequência verificada em 2026-08-31:
|
|
561
|
+
* **um trigger `sync` não consegue declarar o modo sem uma callbackUrl válida**, mesmo que ela nunca
|
|
562
|
+
* seja chamada. `createTrigger` falha alto nesse caso em vez de mandar um config que o BE rejeita.
|
|
563
|
+
*/
|
|
564
|
+
type ResponseTransformMode = 'final' | 'concat';
|
|
565
|
+
/**
|
|
566
|
+
* Como o BE extrai a mensagem do agente a partir do payload de disparo (webhook):
|
|
567
|
+
* - `field`: um campo top-level do payload (`payload[field]`).
|
|
568
|
+
* - `template`: compõe a mensagem com placeholders `{{payload.caminho.aninhado}}` —
|
|
569
|
+
* é o modo para payloads estruturados ricos.
|
|
570
|
+
* - `full_body`: fallback do BE — usa `payload.message || payload.query ||
|
|
571
|
+
* JSON.stringify(payload)` (o payload inteiro serializado vira a mensagem).
|
|
572
|
+
* (Verificado no BE em 2026-08-03: `jmespath` citado em docs antigas NÃO é implementado.)
|
|
573
|
+
*/
|
|
574
|
+
type QueryExtraction = {
|
|
575
|
+
mode: 'field';
|
|
576
|
+
field: string;
|
|
577
|
+
} | {
|
|
578
|
+
mode: 'template';
|
|
579
|
+
template: string;
|
|
580
|
+
} | {
|
|
581
|
+
mode: 'full_body';
|
|
582
|
+
};
|
|
583
|
+
interface CreateTriggerParams {
|
|
584
|
+
agentId: string;
|
|
585
|
+
/** Nome legível do trigger. */
|
|
586
|
+
name: string;
|
|
587
|
+
/** `async` → 202 + callback; `sync` → 200 + JSON na hora. Default: async. */
|
|
588
|
+
mode?: TriggerMode;
|
|
589
|
+
/** URL que o BE chama com o resultado (obrigatório no modo async). */
|
|
590
|
+
callbackUrl?: string;
|
|
591
|
+
/** Método HTTP do callback. Default: POST. */
|
|
592
|
+
callbackMethod?: 'POST' | 'PUT';
|
|
593
|
+
/** Campo do payload de disparo de onde sai a mensagem do agente. Default: "message". */
|
|
594
|
+
queryField?: string;
|
|
595
|
+
/**
|
|
596
|
+
* Extração da mensagem a partir do payload (field/template/full_body). Tem precedência
|
|
597
|
+
* sobre `queryField` (que equivale a `{ mode: 'field', field: queryField }`).
|
|
598
|
+
*/
|
|
599
|
+
queryExtraction?: QueryExtraction;
|
|
600
|
+
/** Auth de disparo aceita. Default: api_key. */
|
|
601
|
+
dispatchAuth?: 'api_key' | 'signature' | 'none';
|
|
602
|
+
/**
|
|
603
|
+
* Modo de extração da resposta do agente. Default do BE: `final`. Exige `callbackUrl` — ver
|
|
604
|
+
* {@link ResponseTransformMode}.
|
|
605
|
+
*/
|
|
606
|
+
responseTransform?: ResponseTransformMode;
|
|
607
|
+
signal?: AbortSignal;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Atualização parcial de um trigger (PATCH). Campos de config (queryExtraction/callback/mode)
|
|
611
|
+
* exigem um GET prévio + merge, porque o PATCH do BE SUBSTITUI `trigger_config` por inteiro.
|
|
612
|
+
*/
|
|
613
|
+
interface UpdateTriggerParams {
|
|
614
|
+
triggerId: string;
|
|
615
|
+
name?: string;
|
|
616
|
+
description?: string;
|
|
617
|
+
enabled?: boolean;
|
|
618
|
+
/** Troca a extração de query preservando o restante do trigger_config. */
|
|
619
|
+
queryExtraction?: QueryExtraction;
|
|
620
|
+
/** Troca a URL do callback (modo async). */
|
|
621
|
+
callbackUrl?: string;
|
|
622
|
+
/** Troca o método do callback. */
|
|
623
|
+
callbackMethod?: 'POST' | 'PUT';
|
|
624
|
+
/** Troca o modo de execução (sync/async). */
|
|
625
|
+
mode?: TriggerMode;
|
|
626
|
+
/**
|
|
627
|
+
* Troca o modo de extração da resposta, preservando `strip_markdown`/`max_length` já
|
|
628
|
+
* configurados. Exige que o trigger já tenha um `callback` (ou que `callbackUrl` venha junto).
|
|
629
|
+
*/
|
|
630
|
+
responseTransform?: ResponseTransformMode;
|
|
631
|
+
signal?: AbortSignal;
|
|
632
|
+
}
|
|
633
|
+
interface TriggerRef {
|
|
634
|
+
triggerId: string;
|
|
635
|
+
/** URL completa de disparo: POST {webhookUrl}. */
|
|
636
|
+
webhookUrl: string;
|
|
637
|
+
}
|
|
638
|
+
/** Resultado de findOrCreateTrigger: o ref + se foi recém-criado ou reaproveitado. */
|
|
639
|
+
interface FindOrCreateResult extends TriggerRef {
|
|
640
|
+
created: boolean;
|
|
641
|
+
}
|
|
642
|
+
/** Trigger como listado/obtido do BE. */
|
|
643
|
+
interface TriggerInfo {
|
|
644
|
+
id: string;
|
|
645
|
+
agentId?: string;
|
|
646
|
+
name?: string;
|
|
647
|
+
description?: string;
|
|
648
|
+
triggerType?: string;
|
|
649
|
+
enabled?: boolean;
|
|
650
|
+
triggerCount?: number;
|
|
651
|
+
lastError?: string;
|
|
652
|
+
/** Presente para triggers do tipo webhook. */
|
|
653
|
+
webhookUrl?: string;
|
|
654
|
+
}
|
|
655
|
+
interface ListTriggersParams {
|
|
656
|
+
/** Filtra por agente. */
|
|
657
|
+
agentId?: string;
|
|
658
|
+
/** Filtra por tipo (`webhook` | `email` | `schedule`; `db_event` foi removido do BE em 03/10/2026). */
|
|
659
|
+
triggerType?: string;
|
|
660
|
+
/** Apenas habilitados. */
|
|
661
|
+
enabledOnly?: boolean;
|
|
662
|
+
signal?: AbortSignal;
|
|
663
|
+
}
|
|
664
|
+
interface GetTriggerParams {
|
|
665
|
+
triggerId: string;
|
|
666
|
+
signal?: AbortSignal;
|
|
667
|
+
}
|
|
668
|
+
interface DeleteTriggerParams {
|
|
669
|
+
triggerId: string;
|
|
670
|
+
signal?: AbortSignal;
|
|
671
|
+
}
|
|
672
|
+
interface DispatchAgentParams {
|
|
673
|
+
triggerId: string;
|
|
674
|
+
/** Payload entregue ao trigger (a mensagem sai do `queryField` configurado). */
|
|
675
|
+
payload: Record<string, unknown>;
|
|
676
|
+
signal?: AbortSignal;
|
|
677
|
+
}
|
|
678
|
+
interface DispatchResult {
|
|
679
|
+
/**
|
|
680
|
+
* true quando o BE aceitou o disparo. Um turno que FALHOU não chega aqui: o BE responde 200
|
|
681
|
+
* com `{ success:false }` e o núcleo lança — antes isso voltava como disparo aceito com
|
|
682
|
+
* resposta vazia.
|
|
683
|
+
*/
|
|
684
|
+
accepted: boolean;
|
|
685
|
+
triggerId?: string;
|
|
686
|
+
agentId?: string;
|
|
687
|
+
/** Id da execução do TRIGGER (`trigger_executions`), não o `execution_id` do turno do agente. */
|
|
688
|
+
executionId?: string;
|
|
689
|
+
/** Presente quando o trigger é `sync`: resposta imediata do agente. */
|
|
690
|
+
agentResponse?: string;
|
|
691
|
+
/**
|
|
692
|
+
* Desfecho do turno no modo `sync` (BE desde 14/10/2026; ausente = backend antigo). Só chegam
|
|
693
|
+
* aqui os desfechos cujo `agentResponse` é resposta (`COMPLETED`, `LENGTH`) e o `PAUSED`; os
|
|
694
|
+
* demais (`REFUSED`, `CONTEXT_LIMIT`, `TIMEOUT`, `ERROR`…) viram `ZihinAgentError` mesmo com
|
|
695
|
+
* `success: true` no corpo — o BE mantém `success` como "entregou", e o texto é a frase da
|
|
696
|
+
* plataforma, não uma resposta. Limite: o sync não traz `status.blocked`, então o `PAUSED` de
|
|
697
|
+
* agente pausado/arquivado chega aqui com o texto de recusa em `agentResponse`.
|
|
698
|
+
*/
|
|
699
|
+
outcome?: AgentOutcome;
|
|
700
|
+
usage?: Usage;
|
|
701
|
+
/** Iterações do laço de tools no turno (sync). */
|
|
702
|
+
iterations?: number;
|
|
703
|
+
/** Latência medida pelo BE, ponta a ponta (sync). */
|
|
704
|
+
latencyMs?: number;
|
|
705
|
+
/**
|
|
706
|
+
* O texto que o BE extraiu do payload e entregou ao agente. É o campo que denuncia um
|
|
707
|
+
* `query_extraction` mal configurado — mensagem vazia, ou o payload inteiro virando a pergunta.
|
|
708
|
+
*/
|
|
709
|
+
queryExtracted?: string;
|
|
710
|
+
raw: unknown;
|
|
711
|
+
}
|
|
712
|
+
interface ExecutionStatus {
|
|
713
|
+
executionId: string;
|
|
714
|
+
/**
|
|
715
|
+
* Status cru do BE. Além de `completed`/`failed`, o `trigger_executions` passou a registrar
|
|
716
|
+
* `skipped`, `quota_exhausted` e `timeout` de verdade (agosto/2026) — não trate como enum
|
|
717
|
+
* fechado; use `terminal` para decidir quando parar de esperar.
|
|
718
|
+
*/
|
|
719
|
+
status: string;
|
|
720
|
+
/**
|
|
721
|
+
* false só enquanto a execução está em andamento. Qualquer status desconhecido conta como
|
|
722
|
+
* terminal, para que o polling pare em vez de girar para sempre num estado novo do BE.
|
|
723
|
+
*/
|
|
724
|
+
terminal: boolean;
|
|
725
|
+
startedAt?: string;
|
|
726
|
+
finishedAt?: string;
|
|
727
|
+
executionTimeMs?: number;
|
|
728
|
+
usage?: Usage;
|
|
729
|
+
result?: string;
|
|
730
|
+
raw: unknown;
|
|
731
|
+
}
|
|
732
|
+
interface GetExecutionParams {
|
|
733
|
+
triggerId: string;
|
|
734
|
+
executionId: string;
|
|
735
|
+
signal?: AbortSignal;
|
|
736
|
+
}
|
|
737
|
+
interface DelegateTaskParams {
|
|
738
|
+
/** Agente que vai executar. */
|
|
739
|
+
agentId: string;
|
|
740
|
+
/** O que o agente deve fazer (obrigatório; sofre guardrail de tamanho por tier no BE). */
|
|
741
|
+
instruction: string;
|
|
742
|
+
/** Título legível para o board. */
|
|
743
|
+
title?: string;
|
|
744
|
+
/** Payload congelado junto da task. Objeto, **≤ 64KB** (teto do BE). */
|
|
745
|
+
payload?: Record<string, unknown>;
|
|
746
|
+
/**
|
|
747
|
+
* Quando executar. Omita para disparar imediatamente. Não pode estar no passado (o BE tolera
|
|
748
|
+
* 60s de diferença de relógio).
|
|
749
|
+
*/
|
|
750
|
+
scheduledFor?: string | Date;
|
|
751
|
+
/** Prazo. Precisa ser futuro e posterior a `scheduledFor`; ao vencer, o sweep aborta o turno. */
|
|
752
|
+
deadlineAt?: string | Date;
|
|
753
|
+
retryPolicy?: Record<string, unknown>;
|
|
754
|
+
notify?: Record<string, unknown>;
|
|
755
|
+
/** Encadeia esta task sob outra (árvore parent/root). */
|
|
756
|
+
parentTaskId?: string;
|
|
757
|
+
signal?: AbortSignal;
|
|
758
|
+
}
|
|
759
|
+
/** Task como o BE a devolve. Campos além dos mapeados ficam em `raw`. */
|
|
760
|
+
interface TaskInfo {
|
|
761
|
+
id: string;
|
|
762
|
+
agentId?: string;
|
|
763
|
+
title?: string;
|
|
764
|
+
instruction?: string;
|
|
765
|
+
/** `pending` → `running` → `completed` | `failed` | `cancelled` | `expired`. Não é enum fechado. */
|
|
766
|
+
status: string;
|
|
767
|
+
/**
|
|
768
|
+
* false só enquanto a task está em andamento. Status desconhecido conta como terminal (parar é
|
|
769
|
+
* melhor que girar para sempre); status ausente conta como não-terminal (não lemos o envelope).
|
|
770
|
+
*/
|
|
771
|
+
terminal: boolean;
|
|
772
|
+
scheduledFor?: string;
|
|
773
|
+
deadlineAt?: string;
|
|
774
|
+
parentTaskId?: string;
|
|
775
|
+
rootTaskId?: string;
|
|
776
|
+
sessionId?: string;
|
|
777
|
+
createdAt?: string;
|
|
778
|
+
updatedAt?: string;
|
|
779
|
+
raw: unknown;
|
|
780
|
+
}
|
|
781
|
+
interface ListTasksParams {
|
|
782
|
+
/** Só estados vivos (board do dia a dia). */
|
|
783
|
+
live?: boolean;
|
|
784
|
+
status?: string;
|
|
785
|
+
agentId?: string;
|
|
786
|
+
rootTaskId?: string;
|
|
787
|
+
limit?: number;
|
|
788
|
+
offset?: number;
|
|
789
|
+
signal?: AbortSignal;
|
|
790
|
+
}
|
|
791
|
+
interface TaskListPage {
|
|
792
|
+
items: TaskInfo[];
|
|
793
|
+
/** Total do tenant para a query. Vem de `meta.total` no envelope novo. */
|
|
794
|
+
total?: number;
|
|
795
|
+
raw: unknown;
|
|
796
|
+
}
|
|
797
|
+
/** Uma execução da task (uma task pode ter várias, com retry). */
|
|
798
|
+
interface TaskExecution {
|
|
799
|
+
id: string;
|
|
800
|
+
status: string;
|
|
801
|
+
exitReason?: string;
|
|
802
|
+
modelUsed?: string;
|
|
803
|
+
totalCostUsd?: number;
|
|
804
|
+
executionTimeMs?: number;
|
|
805
|
+
iterations?: number;
|
|
806
|
+
startedAt?: string;
|
|
807
|
+
finishedAt?: string;
|
|
808
|
+
raw: unknown;
|
|
809
|
+
}
|
|
810
|
+
interface TaskDossier {
|
|
811
|
+
task: TaskInfo;
|
|
812
|
+
executions: TaskExecution[];
|
|
813
|
+
children: TaskInfo[];
|
|
814
|
+
approvals: unknown[];
|
|
815
|
+
raw: unknown;
|
|
816
|
+
}
|
|
817
|
+
interface GetTaskParams {
|
|
818
|
+
taskId: string;
|
|
819
|
+
signal?: AbortSignal;
|
|
820
|
+
}
|
|
821
|
+
interface CancelTaskParams {
|
|
822
|
+
taskId: string;
|
|
823
|
+
/** Registrado na trilha. Default do BE: `user_cancel`. */
|
|
824
|
+
reason?: string;
|
|
825
|
+
signal?: AbortSignal;
|
|
826
|
+
}
|
|
827
|
+
interface CancelTaskResult {
|
|
828
|
+
task: TaskInfo;
|
|
829
|
+
/** true quando havia um turno em voo que foi realmente derrubado. */
|
|
830
|
+
aborted: boolean;
|
|
831
|
+
raw: unknown;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Transporte HTTP do núcleo: resolução/validação de base URL (SSRF), headers de auth,
|
|
836
|
+
* deadline via AbortController que cobre TODO o request (headers + corpo/stream),
|
|
837
|
+
* e retry com exponential backoff + jitter.
|
|
838
|
+
*
|
|
839
|
+
* Política de retry (evita duplicar efeito colateral):
|
|
840
|
+
* - 'idempotent' (GET): retenta em erro de rede, 429 e 5xx.
|
|
841
|
+
* - 'side-effecting' (POST: invoke/dispatch/create): retenta SÓ em 429/503 (servidor disse
|
|
842
|
+
* "tente depois", sem ter processado). NUNCA em erro de rede — o request pode ter sido
|
|
843
|
+
* recebido e processado mesmo com o socket caindo na volta.
|
|
844
|
+
*
|
|
845
|
+
* @module http
|
|
846
|
+
*/
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* Deadline default POR OPERAÇÃO. Cada valor fica ACIMA do orçamento do BE para aquela rota,
|
|
850
|
+
* porque o BE foi desenhado para entregar o desfecho ao cliente — desistir antes dele troca um
|
|
851
|
+
* erro estruturado (TURN_TIMEOUT com execution_id/session_id) por um abort cego nosso.
|
|
852
|
+
*
|
|
853
|
+
* Verificado contra o BE (`zihin-agent-builder/server-llm`) em 2026-08-31:
|
|
854
|
+
* - `stream` POST /api/v2/agents/:id/stream — o SSE fecha em 180s (`agents-v2.js:690`) e o turno
|
|
855
|
+
* do canal `chat` morre em 150s (`turn-deadline.js`). O stream sobrevive ao turno DE
|
|
856
|
+
* PROPÓSITO, para entregar o desfecho; 200s deixa essa cauda chegar.
|
|
857
|
+
* - `dispatch` POST /api/triggers/webhook/:id — fora do timeout global do Express
|
|
858
|
+
* (`express.js:71`); turno de canal `async` = 240s (`turn-deadline.js`).
|
|
859
|
+
* - `short` GETs de listagem/telemetria e CRUD de trigger — timeout global de 30s
|
|
860
|
+
* (`express.js:40`), que responde 408. Esperar mais que isso é esperar à toa.
|
|
861
|
+
*
|
|
862
|
+
* Mudou o BE? Atualize esta tabela e `docs/CONTRATO-INVOCACAO-AGENTE.md` juntos.
|
|
863
|
+
*/
|
|
864
|
+
declare const OPERATION_TIMEOUT_MS: Readonly<{
|
|
865
|
+
stream: 200000;
|
|
866
|
+
dispatch: 260000;
|
|
867
|
+
short: 35000;
|
|
868
|
+
}>;
|
|
869
|
+
type TimeoutOperation = keyof typeof OPERATION_TIMEOUT_MS;
|
|
870
|
+
/**
|
|
871
|
+
* Folga somada ao `options.timeoutMs` do chamador para formar o deadline LOCAL. O mesmo número vai
|
|
872
|
+
* ao BE como `options.timeout_ms` (o teto que ele deve respeitar); sem folga, os dois relógios
|
|
873
|
+
* disparam juntos e o erro do servidor — que tem request_id — perde a corrida para o nosso abort.
|
|
874
|
+
*
|
|
875
|
+
* 30s, não 10s: o relógio do turno no BE só começa DEPOIS da carga (config, tools, gates — um MCP
|
|
876
|
+
* frio leva dezenas de segundos), então o tempo de parede visto daqui é `carga + prazo` (contrato
|
|
877
|
+
* de tempo §4). Com 10s, um `timeoutMs: 60000` num agente com MCP abortava aos 70s e trocava o
|
|
878
|
+
* `done { outcome: 'TIMEOUT' }` do servidor por um abort cego.
|
|
879
|
+
*/
|
|
880
|
+
declare const DEADLINE_GRACE_MS = 30000;
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Listagem de agentes do tenant (GET /api/v2/agents). Alimenta o dropdown do nó n8n (#17)
|
|
884
|
+
* e o select de `agentId` no Paperclip (#20). Aceita key `zhn_live_*` (agents:read em todos os roles).
|
|
885
|
+
*
|
|
886
|
+
* @module agents
|
|
887
|
+
*/
|
|
888
|
+
|
|
889
|
+
interface AgentSummary {
|
|
890
|
+
id: string;
|
|
891
|
+
name?: string;
|
|
892
|
+
commercialName?: string;
|
|
893
|
+
bio?: string;
|
|
894
|
+
type?: string;
|
|
895
|
+
status?: string;
|
|
896
|
+
tags?: string[];
|
|
897
|
+
toolsCount?: number;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* ZihinAgentClient — fachada do núcleo. Carrega a config uma vez (auth, baseUrl, timeout,
|
|
902
|
+
* retries) e expõe os dois caminhos do padrão híbrido:
|
|
903
|
+
* • Direto: invokeAgent / streamAgent (POST /stream, SSE bufferizado)
|
|
904
|
+
* • Triggers: createTrigger / dispatchAgent / getExecution (async + callback)
|
|
905
|
+
* • Auxiliar: listAgents (dropdown de agentes)
|
|
906
|
+
*
|
|
907
|
+
* @module client
|
|
908
|
+
*/
|
|
909
|
+
|
|
910
|
+
declare class ZihinAgentClient {
|
|
911
|
+
private readonly cfg;
|
|
912
|
+
constructor(config: ZihinClientConfig);
|
|
913
|
+
/** Base URL efetiva (após validação SSRF). */
|
|
914
|
+
get baseUrl(): string;
|
|
915
|
+
/** Notifica `config.onError` (falha definitiva); o callback nunca derruba o fluxo. */
|
|
916
|
+
private report;
|
|
917
|
+
private guard;
|
|
918
|
+
/** Invoca o agente e retorna o resultado final consolidado (response + metrics). */
|
|
919
|
+
invokeAgent(params: InvokeAgentParams): Promise<AgentResult>;
|
|
920
|
+
/** Emite deltas ao vivo do agente (async iterator). */
|
|
921
|
+
streamAgent(params: InvokeAgentParams): AsyncGenerator<AgentStreamChunk>;
|
|
922
|
+
/** Provisiona (1x) um trigger de webhook ligado a um agente. */
|
|
923
|
+
createTrigger(params: CreateTriggerParams): Promise<TriggerRef>;
|
|
924
|
+
/** Reaproveita (por agente+nome) ou cria um trigger — evita duplicação. */
|
|
925
|
+
findOrCreateTrigger(params: CreateTriggerParams): Promise<FindOrCreateResult>;
|
|
926
|
+
/** Lista os triggers do tenant (filtros opcionais). */
|
|
927
|
+
listTriggers(params?: ListTriggersParams): Promise<TriggerInfo[]>;
|
|
928
|
+
/** Obtém um trigger pelo id (inclui webhookUrl). */
|
|
929
|
+
getTrigger(params: GetTriggerParams): Promise<TriggerInfo>;
|
|
930
|
+
/** Atualiza um trigger (name/description/enabled/extração/callback) via PATCH. */
|
|
931
|
+
updateTrigger(params: UpdateTriggerParams): Promise<TriggerInfo>;
|
|
932
|
+
/** Remove um trigger (requer key admin/owner). */
|
|
933
|
+
deleteTrigger(params: DeleteTriggerParams): Promise<{
|
|
934
|
+
deleted: boolean;
|
|
935
|
+
}>;
|
|
936
|
+
/** Dispara um trigger: 202 (async) ou JSON imediato (sync). */
|
|
937
|
+
dispatchAgent(params: DispatchAgentParams): Promise<DispatchResult>;
|
|
938
|
+
/** Consulta o resultado de uma execução disparada por trigger (polling). */
|
|
939
|
+
getExecution(params: GetExecutionParams): Promise<ExecutionStatus>;
|
|
940
|
+
/**
|
|
941
|
+
* Delega uma tarefa a um agente (requer key owner/admin). Sem `scheduledFor`, o BE dispara na
|
|
942
|
+
* hora fora do request e devolve só o handle — acompanhe com `getTask`.
|
|
943
|
+
*/
|
|
944
|
+
delegateTask(params: DelegateTaskParams): Promise<TaskInfo>;
|
|
945
|
+
/** Board de tasks do tenant (requer key owner/admin/editor). */
|
|
946
|
+
listTasks(params?: ListTasksParams): Promise<TaskListPage>;
|
|
947
|
+
/** Dossiê de uma task: task + execuções + filhos + aprovações. */
|
|
948
|
+
getTask(params: GetTaskParams): Promise<TaskDossier>;
|
|
949
|
+
/** Cancela uma task (requer key owner/admin); derruba o turno em voo, se houver. */
|
|
950
|
+
cancelTask(params: CancelTaskParams): Promise<CancelTaskResult>;
|
|
951
|
+
/** Lista os agentes do tenant (dropdown). */
|
|
952
|
+
listAgents(opts?: {
|
|
953
|
+
signal?: AbortSignal;
|
|
954
|
+
}): Promise<AgentSummary[]>;
|
|
955
|
+
}
|
|
956
|
+
/** Açúcar: cria um cliente a partir da config. */
|
|
957
|
+
declare function createClient(config: ZihinClientConfig): ZihinAgentClient;
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* Caminho A — invocação direta de agente (POST /api/v2/agents/:id/stream).
|
|
961
|
+
* `streamAgent` emite deltas ao vivo; `invokeAgent` buffeia o stream e consolida
|
|
962
|
+
* o evento `response` (content/sources/session_id/blocks) com o `metrics` (usage/model).
|
|
963
|
+
*
|
|
964
|
+
* @module invoke
|
|
965
|
+
*/
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Decide se o `event: error` terminal do BE foi um timeout do turno (#75).
|
|
969
|
+
*
|
|
970
|
+
* Fonte primária é o campo EXPLÍCITO `error.timed_out` (BE `utils/sse.js`, em prod desde
|
|
971
|
+
* 26/09/2026 — zihin-agent-builder#605): quando presente, é autoritativo nos dois sentidos —
|
|
972
|
+
* `true` classifica como timeout seja qual for o `code`; `false` classifica como `server` mesmo
|
|
973
|
+
* que o nome do código contenha "TIMEOUT". O BE vai renomear esses códigos
|
|
974
|
+
* (zihin-agent-builder#561) e o nome deixa de ser um sinal confiável.
|
|
975
|
+
*
|
|
976
|
+
* O regex no nome só vale como FALLBACK para backends que ainda não emitem `timed_out`
|
|
977
|
+
* (self-hosted/antigos). Some numa major futura, depois do rename no BE.
|
|
978
|
+
*/
|
|
979
|
+
declare function classifyStreamErrorKind(code: string, timedOut?: boolean): ZihinErrorKind;
|
|
980
|
+
/**
|
|
981
|
+
* Converte o chunk `type: 'error'` (evento terminal do BE) num `ZihinAgentError` — a MESMA regra
|
|
982
|
+
* que o `invokeAgent` aplica, exportada para quem consome `streamAgent` direto (o adapter do
|
|
983
|
+
* Paperclip, por exemplo) não reimplementar a classificação por conta e divergir dela.
|
|
984
|
+
* `ctx` leva os ids já vistos no `metadata` (o `error` em si não os traz).
|
|
985
|
+
*/
|
|
986
|
+
declare function errorFromStreamChunk(chunk: Extract<AgentStreamChunk, {
|
|
987
|
+
type: 'error';
|
|
988
|
+
}>, ctx?: {
|
|
989
|
+
executionId?: string;
|
|
990
|
+
sessionId?: string;
|
|
991
|
+
}): ZihinAgentError;
|
|
992
|
+
/**
|
|
993
|
+
* Converte um `outcome` cru (fora do `/stream`: `outcome` do webhook sync, `exit_reason` de uma
|
|
994
|
+
* execução) num `ZihinAgentError`, ou `undefined` quando o texto que o acompanha É resposta
|
|
995
|
+
* (`COMPLETED`, `LENGTH`, `PAUSED`, desconhecido/ausente). Mesma tabela do `errorFromDoneChunk`,
|
|
996
|
+
* sem o contexto de `status.blocked` que só o SSE tem.
|
|
997
|
+
*/
|
|
998
|
+
declare function errorFromOutcome(outcome: string | undefined, ctx?: {
|
|
999
|
+
executionId?: string;
|
|
1000
|
+
sessionId?: string;
|
|
1001
|
+
status?: number;
|
|
1002
|
+
endpoint?: string;
|
|
1003
|
+
attempts?: number;
|
|
1004
|
+
}): ZihinAgentError | undefined;
|
|
1005
|
+
/**
|
|
1006
|
+
* Converte o `done` de um turno que NÃO produziu resposta num `ZihinAgentError`; devolve
|
|
1007
|
+
* `undefined` quando o `content` é resposta. Mesma regra do `invokeAgent`, exportada para quem
|
|
1008
|
+
* consome `streamAgent` direto (o Paperclip) não reimplementá-la.
|
|
1009
|
+
*
|
|
1010
|
+
* Por que erro e não um campo no resultado: nesses turnos o BE já emitiu um `response` com a
|
|
1011
|
+
* mensagem de falha do motor em `content` (contrato de tempo §6). Como campo, cada shell teria
|
|
1012
|
+
* de lembrar de checá-lo — e o que esquecesse publicaria a falha como resposta, que é o defeito
|
|
1013
|
+
* que isto fecha (#81). Como erro, o caminho seguro é o default.
|
|
1014
|
+
*
|
|
1015
|
+
* TIMEOUT: `outcome: 'TIMEOUT'` é a fonte primária; `timed_out: true` sozinho também conta (o BE
|
|
1016
|
+
* só o emite com TIMEOUT). Vira `kind: 'timeout'`, `code: 'TURN_TIMEOUT'`, com `timeoutClock`.
|
|
1017
|
+
* Nenhum é retentável por default: o turno pode ter executado tools com efeito colateral
|
|
1018
|
+
* (exceção: `SESSION_BUSY`, em que o turno nem começou).
|
|
1019
|
+
*/
|
|
1020
|
+
declare function errorFromDoneChunk(chunk: Extract<AgentStreamChunk, {
|
|
1021
|
+
type: 'done';
|
|
1022
|
+
}>, ctx?: {
|
|
1023
|
+
executionId?: string;
|
|
1024
|
+
sessionId?: string;
|
|
1025
|
+
blocked?: string;
|
|
1026
|
+
}): ZihinAgentError | undefined;
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* Security utilities — portado de n8n-nodes-zihin/src/utils/security.ts (zero-dep).
|
|
1030
|
+
* SSRF prevention, payload limits, error sanitization, API-key format.
|
|
1031
|
+
* (A assinatura HMAC vive em `signing.ts` — precisa de ZihinAgentError e criaria ciclo aqui.)
|
|
1032
|
+
*
|
|
1033
|
+
* Núcleo compartilhado: os shells (n8n, Paperclip) reusam estas primitivas via @zihin/agent-client.
|
|
1034
|
+
*
|
|
1035
|
+
* @module security
|
|
1036
|
+
*/
|
|
1037
|
+
interface UrlValidationResult {
|
|
1038
|
+
valid: boolean;
|
|
1039
|
+
error?: string;
|
|
1040
|
+
sanitizedUrl?: string;
|
|
1041
|
+
}
|
|
1042
|
+
interface UrlValidationOptions {
|
|
1043
|
+
allowCustomDomains?: boolean;
|
|
1044
|
+
allowHttp?: boolean;
|
|
1045
|
+
allowLocalhost?: boolean;
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Validates a base URL for SSRF prevention.
|
|
1049
|
+
*
|
|
1050
|
+
* NOTA (S4): a checagem é por NOME de host, não pelo IP resolvido. Com `allowCustomDomains: true`,
|
|
1051
|
+
* um host que resolve para IP privado (DNS rebinding) passa. Em ambiente sensível, mantenha o
|
|
1052
|
+
* whitelist (default) ou faça a resolução/checagem de IP na borda de rede.
|
|
1053
|
+
*
|
|
1054
|
+
* @param url - The URL to validate
|
|
1055
|
+
* @param options - Validation options
|
|
1056
|
+
* @returns Validation result with sanitized URL or error
|
|
1057
|
+
*/
|
|
1058
|
+
declare function validateBaseUrl(url: string, options?: UrlValidationOptions): UrlValidationResult;
|
|
1059
|
+
interface PayloadLimits {
|
|
1060
|
+
maxPromptBytes: number;
|
|
1061
|
+
maxMessageCount: number;
|
|
1062
|
+
maxSingleMessageBytes: number;
|
|
1063
|
+
maxTotalPayloadBytes: number;
|
|
1064
|
+
}
|
|
1065
|
+
declare const DEFAULT_LIMITS: PayloadLimits;
|
|
1066
|
+
interface PayloadValidationResult {
|
|
1067
|
+
valid: boolean;
|
|
1068
|
+
error?: string;
|
|
1069
|
+
truncated?: boolean;
|
|
1070
|
+
details?: {
|
|
1071
|
+
promptBytes?: number;
|
|
1072
|
+
messageCount?: number;
|
|
1073
|
+
largestMessageBytes?: number;
|
|
1074
|
+
totalBytes?: number;
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Validates a single message/prompt size against configured limits.
|
|
1079
|
+
*/
|
|
1080
|
+
declare function validatePromptSize(prompt: string, limits?: Partial<PayloadLimits>): PayloadValidationResult;
|
|
1081
|
+
interface SanitizedError {
|
|
1082
|
+
message: string;
|
|
1083
|
+
code?: string;
|
|
1084
|
+
retryable: boolean;
|
|
1085
|
+
retryAfter?: number;
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Sanitizes API errors to prevent information leakage (keys, request IDs, stack traces).
|
|
1089
|
+
*/
|
|
1090
|
+
declare function sanitizeApiError(error: unknown): SanitizedError;
|
|
1091
|
+
interface ApiKeyValidationResult {
|
|
1092
|
+
valid: boolean;
|
|
1093
|
+
error?: string;
|
|
1094
|
+
environment?: 'live' | 'test' | 'dev';
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Validates API key format (zhn_live_* | zhn_test_* | zhn_dev_*).
|
|
1098
|
+
*/
|
|
1099
|
+
declare function validateApiKeyFormat(apiKey: string): ApiKeyValidationResult;
|
|
1100
|
+
|
|
1101
|
+
/**
|
|
1102
|
+
* Assinatura HMAC opcional de payloads (auth `signature` dos triggers).
|
|
1103
|
+
*
|
|
1104
|
+
* Vive fora de `security.ts` de propósito: precisa de `ZihinAgentError` (errors.ts), e errors.ts
|
|
1105
|
+
* já importa security.ts — juntar os dois criaria um ciclo de import.
|
|
1106
|
+
*
|
|
1107
|
+
* @module signing
|
|
1108
|
+
*/
|
|
1109
|
+
interface SignedRequestHeaders {
|
|
1110
|
+
'X-Zihin-Signature': string;
|
|
1111
|
+
'X-Zihin-Timestamp': string;
|
|
1112
|
+
'X-Zihin-Nonce': string;
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Assina um payload com HMAC-SHA256 (timestamp + nonce + payload).
|
|
1116
|
+
*
|
|
1117
|
+
* ⚠️ No build ESM depende de `process.getBuiltinModule` (Node >=20.16); em Node mais antigo via
|
|
1118
|
+
* `import` lança `ZihinAgentError` com `kind: 'config'`. O build CJS funciona em qualquer Node
|
|
1119
|
+
* suportado (>=20.16). (Todo o resto do núcleo é Web-API puro e roda nos dois.)
|
|
1120
|
+
*/
|
|
1121
|
+
declare function signRequest(payload: string, secretKey: string, timestamp?: number): SignedRequestHeaders;
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* Caminho C — delegação assíncrona durável (`/api/v1/tasks`).
|
|
1125
|
+
*
|
|
1126
|
+
* Diferente do Caminho B (triggers), aqui a conexão pode morrer sem levar o trabalho junto: a
|
|
1127
|
+
* task é persistida (`agent_tasks`), executada fora do request e o resultado é notificado. É o
|
|
1128
|
+
* caminho para runs que estouram o teto do host — o passo do Zapier morre em ~30s, e o próprio
|
|
1129
|
+
* app marca isso como pendência desde o primeiro commit.
|
|
1130
|
+
*
|
|
1131
|
+
* Contrato verificado em `routes/tasks.js` (2026-08-31) — ver o Caminho C de
|
|
1132
|
+
* `docs/CONTRATO-INVOCACAO-AGENTE.md`. Três coisas que diferem do resto da API e custaram
|
|
1133
|
+
* retrabalho quando supostas em vez de lidas:
|
|
1134
|
+
* 1. O envelope é o canônico novo: `{ success, data, meta:{request_id} }`. O `request_id` do
|
|
1135
|
+
* sucesso vive em `meta`, não no topo.
|
|
1136
|
+
* 2. O RBAC é mais estreito do que "admin/editor": delegar e cancelar exigem **owner|admin**;
|
|
1137
|
+
* editor só lê o board e o dossiê.
|
|
1138
|
+
* 3. Sem `scheduled_for` o BE dispara na hora, fora do request (`setImmediate`), e a resposta
|
|
1139
|
+
* devolve só o handle — por isso estas rotas são todas de orçamento curto.
|
|
1140
|
+
*
|
|
1141
|
+
* @module tasks
|
|
1142
|
+
*/
|
|
1143
|
+
|
|
1144
|
+
/**
|
|
1145
|
+
* Teto do `payload` congelado na task. Espelha o `MAX_PAYLOAD_BYTES` do BE
|
|
1146
|
+
* (`routes/tasks.js:26`) — validar aqui evita gastar um round-trip para receber um 400.
|
|
1147
|
+
*/
|
|
1148
|
+
declare const MAX_TASK_PAYLOAD_BYTES: number;
|
|
1149
|
+
|
|
1150
|
+
export { type AgentOutcome, type AgentResult, type AgentStreamChunk, type AgentSummary, type Attachment, type AttachmentType, type CancelTaskParams, type CancelTaskResult, type ClientBlockType, type ClientCapabilities, type ConsumerIdentity, type ContextValue, 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 IgnoredField, 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 TimeoutClock, type TimeoutOperation, type TriggerInfo, type TriggerMode, type TriggerRef, type UpdateTriggerParams, type Usage, ZihinAgentClient, ZihinAgentError, type ZihinClientConfig, type ZihinErrorKind, type ZihinRuntime, classifyStreamErrorKind, createClient, errorFromDoneChunk, errorFromOutcome, errorFromStreamChunk, isCommercialBlock, sanitizeApiError, signRequest, validateApiKeyFormat, validateBaseUrl, validatePromptSize };
|