@engramind/client 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  Todas as mudanças relevantes deste pacote. O formato segue [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/) e o projeto usa [SemVer](https://semver.org/lang/pt-BR/).
4
4
 
5
+ ## [0.2.0]
6
+
7
+ ### Adicionado
8
+
9
+ - Modo app com o Engramind 0.13.0: o painel do canal mostra **Editar** nos canais que o próprio app criou
10
+ (`EngramindChannel.created_by`), e o proxy em modo `app` repassa `PATCH channels/:channel`
11
+ (`update_channel`). Excluir de vez continua só do gestor. `canEditChannel` exportado.
12
+
13
+ ### Alterado
14
+
15
+ - Mensagem de `CHANNEL_SHARED` cobre editar e arquivar.
16
+ - `ConsumerSettings.consolidation` e `search` documentados como enviados pelo Engramind 0.13.0+ (a lib
17
+ mantém os padrões para versões anteriores).
18
+ - `settings.updated` no modo app relê `consumer-settings` (coberto por teste).
19
+
20
+ ## [0.1.1]
21
+
22
+ ### Alterado
23
+
24
+ - Publicação pelo GitHub Actions com **trusted publishing** (OIDC, sem token nem secret) e attestation de
25
+ provenance no npm. Sem mudança de código em relação à `0.1.0`.
26
+
5
27
  ## [0.1.0]
6
28
 
7
29
  ### Adicionado
package/README.md CHANGED
@@ -182,7 +182,7 @@ O webhook `memory.consumed` chega um por canal. A lib junta os eventos da mesma
182
182
 
183
183
  O agente de outro app que lê um canal visível pulsa só o canal. O consumo não invalida o contexto.
184
184
 
185
- O brilho das memórias usa `last_retrieved_at` e o prazo `consolidation.stale_after_days`. No modo app, o prazo vem de `get_consumer_settings` quando o Engramind o expõe ([pedido ao Engramind](./docs/engramind-consumer-settings.md)). Sem ele, a lib usa 90 dias ou o `staleAfterDays` do provider.
185
+ O brilho das memórias usa `last_retrieved_at` e o prazo `consolidation.stale_after_days`. No modo app, o prazo e os pesos da busca vêm de `get_consumer_settings` (Engramind 0.13.0+); o `staleAfterDays` do provider, quando informado, vence. Com Engramind anterior, a lib usa 90 dias e os pesos 1/1/60.
186
186
 
187
187
  ### Textos
188
188
 
@@ -221,12 +221,10 @@ pnpm build
221
221
 
222
222
  A GitHub Action roda TypeScript, Vitest e build em todo PR e push. A tag `vX.Y.Z` (igual à versão do `package.json`) publica no npm público por **trusted publishing**: o pacote aponta para este repositório e este arquivo de workflow em npmjs.com, e o GitHub assina com OIDC (sem token nem secret).
223
223
 
224
- ## Pendências no Engramind
224
+ ## Versão mínima do Engramind no modo app
225
225
 
226
- Algumas leituras que o modo app precisa ainda são só do admin no contrato atual:
227
-
228
- - o prazo de desuso e os pesos da busca;
229
- - o estado do webhook do próprio app;
230
- - a edição dos canais que o app criou.
231
-
232
- A tarefa está descrita em [`docs/engramind-consumer-settings.md`](./docs/engramind-consumer-settings.md).
226
+ Com o Engramind 0.13.0 ou superior, o token de app lê o prazo de desuso e os pesos da busca
227
+ (`get_consumer_settings`), o estado do webhook do próprio app (`get_app`) e edita os canais que o app
228
+ criou (`update_channel`; o painel do canal mostra **Editar** só quando `created_by` é o app). Com versões
229
+ anteriores, a lib usa os padrões e o Engramind recusa a edição. O pedido original está em
230
+ [`docs/engramind-consumer-settings.md`](./docs/engramind-consumer-settings.md).
package/dist/ai.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Tool } from 'ai';
2
- import { E as EngramindClient } from './client-Q0QFU2A6.js';
3
- import './types-BnU5iHwB.js';
2
+ import { E as EngramindClient } from './client-Dzm6Fx6_.js';
3
+ import './types-BxAxPvmk.js';
4
4
 
5
5
  /**
6
6
  * `@engramind/client/ai`: ferramentas prontas para o Vercel AI SDK (`ai` ≥ 5), chamadas com o token **do
@@ -21,7 +21,7 @@ var MESSAGES = {
21
21
  SCOPE_REQUIRED: (d) => `O token do app n\xE3o tem permiss\xE3o para esta opera\xE7\xE3o${d.required_kind ? ` (exige ${String(d.required_kind)}` : ""}${d.required_scope ? ` com ${String(d.required_scope)})` : d.required_kind ? ")" : ""}.`,
22
22
  FOREIGN_APP: () => "Sem acesso: o registro pertence a outro app.",
23
23
  CHANNEL_NOT_SUBSCRIBED: (d) => `Sem acesso: o token n\xE3o alcan\xE7a o canal${d.channel ? ` ${String(d.channel)}` : ""}.`,
24
- CHANNEL_SHARED: () => "O canal \xE9 assinado por outro app e n\xE3o pode ser arquivado por este app.",
24
+ CHANNEL_SHARED: () => "O canal \xE9 assinado por outro app; s\xF3 d\xE1 para edit\xE1-lo ou arquiv\xE1-lo enquanto s\xF3 o app que o criou o assina.",
25
25
  LAST_ADMIN_TOKEN: () => "A rede ficaria sem nenhum token admin ativo com write. Emita outro token admin antes.",
26
26
  SLUG_ALREADY_EXISTS: () => "J\xE1 existe um registro com esse slug. Escolha outro.",
27
27
  SUBSCRIPTION_EXISTS: () => "O agente j\xE1 assina esse alvo.",
@@ -190,4 +190,4 @@ export {
190
190
  mergeConsumption,
191
191
  createConsumptionGrouper
192
192
  };
193
- //# sourceMappingURL=chunk-THVEWMKQ.js.map
193
+ //# sourceMappingURL=chunk-4Z5NG6ZM.js.map
@@ -0,0 +1 @@
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; só dá para editá-lo ou arquivá-lo enquanto só o app que o criou o assina.\",\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":[]}
@@ -1,4 +1,4 @@
1
- import { W as WhoAmI, g as ConsumerSettings, C as Category, E as EngramindAgent, p as EngramindSubscription, k as EngramindChannel, n as EngramindMemory, u as MemoryWrite, M as MemoryRevision, I as ImportResult, S as SearchResults, l as EngramindHint, h as ContextView, q as EngramindToken, s as IssuedToken, j as EngramindApp } from './types-BnU5iHwB.js';
1
+ import { W as WhoAmI, g as ConsumerSettings, C as Category, E as EngramindAgent, p as EngramindSubscription, k as EngramindChannel, n as EngramindMemory, u as MemoryWrite, M as MemoryRevision, I as ImportResult, S as SearchResults, l as EngramindHint, h as ContextView, q as EngramindToken, s as IssuedToken, j as EngramindApp } from './types-BxAxPvmk.js';
2
2
 
3
3
  interface ChannelQuery {
4
4
  include_archived?: boolean;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as AuditEvent, a as Author, C as Category, b as ChannelSettingMeta, c as ChannelSettingsView, d as ChannelStats, e as ConsolidationPlan, f as ConsolidationRun, g as ConsumerSettings, h as ContextView, i as CursorPage, E as EngramindAgent, j as EngramindApp, k as EngramindChannel, l as EngramindHint, m as EngramindJob, n as EngramindMemory, o as EngramindStatus, p as EngramindSubscription, q as EngramindToken, r as EngramindTokenKind, I as ImportResult, s as IssuedToken, L as LlmCall, t as LocalModelStatus, M as MemoryRevision, u as MemoryWrite, R as Ref, S as SearchResults, v as SettingMeta, U as UsageReport, W as WhoAmI } from './types-BnU5iHwB.js';
1
+ export { A as AuditEvent, a as Author, C as Category, b as ChannelSettingMeta, c as ChannelSettingsView, d as ChannelStats, e as ConsolidationPlan, f as ConsolidationRun, g as ConsumerSettings, h as ContextView, i as CursorPage, E as EngramindAgent, j as EngramindApp, k as EngramindChannel, l as EngramindHint, m as EngramindJob, n as EngramindMemory, o as EngramindStatus, p as EngramindSubscription, q as EngramindToken, r as EngramindTokenKind, I as ImportResult, s as IssuedToken, L as LlmCall, t as LocalModelStatus, M as MemoryRevision, u as MemoryWrite, R as Ref, S as SearchResults, v as SettingMeta, U as UsageReport, W as WhoAmI } from './types-BxAxPvmk.js';
2
2
  export { C as ConsumptionEvent, a as ConsumptionGrouperOptions, E as EngramindError, b as EngramindErrorBody, L as LiveEvent, W as WebhookEvent, 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';
3
3
  import { z } from 'zod';
4
4
 
package/dist/index.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/core/constants.ts
17
17
  var ENGRAMIND_QUERY_KEY = "engramind";
package/dist/react.d.ts CHANGED
@@ -783,6 +783,8 @@ interface EngramindChannel {
783
783
  version: number;
784
784
  created_at: string;
785
785
  archived_at: string | null;
786
+ /** Slug do app cujo token de app criou o canal ao assinar; `null` quando foi o admin (Engramind 0.13.0+). */
787
+ created_by: string | null;
786
788
  }
787
789
  interface EngramindSubscription {
788
790
  id: string;
@@ -854,8 +856,8 @@ interface ConsumerSettings {
854
856
  text_max_chars: number;
855
857
  }[];
856
858
  /**
857
- * Prazo de desuso e pesos da busca: pedidos ao Engramind para o modo app (ver
858
- * `docs/engramind-consumer-settings.md`). Até lá a lib usa os padrões (90 dias, 1/1/60).
859
+ * Prazo de desuso e pesos da busca: o Engramind 0.13.0+ sempre manda. Com versões anteriores, a lib
860
+ * usa os padrões (90 dias, 1/1/60).
859
861
  */
860
862
  consolidation?: {
861
863
  stale_after_days?: number;
@@ -867,12 +869,15 @@ interface ConsumerSettings {
867
869
  };
868
870
  }
869
871
 
872
+ /** Editar: o gestor sempre; o app só nos canais que criou (sem slug conhecido, não mostra). */
873
+ declare function canEditChannel(manager: boolean, channel: Pick<EngramindChannel, "created_by">, ownApp: string | null): boolean;
870
874
  /**
871
875
  * Painel lateral do canal (menu "⋯" ou botão direito no node): Editar (slug imutável), Assinantes,
872
876
  * o conteúdo extra do app (`slots.channelDetail`, ex.: as configurações do canal no gestor) e
873
- * Arquivar / Restaurar / Excluir de vez (só arquivado), com confirmação. No modo app, o Engramind só
874
- * deixa o token de app arquivar e restaurar os canais que ele criou (`409 CHANNEL_SHARED` nos outros):
875
- * editar e excluir de vez são do gestor e não aparecem.
877
+ * Arquivar / Restaurar / Excluir de vez (só arquivado), com confirmação. No modo app, o Engramind
878
+ * (0.13.0+) deixa o token de app editar, arquivar e restaurar só os canais que ele criou
879
+ * (`created_by`; `403 FOREIGN_APP` / `409 CHANNEL_SHARED` nos outros): Editar só aparece nesses canais, e
880
+ * Excluir de vez continua só do gestor.
876
881
  */
877
882
  declare function ChannelPanel({ channel, onOpenChange, onDeleted }: {
878
883
  channel: EngramindChannel | null;
@@ -1684,4 +1689,4 @@ declare function PathTraveler({ path, reverse, delayMs, durationMs, radius, clas
1684
1689
  onDone?: () => void;
1685
1690
  }): react.JSX.Element;
1686
1691
 
1687
- export { AGENT_NODE_SIZE_CLASS, AGENT_WRITE_WINDOW_MS, APP_PARAM, type AgentWrite, type AppLink, AppSettingsSkeleton, type AppSettingsTab, CONSUMPTION_GROUP_WINDOW_MS, CONSUMPTION_REFRESH_DELAY_MS, ChannelDetail, ChannelDisplayName, type ChannelLabel, ChannelLabelsProvider, ChannelName, ChannelPanel, ChannelsCard, type ConfigureTarget, type ConsumptionEvent, DEFAULT_MEMORY_TEXT_LIMITS, DEFAULT_MESSAGES, DEFAULT_NETWORK_FILTERS, DEFAULT_OVERVIEW_LAYOUT, DEFAULT_SEARCH_FUSION, DEFAULT_STALE_AFTER_DAYS, EngramindAppSettings, type EngramindConfig, type EngramindDataSource, type EngramindHrefs, type EngramindMode, EngramindNetwork, EngramindProvider, type EngramindProviderProps, type EngramindRouter, FLOW_BACKGROUND_PROPS, FLOW_EDGE_PRIMARY_STROKE, FLOW_SURFACE_CLASS, FLOW_THEME_STYLE, type FooterContext, HelpBadge, type HttpDataSourceOptions, type HttpMethod, LIVE_BUMP_PX, type LiveEffects, type LiveSubscription, type LocalRemovals, MEMORY_CARD_HEIGHT, MEMORY_CARD_MORPH_S, MEMORY_CARD_WIDTH, MEMORY_STATUS_OPTIONS, MOBILE_FIT_MIN_ZOOM, type MemoryCardActions, MemoryEditor, MemoryEditorDialog, type MemoryEditorTarget, type MemoryStatus, type MemoryTextLimits, MemoryUniverse, type MemoryUniverseProps, type MessageKey, type Messages, NETWORK_CANVAS_CLASS, NETWORK_CANVAS_COMPACT_CLASS, NETWORK_FILTERS_STORAGE_KEY, NETWORK_FILTER_KEYS, NETWORK_FLOW_THEME, NETWORK_SKELETON_AREA_CLASS, NETWORK_STATS_CLASS, NETWORK_STATS_TOGGLE_CLASS, NETWORK_TOUCH_BUTTON_CLASS, type NavigationAdapter, type NetworkAgent, type NetworkChannel, type NetworkFilterChip, type NetworkFilters, NetworkFooter, type NetworkLink, type NetworkMemory, NetworkSkeleton, type Notify, type NotifyKind, ORBIT_STROKE_WIDTH, OVERVIEW_AGENT_BOX, OVERVIEW_CHANNEL_BOX, OVERVIEW_FIT_MARGIN, OVERVIEW_LAYOUT_LABEL, OVERVIEW_LAYOUT_MODES, OVERVIEW_LINK_BEND, OVERVIEW_PADDING, OVERVIEW_SCALE_MAX, OVERVIEW_SCALE_MIN, type OverviewLayout, type OverviewLayoutInput, type OverviewLayoutMode, PARTICLE_MAX_PX, PARTICLE_MIN_PX, PINNED_MIN_BRIGHTNESS, PRIMARY_CHANNEL_COLOR, type Params$1 as Params, type PartialMessages, type ParticleVisual, PathTraveler, type Point, QueryBoundary, type Rect, type RenderLinkProps, SEARCH_SINGLE_BRANCH_TOP, STALE_AFTER_DAYS_KEY, type SearchFusion, Section, SubscribersCard, type SubscriptionInput, type SubscriptionMode, type Travel, UNIVERSE_FIRST_RADIUS, UNIVERSE_PARAM, UNIVERSE_RING_GAP, UNIVERSE_SLOT, type UniverseCenter, type UniverseRef, type UniverseStats, WebhookCard, activeNetworkFilterChips, agentDisplayName, agentReach, agentWritesOf, apiErrorCode, apiErrorMessage, apiErrorStatus, appChannels, appLinkEdgeId, appLinks, appOfAgent, appReach, areaKey, areasForEvent, buildParticles, channelBatches, channelColors, channelDisplayHint, channelDisplayName, channelNameParts, channelsWithMemories, chartToken, clampCardRect, clearDistance, clearNetworkFilters, consumerSearchFusion, createHttpDataSource, engramindGet, engramindKey, engramindSend, errorFromResponse, firstSentence, formatUniverse, generateWebhookSecret, hasActiveFilters, hasMemoryFilters, historyNavigation, isAuthoredByCenter, isConsumptionEvent, isNoAccessError, lastRealUseOf, lastUseOf, layoutOverview, layoutUniverse, matchesFilters, memoryCardSize, memoryStatusLabel, memoryTextSchema, memoryUses, mergeMessages, minZoomCenter, needsReconnect, networkFiltersSchema, networkHref, overviewInitialBounds, overviewLinkMidpoint, overviewLinkPath, overviewScale, parseUniverse, particleBrightness, particleColor, particleSize, pickChannelColors, relevantSearchIds, repelOffset, resolveStaleAfterDays, route, searchFusionOf, splitChannelSlug, stableHash, subscribeLive, subscriptionLinks, subscriptionReaches, toNetworkMemory, universeChannelColors, universeRingCapacity, universeRingCounts, universeStats, useChannelLabels, useEngramindConfig, useEngramindConsumption, useEngramindList, useEngramindMutation, useEngramindNetwork, useEngramindQuery, useEngramindRealtime, useIsManager, useLiveEffects, useLocalRemovals, useNetworkSearch, useOwnApp, useT };
1692
+ export { AGENT_NODE_SIZE_CLASS, AGENT_WRITE_WINDOW_MS, APP_PARAM, type AgentWrite, type AppLink, AppSettingsSkeleton, type AppSettingsTab, CONSUMPTION_GROUP_WINDOW_MS, CONSUMPTION_REFRESH_DELAY_MS, ChannelDetail, ChannelDisplayName, type ChannelLabel, ChannelLabelsProvider, ChannelName, ChannelPanel, ChannelsCard, type ConfigureTarget, type ConsumptionEvent, DEFAULT_MEMORY_TEXT_LIMITS, DEFAULT_MESSAGES, DEFAULT_NETWORK_FILTERS, DEFAULT_OVERVIEW_LAYOUT, DEFAULT_SEARCH_FUSION, DEFAULT_STALE_AFTER_DAYS, EngramindAppSettings, type EngramindConfig, type EngramindDataSource, type EngramindHrefs, type EngramindMode, EngramindNetwork, EngramindProvider, type EngramindProviderProps, type EngramindRouter, FLOW_BACKGROUND_PROPS, FLOW_EDGE_PRIMARY_STROKE, FLOW_SURFACE_CLASS, FLOW_THEME_STYLE, type FooterContext, HelpBadge, type HttpDataSourceOptions, type HttpMethod, LIVE_BUMP_PX, type LiveEffects, type LiveSubscription, type LocalRemovals, MEMORY_CARD_HEIGHT, MEMORY_CARD_MORPH_S, MEMORY_CARD_WIDTH, MEMORY_STATUS_OPTIONS, MOBILE_FIT_MIN_ZOOM, type MemoryCardActions, MemoryEditor, MemoryEditorDialog, type MemoryEditorTarget, type MemoryStatus, type MemoryTextLimits, MemoryUniverse, type MemoryUniverseProps, type MessageKey, type Messages, NETWORK_CANVAS_CLASS, NETWORK_CANVAS_COMPACT_CLASS, NETWORK_FILTERS_STORAGE_KEY, NETWORK_FILTER_KEYS, NETWORK_FLOW_THEME, NETWORK_SKELETON_AREA_CLASS, NETWORK_STATS_CLASS, NETWORK_STATS_TOGGLE_CLASS, NETWORK_TOUCH_BUTTON_CLASS, type NavigationAdapter, type NetworkAgent, type NetworkChannel, type NetworkFilterChip, type NetworkFilters, NetworkFooter, type NetworkLink, type NetworkMemory, NetworkSkeleton, type Notify, type NotifyKind, ORBIT_STROKE_WIDTH, OVERVIEW_AGENT_BOX, OVERVIEW_CHANNEL_BOX, OVERVIEW_FIT_MARGIN, OVERVIEW_LAYOUT_LABEL, OVERVIEW_LAYOUT_MODES, OVERVIEW_LINK_BEND, OVERVIEW_PADDING, OVERVIEW_SCALE_MAX, OVERVIEW_SCALE_MIN, type OverviewLayout, type OverviewLayoutInput, type OverviewLayoutMode, PARTICLE_MAX_PX, PARTICLE_MIN_PX, PINNED_MIN_BRIGHTNESS, PRIMARY_CHANNEL_COLOR, type Params$1 as Params, type PartialMessages, type ParticleVisual, PathTraveler, type Point, QueryBoundary, type Rect, type RenderLinkProps, SEARCH_SINGLE_BRANCH_TOP, STALE_AFTER_DAYS_KEY, type SearchFusion, Section, SubscribersCard, type SubscriptionInput, type SubscriptionMode, type Travel, UNIVERSE_FIRST_RADIUS, UNIVERSE_PARAM, UNIVERSE_RING_GAP, UNIVERSE_SLOT, type UniverseCenter, type UniverseRef, type UniverseStats, WebhookCard, activeNetworkFilterChips, agentDisplayName, agentReach, agentWritesOf, apiErrorCode, apiErrorMessage, apiErrorStatus, appChannels, appLinkEdgeId, appLinks, appOfAgent, appReach, areaKey, areasForEvent, buildParticles, canEditChannel, channelBatches, channelColors, channelDisplayHint, channelDisplayName, channelNameParts, channelsWithMemories, chartToken, clampCardRect, clearDistance, clearNetworkFilters, consumerSearchFusion, createHttpDataSource, engramindGet, engramindKey, engramindSend, errorFromResponse, firstSentence, formatUniverse, generateWebhookSecret, hasActiveFilters, hasMemoryFilters, historyNavigation, isAuthoredByCenter, isConsumptionEvent, isNoAccessError, lastRealUseOf, lastUseOf, layoutOverview, layoutUniverse, matchesFilters, memoryCardSize, memoryStatusLabel, memoryTextSchema, memoryUses, mergeMessages, minZoomCenter, needsReconnect, networkFiltersSchema, networkHref, overviewInitialBounds, overviewLinkMidpoint, overviewLinkPath, overviewScale, parseUniverse, particleBrightness, particleColor, particleSize, pickChannelColors, relevantSearchIds, repelOffset, resolveStaleAfterDays, route, searchFusionOf, splitChannelSlug, stableHash, subscribeLive, subscriptionLinks, subscriptionReaches, toNetworkMemory, universeChannelColors, universeRingCapacity, universeRingCounts, universeStats, useChannelLabels, useEngramindConfig, useEngramindConsumption, useEngramindList, useEngramindMutation, useEngramindNetwork, useEngramindQuery, useEngramindRealtime, useIsManager, useLiveEffects, useLocalRemovals, useNetworkSearch, useOwnApp, useT };
package/dist/react.js CHANGED
@@ -2838,6 +2838,9 @@ function ChannelName({ slug, size = "node", className, titleClassName }) {
2838
2838
 
2839
2839
  // src/react/engramind/components/network/ChannelPanel.tsx
2840
2840
  import { Fragment as Fragment3, jsx as jsx34, jsxs as jsxs20 } from "react/jsx-runtime";
2841
+ function canEditChannel(manager, channel, ownApp) {
2842
+ return manager || ownApp !== null && channel.created_by === ownApp;
2843
+ }
2841
2844
  var CONFIRM = {
2842
2845
  archive: { title: (slug) => `Arquivar o canal ${slug}?`, description: "Sai do contexto, da busca e das dicas e deixa de receber escrita. Restaurar desfaz.", label: "Arquivar" },
2843
2846
  delete: { title: (slug) => `Excluir de vez o canal ${slug}?`, description: "Saem junto as mem\xF3rias (com as revis\xF5es) e as assinaturas exatas. N\xE3o d\xE1 para desfazer.", label: "Excluir de vez" }
@@ -2852,6 +2855,8 @@ function ChannelPanel({ channel, onOpenChange, onDeleted }) {
2852
2855
  const archived = Boolean(channel?.archived_at);
2853
2856
  const { mode, hrefs, slots } = useEngramindConfig();
2854
2857
  const manager = mode === "manager";
2858
+ const own = useOwnApp();
2859
+ const canEdit = channel !== null && canEditChannel(manager, channel, own.slug);
2855
2860
  const detailHref = channel && hrefs.channel ? hrefs.channel(channel.slug) : null;
2856
2861
  return /* @__PURE__ */ jsxs20(Fragment3, { children: [
2857
2862
  /* @__PURE__ */ jsx34(Sheet, { open: channel !== null, onOpenChange, children: /* @__PURE__ */ jsx34(SheetContent, { side: "right", className: "flex w-full flex-col gap-4 overflow-y-auto sm:max-w-2xl", children: channel && /* @__PURE__ */ jsxs20(Fragment3, { children: [
@@ -2864,7 +2869,7 @@ function ChannelPanel({ channel, onOpenChange, onDeleted }) {
2864
2869
  ] })
2865
2870
  ] }),
2866
2871
  /* @__PURE__ */ jsxs20("div", { className: "flex flex-wrap gap-2", children: [
2867
- manager && /* @__PURE__ */ jsxs20(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setEditing(true), children: [
2872
+ canEdit && /* @__PURE__ */ jsxs20(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setEditing(true), children: [
2868
2873
  /* @__PURE__ */ jsx34(Pencil2, { className: "mr-1.5 h-4 w-4", "aria-hidden": true }),
2869
2874
  " Editar"
2870
2875
  ] }),
@@ -7082,6 +7087,7 @@ export {
7082
7087
  areaKey,
7083
7088
  areasForEvent,
7084
7089
  buildParticles,
7090
+ canEditChannel,
7085
7091
  channelBatches,
7086
7092
  channelColors,
7087
7093
  channelDisplayHint,