sapiens-mcp 1.82.0 → 1.82.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -49,7 +49,7 @@ O Claude avisa o custo antes de gastar, e geração que falha é estornada. Publ
49
49
 
50
50
  ## Gerar com a sua chave (0 Sinapse)
51
51
 
52
- Tem crédito na fal, na Kie, na WaveSpeed ou no Replicate? Ponha a chave no `env` do servidor, no config do seu cliente (só as que você tem), e a `sapiens_pontes` gera no provedor com ela e traz a peça pro seu acervo. Você paga o preço do provedor; a casa cobra zero em cima.
52
+ Tem crédito na fal, na Kie, na WaveSpeed, no Replicate ou na OpenRouter? Ponha a chave no `env` do servidor, no config do seu cliente (só as que você tem), e a `sapiens_pontes` gera no provedor com ela e traz a peça pro seu acervo. Você paga o preço do provedor; a casa cobra zero em cima.
53
53
 
54
54
  ```json
55
55
  {
@@ -61,7 +61,8 @@ Tem crédito na fal, na Kie, na WaveSpeed ou no Replicate? Ponha a chave no `env
61
61
  "FAL_KEY": "cole-aqui-a-chave-da-fal",
62
62
  "KIE_API_KEY": "cole-aqui-a-chave-da-kie",
63
63
  "WAVESPEED_API_KEY": "cole-aqui-a-chave-da-wavespeed",
64
- "REPLICATE_API_TOKEN": "cole-aqui-o-token-do-replicate"
64
+ "REPLICATE_API_TOKEN": "cole-aqui-o-token-do-replicate",
65
+ "OPENROUTER_API_KEY": "cole-aqui-a-chave-da-openrouter"
65
66
  }
66
67
  }
67
68
  }
@@ -83,7 +84,7 @@ A chave fica na sua máquina e sai dela só pro provedor: não vai em argumento,
83
84
 
84
85
  ## Privacidade
85
86
 
86
- O servidor conversa com o backend público do Sapiens (Convex) e, só quando você gera com a sua chave, com o provedor dela (fal, Kie, WaveSpeed ou Replicate), direto da sua máquina. Sua identidade vem sempre do token de login, nunca de parâmetros soltos. Cada conta só mexe no que é dela.
87
+ O servidor conversa com o backend público do Sapiens (Convex) e, só quando você gera com a sua chave, com o provedor dela (fal, Kie, WaveSpeed, Replicate ou OpenRouter), direto da sua máquina. Sua identidade vem sempre do token de login, nunca de parâmetros soltos. Cada conta só mexe no que é dela.
87
88
 
88
89
  ## Sobre o Sapiens Sintéticos
89
90
 
package/dist/canal.js CHANGED
@@ -14,11 +14,11 @@ export const ENDERECO_SUAS_CHAVES = "https://www.sapiensinteticos.com/conectar-c
14
14
  export function ondeFicaAChave() {
15
15
  switch (CANAL) {
16
16
  case "mcpb":
17
- return "No Claude Desktop: Configurações, Extensões, Sapiens Sintéticos. Os campos de chave (fal, Kie, WaveSpeed, Replicate) ficam ali, só os que você tem, e o que você colar fica guardado no seu computador.";
17
+ return "No Claude Desktop: Configurações, Extensões, Sapiens Sintéticos. Os campos de chave (fal, Kie, WaveSpeed, Replicate, OpenRouter) ficam ali, só os que você tem, e o que você colar fica guardado no seu computador.";
18
18
  case "plugin":
19
- return 'No Claude Code: /plugin, aba Installed, Sapiens Sintéticos, "Configure options". Os campos de chave (fal, Kie, WaveSpeed, Replicate) ficam ali, só os que você tem, e vão pro cofre do sistema; depois, /reload-plugins.';
19
+ return 'No Claude Code: /plugin, aba Installed, Sapiens Sintéticos, "Configure options". Os campos de chave (fal, Kie, WaveSpeed, Replicate, OpenRouter) ficam ali, só os que você tem, e vão pro cofre do sistema; depois, /reload-plugins.';
20
20
  default:
21
- return `No config do cliente MCP, no bloco do sapiens, só as que você tem: "env": { "FAL_KEY": "...", "KIE_API_KEY": "...", "WAVESPEED_API_KEY": "...", "REPLICATE_API_TOKEN": "..." }, e reinicie o cliente. No Claude Code o plugin põe a chave num campo, e no Claude Desktop a extensão (.mcpb), sem editar config. Passo a passo: ${ENDERECO_SUAS_CHAVES}`;
21
+ return `No config do cliente MCP, no bloco do sapiens, só as que você tem: "env": { "FAL_KEY": "...", "KIE_API_KEY": "...", "WAVESPEED_API_KEY": "...", "REPLICATE_API_TOKEN": "...", "OPENROUTER_API_KEY": "..." }, e reinicie o cliente. No Claude Code o plugin põe a chave num campo, e no Claude Desktop a extensão (.mcpb), sem editar config. Passo a passo: ${ENDERECO_SUAS_CHAVES}`;
22
22
  }
23
23
  }
24
24
  /** Como sair de uma versão atrasada, no canal deste processo. */
package/dist/registry.js CHANGED
@@ -63,7 +63,7 @@ export const TOOLS = {
63
63
  handler: pipeline,
64
64
  },
65
65
  sapiens_image: {
66
- description: "Operações de imagem via Sapiens (Gemini, GPT Image 2 e 2.5, Grok, Seedream/ByteDance, Veo). Sub-actions: 'generate' (gera imagem completa imediato — prompt+model+aspectRatio+size; suporta mode=edit/variation e MULTI-REFERÊNCIA: combine até 4 imagens como referência numa geração só, igual ao modal 'Selecionar Referência' do web — via referenceImageUrls (sua galeria + Acervo + personagens públicos de sapiens_character) e/ou sourceImageIds (ids da sua galeria); refs valem pros modelos robustos nano-banana-2/gpt-image-2-*/grok-2-image*/seedream-*), 'request_generation' (cria APENAS row pendente em generatedImages + debita créditos — pra modelos sapiens-video-* ANTES de sapiens_shorts/sapiens_video; whitelist, rate limit 3/min), 'compose' (combina persona+screen via Gemini pra app-demo Shorts; 25 sinapses, rate limit 10/min). generate=image one-shot, request_generation=criar row video, compose=montar start frame app-demo. TEMPLATE: passe templateSlug numa generate pra usar um super-prompt travado da casa — o `prompt` vira só a CENA (quem + pose + objeto-conceito) e o template embrulha estilo+fundo+enquadramento+ref de traço. 'retrato-sapiens-v1' = retrato editorial cartoon de um personagem no grid verde Sapiens (mesma 'mão' dos artigos); sem ref própria, injeta a Helen como âncora de traço (passar referenceImageUrls troca quem aparece). Mutuamente exclusivo com brandSlug. Sub-action 'models' (sem custo, sem login): lista o catálogo vivo (modelos ativos + preço cobrado em 1K com override admin + resolutionAdders, o adder por resolução já clampado ao teto de cada motor + maxResolution + se aceita referência) pra descobrir modelo/preço em vez de chutar. O preço de uma geração é priceSinapses + resolutionAdders[size], então 2K/4K custam MAIS que o número base: nunca cite o base como se fosse o total quando o size não for 1K. NOTA: generate é SÍNCRONA e cobra ao concluir; modelo pesado (Pro, gpt-image-2-high, Grok quality, 2K/4K) cai na REGRA DO TIMEOUT (cheque sapiens_gallery action=list antes de repetir, evita cobrança dupla).",
66
+ description: "Operações de imagem via Sapiens (Gemini, GPT Image 2 e 2.5, Grok, Seedream/ByteDance). Sub-actions: 'generate' (gera imagem completa imediato — prompt+model+aspectRatio+size; suporta mode=edit/variation e MULTI-REFERÊNCIA: combine até 4 imagens como referência numa geração só, igual ao modal 'Selecionar Referência' do web — via referenceImageUrls (sua galeria + Acervo + personagens públicos de sapiens_character) e/ou sourceImageIds (ids da sua galeria); refs valem pros modelos robustos nano-banana-2/gpt-image-2-*/grok-2-image*/seedream-*), 'compose' (combina persona+screen via Gemini pra app-demo Shorts; 25 sinapses, rate limit 10/min). generate=image one-shot, compose=montar start frame app-demo. 'request_generation' é legado e recusa sem cobrar: vídeo nasce inteiro em sapiens_video action=create. TEMPLATE: passe templateSlug numa generate pra usar um super-prompt travado da casa — o `prompt` vira só a CENA (quem + pose + objeto-conceito) e o template embrulha estilo+fundo+enquadramento+ref de traço. 'retrato-sapiens-v1' = retrato editorial cartoon de um personagem no grid verde Sapiens (mesma 'mão' dos artigos); sem ref própria, injeta a Helen como âncora de traço (passar referenceImageUrls troca quem aparece). Mutuamente exclusivo com brandSlug. Sub-action 'models' (sem custo, sem login): lista o catálogo vivo (modelos ativos + preço cobrado em 1K com override admin + resolutionAdders, o adder por resolução já clampado ao teto de cada motor + maxResolution + se aceita referência) pra descobrir modelo/preço em vez de chutar. O preço de uma geração é priceSinapses + resolutionAdders[size], então 2K/4K custam MAIS que o número base: nunca cite o base como se fosse o total quando o size não for 1K. NOTA: generate é SÍNCRONA e cobra ao concluir; modelo pesado (Pro, gpt-image-2-high, Grok quality, 2K/4K) cai na REGRA DO TIMEOUT (cheque sapiens_gallery action=list antes de repetir, evita cobrança dupla).",
67
67
  schema: imageSchema,
68
68
  handler: image,
69
69
  },
@@ -88,7 +88,7 @@ export const TOOLS = {
88
88
  handler: gallery,
89
89
  },
90
90
  sapiens_pontes: {
91
- description: "GERAR COM A CHAVE DA PESSOA (só no MCP instalado): a outra porta ao lado da Sinapse. Chama o provedor com a chave que mora no ambiente DESTA máquina (o campo do plugin ou da extensão, ou o env do config do cliente MCP: FAL_KEY, KIE_API_KEY, WAVESPEED_API_KEY, REPLICATE_API_TOKEN) e traz a peça pro acervo como nativa, custo 0 em Sinapses: a pessoa paga o preço do provedor, sem margem da casa. A chave NUNCA vai em arg, prompt ou chat, nunca volta no resultado e nunca passa pelo Sapiens: a tool lê do ambiente e o pedido sai daqui direto pro provedor. No conector remoto (claude.ai, ChatGPT) esta tool não existe; lá é Sinapse (sapiens_image, sapiens_video) ou o harness da pessoa + sapiens_gallery action=ingest. Sub-actions: 'balcoes' (quais chaves estão configuradas, pelo NOME, e como configurar; grátis, sem rede), 'gerar' (provedor 'fal', 'kie', 'wavespeed' ou 'replicate' + modelo = o slug NO PROVEDOR, igual à página do modelo (fal: o id do endpoint, ex 'fal-ai/flux/dev'; Kie: o 'model' do Market; WaveSpeed: o slug da página, ex 'wavespeed-ai/flux-2-klein-9b/text-to-image'; Replicate: 'dono/nome', ou 'dono/nome:<versão>' pra modelo de comunidade) + input = o corpo que a doc do provedor descreve pra esse modelo, com o prompt DENTRO. COBRA NA CONTA DA PESSOA no provedor: confirme modelo e custo com ela antes. O job fica gravado nesta máquina ANTES de esperar o render, e o pedido idêntico em 30 minutos devolve o job que já existe em vez de cobrar de novo (forcar=true repete de propósito). Espera até aguardarSegundos (padrão 45) e, pronto, ingere sozinho com characterId, referenceImageIds, prompt verbatim e o custo quando o provedor diz), 'status' (jobId: consulta o provedor e, se terminou, traz pro acervo; é o que se chama depois de um gerar que voltou 'rodando', NUNCA gerar de novo), 'jobs' (os jobs desta máquina, sem rede). A FICHA DO PERSONAGEM entra no input: pegue passportPrompt e mainImageUrl/imageUrls em sapiens_character action=get, ponha o descritor no começo do prompt e as imagens no campo de referência que o modelo aceita (image_url, image_urls, conforme a doc dele); o bloco NEGATIVE do passaporte vai no campo negative_prompt quando o modelo tem um. O prompt passa pelo piso de idade da casa antes de sair (18 é o piso). Peça de fal, WaveSpeed ou Replicate entra com o carimbo +18 por classe (peso aberto), igual ao ingest manual; Kie entra sem. Passo a passo na skill 'pontes'.",
91
+ description: "GERAR COM A CHAVE DA PESSOA (só no MCP instalado): a outra porta ao lado da Sinapse. Chama o provedor com a chave que mora no ambiente DESTA máquina (o campo do plugin ou da extensão, ou o env do config do cliente MCP: FAL_KEY, KIE_API_KEY, WAVESPEED_API_KEY, REPLICATE_API_TOKEN, OPENROUTER_API_KEY) e traz a peça pro acervo como nativa, custo 0 em Sinapses: a pessoa paga o preço do provedor, sem margem da casa. A chave NUNCA vai em arg, prompt ou chat, nunca volta no resultado e nunca passa pelo Sapiens: a tool lê do ambiente e o pedido sai daqui direto pro provedor. No conector remoto (claude.ai, ChatGPT) esta tool não existe; lá é Sinapse (sapiens_image, sapiens_video) ou o harness da pessoa + sapiens_gallery action=ingest. Sub-actions: 'balcoes' (quais chaves estão configuradas, pelo NOME, e como configurar; grátis, sem rede), 'gerar' (provedor 'fal', 'kie', 'wavespeed', 'replicate' ou 'openrouter' + modelo = o slug NO PROVEDOR, igual à página do modelo (fal: o id do endpoint, ex 'fal-ai/flux/dev'; Kie: o 'model' do Market; WaveSpeed: o slug da página, ex 'wavespeed-ai/flux-2-klein-9b/text-to-image'; Replicate: 'dono/nome', ou 'dono/nome:<versão>' pra modelo de comunidade) + input = o corpo que a doc do provedor descreve pra esse modelo, com o prompt DENTRO. COBRA NA CONTA DA PESSOA no provedor: confirme modelo e custo com ela antes. O job fica gravado nesta máquina ANTES de esperar o render, e o pedido idêntico em 30 minutos devolve o job que já existe em vez de cobrar de novo (forcar=true repete de propósito). Espera até aguardarSegundos (padrão 45) e, pronto, ingere sozinho com characterId, referenceImageIds, prompt verbatim e o custo quando o provedor diz), 'status' (jobId: consulta o provedor e, se terminou, traz pro acervo; é o que se chama depois de um gerar que voltou 'rodando', NUNCA gerar de novo), 'jobs' (os jobs desta máquina, sem rede). A FICHA DO PERSONAGEM entra no input: pegue passportPrompt e mainImageUrl/imageUrls em sapiens_character action=get, ponha o descritor no começo do prompt e as imagens no campo de referência que o modelo aceita (image_url, image_urls, conforme a doc dele); o bloco NEGATIVE do passaporte vai no campo negative_prompt quando o modelo tem um. O prompt passa pelo piso de idade da casa antes de sair (18 é o piso). Peça de fal, WaveSpeed ou Replicate entra com o carimbo +18 por classe (peso aberto), igual ao ingest manual; Kie entra sem. Passo a passo na skill 'pontes'.",
92
92
  schema: pontesSchema,
93
93
  handler: pontes,
94
94
  },
@@ -138,12 +138,12 @@ export const TOOLS = {
138
138
  handler: musicator,
139
139
  },
140
140
  sapiens_video: {
141
- description: "Sapiens Video — gera vídeo (qualquer membro logado; vídeo é caro, cobra as Sinapses da sua conta). Sub-action 'create' (recomendada): escolhe modelo + config e gera num call (cria a row + renderiza). Modelos: 'sapiens-video-seedance' (Seedance 2.0, cena+áudio nativo, 4-15s, 480/720/1080p, t2v/i2v; aceita até 4 imagens de REFERÊNCIA via referenceImageIds/referenceImageUrls/referenceImagePaths (o H3 aceita até 9, os Veo Fast/Quality e o Omni 1.1 até 3; Lite, Kling, Hailuo, WAN 2.5 e 3.0, Seedance 1.5 e os dois Spicy não), que guiam estilo/personagem/composição SEM virar o 1º frame — é o fluxo STORYBOARD: gere a folha de key poses com sapiens_image templateSlug='storyboard-sapiens-v1', passe folha + personagem como refs num t2v e descreva o take contínuo no prompt, citando as refs por descrição e mandando ignorar o traço do sketch; aceita também 1 VÍDEO DE MOVIMENTO via referenceVideoUrls (role 'refvideo' -> reference_videos, <=15s, host da casa): a coreografia/câmera do clipe guia o take, combinável com a folha), 'sapiens-video-seedance-2-fast' e 'sapiens-video-seedance-2-mini' (os irmãos do 2.0: MESMO repertório completo, incluindo referência, frame final e vídeo de movimento; o Fast custa 20% menos e o Mini METADE, ambos com teto 720p — pedir 1080p neles entrega e cobra 720p. Use o Mini pra iterar enquadramento/prompt barato e feche no 'sapiens-video-seedance' quando o take estiver certo), 'sapiens-video-seedance-25' (Seedance 2.5, a geração SEGUINTE e não um quarto tier da 2.0: take de 4 a 30s num fôlego, edita e estende vídeo, mesmo repertório de referência mais ÁUDIO como referência; teto 720p e ~1,5x o preço por segundo do 2.0. Duração é o que pesa aqui: 30s em 720p passa de 40 mil Sinapses, então confirme a duração com a pessoa antes de disparar. Quem precisa de 1080p fica no 'sapiens-video-seedance'), 'sapiens-video-seedance-15' (Seedance 1.5 Pro: o degrau entre o 1.0 e a linha 2.x; t2v/i2v, 4-12s, 480/720/1080p, som sempre incluso sem toggle; imagem de referência e frame final ficam de fora por ora), 'sapiens-video-kling' (Kling 3.0 Pro, anima imagem, 3-15s, sound opcional, i2v/t2v), 'sapiens-video-hailuo' (Hailuo 2.3 da MiniMax, física e movimento em 768p, 6 ou 10s, t2v/i2v) e 'sapiens-video-hailuo-pro' (o mesmo em 1080p, 5s fixo — duração não é param aqui): motores PUROS, sem áudio nativo, sem imagens de referência e sem frame final, então quem precisa disso fica no Seedance 2.0; o Pro é o 1080p mais barato da casa depois do Seedance 1.0 Fast, 'sapiens-video-h3' (MiniMax H3: som nativo incluso sem toggle, 5 a 15s em '2k' ou '768p' (sem resolution cai no 2k, que custa 33% mais por segundo), t2v/i2v e frame final; ACEITA até 9 imagens e 3 vídeos de referência (somando 15s), o caminho de take longo com personagem travada; referência e frame inicial são exclusivos, mande um dos dois; o 2K de 15s leva uns 9 minutos de render, acompanhe em status e não gere de novo), 'sapiens-video-h3-spicy' (MiniMax H3 Spicy: o H3 SEM freio de conteúdo, i2v puro a partir de uma imagem sua, que é o que trava a identidade; 3 a 15s, 480p/768p/1080p, som nativo, frame final; o segundo de vídeo mais barato e o render mais rápido da casa), 'sapiens-video-seedance-spicy' (Seedance 2.0 Spicy: o 2.0 SEM freio, também i2v puro, som nativo; 4 a 15s em 480p e 720p, para em 10s em 1080p; sem frame final; o som tem interruptor no mesmo preço), 'sapiens-video-wan' (WAN 2.5, imagem que fala/canta com áudio+lip-sync, 5/10s, i2v), 'sapiens-video-wan-3' (WAN 3.0: texto ou imagem, som nativo com interruptor no mesmo preço, frame final, 5/8/10s em 480/720/1080p; sem imagem de referência), 'sapiens-video-kling-motion' (Motion transfer: passa o movimento de um vídeo pra uma imagem, PRECISA de pessoa com tronco visível na imagem E no vídeo), 'sapiens-video-shot-mimic' (Shot Mimic: recria o plano/câmera/cortes de um vídeo de referência como cena nova), 'sapiens-video-omni-11' (Gemini Omni 1.1: texto ou imagem vira vídeo 10s 720p com áudio nativo; aceita até 3 imagens de referência e ignora durationSec/resolution, que são fixos; editOfImageId aponta um vídeo Omni seu e o prompt edita a MESMA cena, preservando câmera e ambiente) e 'sapiens-video-omni' (o 1.0 preview: mesmo preço e mesmo repertório, desligado por default, existe pra comparar com o 1.1), 'sapiens-video-lite/fast/quality' (Veo 3.1). Args create: model, prompt, durationSec, resolution ('480p'/'720p'/'1080p'; no H3 '768p'/'2k'), audio, aspectRatio. RECEITA DE TAKE (templateSlug + brief), o irmão do templateSlug da imagem: em vez de escrever o prompt inteiro, passe templateSlug e a receita travada da casa embrulha a cena com estilo, cenário, arco, áudio e look, e ainda escolhe o motor (por isso model fica opcional). O `prompt` vira só a CENA e o `brief` preenche os campos do formato (subject, persona, hook, shots com voiceLine e propVisible, uvps, language, energy); campo vazio some do prompt em vez de virar buraco. Receitas de hoje: 'ugc-vertical-v1' (selfie que fala, o formato nativo de Reels/TikTok/Shorts), 'unboxing-vertical-v1' (mãos e reveal, som real do papel e do lacre), 'app-demo-vertical-v1' (a tela do app legível na mão da pessoa) e 'reflexao-vertical-v1' (talking-head lento pra ideia ou ensaio). Override de model/aspectRatio/durationSec/resolution vale dentro do que a receita aceita, e o erro lista as opções. Sub-action 'templates' (sem custo, sem login) traz o catálogo vivo com spec default, whitelist e os briefFields de cada uma. Isto substitui a tool sapiens_shorts, que era admin-only e só falava Veo 3.1 Fast; o mesmo UGC de 8s sai a 720p no Seedance 2.0 Mini por bem menos. FRAME INICIAL/FINAL POR REFERÊNCIA (recomendado): startImageId/endImageId (id da sua galeria) ou startImageUrl/endImageUrl (url de galeria/Acervo/personagem) — resolvidos server-side igual à imagem, descubra via sapiens_reference. FRAME POR ARQUIVO LOCAL (só no MCP instalado/stdio, não no remoto): startImagePath/endImagePath = caminho absoluto de uma imagem no seu PC (PNG/JPEG/WebP até 8MB); o processo lê o arquivo e sobe como frame inicial/final, igual a subir no gerador do site — 1 imagem inicial + 1 final por vídeo, então pra vários vídeos rode create uma vez por imagem. No remoto use id/url. Alternativa base64: references (role 'start'=imagem i2v, 'end'=frame final, 'driving'=vídeo de movimento do Motion). Suporte a frame final varia por modelo. Custo server-side por config. Sub-action 'generate' (legado): renderiza um imageId de vídeo já criado no site. Retorna {success, url, imageId, cost}. VITRINE (sem custo): sub-action 'demos' lista os SEUS demo films (kind=demo do Estúdio de Vídeo) com slug + estado de vitrine; sub-action 'showcase' põe/tira um demo (por slug) do mini-cinema da /conectar-claude, com showcaseTag (chip de capacidade) e showcaseOrder (ordem asc). Fluxo: 'demos' pra achar o slug, depois 'showcase' com showcase=true. Só entra na vitrine pública se for a conta da casa. VÍDEOS PROGRAMÁTICOS (ADMIN, sem custo): o Lab do Estúdio de Vídeo (/experimentos/films, tabela videoSpecs, 5 kinds: demo | aula-tour | essay | tipografia-musical | dataviz) opera por aqui sem browser — 'film-list' (todos os kinds; filtros filmKind/filmStatus), 'film-get' (spec inteiro por slug), 'film-upsert' (cria/atualiza por slug, idempotente; spec = objeto JSON no shape do 'Copiar spec' da tela, validação no servidor), 'film-status' (produção por slug: filmStatus + videoUrl + durationSecMeasured; o fecho do render é os três num call), 'film-publish' (Acervo aba Fitas + portfólio; exige pronto+URL), 'film-delete' (limpar rascunho). O RENDER do filme segue no agente local (skill /film, repo da casa): o MCP registra e fecha o ciclo, não renderiza. create é ASSÍNCRONA: cria o row, debita e volta NA HORA com {imageId, status:'rendering', cost} (não espera o render, que leva de segundos a minutos). Acompanhe com a sub-action 'status' (imageId) até status='completed' (traz a url) ou 'error'/'blocked'. NÃO chame create de novo enquanto renderiza (cria outro vídeo e cobra de novo); falha de provider refunda sozinha. SOM: 'sonorize' (imageId de vídeo SEU completed + prompt do som da cena) gera uma VARIANTE nova com trilha sincronizada (20 Sinapses/s, o original fica intacto; sonorize sempre o original, nunca uma variante). ADMIN: 'shadows' (videoUrl + title) extrai o depth map (depth-map) de um vídeo: entra na sua timeline de vídeos e no Acervo como driving reutilizável; 'shadows-list' lista os depth maps prontos. PREÇO DE VÍDEO É FAIXA, NÃO NÚMERO: nos motores por segundo (todo Seedance, Kling, WAN 2.5 e 3.0, Hailuo, H3, os dois Spicy, Shot Mimic) a conta é duração x resolução [x áudio], e OMITIR não pega o barato: sem 'resolution' o servidor cobra o tier MAIS CARO do motor, sem 'durationSec' cobra o piso de duração. Um create de Seedance 2.0 sem resolution debita 1080p. Nunca prometa um valor sem cotar: sub-action 'price' (sem custo, sem login) devolve o número EXATO que o create vai debitar pra model + durationSec + resolution + audio, e avisa (clamped=true) quando o que você pediu não é o que vai ser cobrado. Sub-action 'models' (sem custo, sem login): lista os modelos ativos + a faixa de preço calculada pela mesma função que debita (priceDefault = o que uma chamada sem config custa, priceMin e priceMax = os dois cantos do motor, cada um com a config que o produz) + config (durações/resoluções) + disponibilidade (motor em preview pode aparecer como 'em breve'). LEGENDA (fita, por slug): 'caption-list' (sem custo, mostra os idiomas que a peça já tem), 'caption-generate' (captionLang = idioma FALADO; captionTrio=true já traduz pro trio da casa pt+en+ja na mesma chamada) e 'caption-translate' (captionLang = destino, parte sempre da faixa original). Cobra por minuto começado de vídeo: 50 Sinapses o minuto transcrito, 20 o traduzido, com a duração vindo do doc da peça. A legenda é desenhada pela casa em dois estilos (discreta e social), trocáveis no play sem regerar e sem custo.",
141
+ description: "Sapiens Video — gera vídeo (qualquer membro logado; vídeo é caro, cobra as Sinapses da sua conta). Sub-action 'create' (recomendada): escolhe modelo + config e gera num call (cria a row + renderiza). Modelos: 'sapiens-video-seedance' (Seedance 2.0, cena+áudio nativo, 4-15s, 480/720/1080p, t2v/i2v; aceita até 4 imagens de REFERÊNCIA via referenceImageIds/referenceImageUrls/referenceImagePaths (o H3 aceita até 9, os Veo Fast/Quality e o Omni 1.1 até 3; Lite, Kling, Hailuo, WAN 2.5 e 3.0 e os dois Spicy não), que guiam estilo/personagem/composição SEM virar o 1º frame — é o fluxo STORYBOARD: gere a folha de key poses com sapiens_image templateSlug='storyboard-sapiens-v1', passe folha + personagem como refs num t2v e descreva o take contínuo no prompt, citando as refs por descrição e mandando ignorar o traço do sketch; aceita também 1 VÍDEO DE MOVIMENTO via referenceVideoUrls (role 'refvideo' -> reference_videos, <=15s, host da casa): a coreografia/câmera do clipe guia o take, combinável com a folha), 'sapiens-video-seedance-2-fast' e 'sapiens-video-seedance-2-mini' (os irmãos do 2.0: MESMO repertório completo, incluindo referência, frame final e vídeo de movimento; o Fast custa 20% menos e o Mini METADE, ambos com teto 720p — pedir 1080p neles entrega e cobra 720p. Use o Mini pra iterar enquadramento/prompt barato e feche no 'sapiens-video-seedance' quando o take estiver certo), 'sapiens-video-seedance-25' (Seedance 2.5, a geração SEGUINTE e não um quarto tier da 2.0: take de 4 a 30s num fôlego, edita e estende vídeo, mesmo repertório de referência mais ÁUDIO como referência; teto 720p e ~1,5x o preço por segundo do 2.0. Duração é o que pesa aqui: 30s em 720p passa de 40 mil Sinapses, então confirme a duração com a pessoa antes de disparar. Quem precisa de 1080p fica no 'sapiens-video-seedance'), 'sapiens-video-kling' (Kling 3.0 Pro, anima imagem, 3-15s, sound opcional, i2v/t2v), 'sapiens-video-hailuo' (Hailuo 2.3 da MiniMax, física e movimento em 768p, 6 ou 10s, t2v/i2v) e 'sapiens-video-hailuo-pro' (o mesmo em 1080p, 5s fixo — duração não é param aqui): motores PUROS, sem áudio nativo, sem imagens de referência e sem frame final, então quem precisa disso fica no Seedance 2.0; o Pro é o 1080p mais barato da casa, 'sapiens-video-h3' (MiniMax H3: som nativo incluso sem toggle, 5 a 15s em '2k' ou '768p' (sem resolution cai no 2k, que custa 33% mais por segundo), t2v/i2v e frame final; ACEITA até 9 imagens e 3 vídeos de referência (somando 15s), o caminho de take longo com personagem travada; referência e frame inicial são exclusivos, mande um dos dois; o 2K de 15s leva uns 9 minutos de render, acompanhe em status e não gere de novo), 'sapiens-video-h3-spicy' (MiniMax H3 Spicy: o H3 SEM freio de conteúdo, i2v puro a partir de uma imagem sua, que é o que trava a identidade; 3 a 15s, 480p/768p/1080p, som nativo, frame final; o segundo de vídeo mais barato e o render mais rápido da casa), 'sapiens-video-seedance-spicy' (Seedance 2.0 Spicy: o 2.0 SEM freio, também i2v puro, som nativo; 4 a 15s em 480p e 720p, para em 10s em 1080p; sem frame final; o som tem interruptor no mesmo preço), 'sapiens-video-wan' (WAN 2.5, imagem que fala/canta com áudio+lip-sync, 5/10s, i2v), 'sapiens-video-wan-3' (WAN 3.0: texto ou imagem, som nativo com interruptor no mesmo preço, frame final, 5/8/10s em 480/720/1080p; sem imagem de referência), 'sapiens-video-kling-motion' (Motion transfer: passa o movimento de um vídeo pra uma imagem, PRECISA de pessoa com tronco visível na imagem E no vídeo), 'sapiens-video-shot-mimic' (Shot Mimic: recria o plano/câmera/cortes de um vídeo de referência como cena nova), 'sapiens-video-omni-11' (Gemini Omni 1.1: texto ou imagem vira vídeo 10s 720p com áudio nativo; aceita até 3 imagens de referência e ignora durationSec/resolution, que são fixos; editOfImageId aponta um vídeo Omni seu e o prompt edita a MESMA cena, preservando câmera e ambiente) e 'sapiens-video-omni' (o 1.0 preview: mesmo preço e mesmo repertório, desligado por default, existe pra comparar com o 1.1), 'sapiens-video-lite/fast/quality' (Veo 3.1). Args create: model, prompt, durationSec, resolution ('480p'/'720p'/'1080p'; no H3 '768p'/'2k'), audio, aspectRatio. RECEITA DE TAKE (templateSlug + brief), o irmão do templateSlug da imagem: em vez de escrever o prompt inteiro, passe templateSlug e a receita travada da casa embrulha a cena com estilo, cenário, arco, áudio e look, e ainda escolhe o motor (por isso model fica opcional). O `prompt` vira só a CENA e o `brief` preenche os campos do formato (subject, persona, hook, shots com voiceLine e propVisible, uvps, language, energy); campo vazio some do prompt em vez de virar buraco. Receitas de hoje: 'ugc-vertical-v1' (selfie que fala, o formato nativo de Reels/TikTok/Shorts), 'unboxing-vertical-v1' (mãos e reveal, som real do papel e do lacre), 'app-demo-vertical-v1' (a tela do app legível na mão da pessoa) e 'reflexao-vertical-v1' (talking-head lento pra ideia ou ensaio). Override de model/aspectRatio/durationSec/resolution vale dentro do que a receita aceita, e o erro lista as opções. Sub-action 'templates' (sem custo, sem login) traz o catálogo vivo com spec default, whitelist e os briefFields de cada uma. Isto substitui a tool sapiens_shorts, que era admin-only e só falava Veo 3.1 Fast; o mesmo UGC de 8s sai a 720p no Seedance 2.0 Mini por bem menos. FRAME INICIAL/FINAL POR REFERÊNCIA (recomendado): startImageId/endImageId (id da sua galeria) ou startImageUrl/endImageUrl (url de galeria/Acervo/personagem) — resolvidos server-side igual à imagem, descubra via sapiens_reference. FRAME POR ARQUIVO LOCAL (só no MCP instalado/stdio, não no remoto): startImagePath/endImagePath = caminho absoluto de uma imagem no seu PC (PNG/JPEG/WebP até 8MB); o processo lê o arquivo e sobe como frame inicial/final, igual a subir no gerador do site — 1 imagem inicial + 1 final por vídeo, então pra vários vídeos rode create uma vez por imagem. No remoto use id/url. Alternativa base64: references (role 'start'=imagem i2v, 'end'=frame final, 'driving'=vídeo de movimento do Motion). Suporte a frame final varia por modelo. Custo server-side por config. Sub-action 'generate' (legado): renderiza um imageId de vídeo já criado no site. Retorna {success, url, imageId, cost}. VITRINE (sem custo): sub-action 'demos' lista os SEUS demo films (kind=demo do Estúdio de Vídeo) com slug + estado de vitrine; sub-action 'showcase' põe/tira um demo (por slug) do mini-cinema da /conectar-claude, com showcaseTag (chip de capacidade) e showcaseOrder (ordem asc). Fluxo: 'demos' pra achar o slug, depois 'showcase' com showcase=true. Só entra na vitrine pública se for a conta da casa. VÍDEOS PROGRAMÁTICOS (ADMIN, sem custo): o Lab do Estúdio de Vídeo (/experimentos/films, tabela videoSpecs, 5 kinds: demo | aula-tour | essay | tipografia-musical | dataviz) opera por aqui sem browser — 'film-list' (todos os kinds; filtros filmKind/filmStatus), 'film-get' (spec inteiro por slug), 'film-upsert' (cria/atualiza por slug, idempotente; spec = objeto JSON no shape do 'Copiar spec' da tela, validação no servidor), 'film-status' (produção por slug: filmStatus + videoUrl + durationSecMeasured; o fecho do render é os três num call), 'film-publish' (Acervo aba Fitas + portfólio; exige pronto+URL), 'film-delete' (limpar rascunho). O RENDER do filme segue no agente local (skill /film, repo da casa): o MCP registra e fecha o ciclo, não renderiza. create é ASSÍNCRONA: cria o row, debita e volta NA HORA com {imageId, status:'rendering', cost} (não espera o render, que leva de segundos a minutos). Acompanhe com a sub-action 'status' (imageId) até status='completed' (traz a url) ou 'error'/'blocked'. NÃO chame create de novo enquanto renderiza (cria outro vídeo e cobra de novo); falha de provider refunda sozinha. SOM: 'sonorize' (imageId de vídeo SEU completed + prompt do som da cena) gera uma VARIANTE nova com trilha sincronizada (20 Sinapses/s, o original fica intacto; sonorize sempre o original, nunca uma variante). ADMIN: 'shadows' (videoUrl + title) extrai o depth map (depth-map) de um vídeo: entra na sua timeline de vídeos e no Acervo como driving reutilizável; 'shadows-list' lista os depth maps prontos. PREÇO DE VÍDEO É FAIXA, NÃO NÚMERO: nos motores por segundo (todo Seedance, Kling, WAN 2.5 e 3.0, Hailuo, H3, os dois Spicy, Shot Mimic) a conta é duração x resolução [x áudio], e OMITIR não pega o barato: sem 'resolution' o servidor cobra o tier MAIS CARO do motor, sem 'durationSec' cobra o piso de duração. Um create de Seedance 2.0 sem resolution debita 1080p. Nunca prometa um valor sem cotar: sub-action 'price' (sem custo, sem login) devolve o número EXATO que o create vai debitar pra model + durationSec + resolution + audio, e avisa (clamped=true) quando o que você pediu não é o que vai ser cobrado. Sub-action 'models' (sem custo, sem login): lista os modelos ativos + a faixa de preço calculada pela mesma função que debita (priceDefault = o que uma chamada sem config custa, priceMin e priceMax = os dois cantos do motor, cada um com a config que o produz) + config (durações/resoluções) + disponibilidade (motor em preview pode aparecer como 'em breve'). LEGENDA (fita, por slug): 'caption-list' (sem custo, mostra os idiomas que a peça já tem), 'caption-generate' (captionLang = idioma FALADO; captionTrio=true já traduz pro trio da casa pt+en+ja na mesma chamada) e 'caption-translate' (captionLang = destino, parte sempre da faixa original). Cobra por minuto começado de vídeo: 50 Sinapses o minuto transcrito, 20 o traduzido, com a duração vindo do doc da peça. A legenda é desenhada pela casa em dois estilos (discreta e social), trocáveis no play sem regerar e sem custo.",
142
142
  schema: videoSchema,
143
143
  handler: video,
144
144
  },
145
145
  sapiens_stock_audio: {
146
- description: "Banco de som da casa: trilha pronta E efeito sonoro (tabela stockAudio). Sub-actions de leitura (públicas, sem auth): categories (lista os moods/usos), list (busca com filtros mood/albumSlug/durationMax/search; kind='sfx' traz os EFEITOS: whoosh, clique, impacto, ambiência, foley — devolve {count, items} com title/url/durationSeconds/tags), get (1 item por audioId). Use pra puxar trilha/efeito pronto: pega a `url` e usa direto no ffmpeg. Não achou o efeito? generate (COBRA Sinapses, exige login) cria um novo por texto: prompt + durationSeconds (1-15, default 5) + provider ('mirelo' padrão 35 Sinapses/s mín 70 | 'elevenlabs' premium 60/s mín 120) + promptInfluence opcional (0..1, só elevenlabs: fidelidade ao texto, default 0.3), assíncrono — acompanhe com generation-status (generationId) até 'ready' (audioUrl; o efeito também entra no acervo kind=sfx) ou 'failed' (Sinapses reembolsadas). Efeito é CURTO (1-15s): música/trilha nova é no sapiens_musicator. Mood disponíveis: calmo, intenso, narrativo, épico, sombrio. Precisa de VOZ junto com trilha e efeito (anúncio, vinheta, cena de filme, podcast)? scene (COBRA Sinapses, exige login) cria a CENA INTEIRA num passe só, já batida, sem montar em pedaços e mixar depois: scenePrompt (a direção da cena, quem fala, com que timbre, o que toca atrás, a fala entre aspas) + durationSeconds (3-120, default 15) + loudnessRate/pitchRate/speechRate opcionais + withSubtitles opcional (traz o tempo de cada palavra), 11 Sinapses/s, assíncrono — acompanhe com scene-status (sceneId) até 'ready' (audioUrl + realDurationSeconds) ou 'failed' (Sinapses reembolsadas). A cena fica com o AUTOR e não entra no acervo compartilhado, porque carrega a voz e o roteiro de quem pediu.",
146
+ description: "Banco de som da casa: trilha pronta E efeito sonoro (tabela stockAudio). Sub-actions de leitura (públicas, sem auth): categories (lista os moods/usos), list (busca com filtros mood/albumSlug/durationMax/search; kind='sfx' traz os EFEITOS: whoosh, clique, impacto, ambiência, foley — devolve {count, items} com title/url/durationSeconds/tags), get (1 item por audioId). Use pra puxar trilha/efeito pronto: pega a `url` e usa direto no ffmpeg. Não achou o efeito? generate (COBRA Sinapses, exige login) cria um novo por texto: prompt + durationSeconds (1-15, default 5) + provider ('mirelo' padrão 35 Sinapses/s mín 70 | 'elevenlabs' premium 60/s mín 120) + promptInfluence opcional (0..1, só elevenlabs: fidelidade ao texto, default 0.3), assíncrono — acompanhe com generation-status (generationId) até 'ready' (audioUrl; o efeito também entra no acervo kind=sfx) ou 'failed' (Sinapses reembolsadas). Efeito é CURTO (1-15s): música/trilha nova é no sapiens_musicator. Mood disponíveis: calmo, intenso, narrativo, épico, sombrio. Precisa de VOZ junto com trilha e efeito (anúncio, vinheta, cena de filme, podcast)? scene (COBRA Sinapses, exige login) cria a CENA INTEIRA num passe só, já batida, sem montar em pedaços e mixar depois: scenePrompt (a direção da cena, quem fala, com que timbre, o que toca atrás, a fala entre aspas) + durationSeconds (3-120, default 15) + loudnessRate/pitchRate/speechRate opcionais + withSubtitles opcional (traz o tempo de cada palavra), 13 Sinapses/s, assíncrono — acompanhe com scene-status (sceneId) até 'ready' (audioUrl + realDurationSeconds) ou 'failed' (Sinapses reembolsadas). A cena fica com o AUTOR e não entra no acervo compartilhado, porque carrega a voz e o roteiro de quem pediu.",
147
147
  schema: stockAudioSchema,
148
148
  handler: stockAudio,
149
149
  },
@@ -158,7 +158,7 @@ export const TOOLS = {
158
158
  handler: brand,
159
159
  },
160
160
  sapiens_character: {
161
- description: "Personagens (character sheets) do Sapiens — a tabela `influencers`: personagem reutilizável com imagens (pra character-lock em geração) + alma (systemPrompt), tudo amarrado à conta do dono do token (sem admin). Sub-actions: list_public (catálogo global de personagens públicos do Explorar; cada um traz mainImageUrl/imageUrls usáveis direto como referenceImageUrls em sapiens_image; sem custo, sem login), get (detalhe de 1 por characterId — público+ativo qualquer um vê, draft/privado só o dono; systemPrompt só volta pro dono), list_mine (os personagens do próprio user, inclui drafts/privados), create (cria rascunho na conta: name + gender + opcional form/title/systemPrompt), add_image (adiciona imagem ao próprio personagem via imageUrl público OU sourceImageId da galeria; 1ª vira principal), set_card (edita alma/título/lema em inglês/nome/nome por extenso/forma do próprio), set_presence (escreve onde o personagem existe FORA da casa: redes sociais por handle, Fanvue, site e o email dele, este último privado), set_passport (escreve o PASSAPORTE dele: como ACERTAR este ser, que é outra coisa de quem ele é. Guarda o `descriptor` de geração em inglês pra colar verbatim, o `negative` travado que fica sempre ligado, os `locks` (o que TRAVA nele, um por entrada, que é a lista que vira as perguntas da conferência), as `refs` por PAPEL (proporção, rosto, tatuagem: ref sem papel ninguém sabe usar), as `recipes` medidas por tipo de peça (com o prompt VERBATIM, porque o valor está na palavra exata que funcionou) e o `discarded`, o que já foi recusado. Esse último não é enfeite: o motor traz de volta o que ninguém proibiu, então ideia descartada sem negativo escrito volta sozinha na leva seguinte. É MERGE por campo, então mandar só o negative não apaga o descritor, e campo vazio apaga. Só o DONO lê: receita de produção não sai em personagem público. Em action=get ele volta em duas formas, `passport` (campo a campo, o que se reedita) e `passportPrompt` (o bloco já montado pra colar). Preencha sempre que uma rodada medir algo que a próxima não deve redescobrir: é o que faz a receita viajar pra fora do repo), activate (tira do rascunho e libera o personagem pra USAR nas gerações do dono; exige ≥1 imagem. NÃO torna público: quem publica é set_visibility, e ativar é gesto reversível e privado), set_visibility (isPublic true=Explorar+slug / false=privado), set_hidden (hidden true tira o personagem da TELA do dono: some da lista de personagens e as peças geradas com ele saem da galeria de fotos, vídeos e rascunhos do dono, voltando pelo filtro Ocultos; hidden false devolve. É o gesto de quem vai compartilhar tela. Não despublica, não apaga, não sai dos seletores de geração, e personagem oculto não publica até ser mostrado. Grátis. O list_mine traz `hidden` em cada personagem). GESTÃO de imagem (por url, pegue as urls atuais em action=get campo imageUrls): remove_image (tira uma), set_main_image (define a principal), reorder_images (nova ordem via orderedUrls, posição 0=principal), e delete (apaga o personagem, permanente). FORMA (`form`, o que tipo de CORPO ele tem: human default, humanoid, animal (bicho real), creature (ser inventado), object, abstract): não é enfeite de cadastro, é o que a ficha, as figurinhas e o vídeo leem pra decidir se descrevem rosto e mãos ou silhueta e postura, e é ele que impede uma criatura de voltar desenhada como pessoa. Mande no create sempre que o personagem não for gente; personagem que já existe sem ele conserta com set_card form=creature. Ausente = human, que é o que a casa desenhava antes do campo existir. NOME E NOME POR EXTENSO (`name` e `fullName`): `name` é como o personagem é CHAMADO e é o que toda tela desenha; `fullName` é o nome inteiro, pra quem tem apelido ('Crow Girl' na tela, 'Kaia Crowe' por extenso). Um não substitui o outro, e o extenso não mexe no slug da página nem no cartão de nome do vídeo: batizar personagem que já existe não muda endereço publicado nem assinatura de peça que já saiu. Vem em action=get e list_mine no campo `fullName` (null = ainda sem nome por extenso). TRAÇO (`style`, em que TÉCNICA ele é feito: auto default, realista, anime, manhwa, 3d, custom): irmão da forma, e a divisão é limpa, a forma diz que CORPO é esse e o traço diz como ele é DESENHADO. As mesmas três peças leem daqui. No 'auto' a peça não afirma técnica nenhuma e as imagens de referência mandam; 'realista' é fotografia, 'anime' é cel shading com contorno de tinta, 'manhwa' é pintura de webtoon, '3d' é render com material e oclusão, 'custom' usa a linha escrita em `styleNote` (até 140 caracteres, ex: aquarela sobre papel texturizado). Mande sempre que o personagem tiver técnica definida: até ago/2026 a ficha cravava vocabulário de desenho e devolvia personagem fotográfico virado em ilustração. Conserto sem custo em quem já existe: set_card style=realista. PRESENÇA (set_presence, de graça): o ENDEREÇO do personagem, o que faltava pra ele existir fora da ficha. `socials` é {rede: handle} nas redes instagram, tiktok, youtube, x, threads, bluesky, twitch, spotify, fanvue, patreon, onlyfans, telegram; `websiteUrl` é o site dele e `email` é o email do personagem, o que cadastra e recupera as contas dele nas redes. As redes e o site são PÚBLICOS; o email é PRIVADO e fica só com o dono, mesmo com o personagem publicado, porque email é chave de conta e isca de spam, não canal de contato. Em action=get e list_public de personagem de OUTRA pessoa, o campo simplesmente não vem. É MERGE: mandar {fanvue:'helen'} não apaga o instagram, e pra apagar uma rede se manda ela vazia. O que volta em action=get são as duas formas, `presence` (o handle cru, que é o que se reedita) e `presenceLinks` (o link já montado, pra mostrar sem remontar URL na mão). Vale a pena preencher quando o personagem TEM conta de verdade: é o que separa uma ficha de um ser com endereço, e a página pública dele desenha as redes e o site (nunca o email). FICHA (generate_sheet, COBRA 450 Sinapses): desenha a página-pôster do personagem no traço das imagens que ele já tem (exige pelo menos uma), em duas orientações (arg `orientation`): 'portrait' (default) é a página de processo, pose, expressões, trocas de roupa e adereços soltos na folha; 'landscape' é a prancha larga, com a volta completa à esquerda, a figura grande no meio, poses à direita, estudos de silhueta, expressão e detalhe embaixo e um painel CHARACTER ID na ponta. A personalidade sai da alma (systemPrompt) e é ela que escolhe roupa e objeto, então personagem com alma escrita rende ficha melhor. Cada geração é um estilo NOVO e elas acumulam no personagem (campo sheetUrls em action=get), nenhuma apaga a anterior, e a ficha já entra na galeria dele como referência das próximas gerações. É geração síncrona: se voltar Timeout, cheque action=get antes de repetir, senão cobra duas vezes. STICKERS (generate_stickers, COBRA 550 Sinapses): desenha 5 stickers do personagem DE UMA VEZ. Uma folha só, em 2K, com as cinco figuras separadas sobre um fundo verde chroma, recortada por código em peças 512x512 transparentes abaixo de 100KB (o teto do WhatsApp). É por isso que sai o preço de UMA imagem em 2K e não de cinco: quem paga é a folha. As peças entram no pack do personagem (um por personagem, os lotes acumulam) e já ficam no picker de expressão do dono, no chat e no Fórum. Arg `moods`: até 5 humores do vocabulário comum por slug (kkkkk, amei, isso, hmm, chega, que, aff, bora, socorro, seinao, valeu, ainao, seila, ideia, calma, contatudo, perfeito, euavisei, naovourir, zzz, somaisum, sextou, merecido, quedia, tudobem); faltando, a casa completa com os mais usados. Arg `hint`: direcionamento curto do autor (roupa, adereço, clima), até 140 caracteres. Arg `stickerTier`: 'folha' (default) é esse lote de cinco, desenhado no Gemini 3.1 Flash; 'unica' (COBRA 900 Sinapses) desenha UM sticker por vez no Gemini 3 Pro, o motor mais fiel da casa, com a figura sozinha no quadro e o dobro de pixel por peça, e leva só o PRIMEIRO mood da lista. Pra encher o pack, folha; pra traço difícil ou a reação que vira a cara do personagem, a unica. Ao oferecer a escolha, diga o NOME DO MOTOR e o que ele troca: adjetivo vago de acabamento não informa nada a quem vai pagar. Os dois caem no MESMO pack e acumulam. Sem legenda queimada na imagem, de propósito: modelo erra acento em português, então o rótulo fica na row e serve de busca no picker. Exige pelo menos uma imagem no personagem (é dela que sai a cara) e é geração síncrona: se voltar Timeout, cheque sapiens_gallery antes de repetir. Quando a resposta vem com ok=false, a folha foi gerada e paga mas o corte falhou; ela está na galeria e o recorte de novo é de graça, pela web. Publicar o pack na vitrine e o carimbo da casa (que é o que põe no picker de todo mundo) são gestos da web, não desta tool. PLACAS (generate_plates, COBRA 550 Sinapses): seis ângulos do mesmo rosto numa geração só, no Gemini 3.1 Flash em 2K, recortados por código em seis referências com PAPEL: perfil, camera-alta, camera-baixa, frontal, cabeca-baixa e outro-lado, cada um com uma expressão diferente. Não é vitrine (isso é a ficha): é a referência feita pra máquina. Referência trava o ângulo da câmera, então quando a cena pede perfil, passe a placa de perfil em referenceImageUrls. As placas ficam FORA de imageUrls de propósito, porque placa de perfil como referência genérica puxa toda peça pro perfil; elas voltam em action=get no campo `plates` ([{role, url}], na ordem da grade). Cada lote substitui o anterior, porque é o kit vigente. Sai das fotos do personagem (exige pelo menos uma) e, quando ele tem passaporte, o descritor dele entra no pedido. Só personagem com rosto de gente (form human ou humanoid): pra bicho, criatura, objeto ou forma abstrata o servidor recusa antes de cobrar. O perfil puro e a câmera alta às vezes voltam amolecidos pro frontal; quando esses dois ângulos importarem, confira as placas antes de usar. Geração síncrona: se voltar Timeout, cheque action=get antes de repetir. Resposta com ok=false quer dizer grade gerada e paga com o corte falho; o recorte de novo é de graça, pela web. CHARACTER VIDEO (a ficha que anda): o personagem atravessa 6 mundos em 12s, trocando de roupa em cada um, com som gerado junto, e o último plano fecha a peça com ele parando e encarando a câmera. A peça volta FECHADA pelas duas portas (tela e agente), sem ninguém pedir: o personagem se apresenta no cartão de nome por cima do fim do último plano, com o LEMA em inglês embaixo (o campo `titleEn`, escrito uma vez em set_card), e só depois a casa assina. Quem não tem lema recebe o subtítulo que o roteiro inventou naquele take, e ele muda no take seguinte. O fecho é asset, não geração: zero Sinapse a mais, e é fail-open, então a resposta traz `closed` e `nameCard` dizendo o que entrou de verdade em vez de prometer um cartão que não está no arquivo. Pra colocar esse cartão num vídeo que JÁ existe, o caminho é a ficha do take na web (gaveta do fecho, 'Apresentação do personagem'), também sem custo. Exige FICHA gerada (é dela que saem a cara e o guarda-roupa), não só imagem. Duas actions, nesta ordem: plan_video (COBRA 50 Sinapses) devolve os 6 planos que a alma do personagem escolheu, com ambiente, ato, enquadramento e roupa. Cada plano vem nas duas línguas: em inglês (setting/act/wardrobe, que é o que vai pro motor) e em português (settingPt/actPt/wardrobePt). MOSTRE a versão em português pro autor, o público da casa é brasileiro e roteiro que ele lê de través não é roteiro aprovado; peça de novo quantas vezes ele quiser, porque trocar sai por 50 e o take errado sai por milhares. replan_shot (COBRA 15 Sinapses) reescreve UM plano e não toca nos outros cinco: os seis são cenas independentes, então quando o autor gosta de quatro e implica com um, troque só aquele em vez de sortear tudo de novo (mande os 6 planos em `shots`, o número em `shotIndex`, e o que ele quer diferente em `shotHint`; volta só o plano trocado, e é você que remonta o roteiro). Depois generate_video (COBRA) renderiza, com os planos aprovados no arg `shots` e o MOTOR no arg `videoTier`: 'draft' (5.400 Sinapses) é o Seedance 2.0 Mini, metade do preço, que tropeça em mão, pouca luz e câmera rápida, e 'final' (8.640 Sinapses) é o Seedance 2.0 Fast, que segura o que o Mini erra. A peça é a MESMA nos dois (mesmos 6 planos, 12s, 720p, com som), então ofereça pelo nome do motor e pelo que ele troca, nunca por adjetivo vago de acabamento. Vertical ou horizontal pelo arg `orientation`. Chamar generate_video SEM `shots` funciona, mas escreve um roteiro novo às cegas e cobra igual: não faça isso sem o autor ter visto o que vai receber. É geração demorada: se voltar Timeout, cheque sapiens_gallery antes de repetir. Resposta com `pending: true` (sem `url`) quer dizer ACEITO E COBRADO: o servidor continua renderizando sozinho (até 25 min) e a peça chega na ficha já fechada; NÃO repita a chamada, isso cobraria de novo, confira depois em sapiens_gallery. Fluxo de criação: create → add_image (1+) → set_card (opcional) → activate. PARE AÍ. O personagem nasce e continua PRIVADO, e é assim que ele serve pro dono: dá pra gerar, referenciar e iterar sem ninguém ver. set_visibility isPublic=true é o gesto de PUBLICAR no Explorar, com slug público, e nÃO é passo de fluxo: só chame quando o dono pedir pra publicar aquele personagem, com essas palavras. Na dúvida, não publique e pergunte. Pra usar um personagem público como referência numa geração, pegue mainImageUrl em list_public/get e passe em sapiens_image referenceImageUrls.",
161
+ description: "Personagens (character sheets) do Sapiens — a tabela `influencers`: personagem reutilizável com imagens (pra character-lock em geração) + alma (systemPrompt), tudo amarrado à conta do dono do token (sem admin). Sub-actions: list_public (catálogo global de personagens públicos do Explorar; cada um traz mainImageUrl/imageUrls usáveis direto como referenceImageUrls em sapiens_image; sem custo, sem login), get (detalhe de 1 por characterId — público+ativo qualquer um vê, draft/privado só o dono; systemPrompt só volta pro dono), list_mine (os personagens do próprio user, inclui drafts/privados), create (cria rascunho na conta: name + gender + opcional form/title/systemPrompt), add_image (adiciona imagem ao próprio personagem via imageUrl público OU sourceImageId da galeria; 1ª vira principal), set_card (edita alma/título/lema em inglês/nome/nome por extenso/forma do próprio), set_presence (escreve onde o personagem existe FORA da casa: redes sociais por handle, Fanvue, site e o email dele, este último privado), set_passport (escreve o PASSAPORTE dele: como ACERTAR este ser, que é outra coisa de quem ele é. Guarda o `descriptor` de geração em inglês pra colar verbatim, o `negative` travado que fica sempre ligado, os `locks` (o que TRAVA nele, um por entrada, que é a lista que vira as perguntas da conferência), as `refs` por PAPEL (proporção, rosto, tatuagem: ref sem papel ninguém sabe usar), as `recipes` medidas por tipo de peça (com o prompt VERBATIM, porque o valor está na palavra exata que funcionou) e o `discarded`, o que já foi recusado. Esse último não é enfeite: o motor traz de volta o que ninguém proibiu, então ideia descartada sem negativo escrito volta sozinha na leva seguinte. É MERGE por campo, então mandar só o negative não apaga o descritor, e campo vazio apaga. Só o DONO lê: receita de produção não sai em personagem público. Em action=get ele volta em duas formas, `passport` (campo a campo, o que se reedita) e `passportPrompt` (o bloco já montado pra colar). Preencha sempre que uma rodada medir algo que a próxima não deve redescobrir: é o que faz a receita viajar pra fora do repo), activate (tira do rascunho e libera o personagem pra USAR nas gerações do dono; exige ≥1 imagem. NÃO torna público: quem publica é set_visibility, e ativar é gesto reversível e privado), set_visibility (isPublic true=Explorar+slug / false=privado), set_hidden (hidden true tira o personagem da TELA do dono: some da lista de personagens e as peças geradas com ele saem da galeria de fotos, vídeos e rascunhos do dono, voltando pelo filtro Ocultos; hidden false devolve. É o gesto de quem vai compartilhar tela. Não despublica, não apaga, não sai dos seletores de geração, e personagem oculto não publica até ser mostrado. Grátis. O list_mine traz `hidden` em cada personagem). GESTÃO de imagem (por url, pegue as urls atuais em action=get campo imageUrls): remove_image (tira uma), set_main_image (define a principal), reorder_images (nova ordem via orderedUrls, posição 0=principal), e delete (apaga o personagem, permanente). FORMA (`form`, o que tipo de CORPO ele tem: human default, humanoid, animal (bicho real), creature (ser inventado), object, abstract): não é enfeite de cadastro, é o que a ficha, as figurinhas e o vídeo leem pra decidir se descrevem rosto e mãos ou silhueta e postura, e é ele que impede uma criatura de voltar desenhada como pessoa. Mande no create sempre que o personagem não for gente; personagem que já existe sem ele conserta com set_card form=creature. Ausente = human, que é o que a casa desenhava antes do campo existir. NOME E NOME POR EXTENSO (`name` e `fullName`): `name` é como o personagem é CHAMADO e é o que toda tela desenha; `fullName` é o nome inteiro, pra quem tem apelido ('Crow Girl' na tela, 'Kaia Crowe' por extenso). Um não substitui o outro, e o extenso não mexe no slug da página nem no cartão de nome do vídeo: batizar personagem que já existe não muda endereço publicado nem assinatura de peça que já saiu. Vem em action=get e list_mine no campo `fullName` (null = ainda sem nome por extenso). TRAÇO (`style`, em que TÉCNICA ele é feito: auto default, realista, anime, manhwa, 3d, custom): irmão da forma, e a divisão é limpa, a forma diz que CORPO é esse e o traço diz como ele é DESENHADO. As mesmas três peças leem daqui. No 'auto' a peça não afirma técnica nenhuma e as imagens de referência mandam; 'realista' é fotografia, 'anime' é cel shading com contorno de tinta, 'manhwa' é pintura de webtoon, '3d' é render com material e oclusão, 'custom' usa a linha escrita em `styleNote` (até 140 caracteres, ex: aquarela sobre papel texturizado). Mande sempre que o personagem tiver técnica definida: até ago/2026 a ficha cravava vocabulário de desenho e devolvia personagem fotográfico virado em ilustração. Conserto sem custo em quem já existe: set_card style=realista. PRESENÇA (set_presence, de graça): o ENDEREÇO do personagem, o que faltava pra ele existir fora da ficha. `socials` é {rede: handle} nas redes instagram, tiktok, youtube, x, threads, bluesky, twitch, spotify, fanvue, patreon, onlyfans, telegram; `websiteUrl` é o site dele e `email` é o email do personagem, o que cadastra e recupera as contas dele nas redes. As redes e o site são PÚBLICOS; o email é PRIVADO e fica só com o dono, mesmo com o personagem publicado, porque email é chave de conta e isca de spam, não canal de contato. Em action=get e list_public de personagem de OUTRA pessoa, o campo simplesmente não vem. É MERGE: mandar {fanvue:'helen'} não apaga o instagram, e pra apagar uma rede se manda ela vazia. O que volta em action=get são as duas formas, `presence` (o handle cru, que é o que se reedita) e `presenceLinks` (o link já montado, pra mostrar sem remontar URL na mão). Vale a pena preencher quando o personagem TEM conta de verdade: é o que separa uma ficha de um ser com endereço, e a página pública dele desenha as redes e o site (nunca o email). FICHA (generate_sheet, COBRA 450 Sinapses): desenha a página-pôster do personagem no traço das imagens que ele já tem (exige pelo menos uma), em duas orientações (arg `orientation`): 'portrait' (default) é a página de processo, pose, expressões, trocas de roupa e adereços soltos na folha; 'landscape' é a prancha larga, com a volta completa à esquerda, a figura grande no meio, poses à direita, estudos de silhueta, expressão e detalhe embaixo e um painel CHARACTER ID na ponta. A personalidade sai da alma (systemPrompt) e é ela que escolhe roupa e objeto, então personagem com alma escrita rende ficha melhor. Cada geração é um estilo NOVO e elas acumulam no personagem (campo sheetUrls em action=get), nenhuma apaga a anterior, e a ficha já entra na galeria dele como referência das próximas gerações. É geração síncrona: se voltar Timeout, cheque action=get antes de repetir, senão cobra duas vezes. STICKERS (generate_stickers, COBRA 550 Sinapses): desenha 5 stickers do personagem DE UMA VEZ. Uma folha só, em 2K, com as cinco figuras separadas sobre um fundo verde chroma, recortada por código em peças 512x512 transparentes abaixo de 100KB (o teto do WhatsApp). É por isso que sai o preço de UMA imagem em 2K e não de cinco: quem paga é a folha. As peças entram no pack do personagem (um por personagem, os lotes acumulam) e já ficam no picker de expressão do dono, no chat e no Fórum. Arg `moods`: até 5 humores do vocabulário comum por slug (kkkkk, amei, isso, hmm, chega, que, aff, bora, socorro, seinao, valeu, ainao, seila, ideia, calma, contatudo, perfeito, euavisei, naovourir, zzz, somaisum, sextou, merecido, quedia, tudobem); faltando, a casa completa com os mais usados. Arg `hint`: direcionamento curto do autor (roupa, adereço, clima), até 140 caracteres. Arg `stickerTier`: 'folha' (default) é esse lote de cinco, desenhado no Gemini 3.1 Flash; 'unica' (COBRA 900 Sinapses) desenha UM sticker por vez no Gemini 3 Pro, o motor mais fiel da casa, com a figura sozinha no quadro e o dobro de pixel por peça, e leva só o PRIMEIRO mood da lista. Pra encher o pack, folha; pra traço difícil ou a reação que vira a cara do personagem, a unica. Ao oferecer a escolha, diga o NOME DO MOTOR e o que ele troca: adjetivo vago de acabamento não informa nada a quem vai pagar. Os dois caem no MESMO pack e acumulam. Sem legenda queimada na imagem, de propósito: modelo erra acento em português, então o rótulo fica na row e serve de busca no picker. Exige pelo menos uma imagem no personagem (é dela que sai a cara) e é geração síncrona: se voltar Timeout, cheque sapiens_gallery antes de repetir. Quando a resposta vem com ok=false, a folha foi gerada e paga mas o corte falhou; ela está na galeria e o recorte de novo é de graça, pela web. Publicar o pack na vitrine e o carimbo da casa (que é o que põe no picker de todo mundo) são gestos da web, não desta tool. PLACAS (generate_plates, COBRA 550 Sinapses): seis ângulos do mesmo rosto numa geração só, no Gemini 3.1 Flash em 2K, recortados por código em seis referências com PAPEL: perfil, camera-alta, camera-baixa, frontal, cabeca-baixa e outro-lado, cada um com uma expressão diferente. Não é vitrine (isso é a ficha): é a referência feita pra máquina. Referência trava o ângulo da câmera, então quando a cena pede perfil, passe a placa de perfil em referenceImageUrls. As placas ficam FORA de imageUrls de propósito, porque placa de perfil como referência genérica puxa toda peça pro perfil; elas voltam em action=get no campo `plates` ([{role, url}]). As placas ACUMULAM: cada geração soma seis, e a que não serve sai com remove_image. Em `plates` o lote mais novo vem primeiro, cada lote na ordem da grade, então a primeira placa de um papel é a mais nova. Sai das fotos do personagem (exige pelo menos uma) e, quando ele tem passaporte, o descritor dele entra no pedido. Só personagem com rosto de gente (form human ou humanoid): pra bicho, criatura, objeto ou forma abstrata o servidor recusa antes de cobrar. O perfil puro e a câmera alta às vezes voltam amolecidos pro frontal; quando esses dois ângulos importarem, confira as placas antes de usar. Geração síncrona: se voltar Timeout, cheque action=get antes de repetir. Resposta com ok=false quer dizer grade gerada e paga com o corte falho; o recorte de novo é de graça, pela web. CHARACTER VIDEO (a ficha que anda): o personagem atravessa 6 mundos em 12s, trocando de roupa em cada um, com som gerado junto, e o último plano fecha a peça com ele parando e encarando a câmera. A peça volta FECHADA pelas duas portas (tela e agente), sem ninguém pedir: o personagem se apresenta no cartão de nome por cima do fim do último plano, com o LEMA em inglês embaixo (o campo `titleEn`, escrito uma vez em set_card), e só depois a casa assina. Quem não tem lema recebe o subtítulo que o roteiro inventou naquele take, e ele muda no take seguinte. O fecho é asset, não geração: zero Sinapse a mais, e é fail-open, então a resposta traz `closed` e `nameCard` dizendo o que entrou de verdade em vez de prometer um cartão que não está no arquivo. Pra colocar esse cartão num vídeo que JÁ existe, o caminho é a ficha do take na web (gaveta do fecho, 'Apresentação do personagem'), também sem custo. Exige FICHA gerada (é dela que saem a cara e o guarda-roupa), não só imagem. Duas actions, nesta ordem: plan_video (COBRA 50 Sinapses) devolve os 6 planos que a alma do personagem escolheu, com ambiente, ato, enquadramento e roupa. Cada plano vem nas duas línguas: em inglês (setting/act/wardrobe, que é o que vai pro motor) e em português (settingPt/actPt/wardrobePt). MOSTRE a versão em português pro autor, o público da casa é brasileiro e roteiro que ele lê de través não é roteiro aprovado; peça de novo quantas vezes ele quiser, porque trocar sai por 50 e o take errado sai por milhares. replan_shot (COBRA 15 Sinapses) reescreve UM plano e não toca nos outros cinco: os seis são cenas independentes, então quando o autor gosta de quatro e implica com um, troque só aquele em vez de sortear tudo de novo (mande os 6 planos em `shots`, o número em `shotIndex`, e o que ele quer diferente em `shotHint`; volta só o plano trocado, e é você que remonta o roteiro). Depois generate_video (COBRA) renderiza, com os planos aprovados no arg `shots` e o MOTOR no arg `videoTier`: 'draft' (5.400 Sinapses) é o Seedance 2.0 Mini, metade do preço, que tropeça em mão, pouca luz e câmera rápida, e 'final' (8.640 Sinapses) é o Seedance 2.0 Fast, que segura o que o Mini erra. A peça é a MESMA nos dois (mesmos 6 planos, 12s, 720p, com som), então ofereça pelo nome do motor e pelo que ele troca, nunca por adjetivo vago de acabamento. Vertical ou horizontal pelo arg `orientation`. Chamar generate_video SEM `shots` funciona, mas escreve um roteiro novo às cegas e cobra igual: não faça isso sem o autor ter visto o que vai receber. É geração demorada: se voltar Timeout, cheque sapiens_gallery antes de repetir. Resposta com `pending: true` (sem `url`) quer dizer ACEITO E COBRADO: o servidor continua renderizando sozinho (até 25 min) e a peça chega na ficha já fechada; NÃO repita a chamada, isso cobraria de novo, confira depois em sapiens_gallery. Fluxo de criação: create → add_image (1+) → set_card (opcional) → activate. PARE AÍ. O personagem nasce e continua PRIVADO, e é assim que ele serve pro dono: dá pra gerar, referenciar e iterar sem ninguém ver. set_visibility isPublic=true é o gesto de PUBLICAR no Explorar, com slug público, e nÃO é passo de fluxo: só chame quando o dono pedir pra publicar aquele personagem, com essas palavras. Na dúvida, não publique e pergunte. Pra usar um personagem público como referência numa geração, pegue mainImageUrl em list_public/get e passe em sapiens_image referenceImageUrls.",
162
162
  schema: characterSchema,
163
163
  handler: character,
164
164
  },
@@ -262,7 +262,7 @@ REGRA DE OURO:
262
262
  - PRIMEIRO CONTATO ou "o que você faz?"/"como começo?"/"o que dá pra fazer?": chame sapiens_meta action=start e MOSTRE o resultado na sua voz. Sem login, ele ensina a conectar; logado, traz saldo + primeiros poderes com exemplos. É a porta de entrada: não despeje a lista inteira de tools, deixe o start guiar.
263
263
  - LOGO APÓS UM LOGIN BEM-SUCEDIDO (action=login retornou ok): chame action=start na sequência e mostre a porta de entrada. O recém-chegado não sabe o que pedir; não o deixe na tela em branco, guie a primeira jogada sem ele precisar perguntar.
264
264
  - Antes de gerar algo caro (imagem/música/vídeo), cheque saldo: sapiens_meta action=credits (ou subscription). Saldo baixo, avise o usuário antes. Vídeo é o mais caro da casa: confirme com ele antes de disparar.
265
- - DUAS PORTAS, lado a lado: Sinapse (sapiens_image, sapiens_video: nada pra configurar) ou a CHAVE DA PESSOA num provedor (fal, Kie, WaveSpeed, Replicate, Sogni, a própria placa), que custa zero aqui. No MCP instalado, sapiens_pontes gera com a chave que mora na máquina dela (fal, Kie, WaveSpeed, Replicate) e traz a peça pro acervo; no remoto, ela gera no harness dela e traz por sapiens_gallery action=ingest. sapiens_meta action=pontes diz o que ela já tem configurado: se tiver chave, ofereça as duas portas antes de gastar Sinapse. A chave NUNCA passa pelo Sapiens (nem em arg, nem no chat). Skill 'pontes'.
265
+ - DUAS PORTAS, lado a lado: Sinapse (sapiens_image, sapiens_video: nada pra configurar) ou a CHAVE DA PESSOA num provedor (fal, Kie, WaveSpeed, Replicate, OpenRouter, Sogni, a própria placa), que custa zero aqui. No MCP instalado, sapiens_pontes gera com a chave que mora na máquina dela (fal, Kie, WaveSpeed, Replicate, OpenRouter) e traz a peça pro acervo; no remoto, ela gera no harness dela e traz por sapiens_gallery action=ingest. sapiens_meta action=pontes diz o que ela já tem configurado: se tiver chave, ofereça as duas portas antes de gastar Sinapse. A chave NUNCA passa pelo Sapiens (nem em arg, nem no chat). Skill 'pontes'.
266
266
  - REGRA DO TIMEOUT: geração SÍNCRONA pode estourar o teto de ~120s do cliente e voltar 'Timeout' MESMO tendo gerado e COBRADO. Nunca repita às cegas: confira antes onde o resultado cairia (a tabela de onde conferir está na skill 'primeiros-passos').
267
267
  - "sessionToken expirado" = refaça login: sapiens_meta action=login com o código de sapiensinteticos.com/conectar-claude.
268
268
  - sapiens_meta action=formats devolve os schemas por formato; action=whoami diz tier (user/admin) + saldo. sapiens_image e sapiens_video action=models trazem o catálogo com o preço ATUAL: consulte em vez de chutar custo.
package/dist/skills.js CHANGED
@@ -209,7 +209,7 @@ Dar som a um vídeo SEU já pronto é \`sapiens_video action=sonorize\`, não é
209
209
 
210
210
  \`action=models\` (sem custo, sem login) lista os modelos ativos com durações, resoluções, disponibilidade e a FAIXA de preço: o padrão, o piso e o teto, cada um dizendo em qual configuração acontece. Motor de preço-por-segundo não tem "um preço": o mesmo modelo custa 3x mais em 1080p e 30s do que em 720p e 5s.
211
211
 
212
- Dois campos do payload evitam garimpo na lista. bestFor diz pra que o motor serve (audio = som nativo, fala = lip-sync, referencia = aceita referência de quem aparece, movimento = precisa de vídeo-guia, longo = passa dos 15s): filtre por aqui antes de comparar preço. family + variant dizem quem é o mesmo motor em geração ou tier diferente: as seis linhas de Seedance (1.0 Fast, 1.5 Pro, 2.0, 2.0 Fast, 2.0 Mini, 2.5) são UM motor, não seis. Escolha a família pelo trabalho, depois a variante pela conta.
212
+ Dois campos do payload evitam garimpo na lista. bestFor diz pra que o motor serve (audio = som nativo, fala = lip-sync, referencia = aceita referência de quem aparece, movimento = precisa de vídeo-guia, longo = passa dos 15s): filtre por aqui antes de comparar preço. family + variant dizem quem é o mesmo motor em geração ou tier diferente: as quatro linhas de Seedance (2.0, 2.0 Fast, 2.0 Mini, 2.5) são UM motor, não quatro. Escolha a família pelo trabalho, depois a variante pela conta.
213
213
 
214
214
  \`action=price\` (sem custo, sem login) cota a configuração EXATA antes de rodar: passe \`model\`, \`durationSec\`, \`resolution\` e \`audio\` e receba o número que vai ser debitado, calculado pela mesma função que cobra. Ela também avisa quando o motor não aceita o que você pediu e vai cobrar outra coisa (\`clamped\`), o que acontece quando a duração não existe no enum ou a resolução cai num tier diferente.
215
215
 
@@ -346,7 +346,6 @@ Nas Krea 2 da WaveSpeed a referência é a imagem-base (uma só), e \`refStrengt
346
346
  ## Cuidados
347
347
 
348
348
  - \`generate\` é SÍNCRONA e cobra ao concluir. Modelo pesado (Pro, gpt-image-2-high, Grok quality, Seedream 5.0 Pro, 2K/4K) cai na regra do timeout: cheque \`sapiens_gallery action=list\` antes de repetir, senão cobra duas vezes. O \`seedream-5-0-pro\` foi medido em ~140s, ACIMA do teto de 120s do cliente, então o Timeout nele é o esperado e a imagem está lá.
349
- - \`request_generation\` NÃO gera imagem: só cria a row pendente e debita, pra modelos \`sapiens-video-*\` antes de renderizar. Caminho legado: pra vídeo novo, \`sapiens_video action=create\` faz tudo num call.
350
349
  - Uma imagem só vira pública (e ganha página indexável) com \`sapiens_gallery action=publish\`.
351
350
 
352
351
  ## Quando a pessoa quer "do jeito dela", com o personagem dela
@@ -430,7 +429,7 @@ Redes aceitas: instagram, tiktok, youtube, x, threads, bluesky, twitch, spotify,
430
429
 
431
430
  A partir daí ele é personagem reutilizável: entra como referência nas gerações daqui (\`referenceImageUrls\` em \`sapiens_image\`), rende ficha oficial desenhada no traço dele (\`generate_sheet\`) e aparece no perfil. Personagem que fica solto na galeria vira imagem bonita e some.
432
431
 
433
- **As placas são a referência de ÂNGULO.** Referência trava a posição da câmera: com foto de frente, o motor devolve rosto de frente por mais que o texto peça perfil. \`sapiens_character action=generate_plates characterId="<id>"\` gera seis ângulos do mesmo rosto numa geração só (perfil, camera-alta, camera-baixa, frontal, cabeca-baixa, outro-lado), cada um com uma expressão, e grava as seis no personagem com o papel de cada uma. Elas voltam em \`action=get\` no campo \`plates\` e NÃO entram em \`imageUrls\`: quando a cena pede perfil, passe a placa de perfil em \`referenceImageUrls\`; no resto, as fotos de sempre. Só pra personagem com rosto de gente (form human ou humanoid), e cada lote substitui o anterior.
432
+ **As placas são a referência de ÂNGULO.** Referência trava a posição da câmera: com foto de frente, o motor devolve rosto de frente por mais que o texto peça perfil. \`sapiens_character action=generate_plates characterId="<id>"\` gera seis ângulos do mesmo rosto numa geração só (perfil, camera-alta, camera-baixa, frontal, cabeca-baixa, outro-lado), cada um com uma expressão, e grava as seis no personagem com o papel de cada uma. Elas voltam em \`action=get\` no campo \`plates\` e NÃO entram em \`imageUrls\`: quando a cena pede perfil, passe a placa de perfil em \`referenceImageUrls\`; no resto, as fotos de sempre. Só pra personagem com rosto de gente (form human ou humanoid). Os lotes ACUMULAM (teto de 48 placas por personagem; a que não serve sai com remove_image), e \`plates\` vem do lote mais novo pro mais velho: escolha pelo \`role\`, nunca pela posição, e a primeira placa de cada papel é a mais nova.
434
433
 
435
434
  Duas notas honestas: a imagem adicionada por link continua morando no servidor de onde veio, então quem quiser os bytes guardados aqui gera uma peça a partir dela; e trazer arquivo de fora direto pra galeria (\`sapiens_gallery\` upload/ingest) é porta de admin hoje, então em conta comum isso recusa e o caminho é o de cima.
436
435
 
@@ -735,7 +734,7 @@ A foto NUNCA vai pra banco nenhum, nem da casa nem de terceiro:
735
734
  {
736
735
  name: "pontes",
737
736
  title: "Pontes: gerar com a sua chave e trazer pro acervo",
738
- description: "A pessoa tem crédito na Kie, na fal, na WaveSpeed, no Replicate, na Magnific, na Sogni ou na própria placa e quer usar o personagem dela lá, quer gerar com a chave dela em vez de Sinapse, ou está sem Sinapse pra vídeo. Como levar a ficha, gerar no balcão dela (no MCP instalado, fal, Kie, WaveSpeed e Replicate saem pela sapiens_pontes; a chave nunca passa pelo Sapiens) e a peça voltar pro acervo com a ficha inteira. Puxe quando ouvir 'gera com a minha chave', 'tenho FAL_KEY', 'tem Kie aí?', 'uso a fal', 'tenho WaveSpeed', 'uso o Replicate', 'gastei minhas Sinapses', 'gero na Magnific', 'tenho ComfyUI'.",
737
+ description: "A pessoa tem crédito na Kie, na fal, na WaveSpeed, no Replicate, na OpenRouter, na Magnific, na Sogni ou na própria placa e quer usar o personagem dela lá, quer gerar com a chave dela em vez de Sinapse, ou está sem Sinapse pra vídeo. Como levar a ficha, gerar no balcão dela (no MCP instalado, fal, Kie, WaveSpeed, Replicate e OpenRouter saem pela sapiens_pontes; a chave nunca passa pelo Sapiens) e a peça voltar pro acervo com a ficha inteira. Puxe quando ouvir 'gera com a minha chave', 'tenho FAL_KEY', 'tem Kie aí?', 'uso a fal', 'tenho WaveSpeed', 'uso o Replicate', 'tenho OpenRouter', 'gastei minhas Sinapses', 'gero na Magnific', 'tenho ComfyUI'.",
739
738
  body: `## O que é uma ponte
740
739
 
741
740
  O Sapiens é uma das pontes, não um muro. A pessoa monta o personagem aqui (ficha, passaporte, referências) e gera a peça ONDE TEM CRÉDITO: Kie, fal, Magnific, Sogni, Krea, Replicate, a própria placa. A peça volta pro acervo dela como NATIVA, com motor, prompt, custo real e personagem na ficha, custo 0 em Sinapses. O portfólio dela cresce aqui; o dinheiro dela sai de onde ela já pôs.
@@ -750,7 +749,7 @@ São duas portas lado a lado, e nenhuma é a reserva da outra: Sinapse (sapiens_
750
749
 
751
750
  Ordem de preferência quando há mais de uma opção, pelo que custa DE VERDADE pra pessoa:
752
751
  1. O que ela já paga fixo (assinatura Sogni, Magnific com crédito parado, a própria placa): custo marginal zero.
753
- 2. O balcão onde ela tem crédito pré-pago (Kie, fal, WaveSpeed, Krea, Replicate): custo por peça, em dólar.
752
+ 2. O balcão onde ela tem crédito pré-pago (Kie, fal, WaveSpeed, Krea, Replicate, OpenRouter): custo por peça, em dólar.
754
753
  3. \`sapiens_video\` / \`sapiens_image\` em Sinapse: o único que serve motor exclusivo da casa e o único com a régua de qualidade da casa por trás. Não é o último por ser pior: é o último por ser o mais caro por peça.
755
754
 
756
755
  Confirme com ela ANTES de disparar em qualquer balcão pago. Geração de fora também custa dinheiro real, só que dela.
@@ -766,13 +765,13 @@ Confirme com ela ANTES de disparar em qualquer balcão pago. Geração de fora t
766
765
 
767
766
  Regras que valem fora igual dentro: prompt SEM idade em número; personagem nunca menor (a casa trabalha com 23+ no foco e 18 é piso absoluto); motor citado pelo nome que ele tem no provedor. Prompt novo que funcionar lá fora: grave de volta com \`sapiens_character action=set_passport\` (campo \`recipes\`, prompt verbatim + note com o que provou). É isso que faz a próxima rodada não redescobrir.
768
767
 
769
- ## Passo 3a: gerar pela porta local (fal, Kie, WaveSpeed e Replicate, MCP instalado)
768
+ ## Passo 3a: gerar pela porta local (fal, Kie, WaveSpeed, Replicate e OpenRouter, MCP instalado)
770
769
 
771
770
  Com o \`sapiens-mcp\` instalado (plugin do Claude Code, extensão do Claude Desktop ou npx) e a chave configurada, é a porta mais curta:
772
771
 
773
772
  1. \`sapiens_pontes action=balcoes\` diz quais chaves estão configuradas (pelo nome, sem ler o valor). Faltando, repasse o \`comoConfigurar\` da mesma resposta: ele diz onde a chave entra no jeito que o Sapiens foi instalado.
774
773
  2. Confirme com ela o modelo e o custo no provedor. É dinheiro dela.
775
- 3. \`sapiens_pontes action=gerar\` com \`provedor\` ('fal', 'kie', 'wavespeed' ou 'replicate'), \`modelo\` (o slug como está na página do modelo no provedor; no Replicate, 'dono/nome' ou 'dono/nome:<versão>' pra modelo de comunidade) e \`input\` (o corpo que a doc do modelo descreve, com o prompt DENTRO), mais \`characterId\` e \`referenceImageIds\` pra ficha. O descritor do passaporte vai no começo do prompt; o bloco NEGATIVE vai no campo \`negative_prompt\` quando o modelo tem um (no corpo do prompt, negação acende o que proíbe, e o piso de idade da casa recusa palavra de menor ali). As imagens da ficha vão no campo de referência do modelo (\`image_url\`, \`image_urls\`).
774
+ 3. \`sapiens_pontes action=gerar\` com \`provedor\` ('fal', 'kie', 'wavespeed', 'replicate' ou 'openrouter'), \`modelo\` (o slug como está na página do modelo no provedor; no Replicate, 'dono/nome' ou 'dono/nome:<versão>' pra modelo de comunidade; na OpenRouter, 'dono/nome' e \`midia='video'\` quando é vídeo) e \`input\` (o corpo que a doc do modelo descreve, com o prompt DENTRO), mais \`characterId\` e \`referenceImageIds\` pra ficha. O descritor do passaporte vai no começo do prompt; o bloco NEGATIVE vai no campo \`negative_prompt\` quando o modelo tem um (no corpo do prompt, negação acende o que proíbe, e o piso de idade da casa recusa palavra de menor ali). As imagens da ficha vão no campo de referência do modelo (\`image_url\`, \`image_urls\`).
776
775
  4. Voltou \`rodando\`? Chame \`sapiens_pontes action=status jobId=...\`, nunca \`gerar\` de novo: o job está gravado na máquina dela, e o pedido idêntico em 30 minutos devolve o job que existe em vez de cobrar outra vez.
777
776
  5. Terminado, a peça entra sozinha no acervo, privada, custo 0 em Sinapses, com motor, prompt verbatim e custo na ficha. Mostre com \`sapiens_gallery action=list\`.
778
777
 
@@ -40,7 +40,8 @@ import { getClientName } from "../clientIdentity.js";
40
40
  * ângulos do mesmo rosto, recortada em seis referências com
41
41
  * papel (perfil, camera-alta, camera-baixa, frontal,
42
42
  * cabeca-baixa, outro-lado). Voltam em action=get no campo
43
- * `plates`; cada lote substitui o anterior. Só personagem com
43
+ * `plates`, lote mais novo primeiro; os lotes acumulam e a
44
+ * placa ruim sai com remove_image. Só personagem com
44
45
  * rosto de gente (form human ou humanoid).
45
46
  * - generate_stickers: desenha stickers do personagem (COBRA), em dois
46
47
  * caminhos (`stickerTier`): 'folha' (default) traz 5 de uma
@@ -114,6 +114,9 @@ const CHANNELS = [
114
114
  // mensagem) e POR QUANTO (priceCents, nos dois).
115
115
  "fanvue",
116
116
  "fanvue-dm",
117
+ // Pornhub: a conta de model da Aria, foto e video, sem API. A peca nasce na
118
+ // fila como pacote e a mao sobe. So ilustrada: fotorreal de IA e barrado la.
119
+ "pornhub",
117
120
  // Segunda conta da mesma rede, nomeada (Model A).
118
121
  "instagram-pro",
119
122
  "threads-pro",
@@ -275,7 +278,7 @@ export const distributionSchema = z.object({
275
278
  dispensada: z.boolean().optional(),
276
279
  }))
277
280
  .optional()
278
- .describe("tree-mark: as células da linhagem em `slug`, uma por rede da árvore (x, instagram, bluesky, email, telegram, tumblr, deviantart, threads, tiktok, youtube, fanvue; a lista viva sai em tree, campo redes). handle = o @ (ou a URL do perfil, que vira @) ou, na rede email, o endereço: é a conta que existe lá fora; string vazia limpa. canal = a conta da casa que é desta linhagem nessa rede (instagram-6, x-buffer-3), só em rede de conta própria e nunca no bluesky, que é a mãe (lineage-save). dispensada = true quando a linhagem decidiu não ter a rede; X, Instagram, Bluesky, email e Telegram são obrigatórias e o servidor recusa. Tudo ou nada: uma célula recusada não grava nenhuma."),
281
+ .describe("tree-mark: as células da linhagem em `slug`, uma por rede da árvore (x, instagram, bluesky, email, telegram, tumblr, deviantart, threads, tiktok, youtube, fanvue, pornhub; a lista viva sai em tree, campo redes). handle = o @ (ou a URL do perfil, que vira @) ou, na rede email, o endereço: é a conta que existe lá fora; string vazia limpa. canal = a conta da casa que é desta linhagem nessa rede (instagram-6, x-buffer-3), só em rede de conta própria e nunca no bluesky, que é a mãe (lineage-save). dispensada = true quando a linhagem decidiu não ter a rede; X, Instagram, Bluesky, email e Telegram são obrigatórias e o servidor recusa. Tudo ou nada: uma célula recusada não grava nenhuma."),
279
282
  nome: z.string().optional().describe("lineage-new: o nome da linhagem (obrigatório). lineage-save: renomeia."),
280
283
  grupo: z
281
284
  .enum(["casa", "personagens", "produtos"])
@@ -111,7 +111,7 @@ export const imageSchema = z.object({
111
111
  prompt: z
112
112
  .string()
113
113
  .optional()
114
- .describe("Prompt da imagem (obrigatório em generate/request_generation). Full-bleed, sujeito oversized."),
114
+ .describe("Prompt da imagem (obrigatório em generate). Full-bleed, sujeito oversized."),
115
115
  model: z
116
116
  .enum(MODELS)
117
117
  .optional()
@@ -140,7 +140,7 @@ export const imageSchema = z.object({
140
140
  brandSlug: z
141
141
  .string()
142
142
  .optional()
143
- .describe("Slug do brand (design system) pra aplicar estilo visual. Fonte única: tabela `brands` (ex: 'sapiens', 'solarpunk', 'editorial-duotone', 'brutalista-mono'). Default: nenhum (prompt cru). NÃO herda o padrão da conta de propósito: aqui o estilo é escolha do momento, e quem só quer uma imagem pra outra coisa não deve nascer preso num visual. O padrão da pessoa (quando existe) vem em sapiens_meta action=start, campo designSystem: OFEREÇA quando a peça for da marca dela, deixe cru no resto. Vale em action=generate (mode=create) e request_generation."),
143
+ .describe("Slug do brand (design system) pra aplicar estilo visual. Fonte única: tabela `brands` (ex: 'sapiens', 'solarpunk', 'editorial-duotone', 'brutalista-mono'). Default: nenhum (prompt cru). NÃO herda o padrão da conta de propósito: aqui o estilo é escolha do momento, e quem só quer uma imagem pra outra coisa não deve nascer preso num visual. O padrão da pessoa (quando existe) vem em sapiens_meta action=start, campo designSystem: OFEREÇA quando a peça for da marca dela, deixe cru no resto. Vale em action=generate (mode=create)."),
144
144
  brandMark: z
145
145
  .enum(["persona", "logo", "none"])
146
146
  .optional()
@@ -269,6 +269,14 @@ export async function image(args) {
269
269
  "Nudez é nível, não motor: quando `ousadia` vem preenchido, gere com ousadia.model + loraIntensity pra ter nudez e com o id da linha pra não ter (o preço com nudez vem em ousadia.priceSinapses).",
270
270
  };
271
271
  }
272
+ // request_generation saiu de linha em set/2026. Ele só abria a row e
273
+ // debitava, e o render que vinha depois (vídeo) nasce hoje inteiro no
274
+ // sapiens_video action=create. Fica no enum pra quem chega com instrução
275
+ // velha receber o caminho em vez de um erro de schema, e recusa ANTES de
276
+ // falar com o servidor: nada é cobrado.
277
+ if (args.action === "request_generation") {
278
+ throw new Error("request_generation saiu de linha e não cobrou nada. Imagem: action=generate (gera e cobra num call só). Vídeo: sapiens_video action=create (cria a row de vídeo e renderiza).");
279
+ }
272
280
  const sessionToken = getSessionToken();
273
281
  // v1.8: compose tem args próprios, validação separada
274
282
  if (args.action === "compose") {
@@ -291,25 +299,6 @@ export async function image(args) {
291
299
  if (!args.prompt || args.prompt.trim().length < 5) {
292
300
  throw new Error("Faltando 'prompt' (mínimo 5 chars).");
293
301
  }
294
- // v1.7: request_generation cria row pendente em generatedImages e debita
295
- // créditos. Necessário antes de chamar sapiens_shorts/sapiens_video (que
296
- // exigem imageId pré-existente). Pra modelos de imagem (nano-banana-*,
297
- // gpt-image-2-*), prefira action="generate" que já faz tudo num call.
298
- if (args.action === "request_generation") {
299
- const reqModel = args.model ?? "nano-banana-2";
300
- return await import("../convexClient.js").then(({ convexMutation }) => convexMutation("mcpExtras:mcpRequestGeneration", {
301
- sessionToken,
302
- model: reqModel,
303
- styleId: args.styleId,
304
- prompt: args.prompt,
305
- negativePrompt: args.negativePrompt,
306
- aspectRatio: args.aspectRatio ?? "16:9",
307
- size: args.size ?? defaultSizeFor(reqModel) ?? "1K",
308
- brandSlug: args.brandSlug,
309
- brandMark: args.brandMark,
310
- skills: args.skills,
311
- }));
312
- }
313
302
  // Edit/variation usam desktopMcp.generateImageOnline (suporta sourceImageId
314
303
  // inline como reference). Create sem sourceImageId pode usar a rota mais
315
304
  // direta do pipelineMcpImage (catálogo legacy). Pra simplificar, usamos
@@ -206,6 +206,13 @@ const PONTES = [
206
206
  roda: "catálogo aberto (Flux, WAN, LTX e o que a comunidade sobe)",
207
207
  nota: "A casa não mediu motor por lá; o contrato de ingest é o mesmo.",
208
208
  },
209
+ {
210
+ key: "openrouter",
211
+ nome: "OpenRouter",
212
+ envVars: ["OPENROUTER_API_KEY"],
213
+ roda: "imagem (Seedream, GPT Image, Gemini) e vídeo (Veo, Seedance) num saldo só",
214
+ nota: "A peça vem atrás da chave (base64 ou download autenticado): a porta local baixa pra máquina e ingere pelo arquivo. A casa ainda não mediu motor por lá.",
215
+ },
209
216
  {
210
217
  key: "bancada",
211
218
  nome: "A própria máquina (ComfyUI, SD local)",
@@ -215,7 +222,7 @@ const PONTES = [
215
222
  },
216
223
  ];
217
224
  /** Os balcões que a sapiens_pontes dirige daqui (os outros vão pelo harness + ingest). */
218
- const PORTA_LOCAL = ["fal", "kie", "wavespeed", "replicate"];
225
+ const PORTA_LOCAL = ["fal", "kie", "wavespeed", "replicate", "openrouter"];
219
226
  const FIRST_POWERS = [
220
227
  {
221
228
  icon: "📚",
@@ -39,13 +39,17 @@ import { gallery } from "./gallery.js";
39
39
  * e é por isso que balcão novo entra por código, nunca por campo livre.
40
40
  * 28/09/2026: WaveSpeed e Replicate entraram ("tem que ter liberdade de colocar
41
41
  * várias como wavespeed", o dono).
42
+ * 03/10/2026: OpenRouter entrou. É o único que devolve a peça atrás da chave
43
+ * (imagem em base64 no corpo, vídeo num endereço que pede Authorization), então
44
+ * o adaptador baixa pra esta máquina e o ingest sobe pelo arquivo.
42
45
  */
43
- const PROVEDORES = ["fal", "kie", "wavespeed", "replicate"];
46
+ const PROVEDORES = ["fal", "kie", "wavespeed", "replicate", "openrouter"];
44
47
  const BALCOES = {
45
48
  fal: { nome: "fal.ai", envVar: "FAL_KEY", artigo: "a" },
46
49
  kie: { nome: "Kie", envVar: "KIE_API_KEY", artigo: "a" },
47
50
  wavespeed: { nome: "WaveSpeed", envVar: "WAVESPEED_API_KEY", artigo: "a" },
48
51
  replicate: { nome: "Replicate", envVar: "REPLICATE_API_TOKEN", artigo: "o" },
52
+ openrouter: { nome: "OpenRouter", envVar: "OPENROUTER_API_KEY", artigo: "a" },
49
53
  };
50
54
  /** "na fal.ai", "do Replicate": a preposição já com o artigo do balcão. */
51
55
  function balcaoCom(p, prep) {
@@ -65,6 +69,7 @@ const NOMES_DE_CHAVE = [
65
69
  "SOGNI_API_KEY",
66
70
  "KREA_API_KEY",
67
71
  "REPLICATE_API_TOKEN",
72
+ "OPENROUTER_API_KEY",
68
73
  ];
69
74
  /**
70
75
  * No plugin do Claude Code e na extensão do Claude Desktop, o campo de chave
@@ -79,6 +84,7 @@ const CAMPO_DO_APP = {
79
84
  KIE_API_KEY: "SAPIENS_KIE_API_KEY",
80
85
  WAVESPEED_API_KEY: "SAPIENS_WAVESPEED_API_KEY",
81
86
  REPLICATE_API_TOKEN: "SAPIENS_REPLICATE_API_TOKEN",
87
+ OPENROUTER_API_KEY: "SAPIENS_OPENROUTER_API_KEY",
82
88
  };
83
89
  const JANELA_REPETIDO_MS = 30 * 60 * 1000;
84
90
  const MAX_JOBS_GUARDADOS = 200;
@@ -95,12 +101,16 @@ export const pontesSchema = z.object({
95
101
  provedor: z
96
102
  .enum(PROVEDORES)
97
103
  .optional()
98
- .describe("action=gerar: o balcão. 'fal' (FAL_KEY), 'kie' (KIE_API_KEY), 'wavespeed' (WAVESPEED_API_KEY) ou 'replicate' (REPLICATE_API_TOKEN). A chave mora nesta máquina: no campo do plugin ou da extensão, ou no env do config do cliente MCP. action=balcoes diz quais estão configuradas."),
104
+ .describe("action=gerar: o balcão. 'fal' (FAL_KEY), 'kie' (KIE_API_KEY), 'wavespeed' (WAVESPEED_API_KEY) ou 'replicate' (REPLICATE_API_TOKEN) ou 'openrouter' (OPENROUTER_API_KEY). A chave mora nesta máquina: no campo do plugin ou da extensão, ou no env do config do cliente MCP. action=balcoes diz quais estão configuradas."),
99
105
  modelo: z
100
106
  .string()
101
107
  .max(200)
102
108
  .optional()
103
- .describe("action=gerar: o slug do modelo NO PROVEDOR, igual à página dele. fal: o id do endpoint (ex: 'fal-ai/flux/dev'), que vai no caminho de queue.fal.run. Kie: o campo 'model' do Market (ex: 'kling/v2-1-standard'), que vai no corpo do createTask. WaveSpeed: o slug da página do modelo (ex: 'wavespeed-ai/flux-2-klein-9b/text-to-image'), que vai no caminho de api.wavespeed.ai/api/v3. Replicate: 'dono/nome' pra modelo oficial (ex: 'black-forest-labs/flux-schnell') ou 'dono/nome:<versão de 64 caracteres>' pra modelo de comunidade. Não invente: confira na página do modelo."),
109
+ .describe("action=gerar: o slug do modelo NO PROVEDOR, igual à página dele. fal: o id do endpoint (ex: 'fal-ai/flux/dev'), que vai no caminho de queue.fal.run. Kie: o campo 'model' do Market (ex: 'kling/v2-1-standard'), que vai no corpo do createTask. WaveSpeed: o slug da página do modelo (ex: 'wavespeed-ai/flux-2-klein-9b/text-to-image'), que vai no caminho de api.wavespeed.ai/api/v3. Replicate: 'dono/nome' pra modelo oficial (ex: 'black-forest-labs/flux-schnell') ou 'dono/nome:<versão de 64 caracteres>' pra modelo de comunidade. OpenRouter: o slug 'dono/nome' da página do modelo (ex: 'bytedance-seed/seedream-4.5' pra imagem, 'google/veo-3.1' pra vídeo), que vai no corpo; diga midia='video' quando for vídeo. Não invente: confira na página do modelo."),
110
+ midia: z
111
+ .enum(["imagem", "video"])
112
+ .optional()
113
+ .describe("action=gerar, só provedor='openrouter': 'imagem' (padrão, POST /api/v1/images) ou 'video' (POST /api/v1/videos, fila). Os outros balcões sabem pelo slug e ignoram este campo."),
104
114
  input: z
105
115
  .union([z.record(z.any()), z.string()])
106
116
  .optional()
@@ -314,10 +324,12 @@ function hashDoPedido(provedor, modelo, input) {
314
324
  const MODELO_VALIDO = /^[a-z0-9][a-z0-9._-]*(\/[a-z0-9][a-z0-9._-]*)*$/i;
315
325
  // Replicate: "dono/nome" (modelo oficial) ou "dono/nome:<versão>" (comunidade).
316
326
  const MODELO_REPLICATE = /^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*(:[a-f0-9]{64})?$/i;
327
+ // OpenRouter: "dono/nome", com variante opcional (":free", ":nitro").
328
+ const MODELO_OPENROUTER = /^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*(:[a-z0-9-]+)?$/i;
317
329
  function confereModelo(provedor, modelo) {
318
- const forma = provedor === "replicate" ? MODELO_REPLICATE : MODELO_VALIDO;
330
+ const forma = provedor === "replicate" ? MODELO_REPLICATE : provedor === "openrouter" ? MODELO_OPENROUTER : MODELO_VALIDO;
319
331
  if (!forma.test(modelo) || modelo.includes("..")) {
320
- throw new Error(`modelo inválido: "${modelo.slice(0, 80)}". Use o slug como está na página do modelo no provedor (ex: 'fal-ai/flux/dev', 'kling/v2-1-standard', 'wavespeed-ai/flux-2-klein-9b/text-to-image', 'black-forest-labs/flux-schnell').`);
332
+ throw new Error(`modelo inválido: "${modelo.slice(0, 80)}". Use o slug como está na página do modelo no provedor (ex: 'fal-ai/flux/dev', 'kling/v2-1-standard', 'wavespeed-ai/flux-2-klein-9b/text-to-image', 'black-forest-labs/flux-schnell', 'google/veo-3.1').`);
321
333
  }
322
334
  }
323
335
  /** O nome do motor na ficha: sempre com o prefixo do provedor de verdade. */
@@ -679,11 +691,125 @@ async function replicateLer(chave, job) {
679
691
  return { estado: "falhou", erro: "o Replicate terminou mas não devolveu arquivo" };
680
692
  return { estado: "pronto", urls, custo: "pago no Replicate com a sua chave" };
681
693
  }
694
+ // --- OpenRouter: imagem síncrona em base64, vídeo em fila atrás da chave ---
695
+ const OPENROUTER_API = "https://openrouter.ai/api/v1";
696
+ const OPENROUTER_TETO_BYTES = 200 * 1024 * 1024;
697
+ const EXT_DO_TIPO = {
698
+ "image/png": "png",
699
+ "image/jpeg": "jpg",
700
+ "image/webp": "webp",
701
+ "video/mp4": "mp4",
702
+ "video/quicktime": "mov",
703
+ };
704
+ function pastaDePecas() {
705
+ const dir = path.join(pastaLocal(), "pontes-pecas");
706
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
707
+ return dir;
708
+ }
709
+ function custoOpenRouter(usage) {
710
+ const c = usage?.cost;
711
+ return typeof c === "number" ? `US$ ${c.toFixed(2)} na OpenRouter` : "pago na OpenRouter com a sua chave";
712
+ }
713
+ async function openrouterEnviar(chave, modelo, input, midia = "imagem") {
714
+ const video = midia === "video";
715
+ const res = await fetch(`${OPENROUTER_API}/${video ? "videos" : "images"}`, {
716
+ method: "POST",
717
+ headers: { Authorization: `Bearer ${chave}`, "Content-Type": "application/json" },
718
+ body: JSON.stringify({ ...input, model: modelo }),
719
+ // Imagem volta pronta nesta mesma resposta: o teto cobre o render inteiro.
720
+ signal: AbortSignal.timeout(video ? 30_000 : 90_000),
721
+ });
722
+ const json = await res.json().catch(() => null);
723
+ const recusa = (detalhe) => new Error(`A OpenRouter recusou o pedido (HTTP ${res.status}): ${detalhe}. ` +
724
+ (res.ok ? "Confira em openrouter.ai/activity se o pedido entrou antes de repetir." : "Nada foi gerado."));
725
+ if (video) {
726
+ if (!res.ok || typeof json?.id !== "string")
727
+ throw recusa(detalheDoErro(json) || "resposta sem detalhe");
728
+ return {
729
+ requestId: json.id,
730
+ midia,
731
+ statusUrl: urlNoHost(json.polling_url, "openrouter.ai") ?? `${OPENROUTER_API}/videos/${encodeURIComponent(json.id)}`,
732
+ };
733
+ }
734
+ const itens = Array.isArray(json?.data) ? json.data : [];
735
+ if (!res.ok || !itens.length)
736
+ throw recusa(detalheDoErro(json) || "resposta sem imagem");
737
+ const id = `or_${randomBytes(6).toString("hex")}`;
738
+ const arquivos = [];
739
+ for (const [i, item] of itens.slice(0, MAX_PECAS_POR_JOB).entries()) {
740
+ const ext = EXT_DO_TIPO[String(item?.media_type)];
741
+ if (typeof item?.b64_json !== "string" || !ext)
742
+ continue;
743
+ const arq = path.join(pastaDePecas(), `${id}-${i}.${ext}`);
744
+ fs.writeFileSync(arq, Buffer.from(item.b64_json, "base64"), { mode: 0o600 });
745
+ arquivos.push(arq);
746
+ }
747
+ if (!arquivos.length)
748
+ throw recusa("a resposta não trouxe imagem em png, jpg ou webp");
749
+ return { requestId: id, midia, arquivos, custo: custoOpenRouter(json.usage) };
750
+ }
751
+ async function openrouterLer(chave, job) {
752
+ // Imagem: a peça já está no disco desde o enviar.
753
+ if (job.arquivos?.length) {
754
+ return { estado: "pronto", urls: job.arquivos, custo: job.custo ?? "pago na OpenRouter com a sua chave" };
755
+ }
756
+ const aindaNao = { estado: job.estado === "rodando" ? "rodando" : "fila" };
757
+ const statusUrl = urlNoHost(job.statusUrl, "openrouter.ai");
758
+ if (!statusUrl)
759
+ return { estado: "falhou", erro: "job sem endereço de consulta da OpenRouter" };
760
+ let res;
761
+ try {
762
+ res = await fetch(statusUrl, {
763
+ headers: { Authorization: `Bearer ${chave}` },
764
+ signal: AbortSignal.timeout(20_000),
765
+ });
766
+ }
767
+ catch {
768
+ return aindaNao;
769
+ }
770
+ if (!res.ok)
771
+ return aindaNao;
772
+ const v = await res.json().catch(() => null);
773
+ if (v?.status === "failed") {
774
+ return { estado: "falhou", erro: detalheDoErro(v) || "a OpenRouter marcou falha sem motivo" };
775
+ }
776
+ if (v?.status !== "completed")
777
+ return { estado: v?.status === "in_progress" ? "rodando" : "fila" };
778
+ // O endereço do vídeo não é pré-assinado: baixa daqui, com a chave, só do host dela.
779
+ const fontes = (Array.isArray(v.unsigned_urls) ? v.unsigned_urls : [])
780
+ .map((u) => urlNoHost(u, "openrouter.ai"))
781
+ .filter((u) => Boolean(u))
782
+ .slice(0, MAX_PECAS_POR_JOB);
783
+ if (!fontes.length)
784
+ return { estado: "falhou", erro: "a OpenRouter terminou mas não devolveu arquivo" };
785
+ const arquivos = [];
786
+ for (const [i, u] of fontes.entries()) {
787
+ let out;
788
+ try {
789
+ out = await fetch(u, { headers: { Authorization: `Bearer ${chave}` }, signal: AbortSignal.timeout(60_000) });
790
+ }
791
+ catch {
792
+ return { estado: "rodando" }; // download que cai não é geração que falhou
793
+ }
794
+ if (!out.ok) {
795
+ return out.status >= 500 ? { estado: "rodando" } : { estado: "falhou", erro: `download HTTP ${out.status}` };
796
+ }
797
+ const tipo = (out.headers.get("content-type") ?? "video/mp4").split(";")[0].trim();
798
+ const buf = Buffer.from(await out.arrayBuffer());
799
+ if (buf.length > OPENROUTER_TETO_BYTES)
800
+ return { estado: "falhou", erro: "o vídeo passou de 200 MB" };
801
+ const arq = path.join(pastaDePecas(), `${job.id}-${i}.${EXT_DO_TIPO[tipo] ?? "mp4"}`);
802
+ fs.writeFileSync(arq, buf, { mode: 0o600 });
803
+ arquivos.push(arq);
804
+ }
805
+ return { estado: "pronto", urls: arquivos, custo: custoOpenRouter(v.usage) };
806
+ }
682
807
  const ADAPTADORES = {
683
808
  fal: { enviar: falEnviar, ler: falLer, intervaloMs: 2500 },
684
809
  kie: { enviar: kieEnviar, ler: kieLer, intervaloMs: 3000 },
685
810
  wavespeed: { enviar: wavespeedEnviar, ler: wavespeedLer, intervaloMs: 2500 },
686
811
  replicate: { enviar: replicateEnviar, ler: replicateLer, intervaloMs: 2500 },
812
+ openrouter: { enviar: openrouterEnviar, ler: openrouterLer, intervaloMs: 4000 },
687
813
  };
688
814
  // ---------------------------------------------------------------------------
689
815
  // O laço: consultar, e quando pronto, trazer pro acervo.
@@ -712,9 +838,11 @@ async function ingerirPecas(job) {
712
838
  if (ja?.imageId)
713
839
  continue;
714
840
  try {
841
+ // Peça que o balcão entregou atrás da chave já está no disco: sobe pelo arquivo.
842
+ const local = !url.startsWith("https://");
715
843
  const r = await gallery({
716
844
  action: "ingest",
717
- sourceUrl: url,
845
+ ...(local ? { filePath: url } : { sourceUrl: url }),
718
846
  externalEngine: job.ficha.externalEngine,
719
847
  prompt: job.ficha.prompt,
720
848
  externalCost: job.custo,
@@ -835,7 +963,8 @@ async function gerar(args) {
835
963
  throw new Error("Pra peça voltar pro acervo, a conta Sapiens precisa estar conectada neste MCP (sapiens_meta action=login). Sem conta, mande ingerir=false: a peça fica só no provedor.");
836
964
  }
837
965
  }
838
- const hash = hashDoPedido(provedor, modelo, input);
966
+ const midia = provedor === "openrouter" ? (args.midia ?? "imagem") : undefined;
967
+ const hash = hashDoPedido(provedor, midia ? `${midia}:${modelo}` : modelo, input);
839
968
  if (!args.forcar) {
840
969
  const igual = lerJobs()
841
970
  .reverse()
@@ -852,7 +981,7 @@ async function gerar(args) {
852
981
  }
853
982
  const promptDaFicha = args.prompt?.trim() ||
854
983
  (typeof input.prompt === "string" && input.prompt.trim() ? input.prompt : JSON.stringify(input).slice(0, 4000));
855
- const enviado = await ADAPTADORES[provedor].enviar(chave, modelo, input);
984
+ const enviado = await ADAPTADORES[provedor].enviar(chave, modelo, input, midia);
856
985
  const agora = Date.now();
857
986
  const job = {
858
987
  id: novoId(),
@@ -906,7 +1035,7 @@ function balcoes() {
906
1035
  })),
907
1036
  canal: CANAL,
908
1037
  comoConfigurar: ondeFicaAChave(),
909
- ondeAcharOModelo: "fal: o id do endpoint na página do modelo em fal.ai/models (ex: 'fal-ai/flux/dev'). Kie: o 'model' da página do modelo no Market da Kie. WaveSpeed: o slug da página do modelo em wavespeed.ai/models (ex: 'wavespeed-ai/flux-2-klein-9b/text-to-image'). Replicate: 'dono/nome' do modelo oficial (ex: 'black-forest-labs/flux-schnell'), ou 'dono/nome:<versão>' pra modelo de comunidade. O input é o corpo que a mesma página descreve.",
1038
+ ondeAcharOModelo: "fal: o id do endpoint na página do modelo em fal.ai/models (ex: 'fal-ai/flux/dev'). Kie: o 'model' da página do modelo no Market da Kie. WaveSpeed: o slug da página do modelo em wavespeed.ai/models (ex: 'wavespeed-ai/flux-2-klein-9b/text-to-image'). Replicate: 'dono/nome' do modelo oficial (ex: 'black-forest-labs/flux-schnell'), ou 'dono/nome:<versão>' pra modelo de comunidade. OpenRouter: o slug 'dono/nome' em openrouter.ai/models (imagem: 'bytedance-seed/seedream-4.5'; vídeo: 'google/veo-3.1' com midia='video'). O input é o corpo que a mesma página descreve.",
910
1039
  outrosBalcoes: "Sogni, Krea, Magnific e a própria placa seguem pelo seu harness + sapiens_gallery action=ingest (catálogo em sapiens_meta action=pontes). Balcão novo na porta local entra por código, não por campo livre: cada chave só vai pro endereço do próprio provedor.",
911
1040
  };
912
1041
  }
@@ -69,9 +69,6 @@ import { httpUrl } from "../schema.js";
69
69
  * - film-delete: apaga um spec (limpar rascunho/duplicata).
70
70
  *
71
71
  * Modelos (action=create):
72
- * - sapiens-video-seedance-15 Seedance 1.5 Pro — o meio-termo forte, 4-12s, 480/720/1080p (t2v/i2v).
73
- * Mais fiel que o 1.0 e bem mais barato que o 2.0. Sem áudio, sem
74
- * referência e sem frame final (o repertório completo é o 2.0).
75
72
  * - sapiens-video-seedance Seedance 2.0 — cena com áudio nativo, 4-15s, 480/720/1080p (t2v/i2v).
76
73
  * Aceita até 4 imagens de REFERÊNCIA (referenceImage*): guiam estilo/
77
74
  * personagem/composição sem virar o 1º frame. É o fluxo storyboard:
@@ -96,7 +93,7 @@ import { httpUrl } from "../schema.js";
96
93
  * - sapiens-video-hailuo Hailuo 2.3 (MiniMax) — física e movimento em 768p, 6 ou 10s (t2v/i2v).
97
94
  * Motor puro: SEM áudio, SEM referência, SEM frame final.
98
95
  * - sapiens-video-hailuo-pro Hailuo 2.3 Pro — o mesmo em 1080p, 5s fixo (duração não é param).
99
- * É o 1080p mais barato da casa depois do Seedance 1.0 Fast.
96
+ * É o 1080p mais barato da casa.
100
97
  * - sapiens-video-h3 MiniMax H3 — ÁUDIO nativo, frame final (role 'end'), t2v/i2v. O
101
98
  * menor custo por pixel da prateleira. Áudio vem sempre e já está
102
99
  * no preço (não tem toggle). 5 a 15s em '2k' (2560x1440) ou
@@ -151,14 +148,11 @@ import { httpUrl } from "../schema.js";
151
148
  * action=models (o create ainda aceita o id). Peça o 1.1 a não ser
152
149
  * que a pessoa queira comparar as duas versões.
153
150
  *
154
- * FORA DE PROPÓSITO: o `sapiens-video-seedance-fast` (Seedance 1.0 Fast) está ativo
155
- * no catálogo do backend mas NÃO entra neste enum. Ele é geração velha: sem áudio,
156
- * sem referência e sem frame final. Economiza pouco contra o 1.5 Pro e perde três
157
- * capacidades. Quem quer queimar tentativa barata usa o
158
- * `sapiens-video-seedance-2-mini`, que custa metade do 2.0 com o repertório inteiro.
159
- * Não "conserte" essa ausência: ela é decisão (29/jul/2026), escrita em
160
- * VIDEO_FORA_DE_PROPOSITO logo abaixo do enum. O gate audit:catalog-parity cobra
161
- * que todo motor ATIVO do catálogo esteja no enum ou nessa lista, com motivo.
151
+ * FORA DE PROPÓSITO: hoje nenhum. O `sapiens-video-seedance-fast` (1.0 Fast) morava em
152
+ * VIDEO_FORA_DE_PROPOSITO até sair do catálogo junto do 1.5 Pro, em 28/set/2026. Quem
153
+ * quer queimar tentativa barata usa o `sapiens-video-seedance-2-mini`, que custa metade
154
+ * do 2.0 com o repertório inteiro. O gate audit:catalog-parity cobra que todo motor
155
+ * ATIVO do catálogo esteja no enum ou naquela lista, com motivo.
162
156
  *
163
157
  * CUSTO: server-side por config (duração x resolução [x áudio]). NÃO existe "o preço"
164
158
  * de um motor por segundo: existe faixa. Omitir `resolution` não pega o meio nem o
@@ -170,9 +164,7 @@ import { httpUrl } from "../schema.js";
170
164
  *
171
165
  * Retorna `{ success, url, imageId, cost }` ou `{ success: false, error }`.
172
166
  */
173
- // "sapiens-video-seedance-fast" (1.0 Fast) fica de fora de propósito, ver bloco acima.
174
167
  const VIDEO_MODELS = [
175
- "sapiens-video-seedance-15",
176
168
  "sapiens-video-seedance",
177
169
  "sapiens-video-seedance-2-fast",
178
170
  "sapiens-video-seedance-2-mini",
@@ -200,11 +192,7 @@ const VIDEO_MODELS = [
200
192
  // gate audit:catalog-parity (apps/sapiens) lê este mapa: id ativo que não está
201
193
  // nem no enum nem aqui é erro, porque action=models lista, a skill manda usar e
202
194
  // o create recusa com erro de schema (foi assim com o WAN 3.0 por dois meses).
203
- export const VIDEO_FORA_DE_PROPOSITO = {
204
- "sapiens-video-seedance-fast": "Seedance 1.0 Fast, geração velha: sem áudio, sem referência e sem frame final. " +
205
- "Economiza pouco contra o 1.5 Pro e perde três capacidades; tentativa barata é o " +
206
- "sapiens-video-seedance-2-mini. Decisão de 29/jul/2026.",
207
- };
195
+ export const VIDEO_FORA_DE_PROPOSITO = {};
208
196
  const FILM_KINDS = ["demo", "aula-tour", "essay", "tipografia-musical", "dataviz"];
209
197
  const FILM_STATUSES = ["rascunho", "na_fila", "renderizando", "pronto"];
210
198
  export const videoSchema = z.object({
@@ -347,12 +335,12 @@ export const videoSchema = z.object({
347
335
  .enum(VIDEO_MODELS)
348
336
  .optional()
349
337
  .describe("action=create (opcional quando você passa templateSlug: a receita traz o motor) e action=price (obrigatório). O catálogo POR MODELO (capacidades, referências, tetos, FAIXA de preço) mora na description desta tool e VIVO em action=models; a skill 'video' (sapiens_skill) guia a escolha. " +
350
- "Atalho de famílias: seedance/seedance-2-*/seedance-25 = cena+áudio+referências (o 25 estica até 30s e o preço acompanha), seedance-15 = degrau 1.5, kling = anima imagem, wan = WAN 2.5, imagem que fala, wan-3 = WAN 3.0, texto ou imagem com som e frame final, " +
338
+ "Atalho de famílias: seedance/seedance-2-*/seedance-25 = cena+áudio+referências (o 25 estica até 30s e o preço acompanha), kling = anima imagem, wan = WAN 2.5, imagem que fala, wan-3 = WAN 3.0, texto ou imagem com som e frame final, " +
351
339
  "hailuo/hailuo-pro = movimento puro sem áudio, h3 = 2K ou 768p com áudio e até 9 imagens + 3 vídeos de referência, h3-spicy/seedance-spicy = os mesmos motores sem freio de conteúdo, só a partir de imagem (i2v), kling-motion/shot-mimic = transferência de movimento/plano, lite/fast/quality = Veo 3.1, omni = Gemini com áudio nativo."),
352
340
  durationSec: z
353
341
  .number()
354
342
  .optional()
355
- .describe("action=create e action=price: duração em segundos (Omni ignora). Seedance 2.0/Shot Mimic 4-15, Seedance 2.5 4-30 (o preço acompanha: confirme a duração com a pessoa antes de passar de 15), Seedance 1.5 4-12, Kling 3-15, WAN 2.5 5/10, WAN 3.0 5/8/10, H3 5/6/8/10/12/15 nos dois tiers (768p e 2k), H3 Spicy 3-15, Seedance 2.0 Spicy 4-15 em 480p e 720p, 4-10 em 1080p. " +
343
+ .describe("action=create e action=price: duração em segundos (Omni ignora). Seedance 2.0/Shot Mimic 4-15, Seedance 2.5 4-30 (o preço acompanha: confirme a duração com a pessoa antes de passar de 15), Kling 3-15, WAN 2.5 5/10, WAN 3.0 5/8/10, H3 5/6/8/10/12/15 nos dois tiers (768p e 2k), H3 Spicy 3-15, Seedance 2.0 Spicy 4-15 em 480p e 720p, 4-10 em 1080p. " +
356
344
  "O PREÇO ESCALA COM A DURAÇÃO. Omitir não é 'a config padrão da casa': duração ausente, ou fora do leque do motor, cai no PISO de duração do motor. " +
357
345
  "action=shadows: duração do vídeo-fonte, se souber (cobra 200/s; sem ela, flat ~2000)."),
358
346
  resolution: z
@@ -415,7 +403,7 @@ export const videoSchema = z.object({
415
403
  endImageId: z
416
404
  .string()
417
405
  .optional()
418
- .describe("Frame FINAL: generatedImages:_id da SUA galeria. Vira reference role 'end'. Pelo catálogo aceitam: Seedance 2.0/Fast/Mini/2.5, Kling 3.0 Pro, WAN 3.0, H3 e H3 Spicy; o resto não promete (Seedance Spicy, Hailuo, WAN 2.5 e Seedance 1.5 não expõem)."),
406
+ .describe("Frame FINAL: generatedImages:_id da SUA galeria. Vira reference role 'end'. Pelo catálogo aceitam: Seedance 2.0/Fast/Mini/2.5, Kling 3.0 Pro, WAN 3.0, H3 e H3 Spicy; o resto não promete (Seedance Spicy, Hailuo e WAN 2.5 não expõem)."),
419
407
  endImageUrl: httpUrl()
420
408
  .optional()
421
409
  .describe("Frame FINAL: url pública de galeria/Acervo/personagem. Vira reference role 'end' (mesmos motores de endImageId)."),
@@ -425,7 +413,7 @@ export const videoSchema = z.object({
425
413
  referenceImageIds: z
426
414
  .array(z.string())
427
415
  .optional()
428
- .describe("action=create (Seedance 2.0/Fast/Mini/2.5 até 4; H3 até 9; Veo Fast/Quality e Omni 1.1 até 3; Lite, Kling, Hailuo, WAN 2.5 e 3.0, Seedance 1.5 e os dois Spicy NÃO): generatedImages:_id da SUA galeria " +
416
+ .describe("action=create (Seedance 2.0/Fast/Mini/2.5 até 4; H3 até 9; Veo Fast/Quality e Omni 1.1 até 3; Lite, Kling, Hailuo, WAN 2.5 e 3.0 e os dois Spicy NÃO): generatedImages:_id da SUA galeria " +
429
417
  "como imagens de REFERÊNCIA (role 'ref'). Diferente de startImageId: NÃO viram o 1º frame, guiam " +
430
418
  "estilo/personagem/composição. Fluxo storyboard: gere a folha com sapiens_image " +
431
419
  "templateSlug='storyboard-sapiens-v1' (ou -vertical-v1) e passe folha + personagem aqui. " +
@@ -558,10 +546,10 @@ export async function video(args) {
558
546
  id: m.id,
559
547
  engine: m.engine,
560
548
  label: m.label,
561
- // Família + variante: o Seedance sozinho tem SEIS entradas nesta lista
562
- // (1.0 Fast, 1.5 Pro, 2.0, 2.0 Fast, 2.0 Mini, 2.5) e são o mesmo motor
563
- // em geração/tier diferente. Sem os dois campos o cliente lê seis
564
- // motores sem relação. Ausência = motor sozinho.
549
+ // Família + variante: o Seedance sozinho tem várias entradas nesta lista
550
+ // (2.0, 2.0 Fast, 2.0 Mini, 2.5) e são o mesmo motor em geração/tier
551
+ // diferente. Sem os dois campos o cliente lê motores sem relação.
552
+ // Ausência = motor sozinho.
565
553
  family: m.family ?? null,
566
554
  variant: m.variantLabel ?? null,
567
555
  // Pra que este motor serve: audio, fala, referencia, movimento, longo.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapiens-mcp",
3
- "version": "1.82.0",
3
+ "version": "1.82.2",
4
4
  "mcpName": "com.sapiensinteticos/sapiens",
5
5
  "description": "MCP server pra operar o Sapiens Sintéticos (sapiensinteticos.com) pelo Claude Code: gerar imagem, escrever artigo, voz, música e mais, na sua conta. Login pelo código de sapiensinteticos.com/conectar-claude.",
6
6
  "type": "module",