sapiens-mcp 1.101.0 → 1.102.1

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
@@ -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: 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é 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. 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).",
75
75
  schema: imageSchema,
76
76
  handler: image,
77
77
  },
@@ -221,7 +221,7 @@ export const TOOLS = {
221
221
  handler: decupagem,
222
222
  },
223
223
  sapiens_reference: {
224
- description: "O 'popup global de referência' do Sapiens — espelha o modal 'Selecionar Referência' do gerador web: um lugar só pra navegar os bancos e pegar o que vira referência em imagem/vídeo. READ-ONLY. DUAS sub-actions: 'browse' (navega um banco) e 'resolve' (acha UM asset pelo ID citável). RESOLVE: passe handle=<tipo>_<id> (img_… imagem, vid_… vídeo, film_… vídeo programático, trk_… música, comic_… tirinha, char_… personagem — é o ID que o usuário copia no detalhe do item) e o servidor devolve {mine, isPublic, url, title, mediaKind} SE for seu ou público (senão recusa; nunca acessa asset privado de terceiro). Use a url retornada como referência (referenceImageUrls / startImageUrl / endImageUrl). BROWSE + bucket: 'history' (suas imagens recentes, privadas+públicas), 'favorites' (imagens que você curtiu, só as suas), 'videos' (seus vídeos / Meus Vídeos), 'stock_video' (banco de B-roll da casa, público), 'acervo' (stock + comunidade públicos; aceita term=busca e source=all|stock|community), 'characters' (personagens; mode=mine [default, inclui rascunhos] ou public [Explorar]). Paginado (page/limit, default 20, máx 50; use hasMore). Itens normalizados: imagem PRÓPRIA (history/favorites) traz imageId + url (use imageId em sapiens_image sourceImageIds ou sapiens_video startImageId/endImageId; ou a url em referenceImageUrls); acervo e characters são públicos/de terceiros, use a url (characters trazem mainImageUrl + imageUrls + characterId) em referenceImageUrls / startImageUrl / endImageUrl, NÃO em sourceImageIds. Personagens públicos também têm porta dedicada em sapiens_character action=list_public.",
224
+ description: "O 'popup global de referência' do Sapiens — espelha o modal 'Selecionar Referência' do gerador web: um lugar só pra navegar os bancos e pegar o que vira referência em imagem/vídeo. READ-ONLY. DUAS sub-actions: 'browse' (navega um banco) e 'resolve' (acha UM asset pelo ID citável). RESOLVE: passe handle=<tipo>_<id> (img_… imagem, vid_… vídeo, film_… vídeo programático, trk_… música, comic_… tirinha, char_… personagem — é o ID que o usuário copia no detalhe do item) e o servidor devolve {mine, isPublic, url, title, mediaKind} SE for seu ou público (senão recusa; nunca acessa asset privado de terceiro). Use a url retornada como referência (referenceImageUrls / startImageUrl / endImageUrl). BROWSE + bucket: 'history' (suas imagens recentes, privadas+públicas), 'favorites' (imagens que você curtiu, só as suas), 'videos' (seus vídeos / Meus Vídeos), 'stock_video' (banco de B-roll da casa, público), 'acervo' (stock + comunidade públicos; aceita term=busca e source=all|stock|community), 'characters' (personagens; mode=mine [default, inclui rascunhos] ou public [Explorar]). BUSCA POR SIGNIFICADO: em history e videos, term + semantic=true acha a peça da pessoa pelo sentido mesmo quando o prompt usou outras palavras (volta até limit, do mais parecido pro menos, com score). Paginado (page/limit, default 20, máx 50; use hasMore). Itens normalizados: imagem PRÓPRIA (history/favorites) traz imageId + url (use imageId em sapiens_image sourceImageIds ou sapiens_video startImageId/endImageId; ou a url em referenceImageUrls); acervo e characters são públicos/de terceiros, use a url (characters trazem mainImageUrl + imageUrls + characterId) em referenceImageUrls / startImageUrl / endImageUrl, NÃO em sourceImageIds. Personagens públicos também têm porta dedicada em sapiens_character action=list_public.",
225
225
  schema: referenceSchema,
226
226
  handler: reference,
227
227
  },
package/dist/skills.js CHANGED
@@ -355,9 +355,11 @@ Proibidos no prompt: "tarot card illustration", "intimate scale", "card-style po
355
355
 
356
356
  \`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
357
 
358
+ 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.
359
+
358
360
  ## Escolher motor em dois passos
359
361
 
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:
362
+ 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
363
 
362
364
  - 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
365
  - 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 +371,12 @@ O caminho: filtre por bestFor, escolha a família, e só então compare as varia
369
371
 
370
372
  ## Multi-referência
371
373
 
372
- Combine até 4 imagens como referência numa geração só:
374
+ Combine até 5 imagens como referência numa geração só:
373
375
 
374
376
  - \`referenceImageUrls\`: sua galeria, o Acervo, personagens públicos (descubra em \`sapiens_character\`).
375
377
  - \`sourceImageIds\`: ids da sua galeria.
376
378
 
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.
379
+ 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
380
 
379
381
  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
382
 
@@ -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,6 +106,8 @@ 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;
@@ -119,7 +121,7 @@ export const imageSchema = z.object({
119
121
  model: z
120
122
  .enum(MODELS)
121
123
  .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)."),
124
+ .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
125
  aspectRatio: z
124
126
  .enum(["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"])
125
127
  .optional()
@@ -131,12 +133,12 @@ export const imageSchema = z.object({
131
133
  styleId: z
132
134
  .string()
133
135
  .optional()
134
- .describe("Default 'none'. IDs de estilo no convex/shared/imageStyles.ts."),
136
+ .describe("Legado, sem efeito: o servidor não lê este campo. Estilo vem de brandSlug, templateSlug ou registro."),
135
137
  negativePrompt: z.string().optional(),
136
138
  referenceImageUrls: z
137
139
  .array(httpUrl())
138
140
  .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)."),
141
+ .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
142
  sourceImageIds: z
141
143
  .array(z.string())
142
144
  .optional()
@@ -264,6 +266,14 @@ export async function image(args) {
264
266
  // resolução. null = referência não muda o preço.
265
267
  referenceAdders: m.referencePriceSinapses ?? null,
266
268
  supportsReferences: !!m.supportsReferences,
269
+ // Quantas referências chegam ao motor PELO MCP: o teto do motor no
270
+ // catálogo (ausente = 4, o DEFAULT_MAX_REFERENCES do servidor) cortado
271
+ // no teto de 5 do resolvedor. As descrições de referenceImageUrls e
272
+ // sourceImageIds mandam olhar este campo, que até a 1.103 não existia:
273
+ // a 5ª ref no nano-banana-2 sumia sem aviso. 0 = não aceita referência.
274
+ maxReferences: m.supportsReferences
275
+ ? Math.min(typeof m.maxReferences === "number" ? m.maxReferences : 4, MAX_REFS_PELO_MCP)
276
+ : 0,
267
277
  degen: /^(civitai-|wavespeed-|fal-)/.test(String(m.id)),
268
278
  // O id que gera esta mesma escolha COM nudez (manda junto loraIntensity).
269
279
  // null = sem par; se o próprio motor tem LoRA tunável, a Ousadia é dele.
@@ -297,7 +307,8 @@ export async function image(args) {
297
307
  models,
298
308
  families,
299
309
  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). " +
310
+ "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). " +
311
+ "maxReferences é quantas referências chegam ao motor por aqui (o teto dele, cortado em 5): a mais, somem sem erro. " +
301
312
  "Quando referenceAdders vem preenchido (Krea 3), cada referência soma referenceAdders[size] ao preço, até o teto do motor. " +
302
313
  "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
314
  "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).",
@@ -423,6 +434,9 @@ export async function image(args) {
423
434
  ...(args.transparentBackground === true ? { transparentBackground: true } : {}),
424
435
  // Pago com o saldo em dólar da API: quanto saiu. Ausente quando foi em Sinapses.
425
436
  ...(typeof result?.pagoEmDolarUsd === "number" ? { pagoEmDolarUsd: result.pagoEmDolarUsd } : {}),
437
+ // Pago em Sinapses: o que o extrato debitou (resolução, refs pagas e
438
+ // override já dentro). Backend de antes do campo não manda, e aí não vem.
439
+ ...(typeof result?.custoSinapses === "number" ? { custoSinapses: result.custoSinapses } : {}),
426
440
  // Não incluir base64 no retorno (polui contexto). Quem quiser bytes
427
441
  // chama sapiens_gallery action=get com includeBase64=true.
428
442
  };
@@ -22,6 +22,10 @@ import { convexAction, convexQuery, getSessionToken } from "../convexClient.js";
22
22
  * - characters personagens; `mode` = 'mine' (default) ou 'public'
23
23
  * - depth map guias de movimento; `shadowSource` = 'house' (banco curado, default) ou 'mine'
24
24
  *
25
+ * `semantic` lê o `term` como descrição e busca por SIGNIFICADO em history,
26
+ * videos (creationEmbeddings) e pose_map (poseRefs): a prateleira "Pelo
27
+ * sentido" do picker web, por outra porta porque vetor não pagina como lista.
28
+ *
25
29
  * Cada item volta normalizado:
26
30
  * - imagem PRÓPRIA (history/favorites): `imageId` + `url` → use imageId em
27
31
  * sourceImageIds / startImageId / endImageId, ou url em referenceImageUrls.
@@ -41,11 +45,11 @@ export const referenceSchema = z.object({
41
45
  // proíbe quebrar quem está atrasado. O servidor aceita os dois.
42
46
  .enum(["history", "favorites", "videos", "stock_video", "acervo", "characters", "depth_map", "pose_map", "deepshadow"])
43
47
  .optional()
44
- .describe("Só 'browse': qual banco navegar: 'history' (suas imagens recentes), 'favorites' (as que você curtiu), " +
45
- "'videos' (seus vídeos), 'stock_video' (Banco de Vídeo da casa: clipes/B-roll prontos, aceita term/orientation/loopOnly), " +
48
+ .describe("Só 'browse': qual banco navegar: 'history' (suas imagens recentes; com term+semantic acha pelo significado), " +
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), " +
46
50
  "'acervo' (stock + comunidade públicos de IMAGEM), 'characters' (personagens), " +
47
51
  "'depth_map' (guias de movimento: os SEUS com shadowSource='mine', ou o banco curado da casa por default), " +
48
- "'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)."),
49
53
  handle: z
50
54
  .string()
51
55
  .optional()
@@ -111,9 +115,11 @@ export const referenceSchema = z.object({
111
115
  semantic: z
112
116
  .boolean()
113
117
  .optional()
114
- .describe("Só bucket 'pose_map': lê o `term` como DESCRIÇÃO da cena em vez de palavra-chave. " +
115
- "É o caminho pra pedido que nenhuma tag cobre ('alguém se protegendo do vento', " +
116
- "'caindo de costas apoiado num braço'). Frase inteira funciona melhor que palavra solta."),
118
+ .describe("Buckets 'history', 'videos' e 'pose_map': lê o `term` como DESCRIÇÃO em vez de palavra-chave, e acha " +
119
+ "pelo SIGNIFICADO. Em history/videos acha a peça da pessoa mesmo quando o prompt usou outras palavras " +
120
+ "('praia ao entardecer' acha 'golden hour beach'); em pose_map, o pedido que nenhuma tag cobre " +
121
+ "('alguém se protegendo do vento', 'caindo de costas apoiado num braço'). Frase inteira funciona melhor " +
122
+ "que palavra solta. Sem paginação: volta até `limit` (máx 50), do mais parecido pro menos, com `score`."),
117
123
  similarTo: z
118
124
  .string()
119
125
  .optional()
@@ -123,6 +129,41 @@ export const referenceSchema = z.object({
123
129
  .enum(["recent", "used"])
124
130
  .optional()
125
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."),
126
167
  mode: z
127
168
  .enum(["mine", "public"])
128
169
  .optional()
@@ -165,6 +206,9 @@ export async function reference(args) {
165
206
  similarTo: args.similarTo,
166
207
  people: args.people,
167
208
  limit: args.limit,
209
+ familia: args.familia,
210
+ vista: args.vista,
211
+ leitura: args.leitura,
168
212
  });
169
213
  const itens = res?.items ?? [];
170
214
  return {
@@ -175,11 +219,51 @@ export async function reference(args) {
175
219
  note: itens.length
176
220
  ? "Vizinhança por SENTIDO, da mais parecida pra menos (`score` é a distância). " +
177
221
  "Cada item traz `descricao`: leia antes de escolher, é ela que diz o que o corpo faz. " +
178
- "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."
179
224
  : "Nenhuma pose do seu banco chegou perto disso. Tente descrever o CORPO (apoio, direção do tronco, " +
180
225
  "o que os braços fazem) em vez da cena, ou use browse com people/hasObject.",
181
226
  };
182
227
  }
228
+ // A mesma porta de sentido pras SUAS imagens e vídeos (creationEmbeddings):
229
+ // o `term` vira vetor e volta a vizinhança, no shape dos itens do browse.
230
+ if ((args.bucket === "history" || args.bucket === "videos") && args.semantic && args.term?.trim()) {
231
+ const video = args.bucket === "videos";
232
+ const res = await convexAction("creationEmbeddings:mcpSearchByMeaning", {
233
+ sessionToken,
234
+ term: args.term,
235
+ type: video ? "VIDEO" : "IMAGE",
236
+ limit: args.limit,
237
+ });
238
+ const itens = (res?.items ?? []).map((r) => ({
239
+ refKind: video ? "video" : "image",
240
+ imageId: r.imageId,
241
+ url: r.url,
242
+ thumbnailUrl: r.posterUrl ?? null,
243
+ title: r.seoTitle ?? null,
244
+ prompt: r.prompt ?? null,
245
+ model: r.model ?? null,
246
+ aspectRatio: r.aspectRatio ?? null,
247
+ isPublic: !!r.isPublic,
248
+ createdAt: r.createdAt ?? null,
249
+ source: args.bucket,
250
+ score: r.score,
251
+ }));
252
+ return {
253
+ bucket: args.bucket,
254
+ modo: "sentido",
255
+ count: itens.length,
256
+ items: itens,
257
+ note: itens.length
258
+ ? "Peças SUAS por significado, da mais parecida pra menos (`score`). " +
259
+ (video
260
+ ? "Cada item é um vídeo seu (imageId reusável em sapiens_video)."
261
+ : "Use o imageId em sapiens_image sourceImageIds ou sapiens_video startImageId/endImageId, ou a url em referenceImageUrls.")
262
+ : `Nada do seu acervo chegou perto de "${args.term}" por significado. Tente descrever o que aparece na ` +
263
+ "peça, ou browse com o mesmo `term` sem semantic (pedaço de palavra). Peça muito recente pode ainda " +
264
+ "não estar no índice: ela entra alguns segundos depois de pronta.",
265
+ };
266
+ }
183
267
  const res = await convexAction("mcpReferences:referenceBrowse", {
184
268
  sessionToken,
185
269
  bucket: args.bucket,
@@ -197,6 +281,11 @@ export async function reference(args) {
197
281
  favoritesOnly: args.favoritesOnly,
198
282
  collection: args.collection,
199
283
  sort: args.sort,
284
+ familia: args.familia,
285
+ vista: args.vista,
286
+ enquadramento: args.enquadramento,
287
+ leitura: args.leitura,
288
+ hasSuporte: args.hasSuporte,
200
289
  });
201
290
  let note;
202
291
  if (args.bucket === "videos") {
@@ -213,7 +302,7 @@ export async function reference(args) {
213
302
  }
214
303
  else if (args.bucket === "pose_map") {
215
304
  note =
216
- "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.";
217
306
  }
218
307
  else if (args.bucket === "acervo" || args.bucket === "characters") {
219
308
  note =
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapiens-mcp",
3
- "version": "1.101.0",
3
+ "version": "1.102.1",
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",