@engramind/client 0.1.1 → 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/dist/server.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { a as EngramindMethod, E as EngramindClient } from './client-Q0QFU2A6.js';
2
- export { C as CallOptions, b as CategoryInput, c as ChannelQuery, d as EngramindClientOptions, M as MemoryQuery, Q as Query, e as buildUrl, f as createEngramindClient, g as debugUrl } from './client-Q0QFU2A6.js';
1
+ import { a as EngramindMethod, E as EngramindClient } from './client-Dzm6Fx6_.js';
2
+ export { C as CallOptions, b as CategoryInput, c as ChannelQuery, d as EngramindClientOptions, M as MemoryQuery, Q as Query, e as buildUrl, f as createEngramindClient, g as debugUrl } from './client-Dzm6Fx6_.js';
3
3
  import { L as LiveEvent, W as WebhookEvent } from './events-DYEjIvxs.js';
4
4
  export { C as ConsumptionEvent, a as ConsumptionGrouperOptions, E as EngramindError, b as EngramindErrorBody, c as WebhookEventType, d as clientStatus, e as createConsumptionGrouper, f as engramindCodeOf, i as isConsumedEvent, g as isMemoryChange, m as mergeConsumption, n as notConfiguredError, p as proxyMaskedError, t as toEngramindError, h as toLiveEvent, u as unreachableError } from './events-DYEjIvxs.js';
5
- import { h as ContextView } from './types-BnU5iHwB.js';
5
+ import { h as ContextView } from './types-BxAxPvmk.js';
6
6
 
7
7
  /**
8
8
  * Rotas do proxy da lib: o caminho do REST do Engramind sem o `/v1` (`channels/eng%2Fglobal/archive`).
package/dist/server.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  toEngramindError,
12
12
  toLiveEvent,
13
13
  unreachableError
14
- } from "./chunk-THVEWMKQ.js";
14
+ } from "./chunk-4Z5NG6ZM.js";
15
15
 
16
16
  // src/server/client.ts
17
17
  import { randomUUID } from "crypto";
@@ -161,6 +161,8 @@ var PROXY_ROUTES = [
161
161
  r("GET", "channels/:channel/stats", "channel_stats"),
162
162
  r("POST", "channels/:channel/archive", "archive_channel"),
163
163
  r("POST", "channels/:channel/restore", "restore_channel"),
164
+ // Engramind 0.13.0: o token de app edita os canais que criou (403 FOREIGN_APP / 409 CHANNEL_SHARED nos outros).
165
+ r("PATCH", "channels/:channel", "update_channel"),
164
166
  r("GET", "memories", "list_memories"),
165
167
  r("POST", "memories", "create_memory"),
166
168
  r("POST", "memories/import", "import_memories"),
@@ -188,7 +190,6 @@ var PROXY_ROUTES = [
188
190
  // Só o gestor (token admin).
189
191
  r("GET", "apps", "list_apps", MANAGER),
190
192
  r("POST", "channels", "create_channel", MANAGER),
191
- r("PATCH", "channels/:channel", "update_channel", MANAGER),
192
193
  r("DELETE", "channels/:channel", "delete_channel", MANAGER),
193
194
  r("GET", "channels/:channel/settings", "get_channel_settings", MANAGER),
194
195
  r("PUT", "channels/:channel/settings", "update_channel_settings", MANAGER),
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/client.ts","../src/server/routes.ts","../src/server/proxy.ts","../src/server/webhook.ts","../src/server/bus.ts","../src/server/sse.ts","../src/server/context.ts","../src/server/mcp.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\"\nimport { proxyMaskedError, toEngramindError, unreachableError, type EngramindErrorBody } from \"../core/errors\"\nimport type { CategoryInput, ChannelQuery, MemoryQuery } from \"./client-types\"\nimport type { Category, ConsumerSettings, ContextView, EngramindAgent, EngramindApp, EngramindChannel, EngramindHint, EngramindMemory, EngramindSubscription, EngramindToken, ImportResult, IssuedToken, MemoryRevision, MemoryWrite, SearchResults, WhoAmI } from \"../core/types\"\n\n/**\n * Cliente HTTP do Engramind — SOMENTE servidor. Bearer com o token do app (ou de um agente),\n * `Idempotency-Key` em toda mutação, `If-None-Match` no contexto (`304` → `null`) e erros do envelope\n * `{ error: { code, message, details } }` traduzidos para `EngramindError` (pt-BR), inclusive o 5xx\n * mascarado por um proxy na frente do Engramind (Cloudflare).\n */\n\nexport type EngramindMethod = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\"\nexport type Query = Record<string, string | number | boolean | null | undefined>\n\nexport interface EngramindClientOptions {\n /** URL base do Engramind (ex.: `https://engramind.exemplo.com`). */\n baseUrl: string\n /** Token do app (`kind: app`), de um agente ou, no gestor, admin. Nunca vai ao navegador. */\n token: string\n userAgent?: string\n timeoutMs?: number\n fetch?: typeof fetch\n /** Depuração: método, URL (com `focus` omitido), status e duração. Nunca recebe o corpo nem o token. */\n onDebug?: (entry: { method: string; url: string; status: number | null; durationMs: number; error?: string }) => void\n}\n\nexport interface CallOptions {\n query?: Query\n body?: unknown\n headers?: Record<string, string>\n /** Chave de idempotência fixa (importação); padrão: uma nova por mutação. */\n idempotencyKey?: string\n signal?: AbortSignal\n timeoutMs?: number\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000\nconst USER_TEXT_QUERY = [\"focus\"]\nconst enc = encodeURIComponent\n\nexport function buildUrl(baseUrl: string, path: string, query: Query = {}): string {\n const url = new URL(`${baseUrl.replace(/\\/+$/, \"\")}${path}`)\n for (const [key, value] of Object.entries(query)) if (value !== undefined && value !== null && value !== \"\") url.searchParams.set(key, String(value))\n return url.toString()\n}\n\n/** URL para a depuração: o texto do usuário (`focus`) vira o tamanho. */\nexport function debugUrl(url: string): string {\n const masked = new URL(url)\n for (const key of USER_TEXT_QUERY) {\n const value = masked.searchParams.get(key)\n if (value !== null) masked.searchParams.set(key, `[omitido: ${value.length} caracteres]`)\n }\n return masked.toString()\n}\n\nexport function createEngramindClient(options: EngramindClientOptions) {\n const doFetch = options.fetch ?? fetch\n const userAgent = options.userAgent ?? \"engramind-client\"\n\n async function call<T = unknown>(method: EngramindMethod, path: string, request: CallOptions = {}): Promise<T> {\n const headers: Record<string, string> = { ...request.headers, Authorization: `Bearer ${options.token}`, Accept: \"application/json\", \"User-Agent\": userAgent }\n if (method !== \"GET\") headers[\"Idempotency-Key\"] = request.idempotencyKey ?? `engramind-client-${randomUUID()}`\n const hasBody = request.body !== undefined && method !== \"GET\"\n if (hasBody) headers[\"Content-Type\"] = \"application/json\"\n const timeout = AbortSignal.timeout(request.timeoutMs ?? options.timeoutMs ?? DEFAULT_TIMEOUT_MS)\n const signal = request.signal ? AbortSignal.any([request.signal, timeout]) : timeout\n const url = buildUrl(options.baseUrl, path, request.query)\n const started = Date.now()\n let res: Response\n try {\n res = await doFetch(url, { method, headers, body: hasBody ? JSON.stringify(request.body) : undefined, cache: \"no-store\", signal })\n } catch (error) {\n const reason = error instanceof Error ? (error.name === \"TimeoutError\" || error.name === \"AbortError\" ? \"tempo limite\" : ((error as { cause?: { code?: string } }).cause?.code ?? error.message)) : String(error)\n options.onDebug?.({ method, url: debugUrl(url), status: null, durationMs: Date.now() - started, error: reason })\n throw unreachableError(reason)\n }\n options.onDebug?.({ method, url: debugUrl(url), status: res.status, durationMs: Date.now() - started })\n if (res.status === 304) return null as T\n const text = await res.text()\n let json: unknown = null\n if (text) {\n try {\n json = JSON.parse(text)\n } catch {\n json = null\n }\n }\n if (!res.ok) {\n const body = (json as { error?: EngramindErrorBody } | null)?.error ?? null\n throw toEngramindError(res.status, body && typeof body === \"object\" ? body : null, res.headers.get(\"retry-after\"), body ? null : proxyMaskedError(text, json))\n }\n return json as T\n }\n\n /** `GET /health/ready` (público): `true` quando o serviço recebe tráfego. */\n async function ready(signal?: AbortSignal): Promise<{ ready: boolean; detail: string }> {\n try {\n const res = await doFetch(`${options.baseUrl.replace(/\\/+$/, \"\")}/health/ready`, { headers: { Accept: \"application/json\", \"User-Agent\": userAgent }, cache: \"no-store\", signal: signal ?? AbortSignal.timeout(10_000) })\n const body = (await res.json().catch(() => null)) as { status?: string } | null\n return { ready: res.ok && body?.status === \"ready\", detail: body?.status ?? `HTTP ${res.status}` }\n } catch (error) {\n return { ready: false, detail: error instanceof Error ? error.message : String(error) }\n }\n }\n\n return {\n call,\n ready,\n whoami: () => call<WhoAmI>(\"GET\", \"/v1/whoami\"),\n consumerSettings: () => call<ConsumerSettings>(\"GET\", \"/v1/consumer-settings\"),\n categories: {\n list: () => call<{ categories: Category[] }>(\"GET\", \"/v1/categories\"),\n create: (input: CategoryInput) => call<Category>(\"POST\", \"/v1/categories\", { body: input }),\n },\n agents: {\n list: (query: Query = {}) => call<{ agents: EngramindAgent[]; next_cursor: string | null }>(\"GET\", \"/v1/agents\", { query }),\n get: (agent: string) => call<EngramindAgent>(\"GET\", `/v1/agents/${enc(agent)}`),\n create: (input: { app?: string; slug: string; name: string; description?: string | null }) => call<EngramindAgent & { created?: boolean }>(\"POST\", \"/v1/agents\", { body: input }),\n update: (agent: string, input: { name?: string; description?: string | null; disabled?: boolean }) => call<EngramindAgent>(\"PATCH\", `/v1/agents/${enc(agent)}`, { body: input }),\n delete: (agent: string) => call<null>(\"DELETE\", `/v1/agents/${enc(agent)}`),\n },\n subscriptions: {\n list: (query: Query = {}) => call<{ subscriptions: EngramindSubscription[]; next_cursor: string | null }>(\"GET\", \"/v1/subscriptions\", { query }),\n create: (input: { agent: string; target: string; mode: \"inject\" | \"search\" | \"manage\"; access?: string[] | null; priority?: number | null }) => call<EngramindSubscription & { created?: boolean }>(\"POST\", \"/v1/subscriptions\", { body: input }),\n update: (subscription: string, input: { mode?: \"inject\" | \"search\" | \"manage\"; access?: string[] | null; priority?: number | null }) => call<EngramindSubscription>(\"PATCH\", `/v1/subscriptions/${enc(subscription)}`, { body: input }),\n delete: (subscription: string) => call<null>(\"DELETE\", `/v1/subscriptions/${enc(subscription)}`),\n },\n channels: {\n list: (query: ChannelQuery = {}) => call<{ channels: EngramindChannel[]; next_cursor: string | null }>(\"GET\", \"/v1/channels\", { query: query as Query }),\n get: (channel: string) => call<EngramindChannel>(\"GET\", `/v1/channels/${enc(channel)}`),\n archive: (channel: string) => call<EngramindChannel>(\"POST\", `/v1/channels/${enc(channel)}/archive`, { body: {} }),\n restore: (channel: string) => call<EngramindChannel>(\"POST\", `/v1/channels/${enc(channel)}/restore`, { body: {} }),\n },\n memories: {\n list: (query: MemoryQuery = {}) => call<{ memories: EngramindMemory[]; next_cursor: string | null }>(\"GET\", \"/v1/memories\", { query: query as Query }),\n get: (memory: string) => call<EngramindMemory>(\"GET\", `/v1/memories/${enc(memory)}`),\n create: (input: { channel: string; text: string; category?: string; key?: string; tags?: string[] }) => call<MemoryWrite>(\"POST\", \"/v1/memories\", { body: input }),\n update: (memory: string, input: { text?: string; category?: string; expected_version?: number; tags?: string[] }) => call<MemoryWrite>(\"PATCH\", `/v1/memories/${enc(memory)}`, { body: input }),\n delete: (memory: string) => call<null>(\"DELETE\", `/v1/memories/${enc(memory)}`),\n archive: (memory: string) => call<EngramindMemory>(\"POST\", `/v1/memories/${enc(memory)}/archive`, { body: {} }),\n restore: (memory: string) => call<EngramindMemory>(\"POST\", `/v1/memories/${enc(memory)}/restore`, { body: {} }),\n pin: (memory: string) => call<EngramindMemory>(\"POST\", `/v1/memories/${enc(memory)}/pin`, { body: {} }),\n unpin: (memory: string) => call<EngramindMemory>(\"POST\", `/v1/memories/${enc(memory)}/unpin`, { body: {} }),\n move: (memory: string, channel: string) => call<EngramindMemory>(\"POST\", `/v1/memories/${enc(memory)}/move`, { body: { channel } }),\n history: (memory: string, query: Query = {}) => call<{ revisions: MemoryRevision[]; next_cursor: string | null }>(\"GET\", `/v1/memories/${enc(memory)}/history`, { query }),\n revert: (memory: string, version: number) => call<EngramindMemory>(\"POST\", `/v1/memories/${enc(memory)}/revert`, { body: { version } }),\n import: (input: { channel: string; memories: unknown[] }, idempotencyKey?: string) => call<ImportResult>(\"POST\", \"/v1/memories/import\", { body: input, idempotencyKey }),\n search: (input: { query: string; channels?: string[]; categories?: string[]; limit?: number; include_archived?: boolean; exclude_context?: boolean }) => call<SearchResults & { context_version?: string }>(\"POST\", \"/v1/memories/search\", { body: input }),\n },\n hints: (input: { text: string; channels?: string[]; limit?: number }) => call<{ hints: EngramindHint[] }>(\"POST\", \"/v1/hints\", { body: input }),\n context: (input: { channels?: string[]; format?: string; focus?: string; ifNoneMatch?: string | null }, request: Pick<CallOptions, \"signal\" | \"timeoutMs\"> = {}) =>\n call<ContextView | null>(\"GET\", \"/v1/context\", { query: { channels: input.channels?.join(\",\"), format: input.format, focus: input.focus }, headers: input.ifNoneMatch ? { \"If-None-Match\": `\"${input.ifNoneMatch}\"` } : undefined, ...request }),\n tokens: {\n list: (query: Query = {}) => call<{ tokens: EngramindToken[]; next_cursor: string | null }>(\"GET\", \"/v1/tokens\", { query }),\n issue: (input: { kind: \"agent\" | \"app\" | \"admin\"; agent?: string; app?: string; scopes: string[]; label?: string; expires_at?: string }) => call<IssuedToken>(\"POST\", \"/v1/tokens\", { body: input }),\n update: (token: string, input: { label?: string | null; scopes?: string[]; expires_at?: string | null }) => call<EngramindToken>(\"PATCH\", `/v1/tokens/${enc(token)}`, { body: input }),\n revoke: (token: string) => call<EngramindToken>(\"POST\", `/v1/tokens/${enc(token)}/revoke`, { body: {} }),\n rotate: (token: string) => call<IssuedToken>(\"POST\", `/v1/tokens/${enc(token)}/rotate`, { body: {} }),\n },\n apps: {\n get: (app: string) => call<EngramindApp>(\"GET\", `/v1/apps/${enc(app)}`),\n /** `update_app_webhook`: grava ou remove (com `null`) a URL e o segredo do webhook do app. */\n updateWebhook: (app: string, input: { url: string | null; secret?: string | null }) => call<EngramindApp>(\"PUT\", `/v1/apps/${enc(app)}/webhook`, { body: input }),\n },\n }\n}\n\nexport type EngramindClient = ReturnType<typeof createEngramindClient>\n","import type { EngramindMethod } from \"./client\"\n\n/**\n * Rotas do proxy da lib: o caminho do REST do Engramind sem o `/v1` (`channels/eng%2Fglobal/archive`).\n * `app` é o que o token de app pode fazer (matriz do guia `tokens-e-escopos`); `manager` acrescenta o que\n * o gestor da rede usa nas telas (apps, criar/editar/excluir canais, configurações por canal).\n */\nexport type ProxyMode = \"app\" | \"manager\"\n\nexport interface ProxyRoute {\n method: EngramindMethod\n /** Segmentos; `:nome` casa um segmento qualquer. */\n pattern: string\n modes: readonly ProxyMode[]\n /** Operação do Engramind (auditoria do app e testes). */\n operation: string\n}\n\nconst BOTH = [\"app\", \"manager\"] as const\nconst MANAGER = [\"manager\"] as const\nconst r = (method: EngramindMethod, pattern: string, operation: string, modes: readonly ProxyMode[] = BOTH): ProxyRoute => ({ method, pattern, operation, modes })\n\nexport const PROXY_ROUTES: readonly ProxyRoute[] = [\n r(\"GET\", \"whoami\", \"whoami\"),\n r(\"GET\", \"consumer-settings\", \"get_consumer_settings\"),\n r(\"GET\", \"categories\", \"list_categories\"),\n r(\"POST\", \"categories\", \"create_category\"),\n r(\"GET\", \"agents\", \"list_agents\"),\n r(\"POST\", \"agents\", \"create_agent\"),\n r(\"GET\", \"agents/:agent\", \"get_agent\"),\n r(\"PATCH\", \"agents/:agent\", \"update_agent\"),\n r(\"DELETE\", \"agents/:agent\", \"delete_agent\"),\n r(\"GET\", \"subscriptions\", \"list_subscriptions\"),\n r(\"POST\", \"subscriptions\", \"subscribe\"),\n r(\"PATCH\", \"subscriptions/:subscription\", \"update_subscription\"),\n r(\"DELETE\", \"subscriptions/:subscription\", \"unsubscribe\"),\n r(\"GET\", \"channels\", \"list_channels\"),\n r(\"GET\", \"channels/:channel\", \"get_channel\"),\n r(\"GET\", \"channels/:channel/stats\", \"channel_stats\"),\n r(\"POST\", \"channels/:channel/archive\", \"archive_channel\"),\n r(\"POST\", \"channels/:channel/restore\", \"restore_channel\"),\n r(\"GET\", \"memories\", \"list_memories\"),\n r(\"POST\", \"memories\", \"create_memory\"),\n r(\"POST\", \"memories/import\", \"import_memories\"),\n r(\"POST\", \"memories/search\", \"search_memories\"),\n r(\"POST\", \"hints\", \"get_hints\"),\n r(\"GET\", \"memories/:memory\", \"get_memory\"),\n r(\"PATCH\", \"memories/:memory\", \"update_memory\"),\n r(\"DELETE\", \"memories/:memory\", \"delete_memory\"),\n r(\"GET\", \"memories/:memory/history\", \"memory_history\"),\n r(\"POST\", \"memories/:memory/archive\", \"archive_memory\"),\n r(\"POST\", \"memories/:memory/restore\", \"restore_memory\"),\n r(\"POST\", \"memories/:memory/pin\", \"pin_memory\"),\n r(\"POST\", \"memories/:memory/unpin\", \"unpin_memory\"),\n r(\"POST\", \"memories/:memory/move\", \"move_memory\"),\n r(\"POST\", \"memories/:memory/revert\", \"revert_memory\"),\n r(\"POST\", \"memories/:memory/verify\", \"mark_verified\"),\n r(\"GET\", \"tokens\", \"list_tokens\"),\n r(\"POST\", \"tokens\", \"issue_token\"),\n r(\"PATCH\", \"tokens/:token\", \"update_token\"),\n r(\"POST\", \"tokens/:token/revoke\", \"revoke_token\"),\n r(\"POST\", \"tokens/:token/rotate\", \"rotate_token\"),\n r(\"GET\", \"apps/:app\", \"get_app\"),\n r(\"PUT\", \"apps/:app/webhook\", \"update_app_webhook\"),\n r(\"GET\", \"context\", \"get_context\"),\n // Só o gestor (token admin).\n r(\"GET\", \"apps\", \"list_apps\", MANAGER),\n r(\"POST\", \"channels\", \"create_channel\", MANAGER),\n r(\"PATCH\", \"channels/:channel\", \"update_channel\", MANAGER),\n r(\"DELETE\", \"channels/:channel\", \"delete_channel\", MANAGER),\n r(\"GET\", \"channels/:channel/settings\", \"get_channel_settings\", MANAGER),\n r(\"PUT\", \"channels/:channel/settings\", \"update_channel_settings\", MANAGER),\n r(\"GET\", \"settings\", \"get_settings\", MANAGER),\n]\n\nexport interface RouteMatch {\n route: ProxyRoute\n params: Record<string, string>\n /** Caminho do Engramind (`/v1/...`) com os segmentos recodificados. */\n path: string\n}\n\n/** Casa método + segmentos (já decodificados) com uma rota permitida no modo. */\nexport function matchProxyRoute(method: string, segments: readonly string[], mode: ProxyMode): RouteMatch | null {\n const upper = method.toUpperCase()\n for (const route of PROXY_ROUTES) {\n if (route.method !== upper || !route.modes.includes(mode)) continue\n const parts = route.pattern.split(\"/\")\n if (parts.length !== segments.length) continue\n const params: Record<string, string> = {}\n let ok = true\n for (let index = 0; index < parts.length; index += 1) {\n const part = parts[index]!\n const segment = segments[index]!\n if (part.startsWith(\":\")) {\n if (!segment) ok = false\n else params[part.slice(1)] = segment\n } else if (part !== segment) ok = false\n if (!ok) break\n }\n if (ok) return { route, params, path: `/v1/${segments.map((segment) => encodeURIComponent(segment)).join(\"/\")}` }\n }\n return null\n}\n","import { EngramindError, notConfiguredError } from \"../core/errors\"\nimport type { EngramindClient, EngramindMethod } from \"./client\"\nimport { matchProxyRoute, type ProxyMode, type ProxyRoute } from \"./routes\"\n\n/**\n * Proxy HTTP pronto para os componentes da lib: o app monta numa rota (ex.: catch-all do Next.js\n * `app/api/engramind/[...path]/route.ts`) **atrás do próprio login** — a lib não verifica usuário. Repassa\n * só as rotas do modo (`app`: as do token de app; `manager`: também as do gestor), com o token do\n * servidor; a resposta é `{ success: true, data }` ou `{ success: false, error, code, details }` e nunca\n * traz o token configurado. No modo app, `issue_token` só emite `kind: agent`.\n */\n\nexport interface ProxyMutation {\n operation: string\n method: EngramindMethod\n params: Record<string, string>\n /** Corpo enviado (o app decide o que audita; segredos de token emitido não passam por aqui). */\n body: unknown\n result: unknown\n}\n\nexport interface EngramindProxyOptions {\n /** Cliente do Engramind (ou uma função que o monta a cada requisição, ex.: lendo a configuração). */\n client: EngramindClient | (() => EngramindClient | null | Promise<EngramindClient | null>)\n mode?: ProxyMode\n /** Prefixo da rota no app (ex.: `/api/engramind`); o resto do caminho é a rota do Engramind. */\n basePath: string\n /** Depois de cada mutação bem-sucedida (auditoria do app). */\n onMutation?: (mutation: ProxyMutation) => void | Promise<void>\n /**\n * Token de agente emitido ou rotacionado: o segredo aparece só nesta resposta. O app pode gravá-lo onde\n * quiser (ex.: cofre do servidor); o diálogo da lib também o mostra uma única vez.\n */\n onTokenIssued?: (secret: string, token: { id: string; agent: string | null; app: string | null; kind: string; operation: \"issue_token\" | \"rotate_token\" }) => void | Promise<void>\n onError?: (error: unknown, route: ProxyRoute | null) => void\n}\n\nconst json = (status: number, body: unknown, headers: Record<string, string> = {}) => new Response(JSON.stringify(body), { status, headers: { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\", ...headers } })\n\nexport function errorResponse(error: unknown): Response {\n if (error instanceof EngramindError) {\n const headers: Record<string, string> = error.retryAfter !== null ? { \"Retry-After\": String(error.retryAfter) } : {}\n return json(error.status || 500, { success: false, error: error.message, code: error.code, ...(error.details !== undefined ? { details: error.details } : {}) }, headers)\n }\n return json(500, { success: false, error: \"Falha inesperada ao falar com o Engramind.\", code: \"ENGRAMIND_PROXY_ERROR\" })\n}\n\n/** Segmentos depois do `basePath`, decodificados (`channels/eng%2Fglobal` → `[\"channels\", \"eng/global\"]`). */\nexport function proxySegments(pathname: string, basePath: string): string[] | null {\n const base = basePath.replace(/\\/+$/, \"\")\n if (!pathname.startsWith(`${base}/`) && pathname !== base) return null\n const rest = pathname.slice(base.length).replace(/^\\/+/, \"\")\n if (!rest) return []\n try {\n return rest.split(\"/\").map((segment) => decodeURIComponent(segment))\n } catch {\n return null\n }\n}\n\nasync function readBody(request: Request): Promise<unknown> {\n const text = await request.text()\n if (!text) return undefined\n try {\n return JSON.parse(text)\n } catch {\n throw new EngramindError(400, \"Corpo da requisição inválido (JSON).\", \"ENGRAMIND_VALIDATION_ERROR\")\n }\n}\n\nexport function createEngramindProxy(options: EngramindProxyOptions) {\n const mode: ProxyMode = options.mode ?? \"app\"\n return async function handle(request: Request): Promise<Response> {\n const url = new URL(request.url)\n const segments = proxySegments(url.pathname, options.basePath)\n const match = segments ? matchProxyRoute(request.method, segments, mode) : null\n if (!match) return json(404, { success: false, error: \"Operação do Engramind não disponível neste app.\", code: \"ENGRAMIND_ROUTE_NOT_ALLOWED\" })\n try {\n const client = typeof options.client === \"function\" ? await options.client() : options.client\n if (!client) throw notConfiguredError()\n const method = match.route.method\n let body = method === \"GET\" || method === \"DELETE\" ? undefined : await readBody(request)\n if (match.route.operation === \"issue_token\" && mode === \"app\") {\n const input = (body && typeof body === \"object\" ? body : {}) as Record<string, unknown>\n if (input.kind !== undefined && input.kind !== \"agent\") throw new EngramindError(403, \"O app só emite tokens de agente.\", \"ENGRAMIND_SCOPE_REQUIRED\")\n body = { ...input, kind: \"agent\" }\n }\n const query = Object.fromEntries(url.searchParams.entries())\n const result = await client.call(method, match.path, { query, body: body ?? (method === \"GET\" || method === \"DELETE\" ? undefined : {}), headers: method === \"GET\" && request.headers.get(\"if-none-match\") ? { \"If-None-Match\": request.headers.get(\"if-none-match\")! } : undefined })\n if (method !== \"GET\") {\n const issued = (match.route.operation === \"issue_token\" || match.route.operation === \"rotate_token\") && result && typeof result === \"object\" ? (result as { secret?: unknown; token?: { id: string; kind: string; app?: { slug: string } | null; agent?: { slug: string } | null } }) : null\n if (issued && typeof issued.secret === \"string\" && issued.token) {\n const token = issued.token\n await options.onTokenIssued?.(issued.secret, { id: token.id, kind: token.kind, app: token.app?.slug ?? null, agent: token.agent && token.app ? `${token.app.slug}/${token.agent.slug}` : (token.agent?.slug ?? null), operation: match.route.operation as \"issue_token\" | \"rotate_token\" })\n }\n try {\n await options.onMutation?.({ operation: match.route.operation, method, params: match.params, body: redactBody(match.route.operation, body), result: issued ? { ...issued, secret: undefined } : result })\n } catch (error) {\n options.onError?.(error, match.route)\n }\n }\n return json(200, { success: true, data: result })\n } catch (error) {\n options.onError?.(error, match.route)\n return errorResponse(error)\n }\n }\n}\n\n/** O segredo do webhook nunca vai para a auditoria do app. */\nfunction redactBody(operation: string, body: unknown): unknown {\n if (operation !== \"update_app_webhook\" || !body || typeof body !== \"object\") return body\n const copy = { ...(body as Record<string, unknown>) }\n if (copy.secret) copy.secret = \"[segredo omitido]\"\n return copy\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\"\nimport { toLiveEvent, type WebhookEvent } from \"../core/events\"\nimport type { EventBus } from \"./bus\"\nimport type { ContextCache } from \"./context\"\n\n/**\n * Webhook do Engramind (`POST <webhook_url>`): valida `Engramind-Signature` (`t=<unix>,v1=<hex>`,\n * HMAC-SHA256 do segredo sobre `t + \".\" + corpo bruto`, janela de 300 s, comparação em tempo constante),\n * descarta o `id` repetido (as retentativas repetem o corpo), marca o contexto do canal como sujo (menos\n * `memory.consumed`) e republica no SSE só ids, slugs e tipos. Assinatura inválida: `401` sem corpo.\n */\n\nexport const WEBHOOK_TOLERANCE_SECONDS = 300\n\nexport function parseSignatureHeader(header: string | null): { t: number; v1: string[] } | null {\n if (!header) return null\n let t: number | null = null\n const v1: string[] = []\n for (const part of header.split(\",\")) {\n const [key, ...rest] = part.trim().split(\"=\")\n const value = rest.join(\"=\")\n if (key === \"t\") t = Number(value)\n if (key === \"v1\" && value) v1.push(value)\n }\n if (t === null || !Number.isFinite(t) || !v1.length) return null\n return { t, v1 }\n}\n\nexport function signWebhook(secret: string, timestamp: number, body: string): string {\n return createHmac(\"sha256\", secret).update(`${timestamp}.${body}`).digest(\"hex\")\n}\n\nexport function verifyWebhookSignature(header: string | null, body: string, secret: string, options: { now?: number; toleranceSeconds?: number } = {}): boolean {\n const parsed = parseSignatureHeader(header)\n if (!parsed || !secret) return false\n const now = Math.floor((options.now ?? Date.now()) / 1000)\n if (Math.abs(now - parsed.t) > (options.toleranceSeconds ?? WEBHOOK_TOLERANCE_SECONDS)) return false\n const expected = Buffer.from(signWebhook(secret, parsed.t, body), \"hex\")\n return parsed.v1.some((candidate) => {\n const given = Buffer.from(candidate, \"hex\")\n return given.length === expected.length && timingSafeEqual(given, expected)\n })\n}\n\n/** Registro dos `id` já processados (retentativas do Engramind). */\nexport interface DedupeStore {\n /** `true` se o `id` já foi visto (e o marca como visto). */\n seen(id: string): boolean | Promise<boolean>\n}\n\n/** Padrão em memória: lembra os últimos `max` ids por `ttlMs` (24 h). */\nexport function createMemoryDedupe(options: { ttlMs?: number; max?: number } = {}): DedupeStore {\n const ttl = options.ttlMs ?? 24 * 60 * 60_000\n const max = options.max ?? 10_000\n const ids = new Map<string, number>()\n return {\n seen(id) {\n const now = Date.now()\n for (const [key, at] of ids) {\n if (now - at <= ttl && ids.size <= max) break\n ids.delete(key)\n }\n if (ids.has(id)) return true\n ids.set(id, now)\n return false\n },\n }\n}\n\nexport interface WebhookHandlerOptions {\n /** Segredo do webhook do app (o mesmo gravado no Engramind por `update_app_webhook`). */\n secret: string | (() => string | null | Promise<string | null>)\n bus?: EventBus\n dedupe?: DedupeStore\n /** Contexto em cache dos agentes do app: marca os canais sujos. */\n context?: ContextCache\n /** Depois de validar (ex.: auditoria, outro tempo real do app). Uma falha não muda a resposta. */\n onEvent?: (event: WebhookEvent) => void | Promise<void>\n onError?: (error: unknown) => void\n toleranceSeconds?: number\n}\n\nexport function createWebhookHandler(options: WebhookHandlerOptions) {\n const dedupe = options.dedupe ?? createMemoryDedupe()\n return async function handle(request: Request): Promise<Response> {\n const body = await request.text()\n const secret = typeof options.secret === \"function\" ? await options.secret() : options.secret\n if (!secret || !verifyWebhookSignature(request.headers.get(\"engramind-signature\"), body, secret, { toleranceSeconds: options.toleranceSeconds })) return new Response(null, { status: 401 })\n let event: WebhookEvent\n try {\n event = JSON.parse(body) as WebhookEvent\n } catch {\n return new Response(null, { status: 400 })\n }\n if (!event || typeof event.id !== \"string\" || typeof event.type !== \"string\") return new Response(null, { status: 400 })\n if (await dedupe.seen(event.id)) return Response.json({ ok: true, duplicate: true })\n try {\n options.context?.handleEvent(event)\n await options.bus?.publish(toLiveEvent(event))\n await options.onEvent?.(event)\n } catch (error) {\n options.onError?.(error)\n }\n return Response.json({ ok: true })\n }\n}\n","import type { LiveEvent } from \"../core/events\"\n\n/**\n * Barramento dos eventos ao vivo (webhook → SSE). O padrão vive **no processo**: só os navegadores\n * ligados à instância que recebeu o webhook são avisados. Com várias instâncias, passe um `Broadcaster`\n * (ex.: Redis pub/sub, Postgres `NOTIFY`, Pusher) que entregue o evento a todas.\n */\nexport type LiveListener = (event: LiveEvent) => void\n\nexport interface Broadcaster {\n publish(event: LiveEvent): void | Promise<void>\n subscribe(listener: LiveListener): () => void\n}\n\nexport interface EventBus {\n publish(event: LiveEvent): Promise<void>\n subscribe(listener: LiveListener): () => void\n /** Navegadores ligados nesta instância. */\n readonly size: number\n}\n\nexport function createEventBus(options: { broadcaster?: Broadcaster; onError?: (error: unknown) => void } = {}): EventBus {\n const listeners = new Set<LiveListener>()\n const deliver = (event: LiveEvent) => {\n for (const listener of listeners) {\n try {\n listener(event)\n } catch (error) {\n options.onError?.(error)\n }\n }\n }\n const remote = options.broadcaster?.subscribe(deliver)\n void remote\n return {\n async publish(event) {\n if (options.broadcaster) await options.broadcaster.publish(event)\n else deliver(event)\n },\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n get size() {\n return listeners.size\n },\n }\n}\n","import type { LiveEvent } from \"../core/events\"\nimport type { EventBus } from \"./bus\"\n\n/**\n * Endpoint SSE dos componentes (`EventSource`): repassa os eventos do barramento como\n * `event: engramind` com só ids, slugs e tipos, e um comentário de vida a cada `heartbeatMs`. O app monta\n * **atrás do próprio login**; ao cair, o navegador reconecta e relê as telas.\n */\nexport interface SseOptions {\n bus: EventBus\n heartbeatMs?: number\n /** Filtra o que cada navegador recebe (ex.: por app ou canal). */\n filter?: (event: LiveEvent, request: Request) => boolean\n}\n\nconst encoder = new TextEncoder()\nexport const formatSse = (event: string, data: unknown) => `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`\n\nexport function createSseHandler(options: SseOptions) {\n const heartbeatMs = options.heartbeatMs ?? 25_000\n return function handle(request: Request): Response {\n let cleanup: (() => void) | null = null\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n const send = (chunk: string) => {\n try {\n controller.enqueue(encoder.encode(chunk))\n } catch {\n cleanup?.()\n }\n }\n send(`retry: 3000\\n${formatSse(\"ready\", { at: new Date().toISOString() })}`)\n const unsubscribe = options.bus.subscribe((event) => {\n if (options.filter && !options.filter(event, request)) return\n send(formatSse(\"engramind\", event))\n })\n const timer = setInterval(() => send(\": ping\\n\\n\"), heartbeatMs)\n cleanup = () => {\n if (!cleanup) return\n cleanup = null\n clearInterval(timer)\n unsubscribe()\n try {\n controller.close()\n } catch {\n // Já fechado.\n }\n }\n request.signal.addEventListener(\"abort\", () => cleanup?.(), { once: true })\n },\n cancel() {\n cleanup?.()\n },\n })\n return new Response(stream, { headers: { \"Content-Type\": \"text/event-stream; charset=utf-8\", \"Cache-Control\": \"no-cache, no-transform\", Connection: \"keep-alive\", \"X-Accel-Buffering\": \"no\" } })\n }\n}\n","import { isConsumedEvent, type WebhookEvent } from \"../core/events\"\nimport type { ContextView } from \"../core/types\"\nimport type { EngramindClient } from \"./client\"\n\n/**\n * Contexto das memórias por agente (`get_context`) com cache por `version` (`If-None-Match` → `304`).\n * Os eventos `memory.*`, `channel.updated` e `subscription.updated` marcam os canais como sujos;\n * **`memory.consumed` não** (só avisa que memórias foram entregues). Se o Engramind cair, devolve o\n * último bloco (`stale: true`). Guarde o bloco na parte estável do prompt.\n */\n\nexport interface ContextEntry {\n content: string\n version: string\n counts: ContextView[\"counts\"]\n format: string\n fetchedAt: number\n dirty: boolean\n}\n\n/** Onde o bloco fica (padrão: memória do processo). Troque por Redis/banco para várias instâncias. */\nexport interface ContextStore {\n get(key: string): ContextEntry | null | Promise<ContextEntry | null>\n set(key: string, entry: ContextEntry): void | Promise<void>\n /** Chaves guardadas (para marcar os canais sujos). */\n keys(): string[] | Promise<string[]>\n}\n\nexport function createMemoryContextStore(): ContextStore {\n const map = new Map<string, ContextEntry>()\n return { get: (key) => map.get(key) ?? null, set: (key, entry) => void map.set(key, entry), keys: () => [...map.keys()] }\n}\n\nexport interface ContextResult {\n block: string\n version: string | null\n /** `live` (lido agora ou `304`), `cache` (Engramind indisponível: último bloco) ou `none`. */\n source: \"live\" | \"cache\" | \"none\"\n stale: boolean\n counts: ContextView[\"counts\"] | null\n error: string | null\n}\n\nexport interface ContextCacheOptions {\n /** Cliente com o token **do agente** (o contexto é o do agente). */\n client: (agent: string) => EngramindClient | null | Promise<EngramindClient | null>\n store?: ContextStore\n format?: string\n /**\n * `always` (padrão): toda chamada revalida com `If-None-Match` (o `304` custa poucos bytes).\n * `on-dirty`: só chama o Engramind quando um evento sujou um canal do bloco ou depois de `maxAgeMs`.\n */\n revalidate?: \"always\" | \"on-dirty\"\n maxAgeMs?: number\n onError?: (error: unknown, agent: string) => void\n}\n\nconst SEP = \"\\u0000\"\nexport const contextKey = (agent: string, channels: readonly string[], format: string) => [agent, [...channels].sort().join(\",\"), format].join(SEP)\nconst channelsOfKey = (key: string) => (key.split(SEP)[1] ?? \"\").split(\",\").filter(Boolean)\n\n/** O evento muda o contexto do canal? (`memory.consumed` não muda.) */\nexport function invalidatesContext(event: Pick<WebhookEvent, \"type\">): boolean {\n if (isConsumedEvent(event.type)) return false\n return event.type.startsWith(\"memory.\") || event.type === \"channel.updated\" || event.type === \"subscription.updated\" || event.type === \"settings.updated\"\n}\n\nexport function createContextCache(options: ContextCacheOptions) {\n const store = options.store ?? createMemoryContextStore()\n const format = options.format ?? \"tagged\"\n const maxAge = options.maxAgeMs ?? 5 * 60_000\n /** Canais sujos sem bloco guardado por inteiro (prefixos, `settings.updated` global): suja tudo. */\n let allDirty = false\n\n async function markChannels(channels: readonly (string | null)[]) {\n const wanted = channels.filter((channel): channel is string => Boolean(channel))\n for (const key of await store.keys()) {\n const keyChannels = channelsOfKey(key)\n const hit = !wanted.length || !keyChannels.length || wanted.some((channel) => keyChannels.includes(channel))\n if (!hit) continue\n const entry = await store.get(key)\n if (entry && !entry.dirty) await store.set(key, { ...entry, dirty: true })\n }\n }\n\n return {\n /** Webhook recebido: suja o contexto dos canais afetados (nunca por `memory.consumed`). */\n handleEvent(event: Pick<WebhookEvent, \"type\" | \"channel\">) {\n if (!invalidatesContext(event)) return\n if (!event.channel) allDirty = true\n void markChannels(event.channel ? [event.channel] : [])\n },\n async getContext(agent: string, input: { channels?: readonly string[]; focus?: string } = {}): Promise<ContextResult> {\n const channels = input.channels ?? []\n const key = contextKey(agent, channels, format)\n const cached = await store.get(key)\n const usable = cached && !input.focus\n if (usable && options.revalidate === \"on-dirty\" && !cached.dirty && !allDirty && Date.now() - cached.fetchedAt < maxAge) {\n return { block: cached.content, version: cached.version, source: \"live\", stale: false, counts: cached.counts, error: null }\n }\n try {\n const client = await options.client(agent)\n if (!client) throw new Error(`token do agente ${agent} indisponível`)\n const view = await client.context({ channels: channels.length ? [...channels] : undefined, format, focus: input.focus, ifNoneMatch: usable ? cached.version : null })\n allDirty = false\n if (view === null && cached) {\n await store.set(key, { ...cached, fetchedAt: Date.now(), dirty: false })\n return { block: cached.content, version: cached.version, source: \"live\", stale: false, counts: cached.counts, error: null }\n }\n if (!view) throw new Error(\"o Engramind respondeu sem o contexto\")\n const entry: ContextEntry = { content: typeof view.content === \"string\" ? view.content : \"\", version: view.version ?? \"\", counts: view.counts, format, fetchedAt: Date.now(), dirty: false }\n // Bloco com foco tem outra `version`: não substitui o cache do bloco estável.\n if (!input.focus) await store.set(key, entry)\n return { block: entry.content, version: entry.version, source: \"live\", stale: false, counts: entry.counts, error: null }\n } catch (error) {\n options.onError?.(error, agent)\n const reason = error instanceof Error ? error.message : String(error)\n if (cached) return { block: cached.content, version: cached.version, source: \"cache\", stale: true, counts: cached.counts, error: reason }\n return { block: \"\", version: null, source: \"none\", stale: true, counts: null, error: reason }\n }\n },\n /** Bloco guardado (sem chamar o Engramind). */\n peek: async (agent: string, channels: readonly string[] = []) => store.get(contextKey(agent, channels, format)),\n }\n}\n\nexport type ContextCache = ReturnType<typeof createContextCache>\n","/**\n * Configuração de MCP pronta para registrar o Engramind num agente: `POST /mcp` com o token **do agente**\n * e o cabeçalho `Engramind-Channels` (canais do projeto). `tools/list` já mostra só o que o token pode\n * usar; repasse ao agente só as ferramentas que a sua política permite.\n */\nexport interface EngramindMcpConfig {\n type: \"http\"\n url: string\n headers: Record<string, string>\n}\n\nexport function engramindMcpConfig(input: { baseUrl: string; agentToken: string; channels?: readonly string[] }): EngramindMcpConfig {\n const headers: Record<string, string> = { Authorization: `Bearer ${input.agentToken}` }\n if (input.channels?.length) headers[\"Engramind-Channels\"] = input.channels.join(\",\")\n return { type: \"http\", url: `${input.baseUrl.replace(/\\/+$/, \"\")}/mcp`, headers }\n}\n\n/** Ferramentas de leitura e de escrita do MCP do Engramind (para filtrar o `tools/list`). */\nexport const MCP_READ_TOOLS = [\"get_context\", \"search_memories\", \"get_memory\", \"get_hints\", \"list_memories\", \"list_categories\"] as const\nexport const MCP_WRITE_TOOLS = [\"create_memory\", \"update_memory\"] as const\n"],"mappings":"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","names":["json"]}
1
+ {"version":3,"sources":["../src/server/client.ts","../src/server/routes.ts","../src/server/proxy.ts","../src/server/webhook.ts","../src/server/bus.ts","../src/server/sse.ts","../src/server/context.ts","../src/server/mcp.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\"\nimport { proxyMaskedError, toEngramindError, unreachableError, type EngramindErrorBody } from \"../core/errors\"\nimport type { CategoryInput, ChannelQuery, MemoryQuery } from \"./client-types\"\nimport type { Category, ConsumerSettings, ContextView, EngramindAgent, EngramindApp, EngramindChannel, EngramindHint, EngramindMemory, EngramindSubscription, EngramindToken, ImportResult, IssuedToken, MemoryRevision, MemoryWrite, SearchResults, WhoAmI } from \"../core/types\"\n\n/**\n * Cliente HTTP do Engramind — SOMENTE servidor. Bearer com o token do app (ou de um agente),\n * `Idempotency-Key` em toda mutação, `If-None-Match` no contexto (`304` → `null`) e erros do envelope\n * `{ error: { code, message, details } }` traduzidos para `EngramindError` (pt-BR), inclusive o 5xx\n * mascarado por um proxy na frente do Engramind (Cloudflare).\n */\n\nexport type EngramindMethod = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\"\nexport type Query = Record<string, string | number | boolean | null | undefined>\n\nexport interface EngramindClientOptions {\n /** URL base do Engramind (ex.: `https://engramind.exemplo.com`). */\n baseUrl: string\n /** Token do app (`kind: app`), de um agente ou, no gestor, admin. Nunca vai ao navegador. */\n token: string\n userAgent?: string\n timeoutMs?: number\n fetch?: typeof fetch\n /** Depuração: método, URL (com `focus` omitido), status e duração. Nunca recebe o corpo nem o token. */\n onDebug?: (entry: { method: string; url: string; status: number | null; durationMs: number; error?: string }) => void\n}\n\nexport interface CallOptions {\n query?: Query\n body?: unknown\n headers?: Record<string, string>\n /** Chave de idempotência fixa (importação); padrão: uma nova por mutação. */\n idempotencyKey?: string\n signal?: AbortSignal\n timeoutMs?: number\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000\nconst USER_TEXT_QUERY = [\"focus\"]\nconst enc = encodeURIComponent\n\nexport function buildUrl(baseUrl: string, path: string, query: Query = {}): string {\n const url = new URL(`${baseUrl.replace(/\\/+$/, \"\")}${path}`)\n for (const [key, value] of Object.entries(query)) if (value !== undefined && value !== null && value !== \"\") url.searchParams.set(key, String(value))\n return url.toString()\n}\n\n/** URL para a depuração: o texto do usuário (`focus`) vira o tamanho. */\nexport function debugUrl(url: string): string {\n const masked = new URL(url)\n for (const key of USER_TEXT_QUERY) {\n const value = masked.searchParams.get(key)\n if (value !== null) masked.searchParams.set(key, `[omitido: ${value.length} caracteres]`)\n }\n return masked.toString()\n}\n\nexport function createEngramindClient(options: EngramindClientOptions) {\n const doFetch = options.fetch ?? fetch\n const userAgent = options.userAgent ?? \"engramind-client\"\n\n async function call<T = unknown>(method: EngramindMethod, path: string, request: CallOptions = {}): Promise<T> {\n const headers: Record<string, string> = { ...request.headers, Authorization: `Bearer ${options.token}`, Accept: \"application/json\", \"User-Agent\": userAgent }\n if (method !== \"GET\") headers[\"Idempotency-Key\"] = request.idempotencyKey ?? `engramind-client-${randomUUID()}`\n const hasBody = request.body !== undefined && method !== \"GET\"\n if (hasBody) headers[\"Content-Type\"] = \"application/json\"\n const timeout = AbortSignal.timeout(request.timeoutMs ?? options.timeoutMs ?? DEFAULT_TIMEOUT_MS)\n const signal = request.signal ? AbortSignal.any([request.signal, timeout]) : timeout\n const url = buildUrl(options.baseUrl, path, request.query)\n const started = Date.now()\n let res: Response\n try {\n res = await doFetch(url, { method, headers, body: hasBody ? JSON.stringify(request.body) : undefined, cache: \"no-store\", signal })\n } catch (error) {\n const reason = error instanceof Error ? (error.name === \"TimeoutError\" || error.name === \"AbortError\" ? \"tempo limite\" : ((error as { cause?: { code?: string } }).cause?.code ?? error.message)) : String(error)\n options.onDebug?.({ method, url: debugUrl(url), status: null, durationMs: Date.now() - started, error: reason })\n throw unreachableError(reason)\n }\n options.onDebug?.({ method, url: debugUrl(url), status: res.status, durationMs: Date.now() - started })\n if (res.status === 304) return null as T\n const text = await res.text()\n let json: unknown = null\n if (text) {\n try {\n json = JSON.parse(text)\n } catch {\n json = null\n }\n }\n if (!res.ok) {\n const body = (json as { error?: EngramindErrorBody } | null)?.error ?? null\n throw toEngramindError(res.status, body && typeof body === \"object\" ? body : null, res.headers.get(\"retry-after\"), body ? null : proxyMaskedError(text, json))\n }\n return json as T\n }\n\n /** `GET /health/ready` (público): `true` quando o serviço recebe tráfego. */\n async function ready(signal?: AbortSignal): Promise<{ ready: boolean; detail: string }> {\n try {\n const res = await doFetch(`${options.baseUrl.replace(/\\/+$/, \"\")}/health/ready`, { headers: { Accept: \"application/json\", \"User-Agent\": userAgent }, cache: \"no-store\", signal: signal ?? AbortSignal.timeout(10_000) })\n const body = (await res.json().catch(() => null)) as { status?: string } | null\n return { ready: res.ok && body?.status === \"ready\", detail: body?.status ?? `HTTP ${res.status}` }\n } catch (error) {\n return { ready: false, detail: error instanceof Error ? error.message : String(error) }\n }\n }\n\n return {\n call,\n ready,\n whoami: () => call<WhoAmI>(\"GET\", \"/v1/whoami\"),\n consumerSettings: () => call<ConsumerSettings>(\"GET\", \"/v1/consumer-settings\"),\n categories: {\n list: () => call<{ categories: Category[] }>(\"GET\", \"/v1/categories\"),\n create: (input: CategoryInput) => call<Category>(\"POST\", \"/v1/categories\", { body: input }),\n },\n agents: {\n list: (query: Query = {}) => call<{ agents: EngramindAgent[]; next_cursor: string | null }>(\"GET\", \"/v1/agents\", { query }),\n get: (agent: string) => call<EngramindAgent>(\"GET\", `/v1/agents/${enc(agent)}`),\n create: (input: { app?: string; slug: string; name: string; description?: string | null }) => call<EngramindAgent & { created?: boolean }>(\"POST\", \"/v1/agents\", { body: input }),\n update: (agent: string, input: { name?: string; description?: string | null; disabled?: boolean }) => call<EngramindAgent>(\"PATCH\", `/v1/agents/${enc(agent)}`, { body: input }),\n delete: (agent: string) => call<null>(\"DELETE\", `/v1/agents/${enc(agent)}`),\n },\n subscriptions: {\n list: (query: Query = {}) => call<{ subscriptions: EngramindSubscription[]; next_cursor: string | null }>(\"GET\", \"/v1/subscriptions\", { query }),\n create: (input: { agent: string; target: string; mode: \"inject\" | \"search\" | \"manage\"; access?: string[] | null; priority?: number | null }) => call<EngramindSubscription & { created?: boolean }>(\"POST\", \"/v1/subscriptions\", { body: input }),\n update: (subscription: string, input: { mode?: \"inject\" | \"search\" | \"manage\"; access?: string[] | null; priority?: number | null }) => call<EngramindSubscription>(\"PATCH\", `/v1/subscriptions/${enc(subscription)}`, { body: input }),\n delete: (subscription: string) => call<null>(\"DELETE\", `/v1/subscriptions/${enc(subscription)}`),\n },\n channels: {\n list: (query: ChannelQuery = {}) => call<{ channels: EngramindChannel[]; next_cursor: string | null }>(\"GET\", \"/v1/channels\", { query: query as Query }),\n get: (channel: string) => call<EngramindChannel>(\"GET\", `/v1/channels/${enc(channel)}`),\n archive: (channel: string) => call<EngramindChannel>(\"POST\", `/v1/channels/${enc(channel)}/archive`, { body: {} }),\n restore: (channel: string) => call<EngramindChannel>(\"POST\", `/v1/channels/${enc(channel)}/restore`, { body: {} }),\n },\n memories: {\n list: (query: MemoryQuery = {}) => call<{ memories: EngramindMemory[]; next_cursor: string | null }>(\"GET\", \"/v1/memories\", { query: query as Query }),\n get: (memory: string) => call<EngramindMemory>(\"GET\", `/v1/memories/${enc(memory)}`),\n create: (input: { channel: string; text: string; category?: string; key?: string; tags?: string[] }) => call<MemoryWrite>(\"POST\", \"/v1/memories\", { body: input }),\n update: (memory: string, input: { text?: string; category?: string; expected_version?: number; tags?: string[] }) => call<MemoryWrite>(\"PATCH\", `/v1/memories/${enc(memory)}`, { body: input }),\n delete: (memory: string) => call<null>(\"DELETE\", `/v1/memories/${enc(memory)}`),\n archive: (memory: string) => call<EngramindMemory>(\"POST\", `/v1/memories/${enc(memory)}/archive`, { body: {} }),\n restore: (memory: string) => call<EngramindMemory>(\"POST\", `/v1/memories/${enc(memory)}/restore`, { body: {} }),\n pin: (memory: string) => call<EngramindMemory>(\"POST\", `/v1/memories/${enc(memory)}/pin`, { body: {} }),\n unpin: (memory: string) => call<EngramindMemory>(\"POST\", `/v1/memories/${enc(memory)}/unpin`, { body: {} }),\n move: (memory: string, channel: string) => call<EngramindMemory>(\"POST\", `/v1/memories/${enc(memory)}/move`, { body: { channel } }),\n history: (memory: string, query: Query = {}) => call<{ revisions: MemoryRevision[]; next_cursor: string | null }>(\"GET\", `/v1/memories/${enc(memory)}/history`, { query }),\n revert: (memory: string, version: number) => call<EngramindMemory>(\"POST\", `/v1/memories/${enc(memory)}/revert`, { body: { version } }),\n import: (input: { channel: string; memories: unknown[] }, idempotencyKey?: string) => call<ImportResult>(\"POST\", \"/v1/memories/import\", { body: input, idempotencyKey }),\n search: (input: { query: string; channels?: string[]; categories?: string[]; limit?: number; include_archived?: boolean; exclude_context?: boolean }) => call<SearchResults & { context_version?: string }>(\"POST\", \"/v1/memories/search\", { body: input }),\n },\n hints: (input: { text: string; channels?: string[]; limit?: number }) => call<{ hints: EngramindHint[] }>(\"POST\", \"/v1/hints\", { body: input }),\n context: (input: { channels?: string[]; format?: string; focus?: string; ifNoneMatch?: string | null }, request: Pick<CallOptions, \"signal\" | \"timeoutMs\"> = {}) =>\n call<ContextView | null>(\"GET\", \"/v1/context\", { query: { channels: input.channels?.join(\",\"), format: input.format, focus: input.focus }, headers: input.ifNoneMatch ? { \"If-None-Match\": `\"${input.ifNoneMatch}\"` } : undefined, ...request }),\n tokens: {\n list: (query: Query = {}) => call<{ tokens: EngramindToken[]; next_cursor: string | null }>(\"GET\", \"/v1/tokens\", { query }),\n issue: (input: { kind: \"agent\" | \"app\" | \"admin\"; agent?: string; app?: string; scopes: string[]; label?: string; expires_at?: string }) => call<IssuedToken>(\"POST\", \"/v1/tokens\", { body: input }),\n update: (token: string, input: { label?: string | null; scopes?: string[]; expires_at?: string | null }) => call<EngramindToken>(\"PATCH\", `/v1/tokens/${enc(token)}`, { body: input }),\n revoke: (token: string) => call<EngramindToken>(\"POST\", `/v1/tokens/${enc(token)}/revoke`, { body: {} }),\n rotate: (token: string) => call<IssuedToken>(\"POST\", `/v1/tokens/${enc(token)}/rotate`, { body: {} }),\n },\n apps: {\n get: (app: string) => call<EngramindApp>(\"GET\", `/v1/apps/${enc(app)}`),\n /** `update_app_webhook`: grava ou remove (com `null`) a URL e o segredo do webhook do app. */\n updateWebhook: (app: string, input: { url: string | null; secret?: string | null }) => call<EngramindApp>(\"PUT\", `/v1/apps/${enc(app)}/webhook`, { body: input }),\n },\n }\n}\n\nexport type EngramindClient = ReturnType<typeof createEngramindClient>\n","import type { EngramindMethod } from \"./client\"\n\n/**\n * Rotas do proxy da lib: o caminho do REST do Engramind sem o `/v1` (`channels/eng%2Fglobal/archive`).\n * `app` é o que o token de app pode fazer (matriz do guia `tokens-e-escopos`); `manager` acrescenta o que\n * o gestor da rede usa nas telas (apps, criar/editar/excluir canais, configurações por canal).\n */\nexport type ProxyMode = \"app\" | \"manager\"\n\nexport interface ProxyRoute {\n method: EngramindMethod\n /** Segmentos; `:nome` casa um segmento qualquer. */\n pattern: string\n modes: readonly ProxyMode[]\n /** Operação do Engramind (auditoria do app e testes). */\n operation: string\n}\n\nconst BOTH = [\"app\", \"manager\"] as const\nconst MANAGER = [\"manager\"] as const\nconst r = (method: EngramindMethod, pattern: string, operation: string, modes: readonly ProxyMode[] = BOTH): ProxyRoute => ({ method, pattern, operation, modes })\n\nexport const PROXY_ROUTES: readonly ProxyRoute[] = [\n r(\"GET\", \"whoami\", \"whoami\"),\n r(\"GET\", \"consumer-settings\", \"get_consumer_settings\"),\n r(\"GET\", \"categories\", \"list_categories\"),\n r(\"POST\", \"categories\", \"create_category\"),\n r(\"GET\", \"agents\", \"list_agents\"),\n r(\"POST\", \"agents\", \"create_agent\"),\n r(\"GET\", \"agents/:agent\", \"get_agent\"),\n r(\"PATCH\", \"agents/:agent\", \"update_agent\"),\n r(\"DELETE\", \"agents/:agent\", \"delete_agent\"),\n r(\"GET\", \"subscriptions\", \"list_subscriptions\"),\n r(\"POST\", \"subscriptions\", \"subscribe\"),\n r(\"PATCH\", \"subscriptions/:subscription\", \"update_subscription\"),\n r(\"DELETE\", \"subscriptions/:subscription\", \"unsubscribe\"),\n r(\"GET\", \"channels\", \"list_channels\"),\n r(\"GET\", \"channels/:channel\", \"get_channel\"),\n r(\"GET\", \"channels/:channel/stats\", \"channel_stats\"),\n r(\"POST\", \"channels/:channel/archive\", \"archive_channel\"),\n r(\"POST\", \"channels/:channel/restore\", \"restore_channel\"),\n // Engramind 0.13.0: o token de app edita os canais que criou (403 FOREIGN_APP / 409 CHANNEL_SHARED nos outros).\n r(\"PATCH\", \"channels/:channel\", \"update_channel\"),\n r(\"GET\", \"memories\", \"list_memories\"),\n r(\"POST\", \"memories\", \"create_memory\"),\n r(\"POST\", \"memories/import\", \"import_memories\"),\n r(\"POST\", \"memories/search\", \"search_memories\"),\n r(\"POST\", \"hints\", \"get_hints\"),\n r(\"GET\", \"memories/:memory\", \"get_memory\"),\n r(\"PATCH\", \"memories/:memory\", \"update_memory\"),\n r(\"DELETE\", \"memories/:memory\", \"delete_memory\"),\n r(\"GET\", \"memories/:memory/history\", \"memory_history\"),\n r(\"POST\", \"memories/:memory/archive\", \"archive_memory\"),\n r(\"POST\", \"memories/:memory/restore\", \"restore_memory\"),\n r(\"POST\", \"memories/:memory/pin\", \"pin_memory\"),\n r(\"POST\", \"memories/:memory/unpin\", \"unpin_memory\"),\n r(\"POST\", \"memories/:memory/move\", \"move_memory\"),\n r(\"POST\", \"memories/:memory/revert\", \"revert_memory\"),\n r(\"POST\", \"memories/:memory/verify\", \"mark_verified\"),\n r(\"GET\", \"tokens\", \"list_tokens\"),\n r(\"POST\", \"tokens\", \"issue_token\"),\n r(\"PATCH\", \"tokens/:token\", \"update_token\"),\n r(\"POST\", \"tokens/:token/revoke\", \"revoke_token\"),\n r(\"POST\", \"tokens/:token/rotate\", \"rotate_token\"),\n r(\"GET\", \"apps/:app\", \"get_app\"),\n r(\"PUT\", \"apps/:app/webhook\", \"update_app_webhook\"),\n r(\"GET\", \"context\", \"get_context\"),\n // Só o gestor (token admin).\n r(\"GET\", \"apps\", \"list_apps\", MANAGER),\n r(\"POST\", \"channels\", \"create_channel\", MANAGER),\n r(\"DELETE\", \"channels/:channel\", \"delete_channel\", MANAGER),\n r(\"GET\", \"channels/:channel/settings\", \"get_channel_settings\", MANAGER),\n r(\"PUT\", \"channels/:channel/settings\", \"update_channel_settings\", MANAGER),\n r(\"GET\", \"settings\", \"get_settings\", MANAGER),\n]\n\nexport interface RouteMatch {\n route: ProxyRoute\n params: Record<string, string>\n /** Caminho do Engramind (`/v1/...`) com os segmentos recodificados. */\n path: string\n}\n\n/** Casa método + segmentos (já decodificados) com uma rota permitida no modo. */\nexport function matchProxyRoute(method: string, segments: readonly string[], mode: ProxyMode): RouteMatch | null {\n const upper = method.toUpperCase()\n for (const route of PROXY_ROUTES) {\n if (route.method !== upper || !route.modes.includes(mode)) continue\n const parts = route.pattern.split(\"/\")\n if (parts.length !== segments.length) continue\n const params: Record<string, string> = {}\n let ok = true\n for (let index = 0; index < parts.length; index += 1) {\n const part = parts[index]!\n const segment = segments[index]!\n if (part.startsWith(\":\")) {\n if (!segment) ok = false\n else params[part.slice(1)] = segment\n } else if (part !== segment) ok = false\n if (!ok) break\n }\n if (ok) return { route, params, path: `/v1/${segments.map((segment) => encodeURIComponent(segment)).join(\"/\")}` }\n }\n return null\n}\n","import { EngramindError, notConfiguredError } from \"../core/errors\"\nimport type { EngramindClient, EngramindMethod } from \"./client\"\nimport { matchProxyRoute, type ProxyMode, type ProxyRoute } from \"./routes\"\n\n/**\n * Proxy HTTP pronto para os componentes da lib: o app monta numa rota (ex.: catch-all do Next.js\n * `app/api/engramind/[...path]/route.ts`) **atrás do próprio login** — a lib não verifica usuário. Repassa\n * só as rotas do modo (`app`: as do token de app; `manager`: também as do gestor), com o token do\n * servidor; a resposta é `{ success: true, data }` ou `{ success: false, error, code, details }` e nunca\n * traz o token configurado. No modo app, `issue_token` só emite `kind: agent`.\n */\n\nexport interface ProxyMutation {\n operation: string\n method: EngramindMethod\n params: Record<string, string>\n /** Corpo enviado (o app decide o que audita; segredos de token emitido não passam por aqui). */\n body: unknown\n result: unknown\n}\n\nexport interface EngramindProxyOptions {\n /** Cliente do Engramind (ou uma função que o monta a cada requisição, ex.: lendo a configuração). */\n client: EngramindClient | (() => EngramindClient | null | Promise<EngramindClient | null>)\n mode?: ProxyMode\n /** Prefixo da rota no app (ex.: `/api/engramind`); o resto do caminho é a rota do Engramind. */\n basePath: string\n /** Depois de cada mutação bem-sucedida (auditoria do app). */\n onMutation?: (mutation: ProxyMutation) => void | Promise<void>\n /**\n * Token de agente emitido ou rotacionado: o segredo aparece só nesta resposta. O app pode gravá-lo onde\n * quiser (ex.: cofre do servidor); o diálogo da lib também o mostra uma única vez.\n */\n onTokenIssued?: (secret: string, token: { id: string; agent: string | null; app: string | null; kind: string; operation: \"issue_token\" | \"rotate_token\" }) => void | Promise<void>\n onError?: (error: unknown, route: ProxyRoute | null) => void\n}\n\nconst json = (status: number, body: unknown, headers: Record<string, string> = {}) => new Response(JSON.stringify(body), { status, headers: { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\", ...headers } })\n\nexport function errorResponse(error: unknown): Response {\n if (error instanceof EngramindError) {\n const headers: Record<string, string> = error.retryAfter !== null ? { \"Retry-After\": String(error.retryAfter) } : {}\n return json(error.status || 500, { success: false, error: error.message, code: error.code, ...(error.details !== undefined ? { details: error.details } : {}) }, headers)\n }\n return json(500, { success: false, error: \"Falha inesperada ao falar com o Engramind.\", code: \"ENGRAMIND_PROXY_ERROR\" })\n}\n\n/** Segmentos depois do `basePath`, decodificados (`channels/eng%2Fglobal` → `[\"channels\", \"eng/global\"]`). */\nexport function proxySegments(pathname: string, basePath: string): string[] | null {\n const base = basePath.replace(/\\/+$/, \"\")\n if (!pathname.startsWith(`${base}/`) && pathname !== base) return null\n const rest = pathname.slice(base.length).replace(/^\\/+/, \"\")\n if (!rest) return []\n try {\n return rest.split(\"/\").map((segment) => decodeURIComponent(segment))\n } catch {\n return null\n }\n}\n\nasync function readBody(request: Request): Promise<unknown> {\n const text = await request.text()\n if (!text) return undefined\n try {\n return JSON.parse(text)\n } catch {\n throw new EngramindError(400, \"Corpo da requisição inválido (JSON).\", \"ENGRAMIND_VALIDATION_ERROR\")\n }\n}\n\nexport function createEngramindProxy(options: EngramindProxyOptions) {\n const mode: ProxyMode = options.mode ?? \"app\"\n return async function handle(request: Request): Promise<Response> {\n const url = new URL(request.url)\n const segments = proxySegments(url.pathname, options.basePath)\n const match = segments ? matchProxyRoute(request.method, segments, mode) : null\n if (!match) return json(404, { success: false, error: \"Operação do Engramind não disponível neste app.\", code: \"ENGRAMIND_ROUTE_NOT_ALLOWED\" })\n try {\n const client = typeof options.client === \"function\" ? await options.client() : options.client\n if (!client) throw notConfiguredError()\n const method = match.route.method\n let body = method === \"GET\" || method === \"DELETE\" ? undefined : await readBody(request)\n if (match.route.operation === \"issue_token\" && mode === \"app\") {\n const input = (body && typeof body === \"object\" ? body : {}) as Record<string, unknown>\n if (input.kind !== undefined && input.kind !== \"agent\") throw new EngramindError(403, \"O app só emite tokens de agente.\", \"ENGRAMIND_SCOPE_REQUIRED\")\n body = { ...input, kind: \"agent\" }\n }\n const query = Object.fromEntries(url.searchParams.entries())\n const result = await client.call(method, match.path, { query, body: body ?? (method === \"GET\" || method === \"DELETE\" ? undefined : {}), headers: method === \"GET\" && request.headers.get(\"if-none-match\") ? { \"If-None-Match\": request.headers.get(\"if-none-match\")! } : undefined })\n if (method !== \"GET\") {\n const issued = (match.route.operation === \"issue_token\" || match.route.operation === \"rotate_token\") && result && typeof result === \"object\" ? (result as { secret?: unknown; token?: { id: string; kind: string; app?: { slug: string } | null; agent?: { slug: string } | null } }) : null\n if (issued && typeof issued.secret === \"string\" && issued.token) {\n const token = issued.token\n await options.onTokenIssued?.(issued.secret, { id: token.id, kind: token.kind, app: token.app?.slug ?? null, agent: token.agent && token.app ? `${token.app.slug}/${token.agent.slug}` : (token.agent?.slug ?? null), operation: match.route.operation as \"issue_token\" | \"rotate_token\" })\n }\n try {\n await options.onMutation?.({ operation: match.route.operation, method, params: match.params, body: redactBody(match.route.operation, body), result: issued ? { ...issued, secret: undefined } : result })\n } catch (error) {\n options.onError?.(error, match.route)\n }\n }\n return json(200, { success: true, data: result })\n } catch (error) {\n options.onError?.(error, match.route)\n return errorResponse(error)\n }\n }\n}\n\n/** O segredo do webhook nunca vai para a auditoria do app. */\nfunction redactBody(operation: string, body: unknown): unknown {\n if (operation !== \"update_app_webhook\" || !body || typeof body !== \"object\") return body\n const copy = { ...(body as Record<string, unknown>) }\n if (copy.secret) copy.secret = \"[segredo omitido]\"\n return copy\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\"\nimport { toLiveEvent, type WebhookEvent } from \"../core/events\"\nimport type { EventBus } from \"./bus\"\nimport type { ContextCache } from \"./context\"\n\n/**\n * Webhook do Engramind (`POST <webhook_url>`): valida `Engramind-Signature` (`t=<unix>,v1=<hex>`,\n * HMAC-SHA256 do segredo sobre `t + \".\" + corpo bruto`, janela de 300 s, comparação em tempo constante),\n * descarta o `id` repetido (as retentativas repetem o corpo), marca o contexto do canal como sujo (menos\n * `memory.consumed`) e republica no SSE só ids, slugs e tipos. Assinatura inválida: `401` sem corpo.\n */\n\nexport const WEBHOOK_TOLERANCE_SECONDS = 300\n\nexport function parseSignatureHeader(header: string | null): { t: number; v1: string[] } | null {\n if (!header) return null\n let t: number | null = null\n const v1: string[] = []\n for (const part of header.split(\",\")) {\n const [key, ...rest] = part.trim().split(\"=\")\n const value = rest.join(\"=\")\n if (key === \"t\") t = Number(value)\n if (key === \"v1\" && value) v1.push(value)\n }\n if (t === null || !Number.isFinite(t) || !v1.length) return null\n return { t, v1 }\n}\n\nexport function signWebhook(secret: string, timestamp: number, body: string): string {\n return createHmac(\"sha256\", secret).update(`${timestamp}.${body}`).digest(\"hex\")\n}\n\nexport function verifyWebhookSignature(header: string | null, body: string, secret: string, options: { now?: number; toleranceSeconds?: number } = {}): boolean {\n const parsed = parseSignatureHeader(header)\n if (!parsed || !secret) return false\n const now = Math.floor((options.now ?? Date.now()) / 1000)\n if (Math.abs(now - parsed.t) > (options.toleranceSeconds ?? WEBHOOK_TOLERANCE_SECONDS)) return false\n const expected = Buffer.from(signWebhook(secret, parsed.t, body), \"hex\")\n return parsed.v1.some((candidate) => {\n const given = Buffer.from(candidate, \"hex\")\n return given.length === expected.length && timingSafeEqual(given, expected)\n })\n}\n\n/** Registro dos `id` já processados (retentativas do Engramind). */\nexport interface DedupeStore {\n /** `true` se o `id` já foi visto (e o marca como visto). */\n seen(id: string): boolean | Promise<boolean>\n}\n\n/** Padrão em memória: lembra os últimos `max` ids por `ttlMs` (24 h). */\nexport function createMemoryDedupe(options: { ttlMs?: number; max?: number } = {}): DedupeStore {\n const ttl = options.ttlMs ?? 24 * 60 * 60_000\n const max = options.max ?? 10_000\n const ids = new Map<string, number>()\n return {\n seen(id) {\n const now = Date.now()\n for (const [key, at] of ids) {\n if (now - at <= ttl && ids.size <= max) break\n ids.delete(key)\n }\n if (ids.has(id)) return true\n ids.set(id, now)\n return false\n },\n }\n}\n\nexport interface WebhookHandlerOptions {\n /** Segredo do webhook do app (o mesmo gravado no Engramind por `update_app_webhook`). */\n secret: string | (() => string | null | Promise<string | null>)\n bus?: EventBus\n dedupe?: DedupeStore\n /** Contexto em cache dos agentes do app: marca os canais sujos. */\n context?: ContextCache\n /** Depois de validar (ex.: auditoria, outro tempo real do app). Uma falha não muda a resposta. */\n onEvent?: (event: WebhookEvent) => void | Promise<void>\n onError?: (error: unknown) => void\n toleranceSeconds?: number\n}\n\nexport function createWebhookHandler(options: WebhookHandlerOptions) {\n const dedupe = options.dedupe ?? createMemoryDedupe()\n return async function handle(request: Request): Promise<Response> {\n const body = await request.text()\n const secret = typeof options.secret === \"function\" ? await options.secret() : options.secret\n if (!secret || !verifyWebhookSignature(request.headers.get(\"engramind-signature\"), body, secret, { toleranceSeconds: options.toleranceSeconds })) return new Response(null, { status: 401 })\n let event: WebhookEvent\n try {\n event = JSON.parse(body) as WebhookEvent\n } catch {\n return new Response(null, { status: 400 })\n }\n if (!event || typeof event.id !== \"string\" || typeof event.type !== \"string\") return new Response(null, { status: 400 })\n if (await dedupe.seen(event.id)) return Response.json({ ok: true, duplicate: true })\n try {\n options.context?.handleEvent(event)\n await options.bus?.publish(toLiveEvent(event))\n await options.onEvent?.(event)\n } catch (error) {\n options.onError?.(error)\n }\n return Response.json({ ok: true })\n }\n}\n","import type { LiveEvent } from \"../core/events\"\n\n/**\n * Barramento dos eventos ao vivo (webhook → SSE). O padrão vive **no processo**: só os navegadores\n * ligados à instância que recebeu o webhook são avisados. Com várias instâncias, passe um `Broadcaster`\n * (ex.: Redis pub/sub, Postgres `NOTIFY`, Pusher) que entregue o evento a todas.\n */\nexport type LiveListener = (event: LiveEvent) => void\n\nexport interface Broadcaster {\n publish(event: LiveEvent): void | Promise<void>\n subscribe(listener: LiveListener): () => void\n}\n\nexport interface EventBus {\n publish(event: LiveEvent): Promise<void>\n subscribe(listener: LiveListener): () => void\n /** Navegadores ligados nesta instância. */\n readonly size: number\n}\n\nexport function createEventBus(options: { broadcaster?: Broadcaster; onError?: (error: unknown) => void } = {}): EventBus {\n const listeners = new Set<LiveListener>()\n const deliver = (event: LiveEvent) => {\n for (const listener of listeners) {\n try {\n listener(event)\n } catch (error) {\n options.onError?.(error)\n }\n }\n }\n const remote = options.broadcaster?.subscribe(deliver)\n void remote\n return {\n async publish(event) {\n if (options.broadcaster) await options.broadcaster.publish(event)\n else deliver(event)\n },\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n get size() {\n return listeners.size\n },\n }\n}\n","import type { LiveEvent } from \"../core/events\"\nimport type { EventBus } from \"./bus\"\n\n/**\n * Endpoint SSE dos componentes (`EventSource`): repassa os eventos do barramento como\n * `event: engramind` com só ids, slugs e tipos, e um comentário de vida a cada `heartbeatMs`. O app monta\n * **atrás do próprio login**; ao cair, o navegador reconecta e relê as telas.\n */\nexport interface SseOptions {\n bus: EventBus\n heartbeatMs?: number\n /** Filtra o que cada navegador recebe (ex.: por app ou canal). */\n filter?: (event: LiveEvent, request: Request) => boolean\n}\n\nconst encoder = new TextEncoder()\nexport const formatSse = (event: string, data: unknown) => `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`\n\nexport function createSseHandler(options: SseOptions) {\n const heartbeatMs = options.heartbeatMs ?? 25_000\n return function handle(request: Request): Response {\n let cleanup: (() => void) | null = null\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n const send = (chunk: string) => {\n try {\n controller.enqueue(encoder.encode(chunk))\n } catch {\n cleanup?.()\n }\n }\n send(`retry: 3000\\n${formatSse(\"ready\", { at: new Date().toISOString() })}`)\n const unsubscribe = options.bus.subscribe((event) => {\n if (options.filter && !options.filter(event, request)) return\n send(formatSse(\"engramind\", event))\n })\n const timer = setInterval(() => send(\": ping\\n\\n\"), heartbeatMs)\n cleanup = () => {\n if (!cleanup) return\n cleanup = null\n clearInterval(timer)\n unsubscribe()\n try {\n controller.close()\n } catch {\n // Já fechado.\n }\n }\n request.signal.addEventListener(\"abort\", () => cleanup?.(), { once: true })\n },\n cancel() {\n cleanup?.()\n },\n })\n return new Response(stream, { headers: { \"Content-Type\": \"text/event-stream; charset=utf-8\", \"Cache-Control\": \"no-cache, no-transform\", Connection: \"keep-alive\", \"X-Accel-Buffering\": \"no\" } })\n }\n}\n","import { isConsumedEvent, type WebhookEvent } from \"../core/events\"\nimport type { ContextView } from \"../core/types\"\nimport type { EngramindClient } from \"./client\"\n\n/**\n * Contexto das memórias por agente (`get_context`) com cache por `version` (`If-None-Match` → `304`).\n * Os eventos `memory.*`, `channel.updated` e `subscription.updated` marcam os canais como sujos;\n * **`memory.consumed` não** (só avisa que memórias foram entregues). Se o Engramind cair, devolve o\n * último bloco (`stale: true`). Guarde o bloco na parte estável do prompt.\n */\n\nexport interface ContextEntry {\n content: string\n version: string\n counts: ContextView[\"counts\"]\n format: string\n fetchedAt: number\n dirty: boolean\n}\n\n/** Onde o bloco fica (padrão: memória do processo). Troque por Redis/banco para várias instâncias. */\nexport interface ContextStore {\n get(key: string): ContextEntry | null | Promise<ContextEntry | null>\n set(key: string, entry: ContextEntry): void | Promise<void>\n /** Chaves guardadas (para marcar os canais sujos). */\n keys(): string[] | Promise<string[]>\n}\n\nexport function createMemoryContextStore(): ContextStore {\n const map = new Map<string, ContextEntry>()\n return { get: (key) => map.get(key) ?? null, set: (key, entry) => void map.set(key, entry), keys: () => [...map.keys()] }\n}\n\nexport interface ContextResult {\n block: string\n version: string | null\n /** `live` (lido agora ou `304`), `cache` (Engramind indisponível: último bloco) ou `none`. */\n source: \"live\" | \"cache\" | \"none\"\n stale: boolean\n counts: ContextView[\"counts\"] | null\n error: string | null\n}\n\nexport interface ContextCacheOptions {\n /** Cliente com o token **do agente** (o contexto é o do agente). */\n client: (agent: string) => EngramindClient | null | Promise<EngramindClient | null>\n store?: ContextStore\n format?: string\n /**\n * `always` (padrão): toda chamada revalida com `If-None-Match` (o `304` custa poucos bytes).\n * `on-dirty`: só chama o Engramind quando um evento sujou um canal do bloco ou depois de `maxAgeMs`.\n */\n revalidate?: \"always\" | \"on-dirty\"\n maxAgeMs?: number\n onError?: (error: unknown, agent: string) => void\n}\n\nconst SEP = \"\\u0000\"\nexport const contextKey = (agent: string, channels: readonly string[], format: string) => [agent, [...channels].sort().join(\",\"), format].join(SEP)\nconst channelsOfKey = (key: string) => (key.split(SEP)[1] ?? \"\").split(\",\").filter(Boolean)\n\n/** O evento muda o contexto do canal? (`memory.consumed` não muda.) */\nexport function invalidatesContext(event: Pick<WebhookEvent, \"type\">): boolean {\n if (isConsumedEvent(event.type)) return false\n return event.type.startsWith(\"memory.\") || event.type === \"channel.updated\" || event.type === \"subscription.updated\" || event.type === \"settings.updated\"\n}\n\nexport function createContextCache(options: ContextCacheOptions) {\n const store = options.store ?? createMemoryContextStore()\n const format = options.format ?? \"tagged\"\n const maxAge = options.maxAgeMs ?? 5 * 60_000\n /** Canais sujos sem bloco guardado por inteiro (prefixos, `settings.updated` global): suja tudo. */\n let allDirty = false\n\n async function markChannels(channels: readonly (string | null)[]) {\n const wanted = channels.filter((channel): channel is string => Boolean(channel))\n for (const key of await store.keys()) {\n const keyChannels = channelsOfKey(key)\n const hit = !wanted.length || !keyChannels.length || wanted.some((channel) => keyChannels.includes(channel))\n if (!hit) continue\n const entry = await store.get(key)\n if (entry && !entry.dirty) await store.set(key, { ...entry, dirty: true })\n }\n }\n\n return {\n /** Webhook recebido: suja o contexto dos canais afetados (nunca por `memory.consumed`). */\n handleEvent(event: Pick<WebhookEvent, \"type\" | \"channel\">) {\n if (!invalidatesContext(event)) return\n if (!event.channel) allDirty = true\n void markChannels(event.channel ? [event.channel] : [])\n },\n async getContext(agent: string, input: { channels?: readonly string[]; focus?: string } = {}): Promise<ContextResult> {\n const channels = input.channels ?? []\n const key = contextKey(agent, channels, format)\n const cached = await store.get(key)\n const usable = cached && !input.focus\n if (usable && options.revalidate === \"on-dirty\" && !cached.dirty && !allDirty && Date.now() - cached.fetchedAt < maxAge) {\n return { block: cached.content, version: cached.version, source: \"live\", stale: false, counts: cached.counts, error: null }\n }\n try {\n const client = await options.client(agent)\n if (!client) throw new Error(`token do agente ${agent} indisponível`)\n const view = await client.context({ channels: channels.length ? [...channels] : undefined, format, focus: input.focus, ifNoneMatch: usable ? cached.version : null })\n allDirty = false\n if (view === null && cached) {\n await store.set(key, { ...cached, fetchedAt: Date.now(), dirty: false })\n return { block: cached.content, version: cached.version, source: \"live\", stale: false, counts: cached.counts, error: null }\n }\n if (!view) throw new Error(\"o Engramind respondeu sem o contexto\")\n const entry: ContextEntry = { content: typeof view.content === \"string\" ? view.content : \"\", version: view.version ?? \"\", counts: view.counts, format, fetchedAt: Date.now(), dirty: false }\n // Bloco com foco tem outra `version`: não substitui o cache do bloco estável.\n if (!input.focus) await store.set(key, entry)\n return { block: entry.content, version: entry.version, source: \"live\", stale: false, counts: entry.counts, error: null }\n } catch (error) {\n options.onError?.(error, agent)\n const reason = error instanceof Error ? error.message : String(error)\n if (cached) return { block: cached.content, version: cached.version, source: \"cache\", stale: true, counts: cached.counts, error: reason }\n return { block: \"\", version: null, source: \"none\", stale: true, counts: null, error: reason }\n }\n },\n /** Bloco guardado (sem chamar o Engramind). */\n peek: async (agent: string, channels: readonly string[] = []) => store.get(contextKey(agent, channels, format)),\n }\n}\n\nexport type ContextCache = ReturnType<typeof createContextCache>\n","/**\n * Configuração de MCP pronta para registrar o Engramind num agente: `POST /mcp` com o token **do agente**\n * e o cabeçalho `Engramind-Channels` (canais do projeto). `tools/list` já mostra só o que o token pode\n * usar; repasse ao agente só as ferramentas que a sua política permite.\n */\nexport interface EngramindMcpConfig {\n type: \"http\"\n url: string\n headers: Record<string, string>\n}\n\nexport function engramindMcpConfig(input: { baseUrl: string; agentToken: string; channels?: readonly string[] }): EngramindMcpConfig {\n const headers: Record<string, string> = { Authorization: `Bearer ${input.agentToken}` }\n if (input.channels?.length) headers[\"Engramind-Channels\"] = input.channels.join(\",\")\n return { type: \"http\", url: `${input.baseUrl.replace(/\\/+$/, \"\")}/mcp`, headers }\n}\n\n/** Ferramentas de leitura e de escrita do MCP do Engramind (para filtrar o `tools/list`). */\nexport const MCP_READ_TOOLS = [\"get_context\", \"search_memories\", \"get_memory\", \"get_hints\", \"list_memories\", \"list_categories\"] as const\nexport const MCP_WRITE_TOOLS = [\"create_memory\", \"update_memory\"] as const\n"],"mappings":"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","names":["json"]}
@@ -102,6 +102,8 @@ interface EngramindChannel {
102
102
  version: number;
103
103
  created_at: string;
104
104
  archived_at: string | null;
105
+ /** Slug do app cujo token de app criou o canal ao assinar; `null` quando foi o admin (Engramind 0.13.0+). */
106
+ created_by: string | null;
105
107
  }
106
108
  interface ChannelStats {
107
109
  channel: string;
@@ -423,8 +425,8 @@ interface ConsumerSettings {
423
425
  text_max_chars: number;
424
426
  }[];
425
427
  /**
426
- * Prazo de desuso e pesos da busca: pedidos ao Engramind para o modo app (ver
427
- * `docs/engramind-consumer-settings.md`). Até lá a lib usa os padrões (90 dias, 1/1/60).
428
+ * Prazo de desuso e pesos da busca: o Engramind 0.13.0+ sempre manda. Com versões anteriores, a lib
429
+ * usa os padrões (90 dias, 1/1/60).
428
430
  */
429
431
  consolidation?: {
430
432
  stale_after_days?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@engramind/client",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Cliente do Engramind para apps consumidores: servidor (API, proxy, webhook + SSE, contexto, ferramentas e MCP) e componentes React da rede de memórias e da configuração do app.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/errors.ts","../src/core/events.ts"],"sourcesContent":["/**\n * Erros do Engramind traduzidos para pt-BR. Decide pelo `code` estável do envelope\n * `{ error: { code, message, details } }`, nunca pela `message`. Os códigos expostos pela lib levam o\n * prefixo `ENGRAMIND_` (ex.: `ENGRAMIND_CHANNEL_SHARED`), e os erros da própria lib também\n * (`ENGRAMIND_UNREACHABLE`, `ENGRAMIND_PROXY_MASKED`, `ENGRAMIND_NOT_CONFIGURED`).\n */\n\nexport interface EngramindErrorBody {\n code: string\n message?: string\n details?: unknown\n}\n\nexport class EngramindError extends Error {\n readonly status: number\n readonly code: string\n readonly details?: unknown\n /** Segundos de `Retry-After` (429). */\n readonly retryAfter: number | null\n\n constructor(status: number, message: string, code: string, details?: unknown, retryAfter: number | null = null) {\n super(message)\n this.name = \"EngramindError\"\n this.status = status\n this.code = code\n this.details = details\n this.retryAfter = retryAfter\n }\n}\n\ntype Details = Record<string, unknown>\nconst asDetails = (details: unknown): Details => (details && typeof details === \"object\" ? (details as Details) : {})\nconst list = (value: unknown) => (Array.isArray(value) ? value.map(String).join(\", \") : \"\")\n\nconst MESSAGES: Record<string, (details: Details, retryAfter: string | null) => string> = {\n AUTH_INVALID: () => \"O Engramind recusou o token do app (ausente, inválido, expirado, revogado ou de app desativado). Peça um novo token ao gestor e reconfigure o app.\",\n SCOPE_REQUIRED: (d) =>\n `O token do app não tem permissão para esta operação${d.required_kind ? ` (exige ${String(d.required_kind)}` : \"\"}${d.required_scope ? ` com ${String(d.required_scope)})` : d.required_kind ? \")\" : \"\"}.`,\n FOREIGN_APP: () => \"Sem acesso: o registro pertence a outro app.\",\n CHANNEL_NOT_SUBSCRIBED: (d) => `Sem acesso: o token não alcança o canal${d.channel ? ` ${String(d.channel)}` : \"\"}.`,\n CHANNEL_SHARED: () => \"O canal é assinado por outro app e não pode ser arquivado por este app.\",\n LAST_ADMIN_TOKEN: () => \"A rede ficaria sem nenhum token admin ativo com write. Emita outro token admin antes.\",\n SLUG_ALREADY_EXISTS: () => \"Já existe um registro com esse slug. Escolha outro.\",\n SUBSCRIPTION_EXISTS: () => \"O agente já assina esse alvo.\",\n CATEGORY_NOT_FOUND: (d) => `Categoria fora da lista${d.valid ? ` (aceitas: ${list(d.valid)})` : \"\"}${d.index !== undefined ? `; item ${Number(d.index) + 1}` : \"\"}.`,\n BATCH_TOO_LARGE: (d) => `O lote passa do limite${d.max !== undefined ? ` (${String(d.received)} de no máximo ${String(d.max)})` : \"\"}. Divida o arquivo.`,\n IDEMPOTENCY_KEY_REQUIRED: () => \"A operação exige uma chave de idempotência.\",\n IDEMPOTENCY_KEY_CONFLICT: () => \"A chave de idempotência já foi usada com outro conteúdo.\",\n RATE_LIMITED: (_d, retryAfter) => `O Engramind limitou as requisições do app${retryAfter ? `; tente de novo em ${retryAfter} s` : \"\"}.`,\n VALIDATION_ERROR: (d) => `O Engramind recusou os dados${d.index !== undefined ? ` (item ${Number(d.index) + 1})` : \"\"}${d.field ? ` no campo ${String(d.field)}` : \"\"}.`,\n CHANNEL_NOT_ARCHIVED: () => \"Só um canal arquivado pode ser excluído: arquive antes.\",\n CHANNEL_ARCHIVED: () => \"O canal está arquivado e não recebe escrita: restaure-o antes.\",\n CHANNEL_NOT_FOUND: () => \"Canal não encontrado no Engramind.\",\n APP_NOT_FOUND: () => \"App não encontrado no Engramind.\",\n AGENT_NOT_FOUND: () => \"Agente não encontrado no Engramind.\",\n TOKEN_NOT_FOUND: () => \"Token não encontrado no Engramind.\",\n SUBSCRIPTION_NOT_FOUND: () => \"Assinatura não encontrada no Engramind.\",\n MEMORY_NOT_FOUND: () => \"Memória não encontrada no Engramind.\",\n REVISION_NOT_FOUND: () => \"A memória não tem essa versão.\",\n MEMORY_VERSION_CONFLICT: () => \"Esta memória mudou enquanto você editava. Confira a versão atual.\",\n MEMORY_DUPLICATE: (d) => `Já existe uma memória com esse texto no canal${d.existing_id ? ` (${String(d.existing_id)})` : \"\"}.`,\n MEMORY_KEY_EXISTS: () => \"Já existe uma memória com essa chave no canal.\",\n PAYLOAD_TOO_LARGE: () => \"O corpo passou do limite do Engramind.\",\n DATABASE_UNAVAILABLE: () => \"O banco do Engramind está indisponível. Tente novamente.\",\n MIGRATIONS_PENDING: () => \"O Engramind tem migrações pendentes e ainda não recebe tráfego. Tente novamente em instantes.\",\n REQUEST_TIMEOUT: () => \"O Engramind excedeu o tempo limite da requisição. Tente novamente.\",\n INTERNAL_ERROR: () => \"Falha inesperada no Engramind. Tente novamente.\",\n ROUTE_NOT_FOUND: () => \"Operação não disponível neste Engramind.\",\n}\n\n/** Status devolvido ao navegador: falha do próprio serviço vira 502 e token recusado vira 409 (reconfigurar). */\nexport function clientStatus(status: number): number {\n if (status === 401) return 409\n if (status >= 500) return 502\n return status\n}\n\n/**\n * Corpo de um 5xx trocado pelo proxy na frente do Engramind (o Cloudflare substitui o corpo das respostas\n * 5xx pela própria página/JSON): devolve o nome do erro do proxy, quando reconhecido.\n */\nexport function proxyMaskedError(text: string, json: unknown): string | null {\n if (json && typeof json === \"object\" && (\"ray_id\" in json || \"error_name\" in json || \"cloudflare_error\" in json)) {\n const name = (json as { error_name?: unknown; title?: unknown }).error_name ?? (json as { title?: unknown }).title\n return typeof name === \"string\" && name ? name : \"cloudflare\"\n }\n if (/cloudflare/i.test(text) && /ray id|cf-ray|cloudflare/i.test(text)) return \"cloudflare\"\n return null\n}\n\nconst parseRetryAfter = (value: string | null): number | null => {\n if (!value) return null\n const seconds = Number(value)\n if (Number.isFinite(seconds) && seconds >= 0) return seconds\n const date = Date.parse(value)\n return Number.isNaN(date) ? null : Math.max(0, Math.round((date - Date.now()) / 1000))\n}\n\nexport function toEngramindError(status: number, body: EngramindErrorBody | null, retryAfter: string | null = null, masked: string | null = null): EngramindError {\n if (!body && status >= 500 && masked) {\n return new EngramindError(502, `Um proxy (Cloudflare) escondeu a resposta HTTP ${status} do Engramind (${masked}). Chame a URL de origem do Engramind para ver o motivo.`, \"ENGRAMIND_PROXY_MASKED\", { status, proxyError: masked })\n }\n const code = body?.code ?? (status === 404 ? \"ROUTE_NOT_FOUND\" : `HTTP_${status}`)\n const message = MESSAGES[code]?.(asDetails(body?.details), retryAfter) ?? body?.message ?? `O Engramind respondeu HTTP ${status}.`\n return new EngramindError(clientStatus(status), message, `ENGRAMIND_${code}`, body?.details ?? undefined, parseRetryAfter(retryAfter))\n}\n\n/** Engramind fora do ar ou inalcançável. */\nexport const unreachableError = (reason: string) =>\n new EngramindError(503, `Não foi possível falar com o Engramind (${reason}). Confira a URL base e se o serviço está no ar.`, \"ENGRAMIND_UNREACHABLE\")\n\nexport const notConfiguredError = () => new EngramindError(409, \"Configure a conexão com o Engramind (URL base e token) no servidor do app.\", \"ENGRAMIND_NOT_CONFIGURED\")\n\n/** Código do Engramind (sem o prefixo) de um erro traduzido. */\nexport const engramindCodeOf = (error: unknown): string | null => (error instanceof EngramindError && error.code.startsWith(\"ENGRAMIND_\") ? error.code.slice(\"ENGRAMIND_\".length) : null)\n","/**\n * Eventos do Engramind (webhook) e o formato que a lib repassa ao navegador pelo SSE: só ids, slugs e\n * tipos — nunca texto, título, `query` ou segredo.\n */\n\nexport type WebhookEventType =\n | \"memory.created\"\n | \"memory.updated\"\n | \"memory.archived\"\n | \"memory.deleted\"\n | \"memory.consumed\"\n | \"channel.updated\"\n | \"subscription.updated\"\n | \"settings.updated\"\n | \"identities.updated\"\n\n/** Corpo do webhook (`POST <webhook_url>`), contrato do Engramind ≥ 0.12.0. */\nexport interface WebhookEvent {\n id: string\n type: WebhookEventType | (string & {})\n channel: string | null\n channel_version?: number | null\n memory_ids?: string[]\n at?: string\n keys?: string[]\n subscription?: string\n agent?: string | null\n target?: string\n app?: string | null\n consumption?: string\n operation?: \"get_context\" | \"search_memories\" | \"get_hints\" | \"get_memory\" | (string & {})\n title_ids?: string[]\n context_version?: string\n}\n\n/** Evento ao vivo entregue pelo SSE da lib (`event: engramind`). */\nexport interface LiveEvent {\n /** `id` do webhook (ausente nos eventos gerados pelo próprio app). */\n id?: string\n type: string\n channel?: string | null\n memoryIds?: string[]\n keys?: string[]\n agent?: string | null\n app?: string | null\n consumption?: string\n operation?: string\n titleIds?: string[]\n contextVersion?: string\n at?: string\n}\n\nconst ids = (value: unknown): string[] | undefined => (Array.isArray(value) ? value.filter((item): item is string => typeof item === \"string\") : undefined)\nconst str = (value: unknown): string | undefined => (typeof value === \"string\" && value ? value : undefined)\n\n/** Webhook → evento ao vivo (só os campos permitidos). */\nexport function toLiveEvent(event: WebhookEvent): LiveEvent {\n const live: LiveEvent = { id: event.id, type: event.type, channel: event.channel ?? null }\n const memoryIds = ids(event.memory_ids)\n if (memoryIds) live.memoryIds = memoryIds\n const keys = ids(event.keys)\n if (keys) live.keys = keys\n if (event.agent !== undefined) live.agent = event.agent ?? null\n if (event.app !== undefined) live.app = event.app ?? null\n const consumption = str(event.consumption)\n if (consumption) live.consumption = consumption\n const operation = str(event.operation)\n if (operation) live.operation = operation\n const titleIds = ids(event.title_ids)\n if (titleIds) live.titleIds = titleIds\n const contextVersion = str(event.context_version)\n if (contextVersion) live.contextVersion = contextVersion\n const at = str(event.at)\n if (at) live.at = at\n return live\n}\n\n/** `memory.consumed` não muda nada: nunca marca o contexto como sujo nem invalida as listas. */\nexport const isConsumedEvent = (type: string) => type === \"memory.consumed\"\n\n/** Eventos `memory.*` que mudam o contexto (todos menos `memory.consumed`). */\nexport const isMemoryChange = (type: string) => type.startsWith(\"memory.\") && !isConsumedEvent(type)\n\n/**\n * Consumo agrupado (um por `consumption`): o formato que a rede anima. `channels` são os canais que\n * entregaram memórias (um evento por canal), `delivered` idem.\n */\nexport interface ConsumptionEvent {\n /** Agente que leu (`app/agente`). */\n agent: string\n kind: \"context\" | \"search\"\n channels: string[]\n delivered: string[]\n memoryIds: string[]\n titleIds?: string[]\n version?: string\n /** Id do consumo no Engramind (`cns_…`). */\n consumption?: string\n operation?: string\n}\n\n/** Junta os eventos `memory.consumed` de uma mesma leitura (`consumption`). */\nexport function mergeConsumption(events: readonly LiveEvent[]): ConsumptionEvent | null {\n const first = events.find((event) => event.agent)\n if (!first?.agent) return null\n const channels = new Set<string>()\n const memoryIds = new Set<string>()\n const titleIds = new Set<string>()\n let version: string | undefined\n for (const event of events) {\n if (event.channel && (event.memoryIds?.length ?? 0) > 0) channels.add(event.channel)\n for (const id of event.memoryIds ?? []) memoryIds.add(id)\n for (const id of event.titleIds ?? []) titleIds.add(id)\n version = version ?? event.contextVersion\n }\n if (!memoryIds.size) return null\n const merged: ConsumptionEvent = {\n agent: first.agent,\n kind: first.operation === \"get_context\" ? \"context\" : \"search\",\n channels: [...channels],\n delivered: [...channels],\n memoryIds: [...memoryIds],\n ...(titleIds.size ? { titleIds: [...titleIds] } : {}),\n ...(version ? { version } : {}),\n ...(first.consumption ? { consumption: first.consumption } : {}),\n ...(first.operation ? { operation: first.operation } : {}),\n }\n return merged\n}\n\nexport interface ConsumptionGrouperOptions {\n /** Espera para juntar os eventos de canais da mesma leitura (padrão 400 ms). */\n windowMs?: number\n /** Quantos `consumption` já emitidos são lembrados para descartar repetições (padrão 500). */\n memory?: number\n onGroup: (event: ConsumptionEvent) => void\n}\n\n/**\n * Agrupa `memory.consumed` por `consumption`: emite **um** consumo por leitura, depois de `windowMs` sem\n * novos canais. Evento sem `agent` (leitura por token de app ou admin) é ignorado, e um `consumption` já\n * emitido não é emitido de novo (retentativa do webhook). Isomórfico (servidor e navegador).\n */\nexport function createConsumptionGrouper(options: ConsumptionGrouperOptions) {\n const windowMs = options.windowMs ?? 400\n const limit = options.memory ?? 500\n const pending = new Map<string, { events: LiveEvent[]; timer: ReturnType<typeof setTimeout> }>()\n const done: string[] = []\n const doneSet = new Set<string>()\n const flush = (key: string) => {\n const entry = pending.get(key)\n if (!entry) return\n pending.delete(key)\n doneSet.add(key)\n done.push(key)\n while (done.length > limit) doneSet.delete(done.shift()!)\n const merged = mergeConsumption(entry.events)\n if (merged) options.onGroup(merged)\n }\n return {\n push(event: LiveEvent) {\n if (!isConsumedEvent(event.type) || !event.agent) return\n const key = event.consumption ?? event.id ?? `${event.agent}:${event.at ?? \"\"}`\n if (doneSet.has(key)) return\n const entry = pending.get(key)\n if (entry) {\n clearTimeout(entry.timer)\n entry.events.push(event)\n entry.timer = setTimeout(() => flush(key), windowMs)\n } else pending.set(key, { events: [event], timer: setTimeout(() => flush(key), windowMs) })\n },\n /** Emite os grupos pendentes na hora (desmontagem, testes). */\n flushAll() {\n for (const key of [...pending.keys()]) {\n clearTimeout(pending.get(key)!.timer)\n flush(key)\n }\n },\n dispose() {\n for (const entry of pending.values()) clearTimeout(entry.timer)\n pending.clear()\n },\n }\n}\n"],"mappings":"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","names":[]}