sapiens-mcp 1.102.0 → 1.103.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -280,15 +280,25 @@ export function describeConvexError(e) {
280
280
  // cliente MESMO tendo gerado e COBRADO. 120s cobre esses casos; render de vídeo
281
281
  // (minutos) ainda precisa do fluxo assíncrono. Override por env pra outro teto.
282
282
  const CONVEX_TIMEOUT_MS = Number(process.env.SAPIENS_CONVEX_TIMEOUT_MS) || 120000;
283
- function withTimeout(p, label) {
283
+ /** O fim da mensagem quando quem chama não diz onde o resultado cai. */
284
+ const ONDE_CONFERIR_GENERICO = "Espere alguns minutos e leia o estado real por uma leitura (o `get`/`list`/`status` do mesmo tool) antes de decidir.";
285
+ /**
286
+ * A mensagem do Timeout. `ondeConferir` troca a frase genérica pela leitura
287
+ * certa daquele tool: o "status do mesmo tool" não existe em todo tool, e o
288
+ * agente que seguia a frase genérica no sapiens_image caía na galeria, que não
289
+ * mostra peça ainda gerando, e repetia a cobrança.
290
+ */
291
+ export function mensagemDeTimeout(label, ondeConferir) {
292
+ return (`Timeout: o backend Sapiens não respondeu em ${Math.round(CONVEX_TIMEOUT_MS / 1000)}s (${label}). Isto é o CLIENTE desistindo de esperar, não a ` +
293
+ `operação morrendo: ela segue rodando no servidor e, se cobra ` +
294
+ `Sinapses, JÁ cobrou. NÃO repita a chamada: repetir gera e cobra ` +
295
+ `de novo. ${ondeConferir ?? ONDE_CONFERIR_GENERICO}`);
296
+ }
297
+ function withTimeout(p, label, ondeConferir) {
284
298
  let timer;
285
299
  const timeout = new Promise((_, reject) => {
286
300
  timer = setTimeout(() => {
287
- reject(new Error(`Timeout: o backend Sapiens não respondeu em ${Math.round(CONVEX_TIMEOUT_MS / 1000)}s (${label}). Isto é o CLIENTE desistindo de esperar, não a ` +
288
- `operação morrendo: ela segue rodando no servidor e, se cobra ` +
289
- `Sinapses, JÁ cobrou. NÃO repita a chamada — repetir gera e cobra ` +
290
- `de novo. Espere alguns minutos e leia o estado real por uma ` +
291
- `leitura (o \`get\`/\`list\`/\`status\` do mesmo tool) antes de decidir.`));
301
+ reject(new Error(mensagemDeTimeout(label, ondeConferir)));
292
302
  }, CONVEX_TIMEOUT_MS);
293
303
  });
294
304
  return Promise.race([
@@ -344,7 +354,10 @@ export async function touchSession(token) {
344
354
  // best-effort: renovar é conforto, não pré-requisito da chamada em curso
345
355
  }
346
356
  }
347
- export async function convexAction(fnPath, args) {
357
+ export async function convexAction(fnPath, args,
358
+ // Onde o agente confere o resultado se o cliente desistir de esperar: a
359
+ // leitura daquele tool, no lugar da frase genérica da mensagem de Timeout.
360
+ opts = {}) {
348
361
  const client = getConvex();
349
- return (await withTimeout(client.action(fnPath, args), `action ${fnPath}`));
362
+ return (await withTimeout(client.action(fnPath, args), `action ${fnPath}`, opts.ondeConferir));
350
363
  }
package/dist/registry.js CHANGED
@@ -71,7 +71,7 @@ export const TOOLS = {
71
71
  handler: carrossel,
72
72
  },
73
73
  sapiens_image: {
74
- description: "Operações de imagem via Sapiens (Gemini, GPT Image 2 e 2.5, Grok, Seedream/ByteDance). Sub-actions: 'upscale' (sourceImageId de uma imagem SUA pronta: a mesma peça em resolução maior vira peça nova no acervo, a original fica; cobra Sinapses, preço fixo igual ao da ficha no site; SÍNCRONA e pode passar de 2 min, então Timeout é o cliente desistindo: olhe sapiens_gallery action=list antes de repetir), 'sombra' (sourceImageId de uma foto SUA pronta: extrai a sombra, o mapa de profundidade colorido da foto, como peça nova no acervo, pra usar de referência de pose e volume; cobra Sinapses, preço fixo; síncrona), '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-*; EDIÇÃO POR ÁREA: sourceImageId + maskRegions (caixas em fração da imagem) muda SÓ a região marcada e devolve o resto igual ao pixel, no nano-banana-2 e no nano-banana-max, pelo preço da geração comum; FUNDO TRANSPARENTE: transparentBackground=true devolve PNG com alpha, o recorte feito pelo próprio motor, pelo preço da geração comum e sem o Remover Fundo pago depois, só na linha GPT Image (supportsTransparentBackground em action=models; nos outros recusa sem cobrar)), '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).",
74
+ description: "Operações de imagem via Sapiens (Gemini, GPT Image 2 e 2.5, Grok, Seedream/ByteDance). Sub-actions: 'upscale' (sourceImageId de uma imagem SUA pronta: a mesma peça em resolução maior vira peça nova no acervo, a original fica; cobra Sinapses, preço fixo igual ao da ficha no site; SÍNCRONA e pode passar de 2 min, então Timeout é o cliente desistindo: a peça nova entra pronta em até uns 6 min, leia action=status antes de repetir), 'sombra' (sourceImageId de uma foto SUA pronta: extrai a sombra, o mapa de profundidade colorido da foto, como peça nova no acervo, pra usar de referência de pose e volume; cobra Sinapses, preço fixo; síncrona), 'generate' (gera imagem completa imediato: prompt+model+aspectRatio+size; suporta mode=edit/variation e MULTI-REFERÊNCIA: combine até 5 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); quem aceita e quantas chegam ao motor saem de supportsReferences e maxReferences em action=models (a ref acima do teto do motor some sem erro); EDIÇÃO POR ÁREA: sourceImageId + maskRegions (caixas em fração da imagem) muda SÓ a região marcada e devolve o resto igual ao pixel, no nano-banana-2 e no nano-banana-max, pelo preço da geração comum; FUNDO TRANSPARENTE: transparentBackground=true devolve PNG com alpha, o recorte feito pelo próprio motor, pelo preço da geração comum e sem o Remover Fundo pago depois, só na linha GPT Image (supportsTransparentBackground em action=models; nos outros recusa sem cobrar)), '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 e quantas, maxReferences) 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. O generate devolve custoSinapses, o que o extrato debitou: é esse que se cita depois. Sub-action 'status' (somente leitura, sem custo): as gerações de imagem mais recentes da conta, a mais nova primeiro, com o estado de cada uma (gerando, pronta, falhou, bloqueada, presa) e há quantos minutos nasceu. É a leitura depois de um Timeout: mostra a peça que AINDA está gerando, que a galeria (sapiens_gallery action=list) não mostra. NOTA: generate é SÍNCRONA e cobra no disparo; modelo pesado (Pro, gpt-image-2-high, GPT Image 2.5 Sunburst, Seedream 5.0 Pro, Grok quality, 2K/4K) cai na REGRA DO TIMEOUT: leia action=status antes de repetir. gerando = ainda no servidor e já cobrada, não gere de novo; pronta = mostre essa; falhou = o débito voltou, aí sim gere de novo.",
75
75
  schema: imageSchema,
76
76
  handler: image,
77
77
  },
@@ -91,7 +91,7 @@ export const TOOLS = {
91
91
  handler: repertorio,
92
92
  },
93
93
  sapiens_gallery: {
94
- description: "Browse, publicação e upload das imagens do user. Sub-actions: list (últimas N peças, com prompt/model/url + isPublic + acervoUrl; kind=image por default, kind=video traz os takes com posterUrl, a capa, e kind=all mistura. Depois de ingerir ou gerar vídeo, é o list kind=video que dá a capa pra mostrar miniatura + player em vez de link solto), get (1 imagem com metadados, opcionalmente base64), publish (torna a PRÓPRIA imagem pública: entra na galeria pública + feed Pinterest, e ganha página indexável /imagem/<id> se o modelo não for degen devolve publicPageUrl; publicado NÃO despublica, não existe unpublish), upload (traz pra galeria uma peça gerada FORA da casa, por sourceUrl https, filePath local ou base64; ADMIN por ora), ingest (A PONTE, qualquer conta logada: peça que a PESSOA dirigiu com a ficha do personagem daqui e um motor de fora renderizou, no crédito dela (Kie, fal, Magnific, Sogni, a própria placa): entra NATIVA na timeline e nasce PRIVADA (isPublic false), publicável DEPOIS por ela como qualquer geração da casa, nunca sozinha, com custo 0 em Sinapses e o custo real guardado em externalCost; carrega o prompt VERBATIM, as referências da casa (referenceImageIds), o personagem (characterId) e o motor (externalEngine 'provedor-motor'); a peça chega por filePath (local, só no MCP instalado), sourceUrl (o render, baixado pelo MCP na máquina da pessoa, nunca pelo servidor) ou base64; o carimbo +18 nasce na entrada pela CLASSE do provedor (peso aberto como Sogni, Replicate, HF, fal, WaveSpeed, Civitai e a própria placa entram carimbados, só a curadoria tira; serviço com moderação como Kie, Magnific, Freepik, Krea entra sem) ou por unfiltered=true dito pela pessoa (carimbo dela, ela tira depois); idempotente por sourceUrl ou pelo hash do arquivo, então re-ingerir corrige a ficha em vez de duplicar; tetos 12 MB imagem, 60 MB vídeo, 40 por dia. A chave do provedor NUNCA passa por aqui: quem gerou foi o harness da pessoa. Passo a passo na skill 'pontes'), refs (acopla peças da casa como REFERÊNCIA numa peça que já existe, de upload ou de ingest; ADMIN, e só na própria peça), processo (O COMO JUNTO DO QUE: anexa a uma peça as peças que mostram como ela foi dirigida, o mapa de rota desenhado sobre o still, a prancha de frames com o tempo de cada marco, o antes e depois de duas rodadas; elas viram a seção 'Como foi feito' na página pública da peça, na ordem que você mandar e com a sua legenda por peça; qualquer membro, só na própria peça, teto de 12. É IRMÃ do refs e a diferença importa: refs é o INSUMO que entrou na geração, processo é o REGISTRO de como ela foi dirigida, e uma lista não mexe na outra. Peça de processo privada não aparece pra quem está de fora nem em miniatura: pra aparecer, publique ela também), cast (QUEM ESTÁ EM CENA numa peça que já existe, quando é mais de uma criatura: characterIds em ordem, o protagonista primeiro, e a peça passa a contar na ficha de todos eles em vez de sumir da segunda; qualquer membro, na própria peça), parts (O INTEIRO E OS PEDAÇOS de um vídeo: o clipe de 30s que foi montado a partir de dois takes de 15s, a fita longa feita de partes. COM imageId devolve a ficha de parentesco daquela peça, quais são as partes dela e de qual inteiro ela é parte, com a url do arquivo completo pra baixar. SEM imageId devolve o que AINDA NÃO FOI MONTADO: varre os takes soltos da conta, agrupa os que têm cara de par (mesmo personagem, mesmo período, prompt de 'Part one/Part two' ou arquivo terminando em -a/-b) e diz o PORQUE de cada agrupamento, pra você saber o que falta juntar antes de o autor perguntar. É heurística e não monta nada sozinha: adianta a descoberta, quem decide é quem lê. characterId filtra por personagem. Sem custo), link_parts (LIGA o inteiro aos pedaços: imageId é o vídeo montado e partIds são os takes NA ORDEM em que aparecem nele. O juntador da casa e o ingest que traz referências de vídeo já gravam esse laço sozinhos; esta porta é pra montagem feita FORA (ffmpeg na máquina, ingest do arquivo já pronto) e pra corrigir ligação errada. Só na própria peça, teto de 16 partes, e a lista TROCA o que estava gravado antes. Sem custo), reprovar (O VETO do dono: imageId + motivo, o porquê numa frase concreta. A peça que errou (rosto do personagem trocado, mão quebrada, a referência errada) não sai mais por porta nenhuma, fila do Distribution, publicar, Telegram, e some das levas do garimpo; fica no acervo com o porquê, pra ensinar. Chame NO MESMO TURNO em que o dono condena uma peça: peça condenada e não marcada volta numa leva. desfazer=true tira o veto. Imagem e vídeo, só na própria peça, sem custo), reprovadas (as peças vetadas da conta com o prompt que as gerou e o motivo, a mais recente primeiro; characterId filtra por personagem. LEIA ANTES de gerar de novo um personagem que já teve peça reprovada: é a lista do que não repetir. Sem custo), carimbo (o +18 da peça: imageId + adulta true liga, false tira, com a regra do botão da ficha: a curadoria mexe em qualquer carimbo, inclusive o que a ponte põe por classe de motor sem autor, e o dono liga na própria peça e só tira o que ele mesmo pôs. Peça carimbada não entra em praça sfw do Distribution, então tirar o carimbo de peça vestida destrava Instagram, TikTok, YouTube e LinkedIn; peça com nudez fica carimbada. Sem custo). Use list/get pra reusar imagem como referência (passe o imageId em sapiens_image mode=edit ou mode=variation) ou pra mostrar pro user o que ele já tem; publish quando o user quer divulgar a imagem dele. IMPORTANTE sobre upload: a peça entra PRIVADA e continua privada: o servidor recusa publicar e recusa compartilhar na comunidade, porque ela é do usuário e a responsabilidade é dele. O caminho pra ela virar coisa pública é usar como referência numa geração daqui e publicar o resultado. O refs escreve só proveniência (de que ficha/folha a peça nasceu): não promove nada, e um upload continua sem publicar depois de ganhar referência. COMO A PEÇA FOI FEITA: list aceita skill=<slug> e devolve só o que saiu daquela skill (o mesmo slug que sapiens_image gravou em skills), com a busca varrendo as últimas 200 peças antes de cortar no limit; cada item volta com o campo skills, e null ali quer dizer que ninguém marcou, não que foi feita sem skill.",
94
+ description: "Browse, publicação e upload das imagens do user. Sub-actions: list (últimas N peças, com prompt/model/url + isPublic + acervoUrl; kind=image por default, kind=video traz os takes com posterUrl, a capa, e kind=all mistura. Depois de ingerir ou gerar vídeo, é o list kind=video que dá a capa pra mostrar miniatura + player em vez de link solto), get (1 imagem com metadados, opcionalmente base64), publish (torna a PRÓPRIA peça pública, imagem ou vídeo: a imagem entra na galeria pública + feed Pinterest e ganha página indexável /imagem/<id>, o vídeo ganha a /video/<id>, pela mesma régua do botão Publicar da ficha (peça oculta ou reprovada não publica, upload de fora também não, ingest da ponte sim); publicPageUrl volta null em peça +18 e de modelo degen; publicado NÃO despublica, não existe unpublish), upload (traz pra galeria uma peça gerada FORA da casa, por sourceUrl https, filePath local ou base64; ADMIN por ora), ingest (A PONTE, qualquer conta logada: peça que a PESSOA dirigiu com a ficha do personagem daqui e um motor de fora renderizou, no crédito dela (Kie, fal, Magnific, Sogni, a própria placa): entra NATIVA na timeline e nasce PRIVADA (isPublic false), publicável DEPOIS por ela como qualquer geração da casa, nunca sozinha, com custo 0 em Sinapses e o custo real guardado em externalCost; carrega o prompt VERBATIM, as referências da casa (referenceImageIds), o personagem (characterId) e o motor (externalEngine 'provedor-motor'); a peça chega por filePath (local, só no MCP instalado), sourceUrl (o render, baixado pelo MCP na máquina da pessoa, nunca pelo servidor) ou base64; o carimbo +18 nasce na entrada pela CLASSE do provedor (peso aberto como Sogni, Replicate, HF, fal, WaveSpeed, Civitai e a própria placa entram carimbados, só a curadoria tira; serviço com moderação como Kie, Magnific, Freepik, Krea entra sem) ou por unfiltered=true dito pela pessoa (carimbo dela, ela tira depois); idempotente por sourceUrl ou pelo hash do arquivo, então re-ingerir corrige a ficha em vez de duplicar; tetos 12 MB imagem, 60 MB vídeo, 40 por dia. A chave do provedor NUNCA passa por aqui: quem gerou foi o harness da pessoa. Passo a passo na skill 'pontes'), refs (acopla peças da casa como REFERÊNCIA numa peça que já existe, de upload ou de ingest; ADMIN, e só na própria peça), processo (O COMO JUNTO DO QUE: anexa a uma peça as peças que mostram como ela foi dirigida, o mapa de rota desenhado sobre o still, a prancha de frames com o tempo de cada marco, o antes e depois de duas rodadas; elas viram a seção 'Como foi feito' na página pública da peça, na ordem que você mandar e com a sua legenda por peça; qualquer membro, só na própria peça, teto de 12. É IRMÃ do refs e a diferença importa: refs é o INSUMO que entrou na geração, processo é o REGISTRO de como ela foi dirigida, e uma lista não mexe na outra. Peça de processo privada não aparece pra quem está de fora nem em miniatura: pra aparecer, publique ela também), cast (QUEM ESTÁ EM CENA numa peça que já existe, quando é mais de uma criatura: characterIds em ordem, o protagonista primeiro, e a peça passa a contar na ficha de todos eles em vez de sumir da segunda; qualquer membro, na própria peça), parts (O INTEIRO E OS PEDAÇOS de um vídeo: o clipe de 30s que foi montado a partir de dois takes de 15s, a fita longa feita de partes. COM imageId devolve a ficha de parentesco daquela peça, quais são as partes dela e de qual inteiro ela é parte, com a url do arquivo completo pra baixar. SEM imageId devolve o que AINDA NÃO FOI MONTADO: varre os takes soltos da conta, agrupa os que têm cara de par (mesmo personagem, mesmo período, prompt de 'Part one/Part two' ou arquivo terminando em -a/-b) e diz o PORQUE de cada agrupamento, pra você saber o que falta juntar antes de o autor perguntar. É heurística e não monta nada sozinha: adianta a descoberta, quem decide é quem lê. characterId filtra por personagem. Sem custo), link_parts (LIGA o inteiro aos pedaços: imageId é o vídeo montado e partIds são os takes NA ORDEM em que aparecem nele. O juntador da casa e o ingest que traz referências de vídeo já gravam esse laço sozinhos; esta porta é pra montagem feita FORA (ffmpeg na máquina, ingest do arquivo já pronto) e pra corrigir ligação errada. Só na própria peça, teto de 16 partes, e a lista TROCA o que estava gravado antes. Sem custo), reprovar (O VETO do dono: imageId + motivo, o porquê numa frase concreta. A peça que errou (rosto do personagem trocado, mão quebrada, a referência errada) não sai mais por porta nenhuma, fila do Distribution, publicar, Telegram, e some das levas do garimpo; fica no acervo com o porquê, pra ensinar. Chame NO MESMO TURNO em que o dono condena uma peça: peça condenada e não marcada volta numa leva. desfazer=true tira o veto. Imagem e vídeo, só na própria peça, sem custo), reprovadas (as peças vetadas da conta com o prompt que as gerou e o motivo, a mais recente primeiro; characterId filtra por personagem. LEIA ANTES de gerar de novo um personagem que já teve peça reprovada: é a lista do que não repetir. Sem custo), carimbo (o +18 da peça: imageId + adulta true liga, false tira, com a regra do botão da ficha: a curadoria mexe em qualquer carimbo, inclusive o que a ponte põe por classe de motor sem autor, e o dono liga na própria peça e só tira o que ele mesmo pôs. Peça carimbada não entra em praça sfw do Distribution, então tirar o carimbo de peça vestida destrava Instagram, TikTok, YouTube e LinkedIn; peça com nudez fica carimbada. Sem custo). Use list/get pra reusar imagem como referência (passe o imageId em sapiens_image mode=edit ou mode=variation) ou pra mostrar pro user o que ele já tem; publish quando o user quer divulgar a peça dele (imagem ou vídeo). IMPORTANTE sobre upload: a peça entra PRIVADA e continua privada: o servidor recusa publicar e recusa compartilhar na comunidade, porque ela é do usuário e a responsabilidade é dele. O caminho pra ela virar coisa pública é usar como referência numa geração daqui e publicar o resultado. O refs escreve só proveniência (de que ficha/folha a peça nasceu): não promove nada, e um upload continua sem publicar depois de ganhar referência. COMO A PEÇA FOI FEITA: list aceita skill=<slug> e devolve só o que saiu daquela skill (o mesmo slug que sapiens_image gravou em skills), com a busca varrendo as últimas 200 peças antes de cortar no limit; cada item volta com o campo skills, e null ali quer dizer que ninguém marcou, não que foi feita sem skill.",
95
95
  schema: gallerySchema,
96
96
  handler: gallery,
97
97
  },
@@ -146,7 +146,7 @@ export const TOOLS = {
146
146
  handler: musicator,
147
147
  },
148
148
  sapiens_video: {
149
- 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, o Omni 1.1 até 3; 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-pvideo-2-pro' (P-Video-2 Pro, o H3 acelerado pela Pruna: o RASCUNHO de personagem travada, até 9 imagens contando referências e frame inicial e final no mesmo pedido, 5 a 15s em '480p' ou '768p', som nativo, o take volta em segundos; sem t2v, precisa de ao menos uma imagem ou um clipe; cobra por imagem além do segundo, então cote em 'price' com as mesmas imagens do create; aceita ainda até 3 vídeos (referenceVideoUrls) e 3 áudios (referenceAudioUrls) de referência, 15s somados por tipo, cada segundo cobrado à parte e medido pelo servidor antes do débito (cote com referenceVideoSec/referenceAudioSec), e keepAudio com um áudio só faz ele virar a trilha do take), '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 de 4, 6, 8 ou 10s (sem durationSec, 10s) com áudio nativo, de '360p' a '4k' (sem resolution, 720p; 360p custa 1/3 do 720p, 1080p 1,5x, 4k 3x); aceita até 3 imagens de referência; editOfImageId aponta um vídeo Omni seu e o prompt edita a MESMA cena, preservando câmera e ambiente; e VÍDEO-FONTE: sourceVideoId (vídeo seu da galeria) ou sourceVideoUrl (link da casa), mp4 até 10 s, editado (sourceVideoMode 'edit', o padrão, ex: trocar a pessoa do vídeo pelo personagem da imagem de referência, cobrado pelo tamanho do clipe) ou estendido ('extend', cobrado pelo durationSec do pedaço novo)) ('sapiens-video-omni', o Omni 1.0, e 'sapiens-video-lite/fast/quality', o Veo 3.1, SAÍRAM: o Google apagou os quatro em 22/10/2026 e o pedido é recusado antes de cobrar). Args create: model, prompt, durationSec, resolution ('480p'/'720p'/'1080p'; no H3 '768p'/'2k'; no Omni 1.1 '360p' a '4k'), 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. EDIÇÃO (as ações da ficha do site, cada uma vira peça nova e o original fica): 'trim' (imageId + startSec, endSec opcional: corta um trecho; grátis), 'join' (imageIds: 2 a 8 vídeos SEUS na ordem, até 180 s somados: compilado; grátis), 'fecho' (imageId + ending 'marca'/'sapiens'/'none', watermark, musicUrl de trilha da casa, nameCard {name, subtitle}: fecha o vídeo com a assinatura da casa; grátis), 'render-ref' (imageId do take + refUrl da folha que o gerou: a peça com a referência ao lado; cobra Sinapses, o mesmo par já feito volta de graça). Síncronas, podem passar de 2 min: Timeout é o cliente desistindo, olhe sapiens_gallery action=list kind=video antes de repetir. RESOLUÇÃO: 'upscale' (imageId de vídeo SEU completed + resolution = o alvo, '720p', '1080p', '2k' ou '4k') gera uma VARIANTE nova em resolução maior, o original fica intacto; cobra Sinapses por segundo do clipe, mais caro quanto maior o alvo, assíncrona como o create (acompanhe em status). SOM: 'sonorize' (imageId de vídeo SEU completed + prompt do som da cena) gera uma VARIANTE nova com trilha sincronizada (o original fica intacto; sonorize sempre o original, nunca uma variante). engine escolhe o motor: 'mirelo-sfx-2.0' (42 Sinapses/s, o default: GUARDA a fala do clipe, tira a música e refaz o resto), 'mmaudio-v2' (20 Sinapses/s, segue o texto em vez da imagem: é o motor do bicho trocado, onde o Mirelo devolve o som do bicho original; refaz o som inteiro e a fala se perde) ou 'elevenlabs-video-to-music' (26 Sinapses/s, faz a TRILHA e não o som da cena: uma música que acompanha os cortes, mixada por baixo do som que o clipe já tem; o prompt descreve a música). 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, e desde out/2026 o Gemini Omni) 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 (menos o Gemini Omni 1.1, que fica no 720p), sem 'durationSec' cobra o piso de duração (no Omni, o padrão: 10s). 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.",
149
+ 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, o Omni 1.1 até 3; 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-pvideo-2-pro' (P-Video-2 Pro, o H3 acelerado pela Pruna: o RASCUNHO de personagem travada, até 9 imagens contando referências e frame inicial e final no mesmo pedido, 5 a 15s em '480p' ou '768p', som nativo, o take volta em segundos; sem t2v, precisa de ao menos uma imagem ou um clipe; cobra por imagem além do segundo, então cote em 'price' com as mesmas imagens do create; aceita ainda até 3 vídeos (referenceVideoUrls) e 3 áudios (referenceAudioUrls) de referência, 15s somados por tipo, cada segundo cobrado à parte e medido pelo servidor antes do débito (cote com referenceVideoSec/referenceAudioSec), e keepAudio com um áudio só faz ele virar a trilha do take), '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-flux-3-max' (FLUX 3 Max, fal + Black Forest Labs: texto ou imagem, 5 a 20s em 720p/1080p, som nativo com interruptor, frame final junto do inicial; 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 de 4, 6, 8 ou 10s (sem durationSec, 10s) com áudio nativo, de '360p' a '4k' (sem resolution, 720p; 360p custa 1/3 do 720p, 1080p 1,5x, 4k 3x); aceita até 3 imagens de referência; editOfImageId aponta um vídeo Omni seu e o prompt edita a MESMA cena, preservando câmera e ambiente; e VÍDEO-FONTE: sourceVideoId (vídeo seu da galeria) ou sourceVideoUrl (link da casa), mp4 até 10 s, editado (sourceVideoMode 'edit', o padrão, ex: trocar a pessoa do vídeo pelo personagem da imagem de referência, cobrado pelo tamanho do clipe) ou estendido ('extend', cobrado pelo durationSec do pedaço novo)) ('sapiens-video-omni', o Omni 1.0, e 'sapiens-video-lite/fast/quality', o Veo 3.1, SAÍRAM: o Google apagou os quatro em 22/10/2026 e o pedido é recusado antes de cobrar). Args create: model, prompt, durationSec, resolution ('480p'/'720p'/'1080p'; no H3 '768p'/'2k'; no Omni 1.1 '360p' a '4k'), 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. EDIÇÃO (as ações da ficha do site, cada uma vira peça nova e o original fica): 'trim' (imageId + startSec, endSec opcional: corta um trecho; grátis), 'join' (imageIds: 2 a 8 vídeos SEUS na ordem, até 180 s somados: compilado; grátis), 'fecho' (imageId + ending 'marca'/'sapiens'/'none', watermark, musicUrl de trilha da casa, nameCard {name, subtitle}: fecha o vídeo com a assinatura da casa; grátis), 'render-ref' (imageId do take + refUrl da folha que o gerou: a peça com a referência ao lado; cobra Sinapses, o mesmo par já feito volta de graça). Síncronas, podem passar de 2 min: Timeout é o cliente desistindo, olhe sapiens_gallery action=list kind=video antes de repetir. RESOLUÇÃO: 'upscale' (imageId de vídeo SEU completed + resolution = o alvo, '720p', '1080p', '2k' ou '4k') gera uma VARIANTE nova em resolução maior, o original fica intacto; cobra Sinapses por segundo do clipe, mais caro quanto maior o alvo, assíncrona como o create (acompanhe em status). SOM: 'sonorize' (imageId de vídeo SEU completed + prompt do som da cena) gera uma VARIANTE nova com trilha sincronizada (o original fica intacto; sonorize sempre o original, nunca uma variante). engine escolhe o motor: 'mirelo-sfx-2.0' (42 Sinapses/s, o default: GUARDA a fala do clipe, tira a música e refaz o resto), 'mmaudio-v2' (20 Sinapses/s, segue o texto em vez da imagem: é o motor do bicho trocado, onde o Mirelo devolve o som do bicho original; refaz o som inteiro e a fala se perde) ou 'elevenlabs-video-to-music' (26 Sinapses/s, faz a TRILHA e não o som da cena: uma música que acompanha os cortes, mixada por baixo do som que o clipe já tem; o prompt descreve a música). 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, e desde out/2026 o Gemini Omni) 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 (menos o Gemini Omni 1.1, que fica no 720p), sem 'durationSec' cobra o piso de duração (no Omni, o padrão: 10s). 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.",
150
150
  schema: videoSchema,
151
151
  handler: video,
152
152
  },
@@ -231,7 +231,7 @@ export const TOOLS = {
231
231
  handler: trilhas,
232
232
  },
233
233
  sapiens_distribution: {
234
- description: "Despachar peças (Distribution Workflow) — a peça pronta entra na FILA com canal, copy e a aba de quem ela é. ADMIN-ONLY: despacho depende de rede conectada e hoje só a casa tem. NADA aqui posta em rede nenhuma; quem despacha é a tela em /experimentos/distribution-workflow, que é onde a conta conectada mora. A ABA (`voice`) responde de quem é a peça: sapiens (a casa), helen-ailith, borderless, ou o slug de um personagem seu — slug livre, então personagem novo ganha aba sozinho na primeira peça dele, sem deploy. Em queue, aba ausente = o servidor deriva do personagem da peça e cai em sapiens quando não há. Sub-actions: queue (põe na fila: title e channel obrigatórios, mais copy/hashtags/assetUrl/notes; replyText é o comentário que sai junto do post, onde vai o link, e só o X (resposta no fio) e a Página do LinkedIn (primeiro comentário) publicam, o resto recusa; num canal do Instagram, format instagram_story faz a peça sair como STORY: uma mídia só, sem legenda nem link, some em 24h; num Reel, videoCoverUrl é a CAPA da casa, que pela API só entra aqui, e o retorno traz `aviso` quando a mesma abertura, a 1a linha da copy, já foi ao ar na mesma rede por qualquer aba: a peça entra mesmo assim, e você mostra o aviso ao dono antes de seguir, porque série de molde abre igual e é outra peça, mas a mesma notícia por duas abas é repetição). Em queue, DOIS campos que parecem opcionais e não são: `lang` diz em que língua a copy já está, e sem ele canal de rede internacional reescreve a sua copy sozinho, dois segundos depois, na voz da casa; `assetPageAssetId` é o imageId da obra da casa, e sem ele a peça só encontra a obra se a assetUrl bater letra a letra, então versão web de um master nasce órfã de ficha; e `categoria` diz o que a peça É (arte, personagem, tecnico, novidade, educativo, mercado, o significado de cada uma na descrição do campo): declare sempre que souber, senão a régua chuta pelo título e a peça cai na gaveta errada da fila, list (o que está na fila, filtrável por status, por aba e por categoria ('sem' é a triagem), com copy, hashtags e notas de cada peça; fila maior que o limit se lê inteira pelo cursor antesDe=proximoCursor), voices (as abas que existem hoje com a contagem de cada uma), status (move a peça de lane: fila, agendado, postado, descartado; postado aceita postUrl e fecha o rastro, agendado aceita scheduledFor em ms), edit (reescreve a peça da FILA sem tirar ela do lugar: itemId e só o que muda, copy, title, publicTitle, hashtags, notes, lang, replyText, assetUrl/assetKind, assets, videoCoverUrl ou categoria (esta vale em peça postada também, e null devolve pra régua); é o gesto pra corrigir legenda, nunca descartar e enfileirar de novo; texto de peça postada o servidor recusa), sign (liga ou desliga a ASSINATURA da peça, imagem e vídeo do CDN da casa, com o carimbo da LINHAGEM de quem assina: sint.fyi/<endereço dela> no canto e o fecho de 2s no vídeo, ou a marca própria dela sem nada da casa; itemId, e signed com true de default; o carimbo da casa é recusado em peça +18 e praça adulta, a marca própria passa; o carimbo leva uns 30s e até lá a peça não sai; em queue, signed:true já enfileira assinada), bio (lê ou troca a BIO de uma conta da casa no Bluesky, channel bluesky ou bluesky-2 a bluesky-5: sem texto só lê; bioLine acrescenta uma linha no fim sem mexer no resto e não repete linha que já está lá; bio reescreve a inteira; teto de 256; ESTA sub-action muda o perfil público na hora, e bio não aceita hiperlink, o endereço aparece escrito), lineages (o CADASTRO das linhagens, o mesmo da aba Teia: de cada personagem, produto e da casa, a mãe no Bluesky onde a peça nasce, as contas próprias, as galerias que a mãe alimenta sozinha, as fichas e o post do dia, mais as regras que a teia desdobra e as vagas de conta ainda sem dona), lineage-new (nasce uma linhagem: nome obrigatório, grupo, artigo, lang; nasce sem mãe, então nada viaja sozinho até o lineage-save dar a mãe), lineage-save (slug e só o que muda: mae, contas rede->canal, galerias, fichaIds, rotina, contaNaCasa, assinatura {estilo casa|propria|nenhuma, marca, padrao}; o servidor recusa conta que já é de outra linhagem e canal de outra rede, e a vaga assumida ganha o nome da linhagem), lineage-archive (slug + arquivada), tree (a ÁRVORE DE CONTAS, o formulário de quem existe onde: linhagem × rede, com o email, o @, o canal e o estado de cada conta, ligada, desligada, fora, falta ou dispensa, mais o passo pra ligar cada rede; slug filtra uma linhagem), tree-mark (slug + celulas [{rede, handle, canal, dispensada}]: registra o email ou o @ que existe lá fora, associa a conta da casa ou dispensa rede opcional, a linhagem inteira de uma vez e tudo ou nada; X, Instagram, Bluesky, email e Telegram são obrigatórias e não se dispensam), garimpo (o ACERVO em famílias pra escolher o que vai pra fila: só as candidatas saem, a versão velha de um corte ou upscale vem marcada como substituída, as irmãs da mesma ideia vêm agrupadas pra escolha no olho, o painel cortado vem em recortes (o original e os quadros em ordem, que só saem juntos em carrossel), e cada família diz por onde já saiu; paraCanal tira o que já foi pro canal pedido, soResumo conta sem listar, página por cursor em antesDe; o passo a passo é a skill distribuir). Sem custo em Sinapses: isto organiza, não gera. A copy segue a voz de QUEM ASSINA a peça, que não é sempre a voz da casa: peça da Helen fala como a Helen. E a peça sai pela CONTA de quem assina ela: conta extra da mesma rede é um canal próprio (bluesky-2 é outra conta no Bluesky, instagram-pro outra no Instagram), então case o canal com a aba antes de despachar.",
234
+ description: "Despachar peças (Distribution Workflow) — a peça pronta entra na FILA com canal, copy e a aba de quem ela é. ADMIN-ONLY: despacho depende de rede conectada e hoje só a casa tem. NADA aqui posta em rede nenhuma; quem despacha é a tela em /experimentos/distribution-workflow, que é onde a conta conectada mora. A ABA (`voice`) responde de quem é a peça: sapiens (a casa), helen-ailith, borderless, ou o slug de um personagem seu — slug livre, então personagem novo ganha aba sozinho na primeira peça dele, sem deploy. Em queue, aba ausente = o servidor deriva do personagem da peça e cai em sapiens quando não há. Sub-actions: queue (põe na fila: title e channel obrigatórios, mais copy/hashtags/assetUrl/notes; replyText é o comentário que sai junto do post, onde vai o link, e só o X (resposta no fio) e a Página do LinkedIn (primeiro comentário) publicam, o resto recusa; num canal do Instagram, format instagram_story faz a peça sair como STORY: uma mídia só, sem legenda nem link, some em 24h; num Reel, videoCoverUrl é a CAPA da casa, que pela API só entra aqui, e o retorno traz `aviso` quando a mesma abertura, a 1a linha da copy, já foi ao ar na mesma rede por qualquer aba: a peça entra mesmo assim, e você mostra o aviso ao dono antes de seguir, porque série de molde abre igual e é outra peça, mas a mesma notícia por duas abas é repetição). Em queue, DOIS campos que parecem opcionais e não são: `lang` diz em que língua a copy já está, e sem ele canal de rede internacional reescreve a sua copy sozinho, dois segundos depois, na voz da casa; `assetPageAssetId` é o imageId da obra da casa, e sem ele a peça só encontra a obra se a assetUrl bater letra a letra, então versão web de um master nasce órfã de ficha; e `categoria` diz o que a peça É (arte, personagem, tecnico, novidade, educativo, mercado, pessoal, o significado de cada uma na descrição do campo): declare sempre que souber, senão a régua chuta pelo título e a peça cai na gaveta errada da fila, list (o que está na fila, filtrável por status, por aba e por categoria ('sem' é a triagem), com copy, hashtags e notas de cada peça; fila maior que o limit se lê inteira pelo cursor antesDe=proximoCursor), voices (as abas que existem hoje com a contagem de cada uma), status (move a peça de lane: fila, agendado, postado, descartado; postado aceita postUrl e fecha o rastro, agendado aceita scheduledFor em ms), edit (reescreve a peça da FILA sem tirar ela do lugar: itemId e só o que muda, copy, title, publicTitle, hashtags, notes, lang, replyText, assetUrl/assetKind, assets, videoCoverUrl ou categoria (esta vale em peça postada também, e null devolve pra régua); é o gesto pra corrigir legenda, nunca descartar e enfileirar de novo; texto de peça postada o servidor recusa), sign (liga ou desliga a ASSINATURA da peça, imagem e vídeo do CDN da casa, com o carimbo da LINHAGEM de quem assina: sint.fyi/<endereço dela> no canto e o fecho de 2s no vídeo, ou a marca própria dela sem nada da casa; itemId, e signed com true de default; o carimbo da casa é recusado em peça +18 e praça adulta, a marca própria passa; o carimbo leva uns 30s e até lá a peça não sai; em queue, signed:true já enfileira assinada), bio (lê ou troca a BIO de uma conta da casa no Bluesky, channel bluesky ou bluesky-2 a bluesky-5: sem texto só lê; bioLine acrescenta uma linha no fim sem mexer no resto e não repete linha que já está lá; bio reescreve a inteira; teto de 256; ESTA sub-action muda o perfil público na hora, e bio não aceita hiperlink, o endereço aparece escrito), lineages (o CADASTRO das linhagens, o mesmo da aba Teia: de cada personagem, produto e da casa, a mãe no Bluesky onde a peça nasce, as contas próprias, as galerias que a mãe alimenta sozinha, as fichas e o post do dia, mais as regras que a teia desdobra e as vagas de conta ainda sem dona), lineage-new (nasce uma linhagem: nome obrigatório, grupo, artigo, lang; nasce sem mãe, então nada viaja sozinho até o lineage-save dar a mãe), lineage-save (slug e só o que muda: mae, contas rede->canal, galerias, fichaIds, rotina, contaNaCasa, assinatura {estilo casa|propria|nenhuma, marca, padrao}; o servidor recusa conta que já é de outra linhagem e canal de outra rede, e a vaga assumida ganha o nome da linhagem), lineage-archive (slug + arquivada), tree (a ÁRVORE DE CONTAS, o formulário de quem existe onde: linhagem × rede, com o email, o @, o canal e o estado de cada conta, ligada, desligada, fora, falta ou dispensa, mais o passo pra ligar cada rede; slug filtra uma linhagem), tree-mark (slug + celulas [{rede, handle, canal, dispensada}]: registra o email ou o @ que existe lá fora, associa a conta da casa ou dispensa rede opcional, a linhagem inteira de uma vez e tudo ou nada; X, Instagram, Bluesky, email e Telegram são obrigatórias e não se dispensam), garimpo (o ACERVO em famílias pra escolher o que vai pra fila: só as candidatas saem, a versão velha de um corte ou upscale vem marcada como substituída, as irmãs da mesma ideia vêm agrupadas pra escolha no olho, o painel cortado vem em recortes (o original e os quadros em ordem, que só saem juntos em carrossel), e cada família diz por onde já saiu; paraCanal tira o que já foi pro canal pedido, soResumo conta sem listar, página por cursor em antesDe; o passo a passo é a skill distribuir). Sem custo em Sinapses: isto organiza, não gera. A copy segue a voz de QUEM ASSINA a peça, que não é sempre a voz da casa: peça da Helen fala como a Helen. E a peça sai pela CONTA de quem assina ela: conta extra da mesma rede é um canal próprio (bluesky-2 é outra conta no Bluesky, instagram-pro outra no Instagram), então case o canal com a aba antes de despachar.",
235
235
  schema: distributionSchema,
236
236
  handler: distribution,
237
237
  },
@@ -266,27 +266,27 @@ export const TOOLS = {
266
266
  // É a "skill que anda junto com o pacote": escrevo uma vez, vale pra todos os clients,
267
267
  // sem instalar nada. Cobre os tropeços reais (fluxo do musicator, model no vídeo, o
268
268
  // disjuntor anti-loop). Mantém curto de propósito: viaja em todo handshake.
269
- export const SAPIENS_INSTRUCTIONS = `Você opera o Sapiens Sintéticos (sapiensinteticos.com) NA CONTA de um usuário logado. Cada tool age de verdade na conta dele e muitas COBRAM Sinapses (o crédito da casa). Aja como operador, não no chute.
270
-
271
- AS SKILLS DA CASA (leia ANTES de operar, não improvise o fluxo):
272
- O passo a passo travado de cada fluxo mora na tool sapiens_skill, servida por este mesmo servidor: sem login, sem custo, sem rede. Ler a skill é mais barato que errar uma geração que cobra.
273
- - sapiens_skill action=list -> o índice (slug + quando usar cada uma).
274
- - sapiens_skill action=get name=<slug> -> a skill inteira.
275
- Slugs: ${skillMenuLine()}
276
- Puxe a skill ANTES de: gerar música ou efeito sonoro (musica), escolher modelo de vídeo (video), gerar imagem (imagem), criar personagem ou prompt de Midjourney (personagem), criar na identidade do usuário (studio), postar no Fórum ou no chat (forum-comunidade), redigir qualquer texto publicável (voz-da-casa), incorporar o Sintético (companhia), missão de Desafio (trilhas), montar currículo (curriculo), guardar obra ou ferramenta no acervo (repertorio), gerar em motor de fora com crédito próprio e trazer a peça pro acervo (pontes), publicar peça nas redes da pessoa (publicar), escrever código que chama a API da conta (sapiens-api). Perdido no começo: primeiros-passos.
277
- Cliente que lê resources MCP acha o MESMO conteúdo em skill://sapiens/<slug>/SKILL.md.
278
-
279
- REGRA DE OURO:
280
- - 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.
281
- - 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.
282
- - 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.
283
- - 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'.
284
- - 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').
285
- - "sessionToken expirado" = refaça login: sapiens_meta action=login com o código de sapiensinteticos.com/conectar-claude.
286
- - 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. Preço do App em Sinapses e da API em dólar (carteiras separadas, uma nunca vira a outra): sapiens_meta action=precos.
287
-
288
- MODO COMPANHIA: sapiens_meta action=start e action=whoami podem trazer um bloco 'companion'. Quando vier companion.active=true, você INCORPORA aquele Sintético (a voz dele, o avatar, o oi dele) continuando a operar na conta e nas Sinapses do USUÁRIO. Puxe a skill 'companhia' antes de fazer isso. Se vier 'characterOffer', siga o que ele manda: são personagens que o usuário escreveu e que podem entrar em cena, e quem OFERECE é você, na hora em que a conversa encostar num deles. Só personagem de autoria dele; a alma de personagem alheio não sai por aqui.
289
-
269
+ export const SAPIENS_INSTRUCTIONS = `Você opera o Sapiens Sintéticos (sapiensinteticos.com) NA CONTA de um usuário logado. Cada tool age de verdade na conta dele e muitas COBRAM Sinapses (o crédito da casa). Aja como operador, não no chute.
270
+
271
+ AS SKILLS DA CASA (leia ANTES de operar, não improvise o fluxo):
272
+ O passo a passo travado de cada fluxo mora na tool sapiens_skill, servida por este mesmo servidor: sem login, sem custo, sem rede. Ler a skill é mais barato que errar uma geração que cobra.
273
+ - sapiens_skill action=list -> o índice (slug + quando usar cada uma).
274
+ - sapiens_skill action=get name=<slug> -> a skill inteira.
275
+ Slugs: ${skillMenuLine()}
276
+ Puxe a skill ANTES de: gerar música ou efeito sonoro (musica), escolher modelo de vídeo (video), gerar imagem (imagem), criar personagem ou prompt de Midjourney (personagem), criar na identidade do usuário (studio), postar no Fórum ou no chat (forum-comunidade), redigir qualquer texto publicável (voz-da-casa), incorporar o Sintético (companhia), missão de Desafio (trilhas), montar currículo (curriculo), guardar obra ou ferramenta no acervo (repertorio), gerar em motor de fora com crédito próprio e trazer a peça pro acervo (pontes), publicar peça nas redes da pessoa (publicar), escrever código que chama a API da conta (sapiens-api). Perdido no começo: primeiros-passos.
277
+ Cliente que lê resources MCP acha o MESMO conteúdo em skill://sapiens/<slug>/SKILL.md.
278
+
279
+ REGRA DE OURO:
280
+ - 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.
281
+ - 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.
282
+ - 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.
283
+ - 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'.
284
+ - 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. Imagem: sapiens_image action=status, que mostra até a peça que ainda está gerando (a galeria só mostra pronta). O resto está na tabela de onde conferir da skill 'primeiros-passos'.
285
+ - "sessionToken expirado" = refaça login: sapiens_meta action=login com o código de sapiensinteticos.com/conectar-claude.
286
+ - 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. Preço do App em Sinapses e da API em dólar (carteiras separadas, uma nunca vira a outra): sapiens_meta action=precos.
287
+
288
+ MODO COMPANHIA: sapiens_meta action=start e action=whoami podem trazer um bloco 'companion'. Quando vier companion.active=true, você INCORPORA aquele Sintético (a voz dele, o avatar, o oi dele) continuando a operar na conta e nas Sinapses do USUÁRIO. Puxe a skill 'companhia' antes de fazer isso. Se vier 'characterOffer', siga o que ele manda: são personagens que o usuário escreveu e que podem entrar em cena, e quem OFERECE é você, na hora em que a conversa encostar num deles. Só personagem de autoria dele; a alma de personagem alheio não sai por aqui.
289
+
290
290
  Voz da casa: 1ª pessoa, direto, anti-corporate, sem travessão. Pra bom entendedor, meia palavra basta. Texto que vai ser publicado pede a skill 'voz-da-casa' antes.`;
291
291
  // Annotations MCP: título humano + dica read-only. São HINTS (não-confiáveis por
292
292
  // spec): quem gateia de verdade continua o servidor (saldo, gate de admin,
package/dist/skills.js CHANGED
@@ -57,10 +57,12 @@ Não despeje a lista inteira de tools. O start guia.
57
57
 
58
58
  | O que gerou | Onde conferir |
59
59
  |---|---|
60
- | imagem | \`sapiens_gallery action=list\` |
60
+ | imagem (generate, upscale, sombra) | \`sapiens_image action=status\` |
61
61
  | artigo do perfil | \`sapiens_write action=list\` |
62
62
  | carrossel | \`sapiens_carrossel action=list\` |
63
63
 
64
+ Imagem tem leitura própria porque a galeria (\`sapiens_gallery action=list\`) só mostra peça PRONTA: a que ainda está gerando não aparece lá, parece que falhou, e repetir cobra de novo. O \`status\` lista as gerações recentes da conta com o estado de cada uma: \`gerando\` (já cobrou, NÃO gere de novo, leia de novo em 1 ou 2 minutos), \`pronta\` (mostre essa), \`falhou\` ou \`bloqueada\` (o débito voltou, aí sim gere de novo) e \`presa\` (passou de 15 minutos gerando, a varredura do servidor estorna).
65
+
64
66
  **2. Disjuntor anti-loop.** Três falhas seguidas no MESMO tool fazem o cliente marcar o servidor como "unreachable" por ~56s. Parece que "o MCP caiu", mas foi argumento faltando. Quando um tool volta erro de validação ("exige X", "falta Y"), LEIA o erro e refaça a chamada COM o que falta. Nunca repita a chamada idêntica que falhou.
65
67
 
66
68
  **3. Saldo.** Antes de gerar algo caro (imagem, música, vídeo), cheque com \`sapiens_meta action=credits\` ou \`action=subscription\`. Saldo baixo, avise ANTES de gastar. Vídeo é o mais caro da casa: confirme com a pessoa antes de disparar.
@@ -109,8 +111,10 @@ O modelo repete esses porque soam competentes. Todos caem.
109
111
  - **Frase vazia**: "vale destacar", "é importante notar", "no final das contas", "quando se trata de", "nos dias de hoje".
110
112
  - **Rodízio de sinônimo**: o agente, depois o assistente, depois a ferramenta, tudo pra mesma coisa. Repetir a palavra certa é precisão.
111
113
  - **Kicker de falsa profundidade**: a última linha "funda" que vira aforismo. Deleta, não reescreve numa metáfora melhor. Termina na frase concreta que já estava lá.
112
-
113
- Hedge não entra nessa lista: "ao meu ver", "por enquanto", "talvez" assinam a voz e ficam.
114
+ - **Muleta de hedge**: "ao meu ver", "na minha opinião", "tendo a achar", "talvez" que não carrega dúvida. Corta e afirma. Incerteza real fica, dita como fato: "testei só no plano grátis", "até o preço cair".
115
+ - **Palavra difícil**: "utilizar", "possuir", "visando", "a fim de", "devido ao fato de". Usar, ter, pra, porque.
116
+ - **Advérbio no lugar do número**: "melhorou significativamente", "caiu drasticamente". Fala o número.
117
+ - **Passiva com agente**: "foi lançado pela OpenAI" vira "a OpenAI lançou". Quem fez é o sujeito.
114
118
 
115
119
  ## Revisar sem apagar a pessoa
116
120
 
@@ -256,6 +260,7 @@ Duração é o que pesa aqui, não o modelo: um take de 30s em 720p passa de 40
256
260
  - \`sapiens-video-kling\`: Kling 3.0 Pro, anima imagem, 3 a 15s, som opcional.
257
261
  - \`sapiens-video-wan\`: WAN 2.5, imagem que fala ou canta, com lip-sync, 5 ou 10s.
258
262
  - \`sapiens-video-wan-3\`: WAN 3.0, texto ou imagem, som nativo, frame final e 1080p, 5 a 10s.
263
+ - \`sapiens-video-flux-3-max\`: FLUX 3 Max, texto ou imagem, 5 a 20s em \`720p\` ou \`1080p\`, som nativo e frame final (só junto do inicial). Segue o prompt de perto e volta rápido. Não aceita referência de personagem nem vídeo de movimento.
259
264
  - \`sapiens-video-kling-motion\`: passa o movimento de um vídeo pra uma imagem. Precisa de pessoa com tronco visível na imagem E no vídeo.
260
265
  - \`sapiens-video-shot-mimic\`: recria o plano, a câmera e os cortes de um vídeo de referência como cena nova.
261
266
  - \`sapiens-video-omni-11\`: Gemini Omni 1.1, texto ou imagem vira clipe de 4, 6, 8 ou 10s (sem \`durationSec\`, 10s) com áudio nativo, de \`360p\` a \`4k\` (sem \`resolution\`, 720p). O preço é por segundo e por resolução: o take de 4s custa 40% do de 10s, o 360p custa 1/3 do 720p, o 1080p 1,5x e o 4k 3x. Aceita até 3 imagens suas (frame inicial, frame final ou referência de personagem e estilo); vídeo de movimento não (pra copiar coreografia, Seedance). Edita e estende um vídeo SEU: \`sourceVideoId\` (da galeria) ou \`sourceVideoUrl\` (link da casa), mp4 até 10 s, com \`sourceVideoMode\` \`edit\` (o padrão: a mesma cena com a mudança pedida, cobrada pelo tamanho do clipe; é como se troca a pessoa de um vídeo pelo seu personagem, com a ficha dele em \`referenceImageIds\`) ou \`extend\` (o pedaço seguinte, cobrado pelo \`durationSec\`). A proporção sai a do clipe. O truque: \`editOfImageId\` aponta um vídeo Omni seu e o prompt vira instrução de edição sobre a MESMA cena (troca item ou personagem, preserva câmera e ambiente). É o caminho pra variações com continuidade: gera a base uma vez, edita N vezes. Cada edição debita como geração nova. Pra ir ADIANTE no tempo é \`action=extend\` (o pedaço seguinte, cobrado pela duração dele): o arquivo de cada parte já traz as anteriores na frente, então a última parte pronta é o filme inteiro. Não emende as partes, a abertura repete; \`action=chain\` aponta o \`filmeInteiro\`.
@@ -355,9 +360,11 @@ Proibidos no prompt: "tarot card illustration", "intimate scale", "card-style po
355
360
 
356
361
  \`action=generate\` com \`prompt\`, \`model\` e \`aspectRatio\`. \`action=models\` (sem custo, sem login) lista o catálogo vivo com preço atual, resolução máxima e se o modelo aceita referência.
357
362
 
363
+ O preço de \`models\` é o do 1K. Sem \`size\`, nano-banana-2, nano-banana-max e a família Krea 2 geram em 2K e somam o adder de 2K: passe \`size: "1K"\` quando o barato importar mais. O que foi debitado de verdade volta em \`custoSinapses\` no retorno do generate; cite esse, não o da lista.
364
+
358
365
  ## Escolher motor em dois passos
359
366
 
360
- A lista de action=models é longa e ordenada por PREÇO, que não é a pergunta de quem vai gerar. Três campos do payload resolvem isso:
367
+ A lista de action=models é longa e segue a ordem da prateleira da casa, não o preço, e nenhuma das duas é a pergunta de quem vai gerar. Três campos do payload resolvem isso:
361
368
 
362
369
  - bestFor: pra que o motor serve, num vocabulário fechado (foto, anime, texto pra palavra legível na arte, personagem pra segurar a mesma pessoa, adulto). FILTRE por aqui antes de comparar preço.
363
370
  - family + variant: motores com a MESMA family são o mesmo motor em versão diferente (Flux.2 Klein tem Base, Anime, Celular e Transparência; Krea 2 tem Realism e Transparência; Gemini tem 3 Pro e Nano Banana 2.1; GPT Image tem 2 Low, 2 High, 2.5 Flare e 2.5 Sunburst). O retorno traz um índice families já montado.
@@ -369,12 +376,12 @@ O caminho: filtre por bestFor, escolha a família, e só então compare as varia
369
376
 
370
377
  ## Multi-referência
371
378
 
372
- Combine até 4 imagens como referência numa geração só:
379
+ Combine até 5 imagens como referência numa geração só:
373
380
 
374
381
  - \`referenceImageUrls\`: sua galeria, o Acervo, personagens públicos (descubra em \`sapiens_character\`).
375
382
  - \`sourceImageIds\`: ids da sua galeria.
376
383
 
377
- Refs valem pros modelos robustos (nano-banana-2, gpt-image-2-low/high, gpt-image-2-5-flare/sunburst, grok-2-image*). Use \`sapiens_reference\` pra achar a URL certa em vez de adivinhar.
384
+ Quem aceita e quantas: \`supportsReferences\` e \`maxReferences\` em \`action=models\`. Cada motor corta no próprio teto SEM erro (nano-banana-2 e Klein em 4, Grok em 3, Krea 2 da WaveSpeed em 1): mande as refs que importam primeiro. Use \`sapiens_reference\` pra achar a URL certa em vez de adivinhar.
378
385
 
379
386
  Nas Krea 2 da WaveSpeed a referência é a imagem-base (uma só), e \`refStrength\` diz quanto dela fica: 0.2 guarda a foto quase inteira, com o rosto; 0.45 guarda pose, roupa e fundo, e o prompt muda luz e clima; 0.65 (o default) guarda enquadramento e roupa, e rosto e cenário saem do prompt; 0.9 guarda só a proporção. A peça sai no formato da referência, não no \`aspectRatio\`. Pra segurar o rosto de personagem numa cena nova, o caminho é o Klein, não baixar a força.
380
387
 
@@ -407,7 +414,7 @@ Peça que vai virar overlay, sticker, slide ou camada de vídeo nasce recortada:
407
414
 
408
415
  ## Cuidados
409
416
 
410
- - \`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á.
417
+ - \`generate\` é SÍNCRONA e cobra no disparo. Modelo pesado (Pro, gpt-image-2-high, GPT Image 2.5 Sunburst, Grok quality, Seedream 5.0 Pro, 2K/4K) cai na regra do timeout: leia \`action=status\` antes de repetir, senão cobra duas vezes. Ela lista as gerações recentes da conta, a mais nova primeiro, e é a única leitura que mostra a peça que AINDA está gerando (a galeria só mostra pronta). Ache a sua pelo model, pelo começo do prompt e pelo \`haMinutos\`: \`gerando\` = espere e leia de novo em 1 ou 2 minutos; \`pronta\` = mostre essa; \`falhou\`/\`bloqueada\` = o débito voltou, aí sim gere de novo (bloqueada pede outro prompt ou outro motor); \`presa\` = passou de 15 minutos e o servidor estorna sozinho. 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 chega.
411
418
  - Uma imagem só vira pública (e ganha página indexável) com \`sapiens_gallery action=publish\`.
412
419
 
413
420
  ## Depois de pronta: resolução maior e sombra
@@ -417,7 +424,7 @@ Duas ações da ficha do site saem por aqui, sobre uma imagem SUA já pronta (\`
417
424
  - \`action=upscale\`: a mesma peça em resolução maior. Use quando a pessoa vai imprimir, recortar ou levar pra vídeo em tela grande.
418
425
  - \`action=sombra\`: o mapa de profundidade da foto (colorido, não em cinza), pra usar como referência de pose e volume numa geração seguinte.
419
426
 
420
- As duas cobram Sinapses, preço fixo igual ao da ficha. São síncronas e o upscale pode passar de 2 minutos: Timeout é o cliente desistindo, não o servidor. Olhe \`sapiens_gallery action=list\` antes de repetir, senão cobra duas vezes.
427
+ As duas cobram Sinapses, preço fixo igual ao da ficha. São síncronas e o upscale pode passar de 2 minutos: Timeout é o cliente desistindo, não o servidor. A peça nova não aparece enquanto trabalha: ela entra pronta em até uns 6 minutos, com o prompt começando por "Resolução ampliada:" ou "Sombra da foto:". Leia \`action=status\` antes de repetir, senão cobra duas vezes; passados 6 minutos sem ela, a ação falhou e as Sinapses voltaram.
421
428
 
422
429
  ## De qual saldo sai
423
430
 
@@ -25,6 +25,8 @@ import { convexQuery, convexMutation, convexAction, describeConvexError, getSess
25
25
  * categoria (o que a peça É; queue e edit também a declaram).
26
26
  * - voices: as abas que existem hoje, com a contagem de cada uma.
27
27
  * - status: move a peça de lane (fila, agendado, postado, descartado).
28
+ * Em agendado, volta com `aviso` quando a peça é capítulo de série e o
29
+ * anterior, no mesmo canal, não sai antes dela.
28
30
  * - edit: reescreve a peça que está na fila (copy, título, hashtags,
29
31
  * notas, mídia, língua, comentário). Só manda o que muda. Texto de peça
30
32
  * POSTADA não se reescreve: o post na rede não muda junto.
@@ -173,7 +175,7 @@ const CHANNELS = [
173
175
  * `apps/sapiens/convex/shared/categoriaDaPeca.ts`. O eixo é o ASSUNTO: quem
174
176
  * assina é a aba, a mídia é o canal. "sem" só existe no list (a triagem).
175
177
  */
176
- const CATEGORIAS = ["arte", "personagem", "tecnico", "novidade", "educativo", "mercado"];
178
+ const CATEGORIAS = ["arte", "personagem", "tecnico", "novidade", "educativo", "mercado", "pessoal"];
177
179
  export const distributionSchema = z.object({
178
180
  action: z.enum([
179
181
  "queue",
@@ -271,7 +273,8 @@ export const distributionSchema = z.object({
271
273
  "novidade: o que chegou (motor novo, feature da casa, lançamento).",
272
274
  "educativo: o porquê (aula, artigo, framework, carrossel que explica, quote).",
273
275
  "mercado: o dinheiro e a carreira (cripto, startup, vaga, oportunidade).",
274
- "Quando duas servem, vale a primeira desta ordem: novidade, tecnico, educativo, mercado, personagem, arte (o motor novo testado em páreo é novidade; o páreo com o Batkin no meio é técnico).",
276
+ "pessoal: a própria vida de quem assina (saúde, história, relato em primeira pessoa). Ganha das outras quando o assunto é a vida dele, mesmo que a peça ensine algo de passagem. A régua nunca chuta pessoal: declare.",
277
+ "Fora pessoal, quando duas servem, vale a primeira desta ordem: novidade, tecnico, educativo, mercado, personagem, arte (o motor novo testado em páreo é novidade; o páreo com o Batkin no meio é técnico).",
275
278
  "queue: declare SEMPRE que souber o que a peça é. Ausente, a régua chuta pelo título e pela 1a frase da copy, e a peça cai na gaveta errada ou na triagem; a declarada aparece na gaveta certa da fila sem toque do dono e desce pras filhas da teia.",
276
279
  "edit: troca a categoria (vale em peça já postada) e null devolve pra régua.",
277
280
  "list: filtra pela que vale (a declarada, senão a da régua); 'sem' lista a triagem, o que a régua não sabe ler. Cada item volta com categoria e categoriaDeclarada.",
@@ -454,6 +457,21 @@ function linhaDoJaSaiu(avisos) {
454
457
  const mais = resto.length ? ` e mais ${resto.length}` : "";
455
458
  return `JÁ SAIU no ${a.channel} com a mesma abertura: "${a.title}" (aba ${a.voice ?? "?"}${dia}, itemId ${a.itemId})${mais}. Confira com o dono antes de despachar. Se for repetição, descarte esta.`;
456
459
  }
460
+ /**
461
+ * A linha do "capítulo fora de ordem": a peça marcada é capítulo de série e o
462
+ * anterior, no mesmo canal, não sai antes dela. Aviso, não recusa: a hora fica
463
+ * marcada, mas na hora o tique devolve a peça pra fila em vez de soltar spoiler
464
+ * (17/10/2026, A Vó Roubou a Fila). Servidor anterior a 18/10/2026 não manda o
465
+ * campo, e aí não há linha.
466
+ */
467
+ function linhaDoForaDeOrdem(c) {
468
+ if (!c)
469
+ return null;
470
+ const onde = c.status === "fila" || c.scheduledFor === null
471
+ ? "está na fila sem hora"
472
+ : `está marcado pra ${new Date(c.scheduledFor).toISOString().slice(0, 16).replace("T", " ")} UTC, depois desta`;
473
+ return `FORA DE ORDEM: esta peça é o capítulo ${c.capitulo} e o capítulo ${c.anterior} ("${c.title}", itemId ${c.itemId}) ainda não saiu neste canal: ${onde}. Na hora marcada esta NÃO sai sozinha: volta pra fila com o motivo. Marque o capítulo ${c.anterior} pra antes desta, ou poste ele primeiro, e mostre isto ao dono.`;
474
+ }
457
475
  export async function distribution(args) {
458
476
  const sessionToken = getSessionToken();
459
477
  if ((args.action === "queue" || args.action === "edit") && args.categoria === "sem") {
@@ -613,14 +631,20 @@ export async function distribution(args) {
613
631
  throw new Error("action=status exige itemId.");
614
632
  if (!args.status)
615
633
  throw new Error("action=status exige status.");
616
- await convexMutation("distributionMcp:mcpSetStatus", {
634
+ const r = await convexMutation("distributionMcp:mcpSetStatus", {
617
635
  sessionToken,
618
636
  itemId: args.itemId,
619
637
  status: args.status,
620
638
  postUrl: args.postUrl,
621
639
  scheduledFor: args.scheduledFor,
622
640
  });
623
- return { ok: true, itemId: args.itemId, status: args.status };
641
+ const foraDeOrdem = linhaDoForaDeOrdem(r?.foraDeOrdem);
642
+ return {
643
+ ok: true,
644
+ itemId: args.itemId,
645
+ status: args.status,
646
+ ...(foraDeOrdem ? { aviso: foraDeOrdem, capituloAnterior: r.foraDeOrdem, instruction: foraDeOrdem } : {}),
647
+ };
624
648
  }
625
649
  if (args.action === "bio") {
626
650
  if (!args.channel)
@@ -491,8 +491,10 @@ export async function gallery(args) {
491
491
  includeBase64: args.includeBase64 ?? false,
492
492
  });
493
493
  }
494
- // publish: liga isPublic na própria imagem. Public = entra na galeria
495
- // pública + feed Pinterest (e /imagem/<id> se não for degen). Publicado não
494
+ // publish: liga isPublic na própria peça, imagem ou vídeo (o vídeo desde
495
+ // 20/10/2026, pelo servidor: este pacote só repassa, então versão antiga
496
+ // publica vídeo também). Imagem entra na galeria pública + feed Pinterest (e
497
+ // /imagem/<id> se não for degen); vídeo ganha a /video/<id>. Publicado não
496
498
  // despublica (decisão do dono, 22/08/2026): a action unpublish saiu daqui, e
497
499
  // pacote antigo que ainda mandar isPublic=false toma a recusa do servidor.
498
500
  if (args.action === "publish") {
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { httpUrl } from "../schema.js";
3
- import { convexAction, convexQuery, getSessionToken } from "../convexClient.js";
3
+ import { convexAction, convexQuery, describeConvexError, getSessionToken } from "../convexClient.js";
4
4
  // IDs canônicos do catálogo (apps/sapiens/convex/shared/imageModels.ts).
5
5
  // IDs fora dessa lista caem no fallback e o pricing vira 999. Sempre usar os
6
6
  // IDs canônicos.
@@ -23,7 +23,7 @@ const MODELS = [
23
23
  "flux-3-klein", // FLUX 3 Image Klein (BFL, pela fal) · o FLUX 3 leve, rápido e barato · COM refs (10 no catálogo, 5 pelo MCP) · adder até 2K · moderado, não é +18
24
24
  "krea-3", // Krea 3 (Krea, beta) · edita por referência e segura personagem · COM refs (8 no catálogo, 5 pelo MCP), CADA ref soma no preço · adder até 2K · moderado, não é +18
25
25
  "krea-3-high", // O MESMO Krea 3 com thinking high: planeja a edição antes, pra várias refs combinadas · mais caro e mais lento
26
- // ByteDance via ModelArk direto. Corp/censurado, 2K nativo, COM refs (até 4).
26
+ // ByteDance via ModelArk direto. Corp/censurado, 2K nativo, COM refs (10 no catálogo, 5 pelo MCP).
27
27
  // (O "nova-canvas" ficava aqui e saiu: a AWS matou o modelo. Pedido antigo
28
28
  // ainda funciona, o backend faz o alias pro gpt-image-2-low.)
29
29
  "seedream-4-5", // Seedream 4.5 · cinematográfico
@@ -106,12 +106,55 @@ const DEFAULT_2K_MODELS = new Set([
106
106
  "nano-banana-max",
107
107
  "nano-banana-2",
108
108
  ]);
109
+ /** Teto de referências somadas (urls + ids) que o servidor aceita numa chamada (MAX_REFERENCES do referenceResolver). */
110
+ const MAX_REFS_PELO_MCP = 5;
109
111
  /** Tamanho padrão do motor, quando quem chama não passou o size. */
110
112
  function defaultSizeFor(model) {
111
113
  return model && DEFAULT_2K_MODELS.has(model) ? "2K" : undefined;
112
114
  }
115
+ /**
116
+ * Onde conferir quando o generate passa do teto do cliente (o fim da mensagem
117
+ * de Timeout). Antes a frase genérica mandava ler "o status do mesmo tool", que
118
+ * não existia aqui, e as instruções mandavam pra galeria, que só mostra peça
119
+ * pronta: a que ainda gerava sumia, o agente repetia e a conta pagava duas vezes.
120
+ */
121
+ export const ONDE_CONFERIR_GENERATE = "Leia sapiens_image action=status: ela lista as gerações recentes da conta com o estado de cada uma, inclusive a que ainda está gerando (a galeria só mostra peça pronta). gerando = ainda no servidor, volte em 1 ou 2 minutos; pronta = a peça está no acervo, mostre essa; falhou = o débito voltou, e só aí gere de novo.";
122
+ /**
123
+ * Upscale e sombra não abrem row antes de terminar: a peça nova nasce pronta,
124
+ * então durante o trabalho não há nada pra ver. Os prazos somam os tetos de
125
+ * imageUpscale.ts e depthFoto.ts (pedido, poll, download).
126
+ */
127
+ export const ONDE_CONFERIR_UPSCALE_SOMBRA = "A peça nova só aparece quando fica pronta, em até uns 6 minutos: leia sapiens_image action=status (ou sapiens_gallery action=list), onde ela entra pronta ao lado da original, com o prompt começando por 'Resolução ampliada:' (upscale) ou 'Sombra da foto:' (sombra). Passados 6 minutos sem ela, a ação falhou e as Sinapses voltaram: só aí repita.";
128
+ /**
129
+ * Gerando há mais que isto = presa. A régua é a da varredura do servidor
130
+ * (loadingAindaVale, LOADING_SEM_JOB_MS): 15 minutos contados do maior entre a
131
+ * criação e o claim do render, senão a row do site que o MCP renderizou depois
132
+ * viraria "presa" aqui com o render pago ainda vivo lá.
133
+ */
134
+ const PRESA_MS = 15 * 60 * 1000;
135
+ const LEITURA_STATUS = "As gerações de imagem mais recentes da conta, a mais nova primeiro. As que estão gerando vêm todas; o resto vem cortado. gerando: o servidor ainda está nela e ela JÁ cobrou; NÃO gere de novo, leia de novo em 1 ou 2 minutos. pronta: está no acervo (imageId, url, acervoUrl): mostre essa em vez de gerar outra. falhou: não virou peça e o que saiu em Sinapses volta sozinho; pode gerar de novo. bloqueada: o motor recusou o conteúdo e o que saiu em Sinapses volta sozinho; mude o prompt ou o motor antes de tentar de novo. presa: gerando há mais de 15 minutos, não termina mais; a varredura do servidor marca como falha e devolve as Sinapses, então pode gerar de novo. Pra achar a peça de um Timeout, case pelo model, pelo começo do prompt e pelo haMinutos. Upscale e sombra só aparecem quando ficam prontos.";
136
+ /**
137
+ * A leitura do action=status: o servidor devolve o estado cru e as horas da
138
+ * row (sem relógio nele), e a idade e o "presa" saem daqui.
139
+ */
140
+ export function lerGeracoesRecentes(items, agora, truncado = false) {
141
+ const lidas = items.map((i) => {
142
+ const idadeMs = Math.max(0, agora - Number(i.createdAt));
143
+ const desde = Math.max(Number(i.createdAt), Number(i.renderClaimedAt ?? 0));
144
+ const presa = i.estado === "gerando" && agora - desde >= PRESA_MS;
145
+ return { ...i, estado: presa ? "presa" : i.estado, haMinutos: Math.floor(idadeMs / 60000) };
146
+ });
147
+ return {
148
+ gerando: lidas.filter((i) => i.estado === "gerando").length,
149
+ count: lidas.length,
150
+ items: lidas,
151
+ // A leitura parou no teto de varredura: geração mais antiga que isso não entrou.
152
+ ...(truncado ? { truncado: true } : {}),
153
+ leitura: LEITURA_STATUS,
154
+ };
155
+ }
113
156
  export const imageSchema = z.object({
114
- action: z.enum(["generate", "request_generation", "compose", "models", "upscale", "sombra"]),
157
+ action: z.enum(["generate", "request_generation", "compose", "models", "upscale", "sombra", "status"]),
115
158
  prompt: z
116
159
  .string()
117
160
  .optional()
@@ -119,7 +162,7 @@ export const imageSchema = z.object({
119
162
  model: z
120
163
  .enum(MODELS)
121
164
  .optional()
122
- .describe("Default 'nano-banana-2' (Nano Banana 2.1 com refs). 'nano-banana-max' (Pro 3) = qualidade alta. 'gpt-image-2-low/high' = GPT Image 2 nos tiers low e high. 'gpt-image-2-5-flare'/'gpt-image-2-5-sunburst' = GPT Image 2.5, a geração de set/2026, os dois em quality high e com referência: o Flare é o rápido, pra volume; o Sunburst é o de precisão, pra composição complexa e edição em várias voltas. 'mai-image-2-6' (MAI Image 2.6, da Microsoft) = foto rápida no mesmo recurso do gpt-image-2, e o único da casa que cobra por ÁREA: o adder de resolução aqui é real, o teto é 2K (1536², porque acima disso o provider recusa por total de pixels) e ele não aceita referência. 'muse-image' (Muse Image, da Meta) = barato na edição por referência e bom pra segurar personagem (10 refs no catálogo; pelo MCP vale o teto de 5 do campo referenceImageUrls); sem escada de resolução (o motor decide o tamanho e cobra o mesmo) e moderação apertada, NÃO serve pra +18. 'flux-3' (FLUX 3, da Black Forest Labs) = texto na arte e composição precisos, aceita referência (10 no catálogo; pelo MCP vale o teto de 5 do campo referenceImageUrls), resolução até 2K; com referência a proporção segue a primeira imagem; moderado, NÃO serve pra +18. 'flux-3-klein' (FLUX 3 Image Klein, o irmão leve de pesos abertos do FLUX 3) = foto e personagem rápidos (~5 s) e baratos, aceita referência (10 no catálogo; pelo MCP vale o teto de 5 do campo referenceImageUrls) e segura a pessoa da ref numa cena nova, resolução até 2K; moderado, NÃO serve pra +18. 'krea-3' (Krea 3, da Krea, em beta) = edição por referência que SEGURA PERSONAGEM, inclusive anime e criatura com marcador no rosto, e foto com pele crua; aceita referência (8 no catálogo; pelo MCP vale o teto de 5 do campo referenceImageUrls), resolução até 2K. É o ÚNICO da casa que cobra cada referência à parte: o preço sobe por ref (o valor vivo sai em action=models), então mande só as refs que a cena precisa. Escreva em linguagem simples e chame cada referência pelo que ela mostra ('a primeira imagem é o rosto, a segunda o figurino'); pra edição, a mudança primeiro e uma frase de preservação no fim. 'krea-3-high' = o MESMO Krea 3 com thinking high: monta um plano de edição a partir do prompt e das refs antes de gerar; use quando combinar várias refs ou quando o 'krea-3' errou o pedido. Moderado, NÃO serve pra +18. 'seedream-4-5'/'seedream-5-0'/'seedream-5-0-pro' = ByteDance 2K nativo, cinematográfico, aceita até 4 referências (o 5.0 é a geração nova, entende prompt complexo melhor; o 5.0-pro é o topo da linha). 'grok-2-image'/'grok-2-image-quality' = xAI Grok Imagine da geração anterior (moderação frouxa +18, aceita refs e aspect; quality é mais fiel pra character lock); 'grok-imagine-2' = Grok Imagine Image 2.0, a geração nova de ago/2026, que é a melhor da linha integrando desenho e foto na mesma peça. DEGEN (uncensored, gate +18). NUDEZ É NÍVEL, NÃO MOTOR: três escolhas têm um par com nudez, e o par vem no campo `ousadia` de action=models. Sem nudez = o id da escolha; com nudez = o id do par + loraIntensity. Não existe motor '+18', 'Nu' ou 'Livre' separado pra procurar. 'wavespeed-chroma' (fotorrealista rápido, sem referência), 'wavespeed-flux2' (Flux.2 Klein Base: foto rápida, até 4 referências, segura o rosto) · com nudez = 'wavespeed-klein-nu' (o mesmo Klein com a LoRA de nudez no registro de foto; a identidade continua vindo da referência), 'wavespeed-klein-anime' (Flux.2 Klein + LoRA anime, inteligente e controlável) · com nudez = 'wavespeed-klein-anime-plus', 'wavespeed-flux-nsfw' (Flux dev + LoRA NSFW, sem par: a Ousadia é dele mesmo, via loraIntensity), 'wavespeed-klein-celular' (o MESMO Klein 9B com a LoRA Phone Photography por cima: foto de celular, candid, luz de janela; no edit com a referência da personagem o rosto fica quase idêntico, e é a receita de conteúdo de perfil de criadora pra personagem FOTOGRÁFICA. Em personagem cel-shaded o edit troca o traço, então não use com a Aria. Comece o prompt com 'This is a candid photograph taken with a smartphone of' e enumere 'same face, same hair' antes da cena; sem Ousadia), 'wavespeed-klein-transparencia' (o MESMO Klein 9B com a LoRA de tecido transparente: o efeito vem da LoRA e não da descrição; segura o rosto da ref; sem Ousadia), 'wavespeed-krea2-realism' (Krea 2 Realism: base 12B com LoRA de realismo, pele crua, hospedada na WaveSpeed SEM o classificador de entrada da fal, que devolve 422 até em prompt sem palavra de nudez; img2img de verdade com 1 referência, força regulável em refStrength, e 2K) · com nudez = 'wavespeed-krea2' (o mesmo realismo + LoRA NSFW; é a antiga 'Krea 2 Livre'), 'wavespeed-krea2-transparencia' (a mesma LoRA de transparência na base Krea 2: fotografia mais rica, identidade mais frouxa que a irmã Klein; 1 referência; sem Ousadia) = WaveSpeed rápido. LEGADO, fora da escolha da tela e ainda gerando pelo id (não ofereça): 'wavespeed-klein-plus' (a escolha virou 'wavespeed-klein-nu'), 'wavespeed-krea2-base' (Krea 2 sem LoRA nenhuma; a escolha virou 'wavespeed-krea2-realism'), 'fal-krea2-realism-v2' (Krea 2 Realism pela fal, ~4s: aceita até 3 referências de ESTILO, paleta/luz/textura de uma série, não character lock, e com referência a LoRA não vai junto; a escolha virou 'wavespeed-krea2-realism'). 'civitai-wai-illustrious'/'civitai-nova-anime-xl' (anime), 'civitai-pony-v6' (Pony V6 XL, base nº1) = Civitai sdcpp rápido. 'civitai-anima' (Anima: 2B da CircleStone com a Comfy Org, base própria e não SDXL; entrega anime de PRODUÇÃO, linha fechada e sombra chapada, em vez do registro de ilustração dos Illustrious. O encoder é um LLM, então escreva a cena em FRASE e não em tag booru, e não cole score_9/masterpiece aqui)."),
165
+ .describe("Default 'nano-banana-2' (Nano Banana 2.1 com refs). 'nano-banana-max' (Pro 3) = qualidade alta. 'gpt-image-2-low/high' = GPT Image 2 nos tiers low e high. 'gpt-image-2-5-flare'/'gpt-image-2-5-sunburst' = GPT Image 2.5, a geração de set/2026, os dois em quality high e com referência: o Flare é o rápido, pra volume; o Sunburst é o de precisão, pra composição complexa e edição em várias voltas. 'mai-image-2-6' (MAI Image 2.6, da Microsoft) = foto rápida no mesmo recurso do gpt-image-2, e o único da casa que cobra por ÁREA: o adder de resolução aqui é real, o teto é 2K (1536², porque acima disso o provider recusa por total de pixels) e ele não aceita referência. 'muse-image' (Muse Image, da Meta) = barato na edição por referência e bom pra segurar personagem (10 refs no catálogo; pelo MCP vale o teto de 5 do campo referenceImageUrls); sem escada de resolução (o motor decide o tamanho e cobra o mesmo) e moderação apertada, NÃO serve pra +18. 'flux-3' (FLUX 3, da Black Forest Labs) = texto na arte e composição precisos, aceita referência (10 no catálogo; pelo MCP vale o teto de 5 do campo referenceImageUrls), resolução até 2K; com referência a proporção segue a primeira imagem; moderado, NÃO serve pra +18. 'flux-3-klein' (FLUX 3 Image Klein, o irmão leve de pesos abertos do FLUX 3) = foto e personagem rápidos (~5 s) e baratos, aceita referência (10 no catálogo; pelo MCP vale o teto de 5 do campo referenceImageUrls) e segura a pessoa da ref numa cena nova, resolução até 2K; moderado, NÃO serve pra +18. 'krea-3' (Krea 3, da Krea, em beta) = edição por referência que SEGURA PERSONAGEM, inclusive anime e criatura com marcador no rosto, e foto com pele crua; aceita referência (8 no catálogo; pelo MCP vale o teto de 5 do campo referenceImageUrls), resolução até 2K. É o ÚNICO da casa que cobra cada referência à parte: o preço sobe por ref (o valor vivo sai em action=models), então mande só as refs que a cena precisa. Escreva em linguagem simples e chame cada referência pelo que ela mostra ('a primeira imagem é o rosto, a segunda o figurino'); pra edição, a mudança primeiro e uma frase de preservação no fim. 'krea-3-high' = o MESMO Krea 3 com thinking high: monta um plano de edição a partir do prompt e das refs antes de gerar; use quando combinar várias refs ou quando o 'krea-3' errou o pedido. Moderado, NÃO serve pra +18. 'seedream-4-5'/'seedream-5-0'/'seedream-5-0-pro' = ByteDance 2K nativo, cinematográfico, aceita referência (10 no catálogo; pelo MCP vale o teto de 5 do campo referenceImageUrls) (o 5.0 é a geração nova, entende prompt complexo melhor; o 5.0-pro é o topo da linha). 'grok-2-image'/'grok-2-image-quality' = xAI Grok Imagine da geração anterior (moderação frouxa +18, aceita refs e aspect; quality é mais fiel pra character lock); 'grok-imagine-2' = Grok Imagine Image 2.0, a geração nova de ago/2026, que é a melhor da linha integrando desenho e foto na mesma peça. Daqui em diante, a LINHA DEGEN (sem freio de conteúdo, gate +18; o Grok fica fora dela). NUDEZ É NÍVEL, NÃO MOTOR: três escolhas têm um par com nudez, e o par vem no campo `ousadia` de action=models. Sem nudez = o id da escolha; com nudez = o id do par + loraIntensity. Não existe motor '+18', 'Nu' ou 'Livre' separado pra procurar. 'wavespeed-chroma' (fotorrealista rápido, sem referência), 'wavespeed-flux2' (Flux.2 Klein Base: foto rápida, até 4 referências, segura o rosto) · com nudez = 'wavespeed-klein-nu' (o mesmo Klein com a LoRA de nudez no registro de foto; a identidade continua vindo da referência), 'wavespeed-klein-anime' (Flux.2 Klein + LoRA anime, inteligente e controlável) · com nudez = 'wavespeed-klein-anime-plus', 'wavespeed-flux-nsfw' (Flux dev + LoRA NSFW, sem par: a Ousadia é dele mesmo, via loraIntensity), 'wavespeed-klein-celular' (o MESMO Klein 9B com a LoRA Phone Photography por cima: foto de celular, candid, luz de janela; no edit com a referência da personagem o rosto fica quase idêntico, e é a receita de conteúdo de perfil de criadora pra personagem FOTOGRÁFICA. Em personagem cel-shaded o edit troca o traço, então não use com a Aria. Comece o prompt com 'This is a candid photograph taken with a smartphone of' e enumere 'same face, same hair' antes da cena; sem Ousadia), 'wavespeed-klein-transparencia' (o MESMO Klein 9B com a LoRA de tecido transparente: o efeito vem da LoRA e não da descrição; segura o rosto da ref; sem Ousadia), 'wavespeed-krea2-realism' (Krea 2 Realism: base 12B com LoRA de realismo, pele crua, hospedada na WaveSpeed SEM o classificador de entrada da fal, que devolve 422 até em prompt sem palavra de nudez; img2img de verdade com 1 referência, força regulável em refStrength, e 2K) · com nudez = 'wavespeed-krea2' (o mesmo realismo + LoRA NSFW; é a antiga 'Krea 2 Livre'), 'wavespeed-krea2-transparencia' (a mesma LoRA de transparência na base Krea 2: fotografia mais rica, identidade mais frouxa que a irmã Klein; 1 referência; sem Ousadia) = WaveSpeed rápido. LEGADO, fora da escolha da tela e ainda gerando pelo id (não ofereça): 'wavespeed-klein-plus' (a escolha virou 'wavespeed-klein-nu'), 'wavespeed-krea2-base' (Krea 2 sem LoRA nenhuma; a escolha virou 'wavespeed-krea2-realism'), 'fal-krea2-realism-v2' (Krea 2 Realism pela fal, ~4s: aceita até 3 referências de ESTILO, paleta/luz/textura de uma série, não character lock, e com referência a LoRA não vai junto; a escolha virou 'wavespeed-krea2-realism'). 'civitai-wai-illustrious'/'civitai-nova-anime-xl' (anime), 'civitai-pony-v6' (Pony V6 XL, base nº1) = Civitai sdcpp rápido. 'civitai-anima' (Anima: 2B da CircleStone com a Comfy Org, base própria e não SDXL; entrega anime de PRODUÇÃO, linha fechada e sombra chapada, em vez do registro de ilustração dos Illustrious. O encoder é um LLM, então escreva a cena em FRASE e não em tag booru, e não cole score_9/masterpiece aqui)."),
123
166
  aspectRatio: z
124
167
  .enum(["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"])
125
168
  .optional()
@@ -131,12 +174,12 @@ export const imageSchema = z.object({
131
174
  styleId: z
132
175
  .string()
133
176
  .optional()
134
- .describe("Default 'none'. IDs de estilo no convex/shared/imageStyles.ts."),
177
+ .describe("Legado, sem efeito: o servidor não lê este campo. Estilo vem de brandSlug, templateSlug ou registro."),
135
178
  negativePrompt: z.string().optional(),
136
179
  referenceImageUrls: z
137
180
  .array(httpUrl())
138
181
  .optional()
139
- .describe("URLs públicas de referência pra combinar numa geração só (character/style lock), igual ao modal 'Selecionar Referência' do gerador web. Fontes: sua galeria (sapiens_gallery, campo url), o Acervo, e personagens públicos (sapiens_character action=list_public → mainImageUrl/imageUrls). Restrito a hosts do Sapiens (Bunny CDN / Convex) + Wikimedia. Requer model com refs (o campo supportsReferences em action=models é a fonte): nano-banana-*, gpt-image-2-*, muse-image, flux-3, flux-3-klein, krea-3 e krea-3-high (cada ref soma no preço), seedream-*, grok-* (até 3), a família Klein (wavespeed-flux2 e wavespeed-klein-*, até 4), wavespeed-flux-nsfw (1), a família Krea 2 da WaveSpeed (wavespeed-krea2-realism, wavespeed-krea2 e wavespeed-krea2-transparencia; img2img: a primeira referência vira a imagem-base, 1 ref, e refStrength diz quanto dela fica) ou fal-krea2-* (referência de ESTILO, teto de 3). Soma com sourceImageIds: até 5 no total (acima disso o servidor recusa a chamada), e cada motor corta no seu teto quando ele é menor (Seedream 4, Klein 4, Grok 3, Krea 2 e Flux NSFW 1; o campo maxReferences em action=models é a fonte)."),
182
+ .describe("URLs públicas de referência pra combinar numa geração só (character/style lock), igual ao modal 'Selecionar Referência' do gerador web. Fontes: sua galeria (sapiens_gallery, campo url), o Acervo, e personagens públicos (sapiens_character action=list_public → mainImageUrl/imageUrls). Restrito a hosts do Sapiens (Bunny CDN / Convex) + Wikimedia. Requer model com refs (o campo supportsReferences em action=models é a fonte): nano-banana-*, gpt-image-2-*, muse-image, flux-3, flux-3-klein, krea-3 e krea-3-high (cada ref soma no preço), seedream-*, grok-* (até 3), a família Klein (wavespeed-flux2 e wavespeed-klein-*, até 4), wavespeed-flux-nsfw (1), a família Krea 2 da WaveSpeed (wavespeed-krea2-realism, wavespeed-krea2 e wavespeed-krea2-transparencia; img2img: a primeira referência vira a imagem-base, 1 ref, e refStrength diz quanto dela fica) ou fal-krea2-* (referência de ESTILO, teto de 3). Soma com sourceImageIds: até 5 no total (acima disso o servidor recusa a chamada), e cada motor corta no seu teto quando ele é menor, SEM erro: a ref a mais some (nano-banana-2 e Klein 4, Grok 3, Krea 2 da WaveSpeed e Flux NSFW 1; o campo maxReferences em action=models é a fonte)."),
140
183
  sourceImageIds: z
141
184
  .array(z.string())
142
185
  .optional()
@@ -264,6 +307,14 @@ export async function image(args) {
264
307
  // resolução. null = referência não muda o preço.
265
308
  referenceAdders: m.referencePriceSinapses ?? null,
266
309
  supportsReferences: !!m.supportsReferences,
310
+ // Quantas referências chegam ao motor PELO MCP: o teto do motor no
311
+ // catálogo (ausente = 4, o DEFAULT_MAX_REFERENCES do servidor) cortado
312
+ // no teto de 5 do resolvedor. As descrições de referenceImageUrls e
313
+ // sourceImageIds mandam olhar este campo, que até a 1.103 não existia:
314
+ // a 5ª ref no nano-banana-2 sumia sem aviso. 0 = não aceita referência.
315
+ maxReferences: m.supportsReferences
316
+ ? Math.min(typeof m.maxReferences === "number" ? m.maxReferences : 4, MAX_REFS_PELO_MCP)
317
+ : 0,
267
318
  degen: /^(civitai-|wavespeed-|fal-)/.test(String(m.id)),
268
319
  // O id que gera esta mesma escolha COM nudez (manda junto loraIntensity).
269
320
  // null = sem par; se o próprio motor tem LoRA tunável, a Ousadia é dele.
@@ -297,7 +348,8 @@ export async function image(args) {
297
348
  models,
298
349
  families,
299
350
  note: "priceSinapses é o preço COBRADO em 1K (já com override admin). Acima de 1K some o adder de resolutionAdders[size], que vem clampado ao teto do motor: em modelo que para em 2K, a chave '4K' repete o valor de 2K porque é isso que a cobrança aplica quando você pede 4K nele. " +
300
- "Preço final = priceSinapses + resolutionAdders[size]. degen=+18 (gate na galeria). " +
351
+ "Preço final = priceSinapses + resolutionAdders[size]. Sem size, nano-banana-* e a família Krea 2 geram em 2K (somam o adder de 2K); o valor debitado volta em custoSinapses no generate. degen=+18 (gate na galeria). " +
352
+ "maxReferences é quantas referências chegam ao motor por aqui (o teto dele, cortado em 5): a mais, somem sem erro. " +
301
353
  "Quando referenceAdders vem preenchido (Krea 3), cada referência soma referenceAdders[size] ao preço, até o teto do motor. " +
302
354
  "Modelos com a MESMA family são o mesmo motor em versão diferente (o variant diz qual): compare preço e nota entre eles antes de trocar de família. " +
303
355
  "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).",
@@ -312,6 +364,21 @@ export async function image(args) {
312
364
  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).");
313
365
  }
314
366
  const sessionToken = getSessionToken();
367
+ // status: a leitura depois de um Timeout. Somente leitura, sem custo. Mostra
368
+ // a peça que AINDA está gerando, que a galeria não mostra.
369
+ if (args.action === "status") {
370
+ let r;
371
+ try {
372
+ r = await convexQuery("imagemRecentes:mcpGeracoesRecentes", { sessionToken });
373
+ }
374
+ catch (e) {
375
+ // Pacote na frente do deploy: o servidor ainda não tem a leitura.
376
+ if (!/Could not find public function/i.test(describeConvexError(e)))
377
+ throw e;
378
+ throw new Error("action=status ainda não chegou no servidor. Enquanto isso, sapiens_gallery action=list mostra só a peça PRONTA: a que ainda está gerando não aparece lá. Depois de um Timeout, espere uns minutos e olhe a galeria antes de gerar de novo.");
379
+ }
380
+ return lerGeracoesRecentes(r?.items ?? [], Date.now(), r?.truncado === true);
381
+ }
315
382
  // v1.8: compose tem args próprios, validação separada
316
383
  // Ações da ficha do site sobre uma imagem SUA pronta: a peça nova entra no
317
384
  // acervo como versão, e a de origem fica intacta. Síncronas e com custo fixo;
@@ -321,7 +388,7 @@ export async function image(args) {
321
388
  throw new Error(`action=${args.action} exige sourceImageId (uma imagem SUA pronta; ache em sapiens_gallery action=list).`);
322
389
  }
323
390
  const fn = args.action === "upscale" ? "imageUpscale:aumentarResolucao" : "depthFoto:extrairSombraDaFoto";
324
- return await convexAction(fn, { imageId: args.sourceImageId, sessionToken });
391
+ return await convexAction(fn, { imageId: args.sourceImageId, sessionToken }, { ondeConferir: ONDE_CONFERIR_UPSCALE_SOMBRA });
325
392
  }
326
393
  if (args.action === "compose") {
327
394
  if (!args.instruction || args.instruction.trim().length < 5) {
@@ -402,7 +469,7 @@ export async function image(args) {
402
469
  ...(args.maskRegions ? { maskRegions: args.maskRegions } : {}),
403
470
  ...(args.pagarCom === "dolar" ? { pagarCom: "dolar" } : {}),
404
471
  ...(args.transparentBackground === true ? { transparentBackground: true } : {}),
405
- });
472
+ }, { ondeConferir: ONDE_CONFERIR_GENERATE });
406
473
  // Backend de antes do campo não devolve motor nem ficha: cai no id pedido e
407
474
  // monta a ficha aqui. O nome bonito (modelName) só vem do servidor.
408
475
  const model = result?.model ?? genModel;
@@ -423,6 +490,9 @@ export async function image(args) {
423
490
  ...(args.transparentBackground === true ? { transparentBackground: true } : {}),
424
491
  // Pago com o saldo em dólar da API: quanto saiu. Ausente quando foi em Sinapses.
425
492
  ...(typeof result?.pagoEmDolarUsd === "number" ? { pagoEmDolarUsd: result.pagoEmDolarUsd } : {}),
493
+ // Pago em Sinapses: o que o extrato debitou (resolução, refs pagas e
494
+ // override já dentro). Backend de antes do campo não manda, e aí não vem.
495
+ ...(typeof result?.custoSinapses === "number" ? { custoSinapses: result.custoSinapses } : {}),
426
496
  // Não incluir base64 no retorno (polui contexto). Quem quiser bytes
427
497
  // chama sapiens_gallery action=get com includeBase64=true.
428
498
  };
@@ -49,7 +49,7 @@ export const referenceSchema = z.object({
49
49
  "'favorites' (as que você curtiu), 'videos' (seus vídeos; idem), 'stock_video' (Banco de Vídeo da casa: clipes/B-roll prontos, aceita term/orientation/loopOnly), " +
50
50
  "'acervo' (stock + comunidade públicos de IMAGEM), 'characters' (personagens), " +
51
51
  "'depth_map' (guias de movimento: os SEUS com shadowSource='mine', ou o banco curado da casa por default), " +
52
- "'pose_map' (seu banco privado de POSE: mapa de profundidade de corpo num quadro parado, aceita term/orientation/people/hasObject/objeto/favoritesOnly/collection/sort, mais semantic e similarTo pra busca por sentido)."),
52
+ "'pose_map' (seu banco privado de POSE: mapa de profundidade de corpo num quadro parado, quase 7 mil poses organizadas por familia/vista/enquadramento/leitura; aceita também term/orientation/people/hasObject/hasSuporte/objeto/favoritesOnly/collection/sort, mais semantic e similarTo pra busca por sentido)."),
53
53
  handle: z
54
54
  .string()
55
55
  .optional()
@@ -129,6 +129,41 @@ export const referenceSchema = z.object({
129
129
  .enum(["recent", "used"])
130
130
  .optional()
131
131
  .describe("Só bucket 'pose_map': 'recent' (entrada no banco, default) ou 'used' (as que ele acabou de usar, mais úteis pra continuar uma série)."),
132
+ // As categorias do pose_map. Os ids espelham apps/sapiens/convex/shared/poseTaxonomia.js
133
+ // (fonte única; o teste convex/shared/poseTaxonomia.test.ts trava a paridade).
134
+ familia: z
135
+ .enum([
136
+ "em-pe",
137
+ "em-movimento",
138
+ "inclinada",
139
+ "agachada",
140
+ "ajoelhada",
141
+ "sentada",
142
+ "deitada",
143
+ "quatro-apoios",
144
+ "no-ar",
145
+ "invertida",
146
+ ])
147
+ .optional()
148
+ .describe("Só bucket 'pose_map': o que o corpo faz, a categoria principal do banco (lida por visão, não pela tag do esqueleto). " +
149
+ "'em-movimento' = passada, corrida, chute, giro; 'inclinada' = em pé com o tronco dobrado; 'no-ar' = salto, queda, pendurada; " +
150
+ "'invertida' = de cabeça pra baixo. Combina com vista e com semantic."),
151
+ vista: z
152
+ .enum(["frente", "tres-quartos", "perfil", "costas"])
153
+ .optional()
154
+ .describe("Só bucket 'pose_map': de onde a câmera vê o corpo. 'costas' inclui três quartos de costas: a fronteira é peito ou costas pra câmera, que a sombra sem rosto não diz sozinha."),
155
+ enquadramento: z
156
+ .enum(["corpo-inteiro", "joelhos", "cintura", "busto"])
157
+ .optional()
158
+ .describe("Só bucket 'pose_map': até onde o quadro mostra o corpo."),
159
+ leitura: z
160
+ .enum(["clara", "confusa"])
161
+ .optional()
162
+ .describe("Só bucket 'pose_map': 'clara' = a sombra se entende sozinha. 'confusa' = membro fundido, escondido ou cortado, a sombra que devolve braço sem dono ou duas pernas esquerdas. Pra gerar personagem, prefira 'clara'."),
163
+ hasSuporte: z
164
+ .boolean()
165
+ .optional()
166
+ .describe("Só bucket 'pose_map': true = só poses apoiadas em algo além do chão (banco, bloco, parede, cama: vem em `suporte`); false = só no chão. Omitir = as duas."),
132
167
  mode: z
133
168
  .enum(["mine", "public"])
134
169
  .optional()
@@ -171,6 +206,9 @@ export async function reference(args) {
171
206
  similarTo: args.similarTo,
172
207
  people: args.people,
173
208
  limit: args.limit,
209
+ familia: args.familia,
210
+ vista: args.vista,
211
+ leitura: args.leitura,
174
212
  });
175
213
  const itens = res?.items ?? [];
176
214
  return {
@@ -181,7 +219,8 @@ export async function reference(args) {
181
219
  note: itens.length
182
220
  ? "Vizinhança por SENTIDO, da mais parecida pra menos (`score` é a distância). " +
183
221
  "Cada item traz `descricao`: leia antes de escolher, é ela que diz o que o corpo faz. " +
184
- "Use a `url` em sapiens_image referenceImageUrls."
222
+ "Use a `url` em sapiens_image referenceImageUrls e cole `membros` no prompt (lado de cada braço e perna). " +
223
+ "`usadaPor` diz quem já usou a pose: não repita na mesma personagem."
185
224
  : "Nenhuma pose do seu banco chegou perto disso. Tente descrever o CORPO (apoio, direção do tronco, " +
186
225
  "o que os braços fazem) em vez da cena, ou use browse com people/hasObject.",
187
226
  };
@@ -242,6 +281,11 @@ export async function reference(args) {
242
281
  favoritesOnly: args.favoritesOnly,
243
282
  collection: args.collection,
244
283
  sort: args.sort,
284
+ familia: args.familia,
285
+ vista: args.vista,
286
+ enquadramento: args.enquadramento,
287
+ leitura: args.leitura,
288
+ hasSuporte: args.hasSuporte,
245
289
  });
246
290
  let note;
247
291
  if (args.bucket === "videos") {
@@ -258,7 +302,7 @@ export async function reference(args) {
258
302
  }
259
303
  else if (args.bucket === "pose_map") {
260
304
  note =
261
- "Poses do SEU banco privado (mapa de profundidade de corpo). Use a `url` em sapiens_image referenceImageUrls pra guiar a POSE da figura, do mesmo jeito que o depth_map guia o movimento no vídeo. Cada item diz `people` (solo/dupla/grupo), `objeto` (null = corpo limpo), `favorite` e `useCount`; pra recortar, mande people/hasObject/objeto em vez de tentar pela busca escrita. Pedido vago ('uma pose boa pra isso') começa por favoritesOnly=true ou sort='used': é a curadoria que o dono já fez, e ela vale mais que mil opções. `thumbnailUrl` é só preview da grade. É material privado seu: a peça gerada a partir dele é geração normal e publica normal, mas a pose em si não é acervo público da casa.";
305
+ "Poses do SEU banco privado (mapa de profundidade de corpo). Use a `url` (a SOMBRA pura) em sapiens_image referenceImageUrls pra guiar a POSE da figura, com a linha 'adopt exactly the body pose of the <n>th reference; it is a pose guide only, ignore its grey tones'. Cada item traz `familia`, `vista`, `enquadramento` e `leitura` (prefira 'clara': a 'confusa' devolve membro sem dono), `people`, `objeto` (o que a mão segura), `suporte` (banco, bloco, parede: nomeie no prompt onde o corpo se apoia), `descricao` e `membros` (cada braço e perna com o LADO da pessoa, em inglês: cole no prompt, é o que evita duas pernas esquerdas). `skeletonUrl` é a SOMA (sombra + esqueleto colorido), guia OPCIONAL: em imagem, a sombra pura + `membros` no prompt já dizem o lado de cada membro e não deixam marca; a soma só vale quando a pose é difícil de ler, sempre com 'ignore any coloured overlay lines', e confira a peça, porque o traço às vezes fica na imagem (em VÍDEO a soma ganhou da sombra no A/B da casa). `usadaPor` diz quem já usou: não repita a pose na mesma personagem. Pra recortar, mande familia/vista/leitura/people/hasObject/hasSuporte em vez de tentar pela busca escrita; `poseId` vai em similarTo pra variações do mesmo gesto. Pedido vago ('uma pose boa pra isso') começa por favoritesOnly=true ou sort='used': é a curadoria que o dono já fez. É material privado seu: a peça gerada a partir dele é geração normal e publica normal, mas a pose em si não é acervo público da casa.";
262
306
  }
263
307
  else if (args.bucket === "acervo" || args.bucket === "characters") {
264
308
  note =
@@ -150,6 +150,11 @@ import { httpUrl } from "../schema.js";
150
150
  * final (role 'end'), 5/8/10s em 480/720/1080p. Sem imagem de
151
151
  * referência (o reference-to-video do provider é outro endpoint,
152
152
  * fora até o roteador saber escolher).
153
+ * - sapiens-video-flux-3-max FLUX 3 Max (fal + Black Forest Labs): texto OU imagem (t2v/i2v),
154
+ * 5 a 20s em 720p ou 1080p, som nativo com interruptor, frame final
155
+ * (role 'end', só junto do 'start'). Segue o prompt de perto e
156
+ * volta rápido. Sem referência de personagem nem vídeo de
157
+ * movimento: o servidor recusa e estorna.
153
158
  * - sapiens-video-kling-motion Kling Motion — transfere o movimento de um vídeo pra uma imagem
154
159
  * (PRECISA de pessoa com tronco visível na imagem E no vídeo;
155
160
  * vídeo de referência MÁX 10s — o provider recusa acima disso —
@@ -210,6 +215,7 @@ const VIDEO_MODELS = [
210
215
  "sapiens-video-ltx-spicy",
211
216
  "sapiens-video-wan",
212
217
  "sapiens-video-wan-3",
218
+ "sapiens-video-flux-3-max",
213
219
  "sapiens-video-kling-motion",
214
220
  "sapiens-video-shot-mimic",
215
221
  // Os três Veo 3.1 e o Omni 1.0 seguem no enum por retrocompat (cliente em
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapiens-mcp",
3
- "version": "1.102.0",
3
+ "version": "1.103.0",
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. mcprush.com-bd2545268b8471cdb213c674",
6
6
  "type": "module",