@zihin/agent-client 0.1.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 ADDED
@@ -0,0 +1,156 @@
1
+ # @zihin/agent-client
2
+
3
+ Núcleo de **invocação de agente** Zihin.ai — o padrão único que n8n, Paperclip e integrações
4
+ futuras usam para acionar agentes hospedados na Zihin. Zero dependência de plataforma.
5
+
6
+ > Contexto e decisões: `docs/issues/PLANO-padrao-agent-client.md` (issue #16). Contrato verificado
7
+ > contra o BE (`zihin-agent-builder/server-llm`) em 2026-06-28.
8
+
9
+ ## A ideia
10
+
11
+ Toda entrada externa na Zihin atravessa um **agente** (RBAC, quota, HITL, auditoria, memória) — não
12
+ a call crua de LLM. Este pacote esconde os detalhes de transporte (SSE, triggers) atrás de uma API
13
+ simples, e oferece **dois estilos** de invocação:
14
+
15
+ | Estilo | Método | Quando usar |
16
+ |---|---|---|
17
+ | **Direto / imediato** | `invokeAgent` / `streamAgent` | espera a resposta (n8n, request/resposta). Zero setup. |
18
+ | **Assíncrono / desacoplado** | `createTrigger` + `dispatchAgent` (+ `getExecution`) | dispara e recebe via callback (serverless, execução longa). |
19
+
20
+ ## Instalação
21
+
22
+ ```bash
23
+ npm install @zihin/agent-client
24
+ ```
25
+
26
+ Requer Node >= 18 (usa `fetch`/`ReadableStream` globais).
27
+
28
+ ## Uso — caminho direto
29
+
30
+ ```ts
31
+ import { createClient } from '@zihin/agent-client';
32
+
33
+ const zihin = createClient({ apiKey: process.env.ZIHIN_API_KEY! }); // zhn_live_*
34
+
35
+ // Imediato — buffeia o SSE e consolida response + metrics
36
+ const res = await zihin.invokeAgent({
37
+ agentId: 'uuid-do-agente',
38
+ message: 'Qual a previsão de vendas?',
39
+ sessionId: previousSessionId, // opcional, continua a conversa
40
+ });
41
+ console.log(res.content); // texto final
42
+ console.log(res.usage); // { inputTokens, outputTokens, totalTokens, costUsd }
43
+ console.log(res.sessionId); // guarde para a próxima mensagem
44
+ console.log(res.model); // modelo efetivo
45
+
46
+ // Ao vivo — deltas conforme o agente pensa/usa tools/responde
47
+ for await (const chunk of zihin.streamAgent({ agentId, message: 'Olá' })) {
48
+ if (chunk.type === 'token') process.stdout.write(chunk.content);
49
+ if (chunk.type === 'tool') console.log('tool:', chunk.tool);
50
+ }
51
+ ```
52
+
53
+ ## Uso — caminho assíncrono (triggers)
54
+
55
+ ```ts
56
+ // 1x: provisiona (ou reaproveita) um webhook ligado ao agente.
57
+ // PREFIRA findOrCreateTrigger — o BE não tem unicidade de nome, então createTrigger
58
+ // repetido cria duplicatas. findOrCreate reusa por (agente + nome).
59
+ const { triggerId, webhookUrl, created } = await zihin.findOrCreateTrigger({
60
+ agentId,
61
+ name: 'Integração X',
62
+ mode: 'async',
63
+ callbackUrl: 'https://minha-plataforma.com/webhook/zihin',
64
+ });
65
+
66
+ // N vezes: dispara (responde 202 na hora; resultado chega no callback)
67
+ const { accepted, executionId } = await zihin.dispatchAgent({
68
+ triggerId,
69
+ payload: { message: 'Processa este pedido' },
70
+ });
71
+
72
+ // opcional: polling do resultado em vez de callback
73
+ const exec = await zihin.getExecution({ triggerId, executionId });
74
+ ```
75
+
76
+ > O modo `sync` do trigger (`mode: 'sync'`) responde 200 + JSON na hora (`dispatchResult.agentResponse`).
77
+
78
+ ### Ciclo de vida do trigger
79
+
80
+ ```ts
81
+ await zihin.listTriggers({ agentId }); // lista (filtros: agentId/triggerType/enabledOnly)
82
+ await zihin.getTrigger({ triggerId }); // detalhe (inclui webhookUrl)
83
+ await zihin.deleteTrigger({ triggerId }); // remove (requer key admin/owner; editor → 403)
84
+ ```
85
+
86
+ ## Dropdown de agentes
87
+
88
+ ```ts
89
+ const agents = await zihin.listAgents(); // GET /api/v2/agents
90
+ // [{ id, name, commercialName, status, toolsCount, ... }]
91
+ ```
92
+
93
+ ## Permissões da API key
94
+
95
+ - `invokeAgent` / `streamAgent` — qualquer role.
96
+ - `createTrigger` / `getExecution` — exigem key **admin** ou **editor** (default é admin). Keys
97
+ `member` recebem `ZihinAgentError` com `kind: 'auth'`.
98
+
99
+ ## Erros
100
+
101
+ Todos os erros são `ZihinAgentError` com `kind` (`config | auth | not_found | rate_limit | timeout |
102
+ aborted | server | network | protocol | unknown`), `status`, `code`, `retryable` e — para depurar em
103
+ produção externa — `endpoint` (`"POST host/path"`), `attempts` e **`requestId`** (handle do BE para o
104
+ suporte correlacionar). Erros de rede carregam a causa-raiz (`ECONNREFUSED`/`ENOTFOUND`/TLS…) na
105
+ mensagem. As mensagens redigem **só segredos** (key/Bearer/stack) — `request_id`/`trace_id` são
106
+ preservados. Cancele com `AbortSignal` via `params.signal`.
107
+
108
+ ## Robustez (timeout / retry / limites)
109
+
110
+ - **Timeout cobre tudo** — headers e o corpo/stream inteiro. `timeoutMs` (default 120s, igual ao cap
111
+ do BE) é o tempo máximo total da chamada; `AbortSignal` do chamador funciona durante todo o stream.
112
+ - **Retry sem duplicar efeito** — GET (idempotente) retenta em rede/429/5xx; POST com efeito
113
+ (`invokeAgent`/`dispatchAgent`/`createTrigger`) retenta **só em 429/503** e **nunca em erro de
114
+ rede** (o request pode ter sido processado). Backoff exponencial 1s→10s com jitter.
115
+ - **Anti-DoS de memória** — limites de stream `maxStreamEventBytes` (1MB) e `maxStreamTotalBytes`
116
+ (64MB); estourar aborta com `protocol`. Configuráveis no `createClient`.
117
+ - **`includeRaw`** — por padrão `invokeAgent` não retém os eventos brutos (`raw: []`) para economizar
118
+ memória; passe `includeRaw: true` para recebê-los. `streamAgent` sempre entrega os chunks ao vivo.
119
+ - **Segurança de transporte** — `redirect: 'error'` (um 3xx para outro host vazaria a key) e
120
+ `cache: 'no-store'` (evita polling stale em Next.js/edge) em toda requisição. `Retry-After` do
121
+ servidor é respeitado em 429/503.
122
+
123
+ ### Observabilidade e portabilidade
124
+
125
+ - **Hooks** (opt-in, sem `console`): `onRetry(info)` (a cada retentativa, com `endpoint`/`attempt`/
126
+ `delayMs`/`reason`/`status`) e `onError(err)`.
127
+ - **`fetch` injetável** — `fetch?` na config; por padrão usa `globalThis.fetch` (Node ≥18). Um
128
+ preflight dá erro `config` claro se não houver fetch.
129
+ - **Portável** — sem `Buffer`; `node:crypto` só é carregado se você usar `signRequest`. Roda em Node;
130
+ para edge/serverless, injete `fetch` se necessário.
131
+
132
+ ```ts
133
+ const zihin = createClient({
134
+ apiKey,
135
+ timeoutMs: 60_000,
136
+ maxRetries: 2,
137
+ maxStreamEventBytes: 2 * 1024 * 1024,
138
+ onRetry: (info) => myLogger.warn('zihin retry', info),
139
+ // fetch: customFetch, // ambientes sem fetch global
140
+ });
141
+ ```
142
+
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.
145
+
146
+ ## Segurança
147
+
148
+ Reusa as primitivas de `security.ts`: validação SSRF da base URL (whitelist Zihin por padrão; libere
149
+ com `allowCustomBaseUrl`), limites de payload, sanitização de erro, validação de formato de key e HMAC
150
+ opcional.
151
+
152
+ ## Dev
153
+
154
+ ```bash
155
+ npm run build && npm test # smoke test offline (mocka fetch), sem rede
156
+ ```
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Listagem de agentes do tenant (GET /api/v2/agents). Alimenta o dropdown do nó n8n (#17)
3
+ * e o select de `agentId` no Paperclip (#20). Aceita key `zhn_live_*` (agents:read em todos os roles).
4
+ *
5
+ * @module agents
6
+ */
7
+ import { type ResolvedConfig } from './http';
8
+ export interface AgentSummary {
9
+ id: string;
10
+ name?: string;
11
+ commercialName?: string;
12
+ bio?: string;
13
+ type?: string;
14
+ status?: string;
15
+ tags?: string[];
16
+ toolsCount?: number;
17
+ }
18
+ /** Lista os agentes acessíveis pelo tenant da key. */
19
+ export declare function listAgents(cfg: ResolvedConfig, opts?: {
20
+ signal?: AbortSignal;
21
+ }): Promise<AgentSummary[]>;
package/dist/agents.js ADDED
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ /**
3
+ * Listagem de agentes do tenant (GET /api/v2/agents). Alimenta o dropdown do nó n8n (#17)
4
+ * e o select de `agentId` no Paperclip (#20). Aceita key `zhn_live_*` (agents:read em todos os roles).
5
+ *
6
+ * @module agents
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.listAgents = listAgents;
10
+ const errors_1 = require("./errors");
11
+ const http_1 = require("./http");
12
+ const internal_1 = require("./internal");
13
+ /** Lista os agentes acessíveis pelo tenant da key. */
14
+ async function listAgents(cfg, opts = {}) {
15
+ const res = await (0, http_1.requestBuffered)({
16
+ method: 'GET',
17
+ url: `${cfg.baseUrl}/api/v2/agents`,
18
+ headers: (0, http_1.buildHeaders)(cfg.apiKey),
19
+ timeoutMs: cfg.timeoutMs,
20
+ maxRetries: cfg.maxRetries,
21
+ policy: 'idempotent',
22
+ callerSignal: opts.signal,
23
+ fetchImpl: cfg.fetchImpl,
24
+ onRetry: cfg.onRetry,
25
+ });
26
+ if (!res.ok) {
27
+ throw (0, errors_1.errorFromResponse)(res.status, (0, http_1.parseBody)(res.text), { endpoint: res.endpoint, attempts: res.attempts });
28
+ }
29
+ let json;
30
+ try {
31
+ json = JSON.parse(res.text);
32
+ }
33
+ catch {
34
+ throw new errors_1.ZihinAgentError('protocol', 'Expected JSON from GET /api/v2/agents.');
35
+ }
36
+ const data = (0, internal_1.rec)(json).data;
37
+ const list = Array.isArray(data) ? data : [];
38
+ return list.map((item) => {
39
+ const a = (0, internal_1.rec)(item);
40
+ return {
41
+ id: (0, internal_1.str)(a.id) ?? '',
42
+ name: (0, internal_1.str)(a.name),
43
+ commercialName: (0, internal_1.str)(a.commercial_name),
44
+ bio: (0, internal_1.str)(a.bio),
45
+ type: (0, internal_1.str)(a.type),
46
+ status: (0, internal_1.str)(a.status),
47
+ tags: Array.isArray(a.tags) ? a.tags.filter((t) => typeof t === 'string') : undefined,
48
+ toolsCount: typeof a.tools_count === 'number' ? a.tools_count : undefined,
49
+ };
50
+ });
51
+ }
52
+ //# sourceMappingURL=agents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAkBH,gCAyCC;AAzDD,qCAA8D;AAC9D,iCAAuF;AACvF,yCAAsC;AAatC,sDAAsD;AAC/C,KAAK,UAAU,UAAU,CAC/B,GAAmB,EACnB,OAAiC,EAAE;IAEnC,MAAM,GAAG,GAAG,MAAM,IAAA,sBAAe,EAAC;QACjC,MAAM,EAAE,KAAK;QACb,GAAG,EAAE,GAAG,GAAG,CAAC,OAAO,gBAAgB;QACnC,OAAO,EAAE,IAAA,mBAAY,EAAC,GAAG,CAAC,MAAM,CAAC;QACjC,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,MAAM,EAAE,YAAY;QACpB,YAAY,EAAE,IAAI,CAAC,MAAM;QACzB,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,OAAO,EAAE,GAAG,CAAC,OAAO;KACpB,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACb,MAAM,IAAA,0BAAiB,EAAC,GAAG,CAAC,MAAM,EAAE,IAAA,gBAAS,EAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9G,CAAC;IAED,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACJ,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,IAAI,wBAAe,CAAC,UAAU,EAAE,wCAAwC,CAAC,CAAC;IACjF,CAAC;IAED,MAAM,IAAI,GAAG,IAAA,cAAG,EAAC,IAAI,CAAC,CAAC,IAAI,CAAC;IAC5B,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACxB,MAAM,CAAC,GAAG,IAAA,cAAG,EAAC,IAAI,CAAC,CAAC;QACpB,OAAO;YACN,EAAE,EAAE,IAAA,cAAG,EAAC,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE;YACnB,IAAI,EAAE,IAAA,cAAG,EAAC,CAAC,CAAC,IAAI,CAAC;YACjB,cAAc,EAAE,IAAA,cAAG,EAAC,CAAC,CAAC,eAAe,CAAC;YACtC,GAAG,EAAE,IAAA,cAAG,EAAC,CAAC,CAAC,GAAG,CAAC;YACf,IAAI,EAAE,IAAA,cAAG,EAAC,CAAC,CAAC,IAAI,CAAC;YACjB,MAAM,EAAE,IAAA,cAAG,EAAC,CAAC,CAAC,MAAM,CAAC;YACrB,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAc,CAAC,CAAC,CAAC,SAAS;YACnG,UAAU,EAAE,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;SACzE,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * ZihinAgentClient — fachada do núcleo. Carrega a config uma vez (auth, baseUrl, timeout,
3
+ * retries) e expõe os dois caminhos do padrão híbrido:
4
+ * • Direto: invokeAgent / streamAgent (POST /stream, SSE bufferizado)
5
+ * • Triggers: createTrigger / dispatchAgent / getExecution (async + callback)
6
+ * • Auxiliar: listAgents (dropdown de agentes)
7
+ *
8
+ * @module client
9
+ */
10
+ import { type AgentSummary } from './agents';
11
+ import type { AgentResult, AgentStreamChunk, CreateTriggerParams, DeleteTriggerParams, DispatchAgentParams, DispatchResult, ExecutionStatus, FindOrCreateResult, GetExecutionParams, GetTriggerParams, InvokeAgentParams, ListTriggersParams, TriggerInfo, TriggerRef, ZihinClientConfig } from './types';
12
+ export declare class ZihinAgentClient {
13
+ private readonly cfg;
14
+ constructor(config: ZihinClientConfig);
15
+ /** Base URL efetiva (após validação SSRF). */
16
+ get baseUrl(): string;
17
+ /** Invoca o agente e retorna o resultado final consolidado (response + metrics). */
18
+ invokeAgent(params: InvokeAgentParams): Promise<AgentResult>;
19
+ /** Emite deltas ao vivo do agente (async iterator). */
20
+ streamAgent(params: InvokeAgentParams): AsyncGenerator<AgentStreamChunk>;
21
+ /** Provisiona (1x) um trigger de webhook ligado a um agente. */
22
+ createTrigger(params: CreateTriggerParams): Promise<TriggerRef>;
23
+ /** Reaproveita (por agente+nome) ou cria um trigger — evita duplicação. */
24
+ findOrCreateTrigger(params: CreateTriggerParams): Promise<FindOrCreateResult>;
25
+ /** Lista os triggers do tenant (filtros opcionais). */
26
+ listTriggers(params?: ListTriggersParams): Promise<TriggerInfo[]>;
27
+ /** Obtém um trigger pelo id (inclui webhookUrl). */
28
+ getTrigger(params: GetTriggerParams): Promise<TriggerInfo>;
29
+ /** Remove um trigger (requer key admin/owner). */
30
+ deleteTrigger(params: DeleteTriggerParams): Promise<{
31
+ deleted: boolean;
32
+ }>;
33
+ /** Dispara um trigger: 202 (async) ou JSON imediato (sync). */
34
+ dispatchAgent(params: DispatchAgentParams): Promise<DispatchResult>;
35
+ /** Consulta o resultado de uma execução disparada por trigger (polling). */
36
+ getExecution(params: GetExecutionParams): Promise<ExecutionStatus>;
37
+ /** Lista os agentes do tenant (dropdown). */
38
+ listAgents(opts?: {
39
+ signal?: AbortSignal;
40
+ }): Promise<AgentSummary[]>;
41
+ }
42
+ /** Açúcar: cria um cliente a partir da config. */
43
+ export declare function createClient(config: ZihinClientConfig): ZihinAgentClient;
package/dist/client.js ADDED
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /**
3
+ * ZihinAgentClient — fachada do núcleo. Carrega a config uma vez (auth, baseUrl, timeout,
4
+ * retries) e expõe os dois caminhos do padrão híbrido:
5
+ * • Direto: invokeAgent / streamAgent (POST /stream, SSE bufferizado)
6
+ * • Triggers: createTrigger / dispatchAgent / getExecution (async + callback)
7
+ * • Auxiliar: listAgents (dropdown de agentes)
8
+ *
9
+ * @module client
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ZihinAgentClient = void 0;
13
+ exports.createClient = createClient;
14
+ const agents_1 = require("./agents");
15
+ const http_1 = require("./http");
16
+ const invoke_1 = require("./invoke");
17
+ const triggers_1 = require("./triggers");
18
+ class ZihinAgentClient {
19
+ constructor(config) {
20
+ this.cfg = (0, http_1.resolveConfig)(config);
21
+ }
22
+ /** Base URL efetiva (após validação SSRF). */
23
+ get baseUrl() {
24
+ return this.cfg.baseUrl;
25
+ }
26
+ // ---- Caminho A: direto (SSE) ----
27
+ /** Invoca o agente e retorna o resultado final consolidado (response + metrics). */
28
+ invokeAgent(params) {
29
+ return (0, invoke_1.invokeAgent)(this.cfg, params);
30
+ }
31
+ /** Emite deltas ao vivo do agente (async iterator). */
32
+ streamAgent(params) {
33
+ return (0, invoke_1.streamAgent)(this.cfg, params);
34
+ }
35
+ // ---- Caminho B: triggers (async/callback) ----
36
+ /** Provisiona (1x) um trigger de webhook ligado a um agente. */
37
+ createTrigger(params) {
38
+ return (0, triggers_1.createTrigger)(this.cfg, params);
39
+ }
40
+ /** Reaproveita (por agente+nome) ou cria um trigger — evita duplicação. */
41
+ findOrCreateTrigger(params) {
42
+ return (0, triggers_1.findOrCreateTrigger)(this.cfg, params);
43
+ }
44
+ /** Lista os triggers do tenant (filtros opcionais). */
45
+ listTriggers(params = {}) {
46
+ return (0, triggers_1.listTriggers)(this.cfg, params);
47
+ }
48
+ /** Obtém um trigger pelo id (inclui webhookUrl). */
49
+ getTrigger(params) {
50
+ return (0, triggers_1.getTrigger)(this.cfg, params);
51
+ }
52
+ /** Remove um trigger (requer key admin/owner). */
53
+ deleteTrigger(params) {
54
+ return (0, triggers_1.deleteTrigger)(this.cfg, params);
55
+ }
56
+ /** Dispara um trigger: 202 (async) ou JSON imediato (sync). */
57
+ dispatchAgent(params) {
58
+ return (0, triggers_1.dispatchAgent)(this.cfg, params);
59
+ }
60
+ /** Consulta o resultado de uma execução disparada por trigger (polling). */
61
+ getExecution(params) {
62
+ return (0, triggers_1.getExecution)(this.cfg, params);
63
+ }
64
+ // ---- Auxiliar ----
65
+ /** Lista os agentes do tenant (dropdown). */
66
+ listAgents(opts = {}) {
67
+ return (0, agents_1.listAgents)(this.cfg, opts);
68
+ }
69
+ }
70
+ exports.ZihinAgentClient = ZihinAgentClient;
71
+ /** Açúcar: cria um cliente a partir da config. */
72
+ function createClient(config) {
73
+ return new ZihinAgentClient(config);
74
+ }
75
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAsGH,oCAEC;AAtGD,qCAAyD;AACzD,iCAA4D;AAC5D,qCAAoD;AACpD,yCAQoB;AAmBpB,MAAa,gBAAgB;IAG5B,YAAY,MAAyB;QACpC,IAAI,CAAC,GAAG,GAAG,IAAA,oBAAa,EAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,8CAA8C;IAC9C,IAAI,OAAO;QACV,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;IACzB,CAAC;IAED,oCAAoC;IAEpC,oFAAoF;IACpF,WAAW,CAAC,MAAyB;QACpC,OAAO,IAAA,oBAAW,EAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IAED,uDAAuD;IACvD,WAAW,CAAC,MAAyB;QACpC,OAAO,IAAA,oBAAW,EAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IAED,iDAAiD;IAEjD,gEAAgE;IAChE,aAAa,CAAC,MAA2B;QACxC,OAAO,IAAA,wBAAa,EAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACxC,CAAC;IAED,2EAA2E;IAC3E,mBAAmB,CAAC,MAA2B;QAC9C,OAAO,IAAA,8BAAmB,EAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC9C,CAAC;IAED,uDAAuD;IACvD,YAAY,CAAC,SAA6B,EAAE;QAC3C,OAAO,IAAA,uBAAY,EAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,oDAAoD;IACpD,UAAU,CAAC,MAAwB;QAClC,OAAO,IAAA,qBAAU,EAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IAED,kDAAkD;IAClD,aAAa,CAAC,MAA2B;QACxC,OAAO,IAAA,wBAAa,EAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACxC,CAAC;IAED,+DAA+D;IAC/D,aAAa,CAAC,MAA2B;QACxC,OAAO,IAAA,wBAAa,EAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACxC,CAAC;IAED,4EAA4E;IAC5E,YAAY,CAAC,MAA0B;QACtC,OAAO,IAAA,uBAAY,EAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,qBAAqB;IAErB,6CAA6C;IAC7C,UAAU,CAAC,OAAiC,EAAE;QAC7C,OAAO,IAAA,mBAAU,EAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;CACD;AAnED,4CAmEC;AAED,kDAAkD;AAClD,SAAgB,YAAY,CAAC,MAAyB;IACrD,OAAO,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC;AACrC,CAAC"}
@@ -0,0 +1,35 @@
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
+ export 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
+ export 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
+ export 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
+ export 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
+ /** Mapeia um status HTTP + corpo (já parseado) para um ZihinAgentError completo. */
33
+ export declare function errorFromResponse(status: number, body: unknown, meta?: ErrorMeta): ZihinAgentError;
34
+ /** Normaliza qualquer throw (network/abort/timeout) para ZihinAgentError, com a causa-raiz. */
35
+ export declare function errorFromThrow(err: unknown, timeoutMs?: number, meta?: ErrorMeta): ZihinAgentError;
package/dist/errors.js ADDED
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ /**
3
+ * Erros do @zihin/agent-client — sanitizados (sem vazar key/Bearer/stack), mas COMPLETOS:
4
+ * carregam endpoint, nº de tentativas e request_id do BE para depuração em produção externa.
5
+ *
6
+ * @module errors
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ZihinAgentError = void 0;
10
+ exports.errorFromResponse = errorFromResponse;
11
+ exports.errorFromThrow = errorFromThrow;
12
+ const internal_1 = require("./internal");
13
+ const security_1 = require("./security");
14
+ class ZihinAgentError extends Error {
15
+ constructor(kind, message, opts = {}) {
16
+ super(buildMessage(message, opts));
17
+ this.name = 'ZihinAgentError';
18
+ this.kind = kind;
19
+ this.status = opts.status;
20
+ this.code = opts.code;
21
+ this.requestId = opts.requestId;
22
+ this.attempts = opts.attempts;
23
+ this.endpoint = opts.endpoint;
24
+ this.retryable =
25
+ opts.retryable ?? (kind === 'rate_limit' || kind === 'server' || kind === 'network');
26
+ // Mantém a cadeia de protótipo correta ao transpilar para ES5/ES2021.
27
+ Object.setPrototypeOf(this, ZihinAgentError.prototype);
28
+ }
29
+ }
30
+ exports.ZihinAgentError = ZihinAgentError;
31
+ /** Compõe uma mensagem completa: base + endpoint + tentativas + request_id. */
32
+ function buildMessage(base, opts) {
33
+ let s = base;
34
+ if (opts.endpoint)
35
+ s += ` [${opts.endpoint}]`;
36
+ if (opts.attempts && opts.attempts > 1)
37
+ s += ` (after ${opts.attempts} attempts)`;
38
+ if (opts.requestId)
39
+ s += ` (request_id: ${opts.requestId})`;
40
+ return s;
41
+ }
42
+ function extractRequestId(body) {
43
+ const b = (0, internal_1.rec)(body);
44
+ return (0, internal_1.str)(b.request_id) ?? (0, internal_1.str)(b.requestId) ?? (0, internal_1.str)((0, internal_1.rec)(b.error).request_id);
45
+ }
46
+ /** Mapeia um status HTTP + corpo (já parseado) para um ZihinAgentError completo. */
47
+ function errorFromResponse(status, body, meta = {}) {
48
+ const sanitized = (0, security_1.sanitizeApiError)(body);
49
+ const message = sanitized.message;
50
+ const code = sanitized.code;
51
+ const base = { status, code, requestId: extractRequestId(body), ...meta };
52
+ if (status === 401)
53
+ return new ZihinAgentError('auth', message || 'Authentication failed.', base);
54
+ if (status === 403)
55
+ return new ZihinAgentError('auth', message ||
56
+ 'This API key lacks permission. Use an admin/editor key (member keys cannot create triggers or read telemetry).', base);
57
+ if (status === 404)
58
+ return new ZihinAgentError('not_found', message || 'Resource not found.', base);
59
+ if (status === 429)
60
+ return new ZihinAgentError('rate_limit', message || 'Rate limit exceeded.', { ...base, retryable: true });
61
+ if (status >= 500)
62
+ return new ZihinAgentError('server', message || `Server error (${status}).`, { ...base, retryable: true });
63
+ return new ZihinAgentError('unknown', message || `Request failed (${status}).`, base);
64
+ }
65
+ // Códigos de rede permanentes (não adianta retentar): DNS e TLS.
66
+ const NON_RETRYABLE_NET = /^(ENOTFOUND|EAI_AGAIN|CERT_|ERR_TLS|UNABLE_TO_VERIFY|DEPTH_ZERO)/;
67
+ /** Normaliza qualquer throw (network/abort/timeout) para ZihinAgentError, com a causa-raiz. */
68
+ function errorFromThrow(err, timeoutMs, meta = {}) {
69
+ if (err instanceof ZihinAgentError)
70
+ return err;
71
+ const name = err?.name;
72
+ if (name === 'TimeoutError') {
73
+ return new ZihinAgentError('timeout', `Request timed out after ${timeoutMs ?? '?'}ms.`, {
74
+ ...meta,
75
+ retryable: false,
76
+ });
77
+ }
78
+ if (name === 'AbortError') {
79
+ return new ZihinAgentError('aborted', 'Request was aborted by the caller.', { ...meta, retryable: false });
80
+ }
81
+ // Erros de fetch escondem a causa-raiz em err.cause.code (ENOTFOUND/ECONNREFUSED/TLS...).
82
+ const cause = err?.cause;
83
+ const causeCode = cause?.code;
84
+ const sanitized = (0, security_1.sanitizeApiError)(err);
85
+ const detail = causeCode || sanitized.message || 'Network error';
86
+ const retryable = causeCode ? !NON_RETRYABLE_NET.test(causeCode) : true;
87
+ return new ZihinAgentError('network', `Network error: ${detail}`, { ...meta, retryable });
88
+ }
89
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Transporte HTTP do núcleo: resolução/validação de base URL (SSRF), headers de auth,
3
+ * deadline via AbortController que cobre TODO o request (headers + corpo/stream),
4
+ * e retry com exponential backoff + jitter.
5
+ *
6
+ * Política de retry (evita duplicar efeito colateral):
7
+ * - 'idempotent' (GET): retenta em erro de rede, 429 e 5xx.
8
+ * - 'side-effecting' (POST: invoke/dispatch/create): retenta SÓ em 429/503 (servidor disse
9
+ * "tente depois", sem ter processado). NUNCA em erro de rede — o request pode ter sido
10
+ * recebido e processado mesmo com o socket caindo na volta.
11
+ *
12
+ * @module http
13
+ */
14
+ import type { RetryInfo, ZihinClientConfig } from './types';
15
+ export declare const DEFAULT_BASE_URL = "https://llm.zihin.ai";
16
+ export declare const DEFAULT_TIMEOUT_MS = 120000;
17
+ export declare const DEFAULT_MAX_RETRIES = 2;
18
+ export declare const DEFAULT_MAX_EVENT_BYTES: number;
19
+ export declare const DEFAULT_MAX_TOTAL_BYTES: number;
20
+ export type RetryPolicy = 'idempotent' | 'side-effecting';
21
+ export interface ResolvedConfig {
22
+ apiKey: string;
23
+ baseUrl: string;
24
+ timeoutMs: number;
25
+ maxRetries: number;
26
+ maxStreamEventBytes: number;
27
+ maxStreamTotalBytes: number;
28
+ /** fetch injetado explicitamente; se ausente, usa `globalThis.fetch` no momento da chamada. */
29
+ fetchImpl?: typeof fetch;
30
+ onRetry?: (info: RetryInfo) => void;
31
+ }
32
+ /** Valida a config e devolve valores efetivos. Lança ZihinAgentError('config') se inválida. */
33
+ export declare function resolveConfig(config: ZihinClientConfig): ResolvedConfig;
34
+ /** Rótulo de endpoint para mensagens de erro/observabilidade: "POST host/path". */
35
+ export declare function endpointLabel(method: string, url: string): string;
36
+ export declare function buildHeaders(apiKey: string, extra?: Record<string, string>): Record<string, string>;
37
+ export interface Deadline {
38
+ signal: AbortSignal;
39
+ dispose: () => void;
40
+ timedOut: () => boolean;
41
+ abortedByCaller: () => boolean;
42
+ }
43
+ /**
44
+ * Cria um deadline que aborta o request após `timeoutMs` (cobre headers E corpo/stream) e
45
+ * propaga o abort do chamador. Mantenha vivo até o corpo/stream terminar; chame dispose() ao fim.
46
+ */
47
+ export declare function createDeadline(timeoutMs: number, callerSignal?: AbortSignal): Deadline;
48
+ export interface BufferedResponse {
49
+ status: number;
50
+ ok: boolean;
51
+ text: string;
52
+ /** "POST host/path" — para o chamador anexar ao erro. */
53
+ endpoint: string;
54
+ /** Tentativas feitas (1 = sem retry). */
55
+ attempts: number;
56
+ }
57
+ export interface BufferedRequest {
58
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE';
59
+ url: string;
60
+ headers: Record<string, string>;
61
+ body?: string;
62
+ timeoutMs: number;
63
+ maxRetries: number;
64
+ policy: RetryPolicy;
65
+ callerSignal?: AbortSignal;
66
+ fetchImpl?: typeof fetch;
67
+ onRetry?: (info: RetryInfo) => void;
68
+ }
69
+ /**
70
+ * Executa um request lendo o corpo inteiro dentro do deadline (P1: timeout cobre o download).
71
+ * Retorna mesmo em não-2xx (o chamador converte via errorFromResponse).
72
+ */
73
+ export declare function requestBuffered(args: BufferedRequest): Promise<BufferedResponse>;
74
+ export interface StreamHandle {
75
+ response: Response;
76
+ /** Mantenha vivo durante a iteração do stream; dispose() ao terminar/abortar. */
77
+ deadline: Deadline;
78
+ /** "POST host/path" — para o chamador anexar ao erro no caminho !ok. */
79
+ endpoint: string;
80
+ }
81
+ export interface StreamRequest {
82
+ url: string;
83
+ headers: Record<string, string>;
84
+ body?: string;
85
+ timeoutMs: number;
86
+ maxRetries: number;
87
+ callerSignal?: AbortSignal;
88
+ fetchImpl?: typeof fetch;
89
+ onRetry?: (info: RetryInfo) => void;
90
+ }
91
+ /**
92
+ * Abre um POST SSE. Retenta só pré-stream em 429/503 (POST side-effecting). Em sucesso/4xx
93
+ * devolve a Response COM o deadline ainda ativo — o consumidor do stream chama deadline.dispose()
94
+ * ao finalizar (P1: o timeout cobre toda a duração do stream, e o abort do chamador continua ligado).
95
+ */
96
+ export declare function openStream(args: StreamRequest): Promise<StreamHandle>;
97
+ /** Faz parse seguro de um corpo (JSON quando possível, senão {message}). */
98
+ export declare function parseBody(text: string): unknown;