@zihin/agent-client 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -75,14 +75,49 @@ const exec = await zihin.getExecution({ triggerId, executionId });
75
75
 
76
76
  > O modo `sync` do trigger (`mode: 'sync'`) responde 200 + JSON na hora (`dispatchResult.agentResponse`).
77
77
 
78
+ ### Payload estruturado (não só texto)
79
+
80
+ O caminho direto (`invokeAgent`) só aceita **texto** — o endpoint `/stream` do BE não tem campo de
81
+ payload estruturado. Para disparar um agente a partir de um JSON rico, use triggers: o payload vai
82
+ inteiro no `dispatchAgent` e o `queryExtraction` define como ele vira a mensagem do agente.
83
+
84
+ ```ts
85
+ // template: compõe a mensagem a partir de campos aninhados do payload
86
+ await zihin.findOrCreateTrigger({
87
+ agentId,
88
+ name: 'Pedidos',
89
+ mode: 'sync',
90
+ queryExtraction: { mode: 'template', template: 'Pedido {{payload.order.id}}: {{payload.order.note}}' },
91
+ });
92
+
93
+ // field (default): um campo top-level do payload
94
+ // queryExtraction: { mode: 'field', field: 'texto' }
95
+ // full_body: fallback do BE → payload.message || payload.query || JSON.stringify(payload)
96
+ // queryExtraction: { mode: 'full_body' }
97
+
98
+ await zihin.dispatchAgent({ triggerId, payload: { order: { id: 42, note: 'urgente' }, cliente: {...} } });
99
+ ```
100
+
101
+ ⚠️ O que chega ao agente é **a string extraída**, não o payload inteiro: com `field`/`template`, os
102
+ campos que você não referenciou ficam de fora. Para levar tudo, use `full_body` — e note que ele
103
+ só serializa o payload completo **se não houver** um campo `message`/`query` no topo (nesse caso o
104
+ BE usa esse campo). Precisa de campos extras como contexto estruturado? Isso é `context_mapping`
105
+ no trigger, ainda não exposto pelo núcleo — configure direto no BE.
106
+
78
107
  ### Ciclo de vida do trigger
79
108
 
80
109
  ```ts
81
110
  await zihin.listTriggers({ agentId }); // lista (filtros: agentId/triggerType/enabledOnly)
82
111
  await zihin.getTrigger({ triggerId }); // detalhe (inclui webhookUrl)
112
+ await zihin.updateTrigger({ triggerId, enabled: false }); // PATCH parcial
113
+ await zihin.updateTrigger({ triggerId, queryExtraction: { mode: 'full_body' } }); // GET+merge+PATCH
83
114
  await zihin.deleteTrigger({ triggerId }); // remove (requer key admin/owner; editor → 403)
84
115
  ```
85
116
 
117
+ > `updateTrigger` faz GET + merge + PATCH quando você altera config (o BE substitui
118
+ > `trigger_config` inteiro). Há uma janela entre o GET e o PATCH em que uma escrita concorrente
119
+ > pode ser sobrescrita.
120
+
86
121
  ## Dropdown de agentes
87
122
 
88
123
  ```ts
@@ -93,8 +128,8 @@ const agents = await zihin.listAgents(); // GET /api/v2/agents
93
128
  ## Permissões da API key
94
129
 
95
130
  - `invokeAgent` / `streamAgent` — qualquer role.
96
- - `createTrigger` / `getExecution` — exigem key **admin** ou **editor** (default é admin). Keys
97
- `member` recebem `ZihinAgentError` com `kind: 'auth'`.
131
+ - `createTrigger` / `updateTrigger` / `getExecution` — exigem key **admin** ou **editor** (default é
132
+ admin). Keys `member` recebem `ZihinAgentError` com `kind: 'auth'`.
98
133
 
99
134
  ## Erros
100
135
 
@@ -123,11 +158,17 @@ preservados. Cancele com `AbortSignal` via `params.signal`.
123
158
  ### Observabilidade e portabilidade
124
159
 
125
160
  - **Hooks** (opt-in, sem `console`): `onRetry(info)` (a cada retentativa, com `endpoint`/`attempt`/
126
- `delayMs`/`reason`/`status`) e `onError(err)`.
161
+ `delayMs`/`reason`/`status`) e `onError(err)` — chamado quando uma operação falha em definitivo
162
+ (inclusive `aborted`; filtre por `err.kind`). Exceção lançada dentro do hook é engolida e nunca
163
+ mascara o erro original.
127
164
  - **`fetch` injetável** — `fetch?` na config; por padrão usa `globalThis.fetch` (Node ≥18). Um
128
165
  preflight dá erro `config` claro se não houver fetch.
129
166
  - **Portável** — sem `Buffer`; `node:crypto` só é carregado se você usar `signRequest`. Roda em Node;
130
- para edge/serverless, injete `fetch` se necessário.
167
+ para edge/serverless, injete `fetch` se necessário. O pacote publica **CJS e ESM** (`exports` map):
168
+ `require()` e `import` funcionam. Duas ressalvas: no build ESM, `signRequest` precisa de Node
169
+ >=20.16 (`process.getBuiltinModule`) e sem isso lança `ZihinAgentError` com `kind: 'config'`; e
170
+ carregar **as duas formas no mesmo processo** cria duas classes `ZihinAgentError` distintas, o que
171
+ faz `instanceof` falhar — padronize `require` **ou** `import` para o núcleo na sua árvore de deps.
131
172
 
132
173
  ```ts
133
174
  const zihin = createClient({
@@ -140,8 +181,14 @@ const zihin = createClient({
140
181
  });
141
182
  ```
142
183
 
143
- > SSRF: a validação de base/callback URL é por nome de host. Com `allowCustomBaseUrl: true` um host
144
- > que resolva para IP privado (DNS rebinding) passa — mantenha o whitelist em ambiente sensível.
184
+ > SSRF: a validação de base/callback/attachment URL é por **nome de host** — cobre IPv4 privado
185
+ > (inclusive octal/hex, normalizados pela URL), IPv6 privado **por faixa de bits** (`fc00::/7`,
186
+ > `fe80::/10`, `fec0::/10`, loopback e IPv4 embutido `::ffff:`/`::`), FQDN com ponto final e os
187
+ > hosts de metadata de nuvem.
188
+ >
189
+ > **Limite importante:** é validação por nome, não por IP resolvido. Com `allowCustomBaseUrl: true`,
190
+ > um host que **resolva** para IP privado (DNS rebinding) passa. Isto não substitui bloqueio de
191
+ > egresso na borda de rede — mantenha o whitelist (default) em ambiente sensível.
145
192
 
146
193
  ## Segurança
147
194
 
@@ -0,0 +1,510 @@
1
+ /**
2
+ * Erros do @zihin/agent-client — sanitizados (sem vazar key/Bearer/stack), mas COMPLETOS:
3
+ * carregam endpoint, nº de tentativas e request_id do BE para depuração em produção externa.
4
+ *
5
+ * @module errors
6
+ */
7
+ type ZihinErrorKind = 'config' | 'auth' | 'not_found' | 'rate_limit' | 'timeout' | 'aborted' | 'server' | 'network' | 'protocol' | 'unknown';
8
+ /** Contexto opcional anexado a um erro para correlação/depuração. */
9
+ interface ErrorMeta {
10
+ /** Ex.: "POST llm.zihin.ai/api/v2/agents/abc/stream". */
11
+ endpoint?: string;
12
+ /** Número de tentativas feitas antes de falhar. */
13
+ attempts?: number;
14
+ }
15
+ interface ZihinErrorOptions extends ErrorMeta {
16
+ status?: number;
17
+ code?: string;
18
+ retryable?: boolean;
19
+ /** request_id do BE (NÃO é segredo) — handle para o suporte correlacionar nos logs. */
20
+ requestId?: string;
21
+ }
22
+ declare class ZihinAgentError extends Error {
23
+ readonly kind: ZihinErrorKind;
24
+ readonly status?: number;
25
+ readonly code?: string;
26
+ readonly retryable: boolean;
27
+ readonly requestId?: string;
28
+ readonly attempts?: number;
29
+ readonly endpoint?: string;
30
+ constructor(kind: ZihinErrorKind, message: string, opts?: ZihinErrorOptions);
31
+ }
32
+
33
+ /**
34
+ * Tipos do contrato externo Zihin — fonte de verdade verificada contra o BE
35
+ * (zihin-agent-builder/server-llm, 2026-06-28).
36
+ *
37
+ * @module types
38
+ */
39
+
40
+ interface ZihinClientConfig {
41
+ /** API key `zhn_live_*` (tenant embutido). Para triggers/telemetria use role admin/editor. */
42
+ apiKey: string;
43
+ /** Base URL. Default: https://llm.zihin.ai */
44
+ baseUrl?: string;
45
+ /** Timeout por request (ms). Default: 120000 (= limite do /stream no BE). */
46
+ timeoutMs?: number;
47
+ /** Máximo de retentativas em 5xx/429/erro de rede. Default: 2. */
48
+ maxRetries?: number;
49
+ /** Permite base URL fora da whitelist Zihin (default false → SSRF guard). */
50
+ allowCustomBaseUrl?: boolean;
51
+ /** Permite http:// (apenas dev/local). Default false. */
52
+ allowHttp?: boolean;
53
+ /** Teto de bytes por evento/linha SSE (anti-DoS de memória). Default: 1MB. */
54
+ maxStreamEventBytes?: number;
55
+ /** Teto de bytes do stream inteiro (anti-DoS de memória). Default: 64MB. */
56
+ maxStreamTotalBytes?: number;
57
+ /**
58
+ * Implementação de `fetch` a usar. Default: `globalThis.fetch`. Injete em runtimes onde o
59
+ * fetch não é global (Node <18 com polyfill, testes, proxies).
60
+ */
61
+ fetch?: typeof fetch;
62
+ /**
63
+ * Observabilidade (opt-in, sem console): chamado antes de cada retentativa.
64
+ * Para logar backoff/timeouts/falhas transitórias em produção externa.
65
+ */
66
+ onRetry?: (info: RetryInfo) => void;
67
+ /**
68
+ * Observabilidade: chamado quando uma operação do cliente falha em definitivo (após retries),
69
+ * inclusive `aborted` — filtre por `error.kind` se quiser ignorar cancelamentos. Uma exceção
70
+ * lançada dentro do callback é engolida (não mascara o erro original). Não cobre erros de
71
+ * configuração lançados pelo construtor (o cliente ainda não existe).
72
+ */
73
+ onError?: (error: ZihinAgentError) => void;
74
+ }
75
+ interface RetryInfo {
76
+ /** "POST llm.zihin.ai/api/v2/agents/abc/stream". */
77
+ endpoint: string;
78
+ /** Número da tentativa que acabou de falhar (1 = primeira). */
79
+ attempt: number;
80
+ /** Atraso até a próxima tentativa, em ms. */
81
+ delayMs: number;
82
+ /** Motivo do retry. */
83
+ reason: 'status' | 'network';
84
+ /** Status HTTP, quando `reason === 'status'`. */
85
+ status?: number;
86
+ }
87
+ type AttachmentType = 'image' | 'audio' | 'document';
88
+ interface Attachment {
89
+ url: string;
90
+ type: AttachmentType;
91
+ }
92
+ interface InvokeOptions {
93
+ temperature?: number;
94
+ maxTokens?: number;
95
+ /** Timeout específico desta chamada (ms); sobrepõe o do cliente. */
96
+ timeoutMs?: number;
97
+ /** Just-in-time tool loading (Sprint 33). */
98
+ jitMode?: boolean;
99
+ }
100
+ interface InvokeAgentParams {
101
+ /** UUID do agente (path :agent_id). */
102
+ agentId: string;
103
+ /** Mensagem ao agente. `message` OU `attachments` é obrigatório. */
104
+ message?: string;
105
+ /** Anexos multimodais. `message` OU `attachments` é obrigatório. */
106
+ attachments?: Attachment[];
107
+ /** Continuidade de conversa (Supabase agent_sessions). */
108
+ sessionId?: string;
109
+ options?: InvokeOptions;
110
+ metadata?: {
111
+ traceId?: string;
112
+ correlationId?: string;
113
+ [k: string]: unknown;
114
+ };
115
+ /** Cancelamento externo. */
116
+ signal?: AbortSignal;
117
+ /**
118
+ * Retém todos os eventos SSE brutos em `AgentResult.raw`. Default: false (economiza memória
119
+ * em runs longos). `streamAgent` sempre entrega os chunks ao vivo, independente disto.
120
+ */
121
+ includeRaw?: boolean;
122
+ }
123
+ interface Usage {
124
+ inputTokens?: number;
125
+ outputTokens?: number;
126
+ totalTokens?: number;
127
+ costUsd?: number;
128
+ }
129
+ interface Source {
130
+ title?: string;
131
+ url?: string;
132
+ [k: string]: unknown;
133
+ }
134
+ /**
135
+ * Resultado final de `invokeAgent`: consolida o evento SSE `response`
136
+ * (content/sources/session_id/blocks) com o evento `metrics` (usage/model).
137
+ */
138
+ interface AgentResult {
139
+ content: string;
140
+ sessionId?: string;
141
+ model?: string;
142
+ usage?: Usage;
143
+ sources?: Source[];
144
+ /** Cards/blocos estruturados (SPEC B). */
145
+ blocks?: Array<{
146
+ type: string;
147
+ [k: string]: unknown;
148
+ }>;
149
+ iterations?: number;
150
+ executionTimeMs?: number;
151
+ /** Eventos SSE brutos coletados (para depuração / casos avançados). */
152
+ raw: AgentStreamChunk[];
153
+ }
154
+ /**
155
+ * Chunk normalizado emitido por `streamAgent`. Mapeia os tipos de evento SSE do BE:
156
+ * metadata, status, attachments, phase, thinking, tool, action_proposed, token,
157
+ * warning, response, metrics.
158
+ */
159
+ type AgentStreamChunk = {
160
+ type: 'metadata';
161
+ requestId?: string;
162
+ agentId?: string;
163
+ sessionId?: string;
164
+ data: unknown;
165
+ } | {
166
+ type: 'status';
167
+ phase?: string;
168
+ data: unknown;
169
+ } | {
170
+ type: 'attachments';
171
+ data: unknown;
172
+ } | {
173
+ type: 'phase';
174
+ phase?: string;
175
+ data: unknown;
176
+ } | {
177
+ type: 'thinking';
178
+ thought?: string;
179
+ data: unknown;
180
+ } | {
181
+ type: 'tool';
182
+ tool?: string;
183
+ callId?: string;
184
+ success?: boolean;
185
+ data: unknown;
186
+ } | {
187
+ type: 'action_proposed';
188
+ data: unknown;
189
+ } | {
190
+ type: 'token';
191
+ content: string;
192
+ data: unknown;
193
+ } | {
194
+ type: 'warning';
195
+ message?: string;
196
+ data: unknown;
197
+ } | {
198
+ type: 'response';
199
+ content: string;
200
+ sessionId?: string;
201
+ sources?: Source[];
202
+ blocks?: Array<{
203
+ type: string;
204
+ [k: string]: unknown;
205
+ }>;
206
+ data: unknown;
207
+ } | {
208
+ type: 'metrics';
209
+ usage?: Usage;
210
+ model?: string;
211
+ iterations?: number;
212
+ executionTimeMs?: number;
213
+ data: unknown;
214
+ } | {
215
+ type: 'unknown';
216
+ event: string;
217
+ data: unknown;
218
+ };
219
+ type TriggerMode = 'sync' | 'async';
220
+ /**
221
+ * Como o BE extrai a mensagem do agente a partir do payload de disparo (webhook):
222
+ * - `field`: um campo top-level do payload (`payload[field]`).
223
+ * - `template`: compõe a mensagem com placeholders `{{payload.caminho.aninhado}}` —
224
+ * é o modo para payloads estruturados ricos.
225
+ * - `full_body`: fallback do BE — usa `payload.message || payload.query ||
226
+ * JSON.stringify(payload)` (o payload inteiro serializado vira a mensagem).
227
+ * (Verificado no BE em 2026-08-03: `jmespath` citado em docs antigas NÃO é implementado.)
228
+ */
229
+ type QueryExtraction = {
230
+ mode: 'field';
231
+ field: string;
232
+ } | {
233
+ mode: 'template';
234
+ template: string;
235
+ } | {
236
+ mode: 'full_body';
237
+ };
238
+ interface CreateTriggerParams {
239
+ agentId: string;
240
+ /** Nome legível do trigger. */
241
+ name: string;
242
+ /** `async` → 202 + callback; `sync` → 200 + JSON na hora. Default: async. */
243
+ mode?: TriggerMode;
244
+ /** URL que o BE chama com o resultado (obrigatório no modo async). */
245
+ callbackUrl?: string;
246
+ /** Método HTTP do callback. Default: POST. */
247
+ callbackMethod?: 'POST' | 'PUT';
248
+ /** Campo do payload de disparo de onde sai a mensagem do agente. Default: "message". */
249
+ queryField?: string;
250
+ /**
251
+ * Extração da mensagem a partir do payload (field/template/full_body). Tem precedência
252
+ * sobre `queryField` (que equivale a `{ mode: 'field', field: queryField }`).
253
+ */
254
+ queryExtraction?: QueryExtraction;
255
+ /** Auth de disparo aceita. Default: api_key. */
256
+ dispatchAuth?: 'api_key' | 'signature' | 'none';
257
+ signal?: AbortSignal;
258
+ }
259
+ /**
260
+ * Atualização parcial de um trigger (PATCH). Campos de config (queryExtraction/callback/mode)
261
+ * exigem um GET prévio + merge, porque o PATCH do BE SUBSTITUI `trigger_config` por inteiro.
262
+ */
263
+ interface UpdateTriggerParams {
264
+ triggerId: string;
265
+ name?: string;
266
+ description?: string;
267
+ enabled?: boolean;
268
+ /** Troca a extração de query preservando o restante do trigger_config. */
269
+ queryExtraction?: QueryExtraction;
270
+ /** Troca a URL do callback (modo async). */
271
+ callbackUrl?: string;
272
+ /** Troca o método do callback. */
273
+ callbackMethod?: 'POST' | 'PUT';
274
+ /** Troca o modo de execução (sync/async). */
275
+ mode?: TriggerMode;
276
+ signal?: AbortSignal;
277
+ }
278
+ interface TriggerRef {
279
+ triggerId: string;
280
+ /** URL completa de disparo: POST {webhookUrl}. */
281
+ webhookUrl: string;
282
+ }
283
+ /** Resultado de findOrCreateTrigger: o ref + se foi recém-criado ou reaproveitado. */
284
+ interface FindOrCreateResult extends TriggerRef {
285
+ created: boolean;
286
+ }
287
+ /** Trigger como listado/obtido do BE. */
288
+ interface TriggerInfo {
289
+ id: string;
290
+ agentId?: string;
291
+ name?: string;
292
+ description?: string;
293
+ triggerType?: string;
294
+ enabled?: boolean;
295
+ triggerCount?: number;
296
+ lastError?: string;
297
+ /** Presente para triggers do tipo webhook. */
298
+ webhookUrl?: string;
299
+ }
300
+ interface ListTriggersParams {
301
+ /** Filtra por agente. */
302
+ agentId?: string;
303
+ /** Filtra por tipo (webhook | db_event | email | schedule). */
304
+ triggerType?: string;
305
+ /** Apenas habilitados. */
306
+ enabledOnly?: boolean;
307
+ signal?: AbortSignal;
308
+ }
309
+ interface GetTriggerParams {
310
+ triggerId: string;
311
+ signal?: AbortSignal;
312
+ }
313
+ interface DeleteTriggerParams {
314
+ triggerId: string;
315
+ signal?: AbortSignal;
316
+ }
317
+ interface DispatchAgentParams {
318
+ triggerId: string;
319
+ /** Payload entregue ao trigger (a mensagem sai do `queryField` configurado). */
320
+ payload: Record<string, unknown>;
321
+ signal?: AbortSignal;
322
+ }
323
+ interface DispatchResult {
324
+ /** true quando o BE retorna 202 (async) ou 200 (sync). */
325
+ accepted: boolean;
326
+ triggerId?: string;
327
+ executionId?: string;
328
+ /** Presente quando o trigger é `sync`: resposta imediata do agente. */
329
+ agentResponse?: string;
330
+ usage?: Usage;
331
+ raw: unknown;
332
+ }
333
+ interface ExecutionStatus {
334
+ executionId: string;
335
+ status: string;
336
+ startedAt?: string;
337
+ finishedAt?: string;
338
+ executionTimeMs?: number;
339
+ usage?: Usage;
340
+ result?: string;
341
+ raw: unknown;
342
+ }
343
+ interface GetExecutionParams {
344
+ triggerId: string;
345
+ executionId: string;
346
+ signal?: AbortSignal;
347
+ }
348
+
349
+ /**
350
+ * Listagem de agentes do tenant (GET /api/v2/agents). Alimenta o dropdown do nó n8n (#17)
351
+ * e o select de `agentId` no Paperclip (#20). Aceita key `zhn_live_*` (agents:read em todos os roles).
352
+ *
353
+ * @module agents
354
+ */
355
+
356
+ interface AgentSummary {
357
+ id: string;
358
+ name?: string;
359
+ commercialName?: string;
360
+ bio?: string;
361
+ type?: string;
362
+ status?: string;
363
+ tags?: string[];
364
+ toolsCount?: number;
365
+ }
366
+
367
+ /**
368
+ * ZihinAgentClient — fachada do núcleo. Carrega a config uma vez (auth, baseUrl, timeout,
369
+ * retries) e expõe os dois caminhos do padrão híbrido:
370
+ * • Direto: invokeAgent / streamAgent (POST /stream, SSE bufferizado)
371
+ * • Triggers: createTrigger / dispatchAgent / getExecution (async + callback)
372
+ * • Auxiliar: listAgents (dropdown de agentes)
373
+ *
374
+ * @module client
375
+ */
376
+
377
+ declare class ZihinAgentClient {
378
+ private readonly cfg;
379
+ constructor(config: ZihinClientConfig);
380
+ /** Base URL efetiva (após validação SSRF). */
381
+ get baseUrl(): string;
382
+ /** Notifica `config.onError` (falha definitiva); o callback nunca derruba o fluxo. */
383
+ private report;
384
+ private guard;
385
+ /** Invoca o agente e retorna o resultado final consolidado (response + metrics). */
386
+ invokeAgent(params: InvokeAgentParams): Promise<AgentResult>;
387
+ /** Emite deltas ao vivo do agente (async iterator). */
388
+ streamAgent(params: InvokeAgentParams): AsyncGenerator<AgentStreamChunk>;
389
+ /** Provisiona (1x) um trigger de webhook ligado a um agente. */
390
+ createTrigger(params: CreateTriggerParams): Promise<TriggerRef>;
391
+ /** Reaproveita (por agente+nome) ou cria um trigger — evita duplicação. */
392
+ findOrCreateTrigger(params: CreateTriggerParams): Promise<FindOrCreateResult>;
393
+ /** Lista os triggers do tenant (filtros opcionais). */
394
+ listTriggers(params?: ListTriggersParams): Promise<TriggerInfo[]>;
395
+ /** Obtém um trigger pelo id (inclui webhookUrl). */
396
+ getTrigger(params: GetTriggerParams): Promise<TriggerInfo>;
397
+ /** Atualiza um trigger (name/description/enabled/extração/callback) via PATCH. */
398
+ updateTrigger(params: UpdateTriggerParams): Promise<TriggerInfo>;
399
+ /** Remove um trigger (requer key admin/owner). */
400
+ deleteTrigger(params: DeleteTriggerParams): Promise<{
401
+ deleted: boolean;
402
+ }>;
403
+ /** Dispara um trigger: 202 (async) ou JSON imediato (sync). */
404
+ dispatchAgent(params: DispatchAgentParams): Promise<DispatchResult>;
405
+ /** Consulta o resultado de uma execução disparada por trigger (polling). */
406
+ getExecution(params: GetExecutionParams): Promise<ExecutionStatus>;
407
+ /** Lista os agentes do tenant (dropdown). */
408
+ listAgents(opts?: {
409
+ signal?: AbortSignal;
410
+ }): Promise<AgentSummary[]>;
411
+ }
412
+ /** Açúcar: cria um cliente a partir da config. */
413
+ declare function createClient(config: ZihinClientConfig): ZihinAgentClient;
414
+
415
+ /**
416
+ * Security utilities — portado de n8n-nodes-zihin/src/utils/security.ts (zero-dep).
417
+ * SSRF prevention, payload limits, error sanitization, API-key format.
418
+ * (A assinatura HMAC vive em `signing.ts` — precisa de ZihinAgentError e criaria ciclo aqui.)
419
+ *
420
+ * Núcleo compartilhado: os shells (n8n, Paperclip) reusam estas primitivas via @zihin/agent-client.
421
+ *
422
+ * @module security
423
+ */
424
+ interface UrlValidationResult {
425
+ valid: boolean;
426
+ error?: string;
427
+ sanitizedUrl?: string;
428
+ }
429
+ interface UrlValidationOptions {
430
+ allowCustomDomains?: boolean;
431
+ allowHttp?: boolean;
432
+ allowLocalhost?: boolean;
433
+ }
434
+ /**
435
+ * Validates a base URL for SSRF prevention.
436
+ *
437
+ * NOTA (S4): a checagem é por NOME de host, não pelo IP resolvido. Com `allowCustomDomains: true`,
438
+ * um host que resolve para IP privado (DNS rebinding) passa. Em ambiente sensível, mantenha o
439
+ * whitelist (default) ou faça a resolução/checagem de IP na borda de rede.
440
+ *
441
+ * @param url - The URL to validate
442
+ * @param options - Validation options
443
+ * @returns Validation result with sanitized URL or error
444
+ */
445
+ declare function validateBaseUrl(url: string, options?: UrlValidationOptions): UrlValidationResult;
446
+ interface PayloadLimits {
447
+ maxPromptBytes: number;
448
+ maxMessageCount: number;
449
+ maxSingleMessageBytes: number;
450
+ maxTotalPayloadBytes: number;
451
+ }
452
+ declare const DEFAULT_LIMITS: PayloadLimits;
453
+ interface PayloadValidationResult {
454
+ valid: boolean;
455
+ error?: string;
456
+ truncated?: boolean;
457
+ details?: {
458
+ promptBytes?: number;
459
+ messageCount?: number;
460
+ largestMessageBytes?: number;
461
+ totalBytes?: number;
462
+ };
463
+ }
464
+ /**
465
+ * Validates a single message/prompt size against configured limits.
466
+ */
467
+ declare function validatePromptSize(prompt: string, limits?: Partial<PayloadLimits>): PayloadValidationResult;
468
+ interface SanitizedError {
469
+ message: string;
470
+ code?: string;
471
+ retryable: boolean;
472
+ retryAfter?: number;
473
+ }
474
+ /**
475
+ * Sanitizes API errors to prevent information leakage (keys, request IDs, stack traces).
476
+ */
477
+ declare function sanitizeApiError(error: unknown): SanitizedError;
478
+ interface ApiKeyValidationResult {
479
+ valid: boolean;
480
+ error?: string;
481
+ environment?: 'live' | 'test' | 'dev';
482
+ }
483
+ /**
484
+ * Validates API key format (zhn_live_* | zhn_test_* | zhn_dev_*).
485
+ */
486
+ declare function validateApiKeyFormat(apiKey: string): ApiKeyValidationResult;
487
+
488
+ /**
489
+ * Assinatura HMAC opcional de payloads (auth `signature` dos triggers).
490
+ *
491
+ * Vive fora de `security.ts` de propósito: precisa de `ZihinAgentError` (errors.ts), e errors.ts
492
+ * já importa security.ts — juntar os dois criaria um ciclo de import.
493
+ *
494
+ * @module signing
495
+ */
496
+ interface SignedRequestHeaders {
497
+ 'X-Zihin-Signature': string;
498
+ 'X-Zihin-Timestamp': string;
499
+ 'X-Zihin-Nonce': string;
500
+ }
501
+ /**
502
+ * Assina um payload com HMAC-SHA256 (timestamp + nonce + payload).
503
+ *
504
+ * ⚠️ No build ESM depende de `process.getBuiltinModule` (Node >=20.16); em Node mais antigo via
505
+ * `import` lança `ZihinAgentError` com `kind: 'config'`. O build CJS funciona em qualquer Node
506
+ * >=18. (Todo o resto do núcleo é Web-API puro e roda nos dois.)
507
+ */
508
+ declare function signRequest(payload: string, secretKey: string, timestamp?: number): SignedRequestHeaders;
509
+
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 };