sapiens-mcp 1.51.1 → 1.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/registry.js CHANGED
@@ -53,7 +53,7 @@ export const TOOLS = {
53
53
  handler: pipeline,
54
54
  },
55
55
  sapiens_image: {
56
- description: "Operações de imagem via Sapiens (Gemini, Azure gpt-image-2, Grok, Seedream/ByteDance, Veo). Sub-actions: 'generate' (gera imagem completa imediato — prompt+model+aspectRatio+size; suporta mode=edit/variation e MULTI-REFERÊNCIA: combine até 4 imagens como referência numa geração só, igual ao modal 'Selecionar Referência' do web — via referenceImageUrls (sua galeria + Acervo + personagens públicos de sapiens_character) e/ou sourceImageIds (ids da sua galeria); refs valem pros modelos robustos nano-banana-2/gpt-image-2-*/grok-2-image*/seedream-*), 'request_generation' (cria APENAS row pendente em generatedImages + debita créditos — pra modelos sapiens-video-* ANTES de sapiens_shorts/sapiens_video; whitelist, rate limit 3/min), 'compose' (combina persona+screen via Gemini pra app-demo Shorts; 25 sinapses, rate limit 10/min). generate=image one-shot, request_generation=criar row video, compose=montar start frame app-demo. TEMPLATE: passe templateSlug numa generate pra usar um super-prompt travado da casa — o `prompt` vira só a CENA (quem + pose + objeto-conceito) e o template embrulha estilo+fundo+enquadramento+ref de traço. 'retrato-sapiens-v1' = retrato editorial cartoon de um personagem no grid verde Sapiens (mesma 'mão' dos artigos); sem ref própria, injeta a Helen como âncora de traço (passar referenceImageUrls troca quem aparece). Mutuamente exclusivo com brandSlug. Sub-action 'models' (sem custo, sem login): lista o catálogo vivo (modelos ativos + preço atual com override admin + maxResolution + se aceita referência) pra descobrir modelo/preço em vez de chutar. 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).",
56
+ description: "Operações de imagem via Sapiens (Gemini, Azure gpt-image-2, Grok, Seedream/ByteDance, Veo). Sub-actions: 'generate' (gera imagem completa imediato — prompt+model+aspectRatio+size; suporta mode=edit/variation e MULTI-REFERÊNCIA: combine até 4 imagens como referência numa geração só, igual ao modal 'Selecionar Referência' do web — via referenceImageUrls (sua galeria + Acervo + personagens públicos de sapiens_character) e/ou sourceImageIds (ids da sua galeria); refs valem pros modelos robustos nano-banana-2/gpt-image-2-*/grok-2-image*/seedream-*), 'request_generation' (cria APENAS row pendente em generatedImages + debita créditos — pra modelos sapiens-video-* ANTES de sapiens_shorts/sapiens_video; whitelist, rate limit 3/min), 'compose' (combina persona+screen via Gemini pra app-demo Shorts; 25 sinapses, rate limit 10/min). generate=image one-shot, request_generation=criar row video, compose=montar start frame app-demo. TEMPLATE: passe templateSlug numa generate pra usar um super-prompt travado da casa — o `prompt` vira só a CENA (quem + pose + objeto-conceito) e o template embrulha estilo+fundo+enquadramento+ref de traço. 'retrato-sapiens-v1' = retrato editorial cartoon de um personagem no grid verde Sapiens (mesma 'mão' dos artigos); sem ref própria, injeta a Helen como âncora de traço (passar referenceImageUrls troca quem aparece). Mutuamente exclusivo com brandSlug. Sub-action 'models' (sem custo, sem login): lista o catálogo vivo (modelos ativos + preço cobrado em 1K com override admin + resolutionAdders, o adder por resolução já clampado ao teto de cada motor + maxResolution + se aceita referência) pra descobrir modelo/preço em vez de chutar. O preço de uma geração é priceSinapses + resolutionAdders[size], então 2K/4K custam MAIS que o número base: nunca cite o base como se fosse o total quando o size não for 1K. NOTA: generate é SÍNCRONA e cobra ao concluir; modelo pesado (Pro, gpt-image-2-high, Grok quality, 2K/4K) cai na REGRA DO TIMEOUT (cheque sapiens_gallery action=list antes de repetir, evita cobrança dupla).",
57
57
  schema: imageSchema,
58
58
  handler: image,
59
59
  },
@@ -103,7 +103,7 @@ export const TOOLS = {
103
103
  handler: search,
104
104
  },
105
105
  sapiens_studios: {
106
- description: "O SEU studio (o perfil de EMPRESA da casa do user) + o catálogo dos estúdios/experimentos Sapiens. Sub-actions: mine (o studio do user: nome/endereço público/marca/operador/tamanho do time), create (FUNDA um studio novo, exige name; nasce NO AR com endereço público, sem rascunho, de graça, teto de 4 por pessoa), list (catálogo com URL+status+tags+mcpReady), get (detalhe de 1 slug), publishable_url (formata URL /articles/<slug>). Use quando user pergunta 'que estúdios existem', 'qual o meu studio', ou diz 'quero abrir/fundar meu studio', 'criar a página da minha empresa'. Estúdios cobertos: helen-voice, musicator, persona-sapiens, personagem-atlas, sapiens-shorts, sapiens-video, text-post-builder, comic-builder, carrosel-editorial, comunidade, repertorio.",
106
+ description: "O SEU studio (o perfil de EMPRESA da casa do user) + o catálogo dos estúdios/experimentos Sapiens. Sub-actions: mine (o studio do user: nome/endereço público/marca/operador/tamanho do time), create (FUNDA um studio novo, exige name; nasce NO AR com endereço público, sem rascunho, de graça, teto de 4 por pessoa), list (catálogo com URL+status+tags+mcpReady), get (detalhe de 1 slug), publishable_url (formata URL /articles/<slug>). Use quando user pergunta 'que estúdios existem', 'qual o meu studio', ou diz 'quero abrir/fundar meu studio', 'criar a página da minha empresa'. Estúdios cobertos: helen-voice, musicator, persona-sapiens, personagem-atlas, sapiens-shorts, sapiens-video, text-post-builder, carrosel-editorial, comunidade, repertorio.",
107
107
  schema: studiosSchema,
108
108
  handler: studios,
109
109
  },
@@ -123,12 +123,12 @@ export const TOOLS = {
123
123
  handler: musicator,
124
124
  },
125
125
  sapiens_video: {
126
- 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 (os Veo fast/quality também aceitam, até 3; Lite/Kling/WAN/Omni não), que guiam estilo/personagem/composição SEM virar o 1º frame — é o fluxo STORYBOARD: gere a folha de key poses com sapiens_image templateSlug='storyboard-sapiens-v1', passe folha + personagem como refs num t2v e descreva o take contínuo no prompt, citando as refs por descrição e mandando ignorar o traço do sketch; aceita também 1 VÍDEO DE MOVIMENTO via referenceVideoUrls (role 'refvideo' -> reference_videos, <=15s, host da casa): a coreografia/câmera do clipe guia o take, combinável com a folha), 'sapiens-video-seedance-2-fast' e 'sapiens-video-seedance-2-mini' (os irmãos do 2.0: MESMO repertório completo, incluindo referência, frame final e vídeo de movimento; o Fast custa 20% menos e o Mini METADE, ambos com teto 720p — pedir 1080p neles entrega e cobra 720p. Use o Mini pra iterar enquadramento/prompt barato e feche no 'sapiens-video-seedance' quando o take estiver certo), 'sapiens-video-seedance-25' (Seedance 2.5, a geração SEGUINTE e não um quarto tier da 2.0: take de 4 a 30s num fôlego, edita e estende vídeo, mesmo repertório de referência mais ÁUDIO como referência; teto 720p e ~1,5x o preço por segundo do 2.0. Duração é o que pesa aqui: 30s em 720p passa de 40 mil Sinapses, então confirme a duração com a pessoa antes de disparar. Quem precisa de 1080p fica no 'sapiens-video-seedance'), 'sapiens-video-seedance-15' (Seedance 1.5 Pro: o degrau entre o 1.0 e a linha 2.x; t2v/i2v, 4-12s, 480/720/1080p, som sempre incluso sem toggle; imagem de referência e frame final ficam de fora por ora), 'sapiens-video-kling' (Kling 3.0 Pro, anima imagem, 3-15s, sound opcional, i2v/t2v), 'sapiens-video-hailuo' (Hailuo 2.3 da MiniMax, física e movimento em 768p, 6 ou 10s, t2v/i2v) e 'sapiens-video-hailuo-pro' (o mesmo em 1080p, 5s fixo — duração não é param aqui): motores PUROS, sem áudio nativo, sem imagens de referência e sem frame final, então quem precisa disso fica no Seedance 2.0; o Pro é o 1080p mais barato da casa depois do Seedance 1.0 Fast, 'sapiens-video-h3' (MiniMax H3: 2K com áudio nativo incluso sem toggle, 5 a 10s, t2v/i2v e frame final; não aceita imagem de referência), 'sapiens-video-wan' (WAN 2.5, imagem que fala/canta com áudio+lip-sync, 5/10s, i2v), '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' (Gemini Omni: texto vira vídeo 10s 720p com áudio nativo; NÃO aceita mídia do user, ignora references/durationSec/resolution; editOfImageId aponta um vídeo Omni seu e o prompt edita a MESMA cena, preservando câmera e ambiente), 'sapiens-video-lite/fast/quality' (Veo 3.1). Args create: model, prompt, durationSec, resolution ('480p'/'720p'/'1080p'), audio, aspectRatio. RECEITA DE TAKE (templateSlug + brief), o irmão do templateSlug da imagem: em vez de escrever o prompt inteiro, passe templateSlug e a receita travada da casa embrulha a cena com estilo, cenário, arco, áudio e look, e ainda escolhe o motor (por isso model fica opcional). O `prompt` vira só a CENA e o `brief` preenche os campos do formato (subject, persona, hook, shots com voiceLine e propVisible, uvps, language, energy); campo vazio some do prompt em vez de virar buraco. Receitas de hoje: 'ugc-vertical-v1' (selfie que fala, o formato nativo de Reels/TikTok/Shorts), 'unboxing-vertical-v1' (mãos e reveal, som real do papel e do lacre), 'app-demo-vertical-v1' (a tela do app legível na mão da pessoa) e 'reflexao-vertical-v1' (talking-head lento pra ideia ou ensaio). Override de model/aspectRatio/durationSec/resolution vale dentro do que a receita aceita, e o erro lista as opções. Sub-action 'templates' (sem custo, sem login) traz o catálogo vivo com spec default, whitelist e os briefFields de cada uma. Isto substitui a tool sapiens_shorts, que era admin-only e só falava Veo 3.1 Fast; o mesmo UGC de 8s sai a 720p no Seedance 2.0 Mini por bem menos. FRAME INICIAL/FINAL POR REFERÊNCIA (recomendado): startImageId/endImageId (id da sua galeria) ou startImageUrl/endImageUrl (url de galeria/Acervo/personagem) — resolvidos server-side igual à imagem, descubra via sapiens_reference. FRAME POR ARQUIVO LOCAL (só no MCP instalado/stdio, não no remoto): startImagePath/endImagePath = caminho absoluto de uma imagem no seu PC (PNG/JPEG/WebP até 8MB); o processo lê o arquivo e sobe como frame inicial/final, igual a subir no gerador do site — 1 imagem inicial + 1 final por vídeo, então pra vários vídeos rode create uma vez por imagem. No remoto use id/url. Alternativa base64: references (role 'start'=imagem i2v, 'end'=frame final, 'driving'=vídeo de movimento do Motion). Suporte a frame final varia por modelo. Custo server-side por config. Sub-action 'generate' (legado): renderiza um imageId de vídeo já criado no site. Retorna {success, url, imageId, cost}. VITRINE (sem custo): sub-action 'demos' lista os SEUS demo films (kind=demo do Estúdio de Vídeo) com slug + estado de vitrine; sub-action 'showcase' põe/tira um demo (por slug) do mini-cinema da /conectar-claude, com showcaseTag (chip de capacidade) e showcaseOrder (ordem asc). Fluxo: 'demos' pra achar o slug, depois 'showcase' com showcase=true. Só entra na vitrine pública se for a conta da casa. VÍDEOS PROGRAMÁTICOS (ADMIN, sem custo): o Lab do Estúdio de Vídeo (/experimentos/films, tabela videoSpecs, 5 kinds: demo | aula-tour | essay | tipografia-musical | dataviz) opera por aqui sem browser — 'film-list' (todos os kinds; filtros filmKind/filmStatus), 'film-get' (spec inteiro por slug), 'film-upsert' (cria/atualiza por slug, idempotente; spec = objeto JSON no shape do 'Copiar spec' da tela, validação no servidor), 'film-status' (produção por slug: filmStatus + videoUrl + durationSecMeasured; o fecho do render é os três num call), 'film-publish' (Acervo aba Fitas + portfólio; exige pronto+URL), 'film-delete' (limpar rascunho). O RENDER do filme segue no agente local (skill /film, repo da casa): o MCP registra e fecha o ciclo, não renderiza. create é ASSÍNCRONA: cria o row, debita e volta NA HORA com {imageId, status:'rendering', cost} (não espera o render, que leva de segundos a minutos). Acompanhe com a sub-action 'status' (imageId) até status='completed' (traz a url) ou 'error'/'blocked'. NÃO chame create de novo enquanto renderiza (cria outro vídeo e cobra de novo); falha de provider refunda sozinha. SOM: 'sonorize' (imageId de vídeo SEU completed + prompt do som da cena) gera uma VARIANTE nova com trilha sincronizada (20 Sinapses/s, o original fica intacto; sonorize sempre o original, nunca uma variante). ADMIN: 'shadows' (videoUrl + title) extrai o deepshadow (depth-map) de um vídeo: entra na sua timeline de vídeos e no Acervo como driving reutilizável; 'shadows-list' lista os deepshadows prontos. Sub-action 'models' (sem custo, sem login): lista os modelos de vídeo ativos + preço-piso + config (durações/resoluções) + disponibilidade (Omni depende de env). 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.",
126
+ 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 (os Veo fast/quality também aceitam, até 3; Lite/Kling/WAN/Omni não), que guiam estilo/personagem/composição SEM virar o 1º frame — é o fluxo STORYBOARD: gere a folha de key poses com sapiens_image templateSlug='storyboard-sapiens-v1', passe folha + personagem como refs num t2v e descreva o take contínuo no prompt, citando as refs por descrição e mandando ignorar o traço do sketch; aceita também 1 VÍDEO DE MOVIMENTO via referenceVideoUrls (role 'refvideo' -> reference_videos, <=15s, host da casa): a coreografia/câmera do clipe guia o take, combinável com a folha), 'sapiens-video-seedance-2-fast' e 'sapiens-video-seedance-2-mini' (os irmãos do 2.0: MESMO repertório completo, incluindo referência, frame final e vídeo de movimento; o Fast custa 20% menos e o Mini METADE, ambos com teto 720p — pedir 1080p neles entrega e cobra 720p. Use o Mini pra iterar enquadramento/prompt barato e feche no 'sapiens-video-seedance' quando o take estiver certo), 'sapiens-video-seedance-25' (Seedance 2.5, a geração SEGUINTE e não um quarto tier da 2.0: take de 4 a 30s num fôlego, edita e estende vídeo, mesmo repertório de referência mais ÁUDIO como referência; teto 720p e ~1,5x o preço por segundo do 2.0. Duração é o que pesa aqui: 30s em 720p passa de 40 mil Sinapses, então confirme a duração com a pessoa antes de disparar. Quem precisa de 1080p fica no 'sapiens-video-seedance'), 'sapiens-video-seedance-15' (Seedance 1.5 Pro: o degrau entre o 1.0 e a linha 2.x; t2v/i2v, 4-12s, 480/720/1080p, som sempre incluso sem toggle; imagem de referência e frame final ficam de fora por ora), 'sapiens-video-kling' (Kling 3.0 Pro, anima imagem, 3-15s, sound opcional, i2v/t2v), 'sapiens-video-hailuo' (Hailuo 2.3 da MiniMax, física e movimento em 768p, 6 ou 10s, t2v/i2v) e 'sapiens-video-hailuo-pro' (o mesmo em 1080p, 5s fixo — duração não é param aqui): motores PUROS, sem áudio nativo, sem imagens de referência e sem frame final, então quem precisa disso fica no Seedance 2.0; o Pro é o 1080p mais barato da casa depois do Seedance 1.0 Fast, 'sapiens-video-h3' (MiniMax H3: 2K com áudio nativo incluso sem toggle, 5 a 10s, t2v/i2v e frame final; não aceita imagem de referência), 'sapiens-video-wan' (WAN 2.5, imagem que fala/canta com áudio+lip-sync, 5/10s, i2v), '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' (Gemini Omni: texto vira vídeo 10s 720p com áudio nativo; NÃO aceita mídia do user, ignora references/durationSec/resolution; editOfImageId aponta um vídeo Omni seu e o prompt edita a MESMA cena, preservando câmera e ambiente), 'sapiens-video-lite/fast/quality' (Veo 3.1). Args create: model, prompt, durationSec, resolution ('480p'/'720p'/'1080p'), audio, aspectRatio. RECEITA DE TAKE (templateSlug + brief), o irmão do templateSlug da imagem: em vez de escrever o prompt inteiro, passe templateSlug e a receita travada da casa embrulha a cena com estilo, cenário, arco, áudio e look, e ainda escolhe o motor (por isso model fica opcional). O `prompt` vira só a CENA e o `brief` preenche os campos do formato (subject, persona, hook, shots com voiceLine e propVisible, uvps, language, energy); campo vazio some do prompt em vez de virar buraco. Receitas de hoje: 'ugc-vertical-v1' (selfie que fala, o formato nativo de Reels/TikTok/Shorts), 'unboxing-vertical-v1' (mãos e reveal, som real do papel e do lacre), 'app-demo-vertical-v1' (a tela do app legível na mão da pessoa) e 'reflexao-vertical-v1' (talking-head lento pra ideia ou ensaio). Override de model/aspectRatio/durationSec/resolution vale dentro do que a receita aceita, e o erro lista as opções. Sub-action 'templates' (sem custo, sem login) traz o catálogo vivo com spec default, whitelist e os briefFields de cada uma. Isto substitui a tool sapiens_shorts, que era admin-only e só falava Veo 3.1 Fast; o mesmo UGC de 8s sai a 720p no Seedance 2.0 Mini por bem menos. FRAME INICIAL/FINAL POR REFERÊNCIA (recomendado): startImageId/endImageId (id da sua galeria) ou startImageUrl/endImageUrl (url de galeria/Acervo/personagem) — resolvidos server-side igual à imagem, descubra via sapiens_reference. FRAME POR ARQUIVO LOCAL (só no MCP instalado/stdio, não no remoto): startImagePath/endImagePath = caminho absoluto de uma imagem no seu PC (PNG/JPEG/WebP até 8MB); o processo lê o arquivo e sobe como frame inicial/final, igual a subir no gerador do site — 1 imagem inicial + 1 final por vídeo, então pra vários vídeos rode create uma vez por imagem. No remoto use id/url. Alternativa base64: references (role 'start'=imagem i2v, 'end'=frame final, 'driving'=vídeo de movimento do Motion). Suporte a frame final varia por modelo. Custo server-side por config. Sub-action 'generate' (legado): renderiza um imageId de vídeo já criado no site. Retorna {success, url, imageId, cost}. VITRINE (sem custo): sub-action 'demos' lista os SEUS demo films (kind=demo do Estúdio de Vídeo) com slug + estado de vitrine; sub-action 'showcase' põe/tira um demo (por slug) do mini-cinema da /conectar-claude, com showcaseTag (chip de capacidade) e showcaseOrder (ordem asc). Fluxo: 'demos' pra achar o slug, depois 'showcase' com showcase=true. Só entra na vitrine pública se for a conta da casa. VÍDEOS PROGRAMÁTICOS (ADMIN, sem custo): o Lab do Estúdio de Vídeo (/experimentos/films, tabela videoSpecs, 5 kinds: demo | aula-tour | essay | tipografia-musical | dataviz) opera por aqui sem browser — 'film-list' (todos os kinds; filtros filmKind/filmStatus), 'film-get' (spec inteiro por slug), 'film-upsert' (cria/atualiza por slug, idempotente; spec = objeto JSON no shape do 'Copiar spec' da tela, validação no servidor), 'film-status' (produção por slug: filmStatus + videoUrl + durationSecMeasured; o fecho do render é os três num call), 'film-publish' (Acervo aba Fitas + portfólio; exige pronto+URL), 'film-delete' (limpar rascunho). O RENDER do filme segue no agente local (skill /film, repo da casa): o MCP registra e fecha o ciclo, não renderiza. create é ASSÍNCRONA: cria o row, debita e volta NA HORA com {imageId, status:'rendering', cost} (não espera o render, que leva de segundos a minutos). Acompanhe com a sub-action 'status' (imageId) até status='completed' (traz a url) ou 'error'/'blocked'. NÃO chame create de novo enquanto renderiza (cria outro vídeo e cobra de novo); falha de provider refunda sozinha. SOM: 'sonorize' (imageId de vídeo SEU completed + prompt do som da cena) gera uma VARIANTE nova com trilha sincronizada (20 Sinapses/s, o original fica intacto; sonorize sempre o original, nunca uma variante). ADMIN: 'shadows' (videoUrl + title) extrai o deepshadow (depth-map) de um vídeo: entra na sua timeline de vídeos e no Acervo como driving reutilizável; 'shadows-list' lista os deepshadows prontos. PREÇO DE VÍDEO É FAIXA, NÃO NÚMERO: nos motores por segundo (todo Seedance, Kling, WAN, Hailuo, H3, Shot Mimic) a conta é duração x resolução [x áudio], e OMITIR não pega o barato: sem 'resolution' o servidor cobra o tier MAIS CARO do motor, sem 'durationSec' cobra o piso de duração. Um create de Seedance 2.0 sem resolution debita 1080p. Nunca prometa um valor sem cotar: sub-action 'price' (sem custo, sem login) devolve o número EXATO que o create vai debitar pra model + durationSec + resolution + audio, e avisa (clamped=true) quando o que você pediu não é o que vai ser cobrado. Sub-action 'models' (sem custo, sem login): lista os modelos ativos + a faixa de preço calculada pela mesma função que debita (priceDefault = o que uma chamada sem config custa, priceMin e priceMax = os dois cantos do motor, cada um com a config que o produz) + config (durações/resoluções) + disponibilidade (Omni depende de env). 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.",
127
127
  schema: videoSchema,
128
128
  handler: video,
129
129
  },
130
130
  sapiens_stock_audio: {
131
- description: "Banco de som da casa: trilha pronta E efeito sonoro (tabela stockAudio). Sub-actions de leitura (públicas, sem auth): categories (lista os moods/usos), list (busca com filtros mood/albumSlug/durationMax/search; kind='sfx' traz os EFEITOS: whoosh, clique, impacto, ambiência, foley — devolve {count, items} com title/url/durationSeconds/tags), get (1 item por audioId). Use pra puxar trilha/efeito pronto: pega a `url` e usa direto no ffmpeg. Não achou o efeito? generate (COBRA Sinapses, exige login) cria um novo por texto: prompt + durationSeconds (1-15, default 5) + provider ('mirelo' padrão 35 Sinapses/s mín 70 | 'elevenlabs' premium 60/s mín 120) + promptInfluence opcional (0..1, só elevenlabs: fidelidade ao texto, default 0.3), assíncrono — acompanhe com generation-status (generationId) até 'ready' (audioUrl; o efeito também entra no acervo kind=sfx) ou 'failed' (Sinapses reembolsadas). Efeito é CURTO (1-15s): música/trilha nova é no sapiens_musicator. Mood disponíveis: calmo, intenso, narrativo, épico, sombrio.",
131
+ description: "Banco de som da casa: trilha pronta E efeito sonoro (tabela stockAudio). Sub-actions de leitura (públicas, sem auth): categories (lista os moods/usos), list (busca com filtros mood/albumSlug/durationMax/search; kind='sfx' traz os EFEITOS: whoosh, clique, impacto, ambiência, foley — devolve {count, items} com title/url/durationSeconds/tags), get (1 item por audioId). Use pra puxar trilha/efeito pronto: pega a `url` e usa direto no ffmpeg. Não achou o efeito? generate (COBRA Sinapses, exige login) cria um novo por texto: prompt + durationSeconds (1-15, default 5) + provider ('mirelo' padrão 35 Sinapses/s mín 70 | 'elevenlabs' premium 60/s mín 120) + promptInfluence opcional (0..1, só elevenlabs: fidelidade ao texto, default 0.3), assíncrono — acompanhe com generation-status (generationId) até 'ready' (audioUrl; o efeito também entra no acervo kind=sfx) ou 'failed' (Sinapses reembolsadas). Efeito é CURTO (1-15s): música/trilha nova é no sapiens_musicator. Mood disponíveis: calmo, intenso, narrativo, épico, sombrio. Precisa de VOZ junto com trilha e efeito (anúncio, vinheta, cena de filme, podcast)? scene (COBRA Sinapses, exige login) cria a CENA INTEIRA num passe só, já batida, sem montar em pedaços e mixar depois: scenePrompt (a direção da cena, quem fala, com que timbre, o que toca atrás, a fala entre aspas) + durationSeconds (3-120, default 15) + loudnessRate/pitchRate/speechRate opcionais + withSubtitles opcional (traz o tempo de cada palavra), 11 Sinapses/s, assíncrono — acompanhe com scene-status (sceneId) até 'ready' (audioUrl + realDurationSeconds) ou 'failed' (Sinapses reembolsadas). A cena fica com o AUTOR e não entra no acervo compartilhado, porque carrega a voz e o roteiro de quem pediu.",
132
132
  schema: stockAudioSchema,
133
133
  handler: stockAudio,
134
134
  },
@@ -225,7 +225,7 @@ O passo a passo travado de cada fluxo mora na tool sapiens_skill, servida por es
225
225
  - sapiens_skill action=list -> o índice (slug + quando usar cada uma).
226
226
  - sapiens_skill action=get name=<slug> -> a skill inteira.
227
227
  Slugs: ${skillMenuLine()}
228
- 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), montar tirinha (tirinha), 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). Perdido no começo: primeiros-passos.
228
+ 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). Perdido no começo: primeiros-passos.
229
229
  Cliente que lê resources MCP acha o MESMO conteúdo em skill://sapiens/<slug>/SKILL.md.
230
230
 
231
231
  REGRA DE OURO:
package/dist/skills.js CHANGED
@@ -204,7 +204,11 @@ Dar som a um vídeo SEU já pronto é \`sapiens_video action=sonorize\`, não é
204
204
 
205
205
  \`action=create\` EXIGE \`model\`. Sem model, falha de cara. Vídeo é o mais caro: confirme com a pessoa antes de disparar.
206
206
 
207
- \`action=models\` (sem custo, sem login) lista os modelos ativos com preço-piso, durações, resoluções e disponibilidade. Consulte em vez de chutar.
207
+ \`action=models\` (sem custo, sem login) lista os modelos ativos com durações, resoluções, disponibilidade e a FAIXA de preço: o padrão, o piso e o teto, cada um dizendo em qual configuração acontece. Motor de preço-por-segundo não tem "um preço": o mesmo modelo custa 3x mais em 1080p e 30s do que em 720p e 5s.
208
+
209
+ \`action=price\` (sem custo, sem login) cota a configuração EXATA antes de rodar: passe \`model\`, \`durationSec\`, \`resolution\` e \`audio\` e receba o número que vai ser debitado, calculado pela mesma função que cobra. Ela também avisa quando o motor não aceita o que você pediu e vai cobrar outra coisa (\`clamped\`), o que acontece quando a duração não existe no enum ou a resolução cai num tier diferente.
210
+
211
+ Use \`price\` sempre que a pessoa perguntar quanto custa, e ANTES de qualquer \`create\` que não seja o default. Dizer o piso como se fosse o preço final é o jeito mais rápido de queimar a confiança dela: a cobrança vem maior e a culpa é sua.
208
212
 
209
213
  ## Iterar barato, fechar caro
210
214
 
@@ -421,34 +425,6 @@ Pergunte qual, não adivinhe.
421
425
  Gente entra por convite, e o convite exige **follow mútuo**: os dois precisam já se seguir. Personagem do próprio elenco entra sem convite, é posse. Teto de 4 pessoas por casa.
422
426
 
423
427
  A obra da página é automática por default: a soma dos Destaques de quem está no time. Se alguém empurrar uma peça à mão, ela passa a ser curada. Tudo isso se faz na página, não por aqui.`,
424
- },
425
- {
426
- name: "tirinha",
427
- title: "Tirinha e quadrinho",
428
- description: "O fluxo de 2 fases da tirinha e a regra de ouro (a imagem sai SEM texto, a fala vai separada). Puxe ANTES de gerar qualquer quadrinho, assar a fala no prompt é o erro clássico.",
429
- body: `## Duas fases. Decida o modo antes de gerar pixel.
430
-
431
- ### Fase 1: roteiro
432
-
433
- Monte 3 a 6 painéis, cada um com a CENA visual e a FALA/legenda SEPARADAS. Salve em \`sapiens_pipeline action=create_production format=tirinha\`.
434
-
435
- O texto dos balões mora no payload (campos \`dialogue\` e \`caption\`), NUNCA dentro da imagem.
436
-
437
- ### Fase 2: imagem, escolha um dos modos
438
-
439
- **Modo A, tira inteira numa imagem só.** UMA chamada \`sapiens_image\` com prompt em grade: descreva "Painel 1 (cima-esquerda): ...; Painel 2 (cima-direita): ...", peça grade NxM com calhas brancas finas, e o MESMO personagem, estilo e luz em todos os quadros. Mais barato, estilo mais travado, menos controle quadro a quadro.
440
-
441
- **Modo B, painel a painel.** UMA \`sapiens_image\` por painel, com character-lock: passe o painel anterior e a ref da Helen em \`referenceImageUrls\`. Mais controle e consistência de enquadramento, custa N gerações.
442
-
443
- Pergunte à pessoa, ou decida pelo caso: tira curta de humor pede A, narrativa com continuidade pede B.
444
-
445
- ## A regra de ouro
446
-
447
- **A imagem sai SEM texto.** O estilo da casa é no-text e modelo de imagem erra letra.
448
-
449
- A fala vai SEPARADA, como legenda junto da imagem (por exemplo no Telegram: "Painel 1 · Helen: '...'"). Os balões editáveis e a diagramação final ficam no comic-builder do dashboard (/experimentos/comic-builder).
450
-
451
- Não tente assar a fala dentro do prompt. Nunca funciona.`,
452
428
  },
453
429
  {
454
430
  name: "forum-comunidade",
@@ -15,7 +15,10 @@ import { convexAction, convexQuery, convexMutation, getSessionToken, } from "../
15
15
  * - generate: cria um design system NOVO a partir de descrição em texto livre
16
16
  * (Gemini monta paleta+tipografia+voz+imageStyle e já nasce com
17
17
  * card premium gpt-image-2). Cobra ~950 Sinapses (texto + card),
18
- * reembolsa se falhar. Identidade vem do sessionToken.
18
+ * reembolsa se falhar. Identidade vem do sessionToken. Pode
19
+ * partir do briefing de um brand JÁ SEU (fromSlug) e aplicar uma
20
+ * torção (twist): é assim que se desenha várias direções pro
21
+ * mesmo projeto sem redigitar o briefing a cada uma.
19
22
  * - refine: ajusta um brand custom existente por feedback em texto livre
20
23
  * ("fundo mais escuro", "voz menos professoral"). ~75 Sinapses.
21
24
  * - reroll: regenera SÓ uma peça (voice | palette | imageStyle) numa direção
@@ -69,6 +72,14 @@ export const brandSchema = z.object({
69
72
  .string()
70
73
  .optional()
71
74
  .describe("Pra action=generate: nome sugerido pro brand (opcional, ≤40 chars). Sem isso o Gemini sugere um."),
75
+ fromSlug: z
76
+ .string()
77
+ .optional()
78
+ .describe("Pra action=generate: slug de um brand SEU cujo briefing vira a base da variação (o brand original não é tocado, nasce um novo). Com isso, `description` fica opcional. Só funciona em brand que guarda briefing (os criados a partir de ago/2026); veja em action=get, campo `brief`."),
79
+ twist: z
80
+ .string()
81
+ .optional()
82
+ .describe("Pra action=generate: a torção aplicada sobre o briefing ('mais bruto', 'mais editorial', 'paleta clara'). Combina com fromSlug (variação de um brand seu) ou com description (torção sobre o briefing que você acabou de escrever). ≤400 chars."),
72
83
  feedback: z
73
84
  .string()
74
85
  .optional()
@@ -113,19 +124,32 @@ export async function brand(args) {
113
124
  if (!b) {
114
125
  throw new Error(`Brand "${args.slug}" não encontrado (ou é custom de outro user). Veja os seus em action=list.`);
115
126
  }
116
- return b;
127
+ return {
128
+ ...b,
129
+ // O briefing só vem em brand seu (ou oficial) e só existe nos criados a
130
+ // partir de ago/2026. Quando existe, é o atalho pra desenhar variações.
131
+ briefNote: b.brief?.text
132
+ ? `Esse brand guarda o briefing que gerou ele. Pra desenhar outra direção a partir dele: action=generate fromSlug=${b.slug} twist='mais bruto' (não toca neste aqui, nasce um brand novo).`
133
+ : "Esse brand não guarda o briefing (nasceu antes disso existir), então variação por fromSlug não funciona nele. Pra criar um parecido, escreva a description na mão.",
134
+ };
117
135
  }
118
136
  // -------- generate: cria design system novo (cobra Sinapses) --------
119
137
  if (args.action === "generate") {
120
138
  const description = (args.description || "").trim();
121
- if (description.length < 30) {
139
+ const fromSlug = args.fromSlug?.trim();
140
+ // Com fromSlug o briefing vem do brand de origem (o servidor lê e confere a
141
+ // posse), então a description deixa de ser obrigatória.
142
+ if (!fromSlug && description.length < 30) {
122
143
  throw new Error("action=generate exige description com pelo menos 30 chars (vibe, cores, voz, referências). " +
123
- "Quanto mais rico, melhor o brand. Converse com o user e monte a descrição antes de chamar.");
144
+ "Quanto mais rico, melhor o brand. Converse com o user e monte a descrição antes de chamar. " +
145
+ "Pra variar um brand que já é dele, use fromSlug=<slug> (+ twist='mais bruto').");
124
146
  }
125
147
  const res = await convexAction("customBrandsActions:mcpGenerateBrand", {
126
148
  sessionToken,
127
149
  description,
128
150
  name: args.name?.trim() || undefined,
151
+ fromSlug: fromSlug || undefined,
152
+ twist: args.twist?.trim() || undefined,
129
153
  });
130
154
  return {
131
155
  ...res,
@@ -138,7 +162,10 @@ export async function brand(args) {
138
162
  res.slug +
139
163
  " feedback='...'. Regenerar uma peça: action=reroll slug=" +
140
164
  res.slug +
141
- " piece=voice|palette|imageStyle.",
165
+ " piece=voice|palette|imageStyle. Outra direção do MESMO briefing: " +
166
+ "action=generate fromSlug=" +
167
+ res.slug +
168
+ " twist='mais bruto'.",
142
169
  };
143
170
  }
144
171
  // -------- refine: ajusta brand custom existente (cobra Sinapses) --------
@@ -4,35 +4,41 @@ import { convexAction, convexQuery, 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.
7
+ //
8
+ // SEM PREÇO AQUI de propósito: esta lista já carregou o valor de cada motor em
9
+ // comentário, e os números ficaram anos-luz da fonte (o gpt-image-2-high dizia
10
+ // 800 quando o catálogo cobrava 1200, o nano-banana-max dizia 450 contra 900).
11
+ // Preço de imagem tem UMA fonte viva, `action=models`, que já traz o override
12
+ // admin e o adder de resolução clampado ao teto do motor.
7
13
  const MODELS = [
8
- "nano-banana-max", // gemini-3-pro-image-preview · 450 + adder
9
- "nano-banana-2", // gemini-3.1-flash-image-preview (V2, Flash 3.1) · 450 + adder · COM refs · DEFAULT
10
- "gpt-image-2-low", // Azure gpt-image-2 quality=low · 250
11
- "gpt-image-2-high", // Azure gpt-image-2 quality=high · 800
14
+ "nano-banana-max", // gemini-3-pro-image-preview · COM refs · adder até 4K
15
+ "nano-banana-2", // gemini-3.1-flash-image-preview (V2, Flash 3.1) · COM refs · adder até 4K · DEFAULT
16
+ "gpt-image-2-low", // Azure gpt-image-2 quality=low
17
+ "gpt-image-2-high", // Azure gpt-image-2 quality=high · o motor de texto legível
12
18
  // ByteDance via ModelArk direto. Corp/censurado, 2K nativo, COM refs (até 4).
13
19
  // (O "nova-canvas" ficava aqui e saiu: a AWS matou o modelo. Pedido antigo
14
20
  // ainda funciona, o backend faz o alias pro gpt-image-2-low.)
15
- "seedream-4-5", // Seedream 4.5 · 400 · cinematográfico
16
- "seedream-5-0", // Seedream 5.0, a geração nova · 400 · lê prompt complexo melhor
17
- "seedream-5-0-pro", // Seedream 5.0 Pro, topo da linha · 450 (roda no pacote pago)
21
+ "seedream-4-5", // Seedream 4.5 · cinematográfico
22
+ "seedream-5-0", // Seedream 5.0, a geração nova · lê prompt complexo melhor
23
+ "seedream-5-0-pro", // Seedream 5.0 Pro, topo da linha (roda no pacote pago)
18
24
  // xAI Grok Imagine. Moderação frouxa (+18), aceita refs (img2img) + aspect.
19
- "grok-2-image", // grok-imagine-image · 450 + adder 2K · COM refs
20
- "grok-2-image-quality", // grok-imagine-image-quality, mais fiel pra character lock · 900 + adder 2K · COM refs
25
+ "grok-2-image", // grok-imagine-image · COM refs · adder até 2K
26
+ "grok-2-image-quality", // grok-imagine-image-quality, mais fiel pra character lock · COM refs · adder até 2K
21
27
  // Degen (uncensored, gate +18 na galeria). WaveSpeed = rápido (6-25s):
22
- "wavespeed-chroma", // Chroma uncensored fotorrealista · 600
23
- "wavespeed-flux2", // Flux.2 Klein 9B · 600
24
- "wavespeed-flux-nsfw", // Flux dev + LoRA NSFW (AIDMA) · 600 · Ousadia regulável
25
- "wavespeed-klein-anime", // Flux.2 Klein + LoRA anime (inteligente + controlável) · 600
26
- "wavespeed-klein-anime-plus", // Klein Anime + SNOFS uncensored (+18) · 600 · Ousadia regulável
28
+ "wavespeed-chroma", // Chroma uncensored fotorrealista
29
+ "wavespeed-flux2", // Flux.2 Klein 9B
30
+ "wavespeed-flux-nsfw", // Flux dev + LoRA NSFW (AIDMA) · Ousadia regulável
31
+ "wavespeed-klein-anime", // Flux.2 Klein + LoRA anime (inteligente + controlável)
32
+ "wavespeed-klein-anime-plus", // Klein Anime + SNOFS uncensored (+18) · Ousadia regulável
27
33
  // Civitai (sdcpp, rápido). Família FLUX no Civitai saiu: lenta demais (>5min,
28
34
  // estoura o poll). Pra flux uncensored use wavespeed-flux-nsfw.
29
- "civitai-wai-illustrious", // anime Illustrious · 400
30
- "civitai-nova-anime-xl", // anime Illustrious · 400
31
- "civitai-pony-v6", // Pony Diffusion V6 XL, a base nº1 do Civitai · 400
35
+ "civitai-wai-illustrious", // anime Illustrious
36
+ "civitai-nova-anime-xl", // anime Illustrious
37
+ "civitai-pony-v6", // Pony Diffusion V6 XL, a base nº1 do Civitai
32
38
  // fal.ai (Krea-2 Turbo 12B + LoRA de realismo, ~4s). Grupo Degen (+18, checker
33
39
  // off). SEM referência: o endpoint krea-2/turbo/lora é text-to-image puro.
34
- "fal-krea2-realism", // Krea-2 + LoRA realismo (gokaygokay) · 600 · txt2img
35
- "fal-krea2-realism-v2", // Krea-2 + LoRA realismo alt (RudySen), pro A/B · 600 · txt2img
40
+ "fal-krea2-realism", // Krea-2 + LoRA realismo (gokaygokay) · txt2img
41
+ "fal-krea2-realism-v2", // Krea-2 + LoRA realismo alt (RudySen), pro A/B · txt2img
36
42
  ];
37
43
  export const imageSchema = z.object({
38
44
  action: z.enum(["generate", "request_generation", "compose", "models"]),
@@ -51,7 +57,7 @@ export const imageSchema = z.object({
51
57
  size: z
52
58
  .enum(["1K", "2K", "4K"])
53
59
  .optional()
54
- .describe("Default '1K'. Adder de resolução só nos modelos com hasResolutionAdder: nano-banana-max e nano-banana-2 aceitam 2K (+100) e 4K (+300); grok-2-image e grok-2-image-quality aceitam só 2K (+100), sem 4K. Nos demais modelos o size é ignorado (fica em 1K)."),
60
+ .describe("Default '1K'. Resolução acima de 1K SOMA um adder ao preço do modelo, e só nos modelos com hasResolutionAdder (nano-banana-max e nano-banana-2 vão até 4K; grok-2-image e grok-2-image-quality param em 2K, e pedir 4K neles entrega 2K e cobra o adder de 2K). Nos demais modelos o size é ignorado (fica em 1K). O valor VIVO do adder por modelo sai em action=models (campo resolutionAdders, já clampado ao teto do motor): não escreva o número de cabeça, ele muda quando o provider reajusta."),
55
61
  styleId: z
56
62
  .string()
57
63
  .optional()
@@ -136,7 +142,8 @@ export async function image(args) {
136
142
  count: models.length,
137
143
  default: "nano-banana-2",
138
144
  models,
139
- note: "priceSinapses é o base (1K); 2K/4K somam resolutionAdders quando o modelo tem teto pra isso. degen=+18 (gate na galeria). O preço já inclui override admin.",
145
+ 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. " +
146
+ "Preço final = priceSinapses + resolutionAdders[size]. degen=+18 (gate na galeria).",
140
147
  };
141
148
  }
142
149
  const sessionToken = getSessionToken();
@@ -86,23 +86,8 @@ const FORMAT_GUIDE = {
86
86
  ],
87
87
  },
88
88
  },
89
- tirinha: {
90
- description: "Tirinha/quadrinho em painéis (3-6). DOIS modos de imagem: 'strip' (tira inteira numa imagem só, 1 sapiens_image em grade) ou 'panels' (painel a painel, N sapiens_image com character-lock). A imagem SEMPRE sai sem texto; a fala/legenda vive no payload (dialogue/caption) e os balões finalizam no comic-builder do dashboard.",
91
- payloadHint: {
92
- title: "string",
93
- mode: "'strip' (tudo numa imagem) | 'panels' (uma imagem por painel)",
94
- layout: "string opcional pro modo strip: '2x2' | 'tira' (horizontal) | 'vertical'",
95
- panels: [
96
- {
97
- description: "string narrativo: a CENA visual do painel (vai pro prompt da imagem, SEM texto)",
98
- dialogue: "string opcional: a fala do balão (NÃO entra na imagem, é legenda)",
99
- caption: "string opcional: legenda de narração",
100
- imagePrompt: "string: prompt final do painel (full-bleed, no-text)",
101
- imageUrl: "string opcional (preenchido depois de gerar)",
102
- },
103
- ],
104
- },
105
- },
89
+ // tirinha saiu do guia em ago/2026: peça aposentada (format vive só como
90
+ // tolerância de validação pra productions antigas, ver pipeline.ts).
106
91
  musica: {
107
92
  description: "Faixa curta com letra + estilo + mood.",
108
93
  payloadHint: {
@@ -6,10 +6,8 @@ import { need } from "../schema.js";
6
6
  // lint: `npm run audit:mcp-format-parity` falha o CI se divergir. Ao adicionar
7
7
  // ou remover formato, mexa nos dois lados.
8
8
  const FORMATS = [
9
- "tirinha",
10
9
  "carrossel_ig",
11
10
  "post_social",
12
- "mega_grafico",
13
11
  "shorts_yt",
14
12
  "video_yt",
15
13
  "musica",
@@ -17,8 +15,19 @@ const FORMATS = [
17
15
  ];
18
16
  // Formatos legados: fora do FORMAT_GUIDE (não advertidos), mas ainda aceitos na
19
17
  // validação pra editar productions antigas sem quebrar. cena_visual ("Vídeo
20
- // Programático") saiu de linha em jul/2026: Films + gerador de vídeo cobrem o caso.
21
- const LEGACY_FORMATS = ["post_linkedin", "post_twitter", "post_threads", "cena_visual"];
18
+ // Programático") saiu de linha em jul/2026: Films + gerador de vídeo cobrem o
19
+ // caso. tirinha saiu de linha em ago/2026 (peça aposentada; as publicadas
20
+ // seguem de pé). mega_grafico saiu do picker web em ago/2026 mas o fluxo MCP
21
+ // segue vivo e ADVERTIDO (propose_mega_grafico_plan / run_mega_grafico_full
22
+ // criam a production no servidor; o formato aqui é só tolerância de validação).
23
+ const LEGACY_FORMATS = [
24
+ "post_linkedin",
25
+ "post_twitter",
26
+ "post_threads",
27
+ "cena_visual",
28
+ "tirinha",
29
+ "mega_grafico",
30
+ ];
22
31
  const ACCEPTED_FORMATS = [...FORMATS, ...LEGACY_FORMATS];
23
32
  const STATUSES = ["draft", "ready", "finalized"];
24
33
  export const pipelineSchema = z.object({
@@ -17,10 +17,12 @@ import { convexQuery, convexAction, getSessionToken } from "../convexClient.js";
17
17
  */
18
18
  export const stockAudioSchema = z.object({
19
19
  action: z
20
- .enum(["list", "get", "categories", "generate", "generation-status"])
20
+ .enum(["list", "get", "categories", "generate", "generation-status", "scene", "scene-status"])
21
21
  .describe("list = busca faixas/efeitos; get = 1 item por id; categories = moods/usos; " +
22
22
  "generate = cria efeito sonoro novo (COBRA Sinapses, assíncrono); " +
23
- "generation-status = status de uma geração (até ready/failed)."),
23
+ "generation-status = status de uma geração (até ready/failed); " +
24
+ "scene = cria uma CENA de áudio completa, voz + trilha + efeito + ambiência num passe só " +
25
+ "(COBRA Sinapses, assíncrono); scene-status = status de uma cena (até ready/failed)."),
24
26
  limit: z
25
27
  .number()
26
28
  .int()
@@ -63,7 +65,9 @@ export const stockAudioSchema = z.object({
63
65
  durationSeconds: z
64
66
  .number()
65
67
  .optional()
66
- .describe("action=generate: duração alvo em segundos, 1 a 15. Default 5. O custo escala por segundo."),
68
+ .describe("Duração alvo em segundos. action=generate: 1 a 15, default 5. action=scene: 3 a 120, default 15. " +
69
+ "O custo escala por segundo nos dois. Na CENA a duração não é parâmetro do modelo, ela sai da " +
70
+ "direção escrita: o alvo entra no pedido como instrução e o preço é acertado pela duração que voltou."),
67
71
  provider: z
68
72
  .enum(["mirelo", "elevenlabs"])
69
73
  .optional()
@@ -78,6 +82,46 @@ export const stockAudioSchema = z.object({
78
82
  .string()
79
83
  .optional()
80
84
  .describe("action=generation-status: o generationId que o generate devolveu."),
85
+ // --- action=scene (cena de áudio completa, Seed Audio 1.0) ---
86
+ scenePrompt: z
87
+ .string()
88
+ .optional()
89
+ .describe("action=scene: a DIREÇÃO da cena inteira, não só a fala. Diga quem fala, com que timbre e " +
90
+ "humor, em que idioma, o que toca atrás e o que se ouve no fundo, e ponha a fala entre aspas. " +
91
+ "Ex: 'Voz masculina adulta, calma e um pouco rouca, em português do Brasil. Estúdio à noite, " +
92
+ "um teclado bem baixo atrás. Ela fala: Sapiens Sintéticos, o Lab onde a sua ideia vira peça " +
93
+ "pronta.' Mín. 10 chars, máx. 2.000. Escreva como quem dirige, não como quem preenche campo: " +
94
+ "o modelo entende cena inteira e é daí que sai a qualidade."),
95
+ loudnessRate: z
96
+ .number()
97
+ .int()
98
+ .min(-50)
99
+ .max(100)
100
+ .optional()
101
+ .describe("action=scene: volume. -50 = metade, 100 = dobro. Default 0 (natural do modelo)."),
102
+ pitchRate: z
103
+ .number()
104
+ .int()
105
+ .min(-12)
106
+ .max(12)
107
+ .optional()
108
+ .describe("action=scene: tom da voz. Default 0 (natural do modelo)."),
109
+ speechRate: z
110
+ .number()
111
+ .int()
112
+ .min(-50)
113
+ .max(100)
114
+ .optional()
115
+ .describe("action=scene: velocidade da fala. -50 = metade, 100 = dobro. Default 0."),
116
+ withSubtitles: z
117
+ .boolean()
118
+ .optional()
119
+ .describe("action=scene: guarda a legenda com o tempo de cada palavra, pra casar texto com a fala " +
120
+ "sem transcrever depois. Default false."),
121
+ sceneId: z
122
+ .string()
123
+ .optional()
124
+ .describe("action=scene-status: o sceneId que o scene devolveu."),
81
125
  });
82
126
  export async function stockAudio(args) {
83
127
  if (args.action === "categories") {
@@ -102,6 +146,31 @@ export async function stockAudio(args) {
102
146
  promptInfluence: args.promptInfluence,
103
147
  });
104
148
  }
149
+ if (args.action === "scene") {
150
+ if (!args.scenePrompt || args.scenePrompt.trim().length < 10) {
151
+ throw new Error("action=scene exige scenePrompt (mín. 10 chars dirigindo a cena: quem fala, como, o que se ouve atrás).");
152
+ }
153
+ const sessionToken = getSessionToken();
154
+ return await convexAction("mcpExtrasActions:mcpAudioSceneGenerate", {
155
+ sessionToken,
156
+ scenePrompt: args.scenePrompt,
157
+ durationSeconds: args.durationSeconds,
158
+ loudnessRate: args.loudnessRate,
159
+ pitchRate: args.pitchRate,
160
+ speechRate: args.speechRate,
161
+ withSubtitles: args.withSubtitles,
162
+ });
163
+ }
164
+ if (args.action === "scene-status") {
165
+ if (!args.sceneId) {
166
+ throw new Error("action=scene-status exige sceneId (o que o scene devolveu).");
167
+ }
168
+ const sessionToken = getSessionToken();
169
+ return await convexQuery("mcpExtras:mcpAudioSceneStatus", {
170
+ sessionToken,
171
+ sceneId: args.sceneId,
172
+ });
173
+ }
105
174
  if (args.action === "generation-status") {
106
175
  if (!args.generationId) {
107
176
  throw new Error("action=generation-status exige generationId (o que o generate devolveu).");
@@ -117,16 +117,8 @@ const STUDIOS = {
117
117
  mcpReady: true,
118
118
  mcpNote: "Parcialmente coberto via sapiens_pipeline format=post_social (texto Claude-side) + sapiens_image.",
119
119
  },
120
- "comic-builder": {
121
- name: "Comic Builder (Tirinhas)",
122
- description: "Estúdio oficial de tirinhas/quadrinhos. A IA roteiriza e gera os painéis de dois jeitos: painel a painel (controle fino) ou a tira inteira numa imagem só (estilo e lógica mais travados). Traço dos artigos como estilo base, Helen opcional, diagramação (2×2, tira, vertical) e histórico das tirinhas.",
123
- url: `${APP}/experimentos/comic-builder`,
124
- status: "stable",
125
- tags: ["tirinha", "tirinhas", "comic", "quadrinhos", "hq", "painel", "roteiro", "narrativa"],
126
- convex: "comicStrips",
127
- mcpReady: true,
128
- mcpNote: "Coberto via sapiens_pipeline format=tirinha (roteiro: cena + fala dos balões) + sapiens_image pra render. DOIS modos: MODO A tira inteira numa imagem só (1 sapiens_image em grade) ou MODO B painel a painel (N sapiens_image com character-lock por referência). Decida o modo antes de gerar. A imagem sai SEM texto (no-text da casa, modelo erra letra); a fala vai como legenda separada e os balões editáveis + diagramação + histórico finalizam no dashboard. Estúdio canônico de tirinha: não precisa freestyle de prompt.",
129
- },
120
+ // comic-builder (Tirinhas) saiu do roster em ago/2026: peça aposentada. As
121
+ // tirinhas publicadas seguem de pé (/tirinha/<id>, cards na Comunidade).
130
122
  "carrosel-editorial": {
131
123
  name: "Carrossel Editorial",
132
124
  description: "Gerador de carrossel Instagram com templates fixos (role × variant A/B/C), 9 slides Sapiens. Render via Playwright.",
@@ -11,6 +11,12 @@ import { httpUrl } from "../schema.js";
11
11
  * Com `templateSlug`, a RECEITA da casa embrulha a cena (estilo, arco,
12
12
  * áudio, look) e escolhe o motor: `prompt` vira só a cena e `brief`
13
13
  * preenche os campos do formato.
14
+ * - models: catálogo vivo dos motores ativos com a FAIXA de preço (o que uma
15
+ * chamada sem config debita + o canto mais barato e o mais caro).
16
+ * Sem custo, sem login.
17
+ * - price: cotação exata de uma config (model + durationSec/resolution/audio),
18
+ * calculada pela mesma função que debita. Sem custo, sem login. Use
19
+ * antes de create pra dizer o número certo pra pessoa.
14
20
  * - templates: catálogo das receitas de take (slug, spec default, whitelist de
15
21
  * override e os campos de brief que cada uma usa). Sem custo, sem login.
16
22
  * Comeu o antigo sapiens_shorts, que era admin-only e só falava Veo.
@@ -85,22 +91,27 @@ import { httpUrl } from "../schema.js";
85
91
  * - sapiens-video-shot-mimic Shot Mimic — recria o plano do vídeo de referência (câmera,
86
92
  * cortes, blocking) como cena nova; personagem via role 'start'
87
93
  * (vídeo de referência MÁX 15s — acima o provider corta em 15s)
88
- * - sapiens-video-lite/fast/quality Veo 3.1 (2000/5000/25000 sinapses)
94
+ * - sapiens-video-lite/fast/quality Veo 3.1 (preço fixo por clipe; o número vivo
95
+ * sai em action=models, campo priceDefault)
89
96
  * - sapiens-video-omni Gemini Omni — texto -> vídeo 10s 720p com áudio nativo embutido.
90
97
  * t2v + EDIÇÃO conversacional: `editOfImageId` aponta um vídeo Omni
91
98
  * seu e o prompt edita a MESMA cena (troca item/personagem preservando
92
99
  * o resto). Não aceita mídia do user (ignora references/durationSec/resolution).
93
100
  *
94
- * FORA DE PROPÓSITO: o `sapiens-video-seedance-fast` (Seedance 1.0 Fast, piso 600)
95
- * está ativo no catálogo do backend mas NÃO entra neste enum. Ele é geração velha:
96
- * sem áudio, sem referência e sem frame final. Economiza 200 sinapses no piso contra
97
- * o 1.5 Pro e perde três capacidades. Quem quer queimar tentativa barata usa o
101
+ * FORA DE PROPÓSITO: o `sapiens-video-seedance-fast` (Seedance 1.0 Fast) está ativo
102
+ * no catálogo do backend mas NÃO entra neste enum. Ele é geração velha: sem áudio,
103
+ * sem referência e sem frame final. Economiza pouco contra o 1.5 Pro e perde três
104
+ * capacidades. Quem quer queimar tentativa barata usa o
98
105
  * `sapiens-video-seedance-2-mini`, que custa metade do 2.0 com o repertório inteiro.
99
106
  * Não "conserte" essa ausência: ela é decisão (29/jul/2026).
100
107
  *
101
- * Custo: server-side por config (duração x resolução [x áudio]). i2v/motion usam
102
- * `references` em base64 (role 'start' = imagem inicial, 'end' = frame final,
103
- * 'driving' = vídeo de movimento do Motion).
108
+ * CUSTO: server-side por config (duração x resolução [x áudio]). NÃO existe "o preço"
109
+ * de um motor por segundo: existe faixa. Omitir `resolution` não pega o meio nem o
110
+ * mais barato, pega o tier MAIS CARO do motor (é o clamp de `resolveBilledVideoConfig`,
111
+ * que não subcobra), e omitir `durationSec` pega o piso de duração. Antes de prometer
112
+ * um número pra pessoa, tire a cotação em action=price (sem custo, sem login).
113
+ * i2v/motion usam `references` em base64 (role 'start' = imagem inicial,
114
+ * 'end' = frame final, 'driving' = vídeo de movimento do Motion).
104
115
  *
105
116
  * Retorna `{ success, url, imageId, cost }` ou `{ success: false, error }`.
106
117
  */
@@ -131,6 +142,7 @@ export const videoSchema = z.object({
131
142
  "generate",
132
143
  "status",
133
144
  "models",
145
+ "price",
134
146
  "templates",
135
147
  "demos",
136
148
  "showcase",
@@ -259,23 +271,25 @@ export const videoSchema = z.object({
259
271
  model: z
260
272
  .enum(VIDEO_MODELS)
261
273
  .optional()
262
- .describe("action=create: modelo de vídeo (opcional quando você passa templateSlug: a receita traz o motor). O catálogo POR MODELO (capacidades, referências, tetos, preço) mora na description desta tool e VIVO em action=models; a skill 'video' (sapiens_skill) guia a escolha. " +
274
+ .describe("action=create (opcional quando você passa templateSlug: a receita traz o motor) e action=price (obrigatório). O catálogo POR MODELO (capacidades, referências, tetos, FAIXA de preço) mora na description desta tool e VIVO em action=models; a skill 'video' (sapiens_skill) guia a escolha. " +
263
275
  "Atalho de famílias: seedance/seedance-2-*/seedance-25 = cena+áudio+referências (o 25 estica até 30s e o preço acompanha), seedance-15 = degrau 1.5, kling = anima imagem, wan = imagem que fala, " +
264
276
  "hailuo/hailuo-pro = movimento puro sem áudio, h3 = 2K com áudio, kling-motion/shot-mimic = transferência de movimento/plano, lite/fast/quality = Veo 3.1, omni = Gemini com áudio nativo."),
265
277
  durationSec: z
266
278
  .number()
267
279
  .optional()
268
- .describe("action=create: duração em segundos (Omni ignora). Seedance 2.0/Shot Mimic 4-15, Seedance 2.5 4-30 (o preço acompanha: confirme a duração com a pessoa antes de passar de 15), Seedance 1.5 4-12, Kling 3-15, WAN 5/10, H3 5/6/8/10. " +
269
- "Sem isso usa a config mais barata. O preço escala com a duração. " +
280
+ .describe("action=create e action=price: duração em segundos (Omni ignora). Seedance 2.0/Shot Mimic 4-15, Seedance 2.5 4-30 (o preço acompanha: confirme a duração com a pessoa antes de passar de 15), Seedance 1.5 4-12, Kling 3-15, WAN 5/10, H3 5/6/8/10. " +
281
+ "O PREÇO ESCALA COM A DURAÇÃO. Omitir não é 'a config padrão da casa': duração ausente, ou fora do leque do motor, cai no PISO de duração do motor. " +
270
282
  "action=shadows: duração do vídeo-fonte, se souber (cobra 200/s; sem ela, flat ~2000)."),
271
283
  resolution: z
272
284
  .enum(["480p", "720p", "1080p"])
273
285
  .optional()
274
- .describe("action=create: resolução (Seedance/WAN/Shot Mimic). Default 720p. Kling não usa (1080p nativo)."),
286
+ .describe("action=create e action=price: resolução (Seedance/WAN/Shot Mimic). NÃO existe default 720p aqui: resolução ausente, ou fora do que o motor aceita, cai no tier MAIS CARO do motor. " +
287
+ "É isso que faz um create sem este campo custar muito mais que o piso do catálogo (em sapiens-video-seedance vira 1080p, não 480p). Passe sempre a resolução que você quer pagar, e confira com action=price antes. " +
288
+ "Kling e a linha Hailuo/H3 não usam este campo (resolução fixa pelo motor)."),
275
289
  audio: z
276
290
  .boolean()
277
291
  .optional()
278
- .describe("action=create: liga áudio. Seedance = on por default; Kling 'sound' = +50%. WAN é áudio nativo sempre."),
292
+ .describe("action=create e action=price: liga áudio. Seedance = on por default; Kling 'sound' = +50% no preço. WAN é áudio nativo sempre."),
279
293
  editOfImageId: z
280
294
  .string()
281
295
  .optional()
@@ -447,9 +461,17 @@ export async function localFrameReferences(args) {
447
461
  return refs;
448
462
  }
449
463
  export async function video(args) {
450
- // models: catálogo VIVO dos modelos de vídeo (ativos + preço-piso/config +
451
- // override admin + disponibilidade). Público, sem custo e sem login — antes de
452
- // exigir sessão. Espelha sapiens_image action=models.
464
+ // models: catálogo VIVO dos modelos de vídeo (ativos + faixa de preço real +
465
+ // config + disponibilidade). Público, sem custo e sem login, antes de exigir
466
+ // sessão. Espelha sapiens_image action=models.
467
+ //
468
+ // A faixa vem calculada do servidor pela MESMA função que debita
469
+ // (resolveVideoCostSinapses). Até ago/2026 esta action entregava só o
470
+ // `basePriceSinapses`, que é o PISO do motor, e o agente do outro lado
471
+ // repassava esse número como se fosse o preço: em `sapiens-video-seedance` o
472
+ // piso é 1600 e um create sem resolução debita 9000, porque resolução ausente
473
+ // cai no tier mais caro. Piso sozinho vira promessa quebrada, então ele saiu
474
+ // do retorno e no lugar entrou o trio "sem config / mais barato / mais caro".
453
475
  if (args.action === "models") {
454
476
  const all = await convexQuery("videoModels:listWithOverrides", {});
455
477
  const models = (all ?? [])
@@ -458,7 +480,17 @@ export async function video(args) {
458
480
  id: m.id,
459
481
  engine: m.engine,
460
482
  label: m.label,
461
- basePriceSinapses: m.basePriceSinapses,
483
+ // "por-segundo" = o preço MUDA com duração, resolução e (no Kling) áudio.
484
+ // "por-clipe" = preço fixo, config não mexe na conta.
485
+ priceMode: m.priceMode ?? null,
486
+ // O que um create SEM durationSec/resolution debita hoje neste motor,
487
+ // com a config que esse preço paga de fato.
488
+ priceDefault: m.priceDefault ?? null,
489
+ // Os dois cantos do motor: a config mais barata e a mais cara possíveis.
490
+ priceMin: m.priceMin ?? null,
491
+ priceMax: m.priceMax ?? null,
492
+ // +50% no Kling quando o som liga; null = som já embutido no preço.
493
+ audioMultiplier: m.audioMultiplier ?? null,
462
494
  modes: m.modes,
463
495
  durationsSec: m.durationsSec,
464
496
  resolutions: m.resolutions,
@@ -471,7 +503,42 @@ export async function video(args) {
471
503
  count: models.length,
472
504
  default: "sapiens-video-seedance",
473
505
  models,
474
- note: "basePriceSinapses é o PISO (config mais barata, já com override admin); o preço real escala por duração x resolução [x áudio]. available=false = 'em breve' (depende de env, ex: Omni).",
506
+ note: "NÃO EXISTE 'o preço' de um motor de vídeo com priceMode='por-segundo': o custo é duração x resolução [x áudio], então o que existe é uma FAIXA. " +
507
+ "priceDefault.sinapses é o que um create SEM durationSec e SEM resolution debita hoje (o clamp da casa manda duração ausente pro piso e resolução ausente pro tier MAIS CARO, por isso ele costuma ser bem maior que priceMin). " +
508
+ "priceMin e priceMax são os dois cantos do motor, cada um com a config que o produz. " +
509
+ "Ao falar de preço com a pessoa, diga a faixa e a config, nunca um número solto. " +
510
+ "Pro número exato de uma config antes de gerar, use action=price (sem custo, sem login): ele devolve o valor que o create vai debitar. " +
511
+ "Motor com priceMode='por-clipe' (linha Veo, Omni) tem preço fixo e as três pontas são iguais. " +
512
+ "available=false = 'em breve' (depende de env, ex: Omni).",
513
+ };
514
+ }
515
+ // price: cotação EXATA de uma config, pela mesma função que debita. Sem custo
516
+ // e sem login, igual ao models: confirmar o preço é de graça, errar o take é
517
+ // que custa. É a porta pra dizer o número certo pra pessoa antes do create.
518
+ if (args.action === "price") {
519
+ if (!args.model) {
520
+ throw new Error("action=price exige model (o id do motor; a lista viva sai em action=models).");
521
+ }
522
+ const quote = await convexQuery("videoModels:priceQuote", {
523
+ model: args.model,
524
+ durationSec: args.durationSec,
525
+ resolution: args.resolution,
526
+ audio: args.audio,
527
+ });
528
+ // Três avisos possíveis, e a ordem importa: o clamp é o que mais surpreende
529
+ // (a conta vem diferente do pedido), depois o campo omitido (que o servidor
530
+ // preenche pelo lado CARO da régua) e por último o caso limpo.
531
+ const perSecond = quote?.priceMode === "por-segundo";
532
+ const omitiu = perSecond &&
533
+ (args.resolution === undefined || args.durationSec === undefined) &&
534
+ !quote?.clamped;
535
+ return {
536
+ ...quote,
537
+ note: quote?.clamped
538
+ ? "ATENÇÃO: a config cobrada (billed) não é a que você pediu (asked). Duração fora do leque do motor cai no piso e resolução inválida cai no tier MAIS CARO, e é esse valor clampado que o create vai debitar e mandar pro provider. Ajuste o pedido pra um valor que o motor aceita (veja durationsSec/resolutions em action=models)."
539
+ : omitiu
540
+ ? "Você não passou durationSec e/ou resolution, então o servidor preencheu (billed mostra o quê): duração ausente vira o piso, resolução ausente vira o tier MAIS CARO do motor. O valor em sinapses já é o desse preenchimento, e é o que o create vai debitar se você chamar do mesmo jeito. Passe os dois campos pra pagar o que você escolheu."
541
+ : "sinapses é o valor que o create vai debitar com exatamente essa config. Confirme com a pessoa antes de gerar.",
475
542
  };
476
543
  }
477
544
  // templates: catálogo VIVO das receitas de take (estilo + spec + os campos de
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapiens-mcp",
3
- "version": "1.51.1",
3
+ "version": "1.53.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.",
6
6
  "type": "module",