sapiens-mcp 1.93.1 → 1.94.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 +9 -2
- package/dist/skills.js +38 -1
- package/dist/tools/brand.js +120 -4
- package/dist/tools/carrossel.js +9 -1
- package/dist/tools/collection.js +141 -0
- package/dist/tools/meta.js +2 -2
- package/package.json +1 -1
package/dist/registry.js
CHANGED
|
@@ -26,6 +26,7 @@ import { stockAudio, stockAudioSchema } from "./tools/stockAudio.js";
|
|
|
26
26
|
import { voiceJp, voiceJpSchema } from "./tools/voiceJp.js";
|
|
27
27
|
import { stockVideo, stockVideoSchema } from "./tools/stockVideo.js";
|
|
28
28
|
import { brand, brandSchema } from "./tools/brand.js";
|
|
29
|
+
import { collection, collectionSchema } from "./tools/collection.js";
|
|
29
30
|
import { character, characterSchema } from "./tools/character.js";
|
|
30
31
|
import { profile, profileSchema } from "./tools/profile.js";
|
|
31
32
|
import { sintetico, sinteticoSchema } from "./tools/sintetico.js";
|
|
@@ -65,7 +66,7 @@ export const TOOLS = {
|
|
|
65
66
|
handler: pipeline,
|
|
66
67
|
},
|
|
67
68
|
sapiens_carrossel: {
|
|
68
|
-
description: "O CARROSSEL do Instagram da pessoa, pelo agente (qualquer conta logada, só os carrosséis DELA). Puxe a skill 'carrossel' antes. Sub-actions: generate (gera do zero a partir de UMA fonte: brief, articleId de artigo publicado ou userArticleId de artigo dela; 350 Sinapses já com a escolha de fotos, estorna se falhar; model é a FORMA: feed (padrão, 9 slides), reflexao, case ou editorial; aspect é o TAMANHO: 4:5 (padrão, a folha que o feed mostra inteira) ou 9:16; devolve id + url do editor), list (os carrosséis dela, com id, título, nº de slides e url), get (payload inteiro + catálogo de layouts com os slots e limites de cada um, paletas, folhas e a assinatura: tudo pra VOCÊ escrever os slides), update (payload INTEIRO de volta, normalizado no servidor: mande o theme inteiro, com folha e assinatura, e o image de todo slide que não mexeu, senão volta pro padrão e a foto some), auto_images (a IA escolhe fotos do banco pros slides, 60 Sinapses, estorna se não tiver o que preencher; makeVisual converte slides de texto em layout de foto antes; generateMissing gera foto nova pros que o banco não cobrir, até 4, ~450 Sinapses cada, avise o custo antes), generate_image (foto nova pra UM slide, ~450 Sinapses, customPrompt opcional), set_image (põe no slide uma peça da pessoa ou pública pelo ID citável img_…, sem custo; é como ela usa as PRÓPRIAS fotos), cover (a Capa IA: capa 16:9 com a manchete QUEIMADA na imagem e um elemento vivo cruzando as letras; com carouselId fatia em duas e põe na frente do carrossel no lugar da capa IA anterior; cobra pelo motor da capa, confirme o custo antes). A ASSINATURA (theme.signature: o nome no topo, a marca no canto e o @ do fecho) nasce da marca da pessoa, ou do nome dela quando não tem marca; o @ é o dela no Instagram, não invente. O carrossel vai até 15 slides. generate, auto_images com generateMissing, generate_image e cover são SÍNCRONAS e pesadas: vale a REGRA DO TIMEOUT (confira em action=list antes de repetir). Baixar o arquivo é no editor (a url): aqui sai a receita pronta. O carrossel atrelado a uma jogada do Workflow segue pela sapiens_pipeline.",
|
|
69
|
+
description: "O CARROSSEL do Instagram da pessoa, pelo agente (qualquer conta logada, só os carrosséis DELA). Puxe a skill 'carrossel' antes. Sub-actions: generate (gera do zero a partir de UMA fonte: brief, articleId de artigo publicado ou userArticleId de artigo dela; 350 Sinapses já com a escolha de fotos, estorna se falhar; model é a FORMA: feed (padrão, 9 slides), reflexao, case ou editorial; aspect é o TAMANHO: 4:5 (padrão, a folha que o feed mostra inteira) ou 9:16; devolve id + url do editor), list (os carrosséis dela, com id, título, nº de slides e url), get (payload inteiro + catálogo de layouts com os slots e limites de cada um, paletas, folhas e a assinatura: tudo pra VOCÊ escrever os slides), update (payload INTEIRO de volta, normalizado no servidor: mande o theme inteiro, com folha e assinatura, e o image de todo slide que não mexeu, senão volta pro padrão e a foto some), auto_images (a IA escolhe fotos do banco pros slides, 60 Sinapses, estorna se não tiver o que preencher; makeVisual converte slides de texto em layout de foto antes; generateMissing gera foto nova pros que o banco não cobrir, até 4, ~450 Sinapses cada, avise o custo antes), generate_image (foto nova pra UM slide, ~450 Sinapses, customPrompt opcional), set_image (põe no slide uma peça da pessoa ou pública pelo ID citável img_…, sem custo; é como ela usa as PRÓPRIAS fotos), cover (a Capa IA: capa 16:9 com a manchete QUEIMADA na imagem e um elemento vivo cruzando as letras; com carouselId fatia em duas e põe na frente do carrossel no lugar da capa IA anterior; cobra pelo motor da capa, confirme o custo antes). A ASSINATURA (theme.signature: o nome no topo, a marca no canto e o @ do fecho) nasce da marca da pessoa, ou do nome dela quando não tem marca; o @ é o dela no Instagram, não invente. O BRAND veste a peça inteira: cor (o destaque é a cor do brand que lê no fundo, trocável no editor), fonte do título e do corpo, voz e assinatura; no generate, brandSlug escolhe outro brand que não o padrão da conta. Carrossel que vem de FORA entra pelo update: cada slide de lá casa com um layout do catálogo do get pelo papel (capa, corpo, cta) e pela foto, e o estilo vem do brand (sapiens_brand action=import), não do layout. O carrossel vai até 15 slides. generate, auto_images com generateMissing, generate_image e cover são SÍNCRONAS e pesadas: vale a REGRA DO TIMEOUT (confira em action=list antes de repetir). Baixar o arquivo é no editor (a url): aqui sai a receita pronta. O carrossel atrelado a uma jogada do Workflow segue pela sapiens_pipeline.",
|
|
69
70
|
schema: carrosselSchema,
|
|
70
71
|
handler: carrossel,
|
|
71
72
|
},
|
|
@@ -160,10 +161,15 @@ export const TOOLS = {
|
|
|
160
161
|
handler: stockVideo,
|
|
161
162
|
},
|
|
162
163
|
sapiens_brand: {
|
|
163
|
-
description: "Brand Sapiens (= Design System) — paleta + tipografia + voz + estilo de imagem + persona como FONTE ÚNICA de estilo (espelha o Estúdio de Brand do app). Sub-actions: list (oficiais curados + os custom do user, leve, grátis), get (1 brand completo por slug: voz + imageStyle + persona/logo; só oficial ou o próprio), generate (CRIA design system novo a partir de descrição em texto livre; Gemini monta tudo e já nasce com card premium gpt-image-2; 150 Sinapses pelo texto + o card no gpt-image-2-high a preço de catálogo, hoje 1350 Sinapses no total, reembolsa se falhar), refine (ajusta brand custom por feedback livre tipo 'fundo mais escuro'/'voz mais seca', ~75 Sinapses), reroll (regenera SÓ 1 peça voice|palette|imageStyle numa direção diferente, grátis), card ((re)gera o card premium, preço de catálogo do modelo), delete (apaga brand custom do próprio user), set_visibility (torna um brand custom do user público/privado: público = aparece no perfil + galeria da comunidade, qualquer logado adota), list_public (galeria de design systems PÚBLICOS da comunidade, opt-in pelos donos, com atribuição), adopt (clona um brand público/oficial numa cópia NOVA e PRIVADA na conta do user; persona é dropada), persona (pluga um personagem DO PRÓPRIO user como a cara recorrente das imagens do brand custom dele, por character=<slug|id> de sapiens_character action=list_mine, ou character=null pra tirar; as refs saem da galeria do personagem e o personagem precisa ter pelo menos uma imagem; grátis). O personagem só APARECE na imagem quando a marca é persona: depois de plugar, gere com sapiens_image brandSlug=<slug> brandMark=persona. Qualquer conta logada; Sinapses saem do dono do sessionToken. Pra generate: converse com o user e monte uma description rica (vibe, cores, voz, públicos, refs; se ele colar amostra de texto dele, inclua pra a voz sair dali) ANTES de chamar. Confirme o custo (1350 Sinapses) antes de gerar.",
|
|
164
|
+
description: "Brand Sapiens (= Design System) — paleta + tipografia + voz + estilo de imagem + persona como FONTE ÚNICA de estilo (espelha o Estúdio de Brand do app). Sub-actions: list (oficiais curados + os custom do user, leve, grátis), get (1 brand completo por slug: voz + imageStyle + persona/logo; só oficial ou o próprio), generate (CRIA design system novo a partir de descrição em texto livre; Gemini monta tudo e já nasce com card premium gpt-image-2; 150 Sinapses pelo texto + o card no gpt-image-2-high a preço de catálogo, hoje 1350 Sinapses no total, reembolsa se falhar), refine (ajusta brand custom por feedback livre tipo 'fundo mais escuro'/'voz mais seca', ~75 Sinapses), reroll (regenera SÓ 1 peça voice|palette|imageStyle numa direção diferente, grátis), card ((re)gera o card premium, preço de catálogo do modelo), delete (apaga brand custom do próprio user), set_visibility (torna um brand custom do user público/privado: público = aparece no perfil + galeria da comunidade, qualquer logado adota), list_public (galeria de design systems PÚBLICOS da comunidade, opt-in pelos donos, com atribuição), adopt (clona um brand público/oficial numa cópia NOVA e PRIVADA na conta do user; persona é dropada), persona (pluga um personagem DO PRÓPRIO user como a cara recorrente das imagens do brand custom dele, por character=<slug|id> de sapiens_character action=list_mine, ou character=null pra tirar; as refs saem da galeria do personagem e o personagem precisa ter pelo menos uma imagem; grátis), favorite (marca, ou com isFavorite=false desmarca, um brand custom do user como o FAVORITO, o escolhido entre as tentativas de um projeto: sobe pro topo da list e dos seletores do app, as outras tentativas ficam guardadas; oficial não se favorita; grátis), import (TRANSCREVE um design system que JÁ EXISTE fora, de outro app, do Figma, do kit de um cliente: o que veio é o que grava, hex e fonte exatos, sem IA no meio, mesmo título e corpo na mesma família ou fonte fora do Google Fonts; grátis, o card nasce em SVG; o shape está no arg `brand`, e o retorno avisa em `warnings` o que pode não aparecer igual), logo (põe a logo no brand custom pelo ID da imagem na galeria do user, img_… de sapiens_gallery action=upload, ou handle=null pra tirar; grátis). Brand que a pessoa JÁ TEM vai por import, nunca por generate: o generate cria e reinterpreta. O personagem só APARECE na imagem quando a marca é persona: depois de plugar, gere com sapiens_image brandSlug=<slug> brandMark=persona. Qualquer conta logada; Sinapses saem do dono do sessionToken. Pra generate: converse com o user e monte uma description rica (vibe, cores, voz, públicos, refs; se ele colar amostra de texto dele, inclua pra a voz sair dali) ANTES de chamar. Confirme o custo (1350 Sinapses) antes de gerar. Valor explícito na descrição (hex, nome de fonte, nome) o generate respeita, mas pra copiar um brand inteiro o import é exato e de graça.",
|
|
164
165
|
schema: brandSchema,
|
|
165
166
|
handler: brand,
|
|
166
167
|
},
|
|
168
|
+
sapiens_collection: {
|
|
169
|
+
description: "Collections do Acervo: o álbum de QUALQUER peça (imagem, vídeo, design system, personagem, música, artigo, aula, carrossel), operado na conta do user. Uma Collection junta as peças de um mesmo mundo, e cada peça leva uma NOTA do dono. É o lugar do ESCOLHIDO entre as tentativas de um projeto: junte as tentativas e a escolhida na mesma Collection, nota 'escolhida' na que venceu e 'tentativa' nas outras, e a escolhida no topo (reorder). Sub-actions: list (as Collections do user), get (uma, por id ou slug, com as peças na ordem e as notas), create (title obrigatório; nasce rascunho e privada), update (nome e descrição), add (itemType + refId, note opcional; peça que já está dentro e chega com note tem a nota trocada), remove (tira a peça da Collection, a peça em si não é tocada), note (troca a nota; sem note, apaga), reorder (refIds na ordem nova, o primeiro no topo; os não citados vêm depois, como estavam). itemType: image e video (o id que sapiens_image, sapiens_video e sapiens_gallery devolvem), brand (o design system, pelo SLUG mesmo), character (characterId), track, article, aula, carousel, comic, sticker, fita, app. A Collection se acha pelo id ou pelo slug, e o nome também serve. Só mexe nas Collections e nas peças da própria conta. Publicar a Collection e escolher capa são gestos do dono no Acervo, nunca do agente. Grátis.",
|
|
170
|
+
schema: collectionSchema,
|
|
171
|
+
handler: collection,
|
|
172
|
+
},
|
|
167
173
|
sapiens_character: {
|
|
168
174
|
description: "Personagens (character sheets) do Sapiens — a tabela `influencers`: personagem reutilizável com imagens (pra character-lock em geração) + alma (systemPrompt), tudo amarrado à conta do dono do token (sem admin). Sub-actions: list_public (catálogo global de personagens públicos do Explorar; cada um traz mainImageUrl/imageUrls usáveis direto como referenceImageUrls em sapiens_image; sem custo, sem login), get (detalhe de 1 por characterId — público+ativo qualquer um vê, draft/privado só o dono; systemPrompt só volta pro dono), list_mine (os personagens do próprio user, inclui drafts/privados), create (cria rascunho na conta: name + gender + opcional form/title/systemPrompt), add_image (adiciona imagem ao próprio personagem via imageUrl público OU sourceImageId da galeria; 1ª vira principal), set_card (edita alma/título/lema em inglês/nome/nome por extenso/forma do próprio), set_presence (escreve onde o personagem existe FORA da casa: redes sociais por handle, Fanvue, site e o email dele, este último privado), set_passport (escreve o PASSAPORTE dele: como ACERTAR este ser, que é outra coisa de quem ele é. Guarda o `descriptor` de geração em inglês pra colar verbatim, o `negative` travado que fica sempre ligado, os `locks` (o que TRAVA nele, um por entrada, que é a lista que vira as perguntas da conferência), as `refs` por PAPEL (proporção, rosto, tatuagem: ref sem papel ninguém sabe usar), as `recipes` medidas por tipo de peça (com o prompt VERBATIM, porque o valor está na palavra exata que funcionou) e o `discarded`, o que já foi recusado. Esse último não é enfeite: o motor traz de volta o que ninguém proibiu, então ideia descartada sem negativo escrito volta sozinha na leva seguinte. É MERGE por campo, então mandar só o negative não apaga o descritor, e campo vazio apaga. Só o DONO lê: receita de produção não sai em personagem público. Em action=get ele volta em duas formas, `passport` (campo a campo, o que se reedita) e `passportPrompt` (o bloco já montado pra colar). Preencha sempre que uma rodada medir algo que a próxima não deve redescobrir: é o que faz a receita viajar pra fora do repo), activate (tira do rascunho e libera o personagem pra USAR nas gerações do dono; exige ≥1 imagem. NÃO torna público: quem publica é set_visibility, e ativar é gesto reversível e privado), set_visibility (isPublic true=Explorar+slug / false=privado), set_hidden (hidden true tira o personagem da TELA do dono: some da lista de personagens e as peças geradas com ele saem da galeria de fotos, vídeos e rascunhos do dono, voltando pelo filtro Ocultos; hidden false devolve. É o gesto de quem vai compartilhar tela. Não despublica, não apaga, não sai dos seletores de geração, e personagem oculto não publica até ser mostrado. Grátis. O list_mine traz `hidden` em cada personagem). GESTÃO de imagem (por url, pegue as urls atuais em action=get campo imageUrls): remove_image (tira uma), set_main_image (define a principal), reorder_images (nova ordem via orderedUrls, posição 0=principal), e delete (apaga o personagem, permanente). FORMA (`form`, o que tipo de CORPO ele tem: human default, humanoid, animal (bicho real), creature (ser inventado), object, abstract): não é enfeite de cadastro, é o que a ficha, as figurinhas e o vídeo leem pra decidir se descrevem rosto e mãos ou silhueta e postura, e é ele que impede uma criatura de voltar desenhada como pessoa. Mande no create sempre que o personagem não for gente; personagem que já existe sem ele conserta com set_card form=creature. Ausente = human, que é o que a casa desenhava antes do campo existir. NOME E NOME POR EXTENSO (`name` e `fullName`): `name` é como o personagem é CHAMADO e é o que toda tela desenha; `fullName` é o nome inteiro, pra quem tem apelido ('Crow Girl' na tela, 'Kaia Crowe' por extenso). Um não substitui o outro, e o extenso não mexe no slug da página nem no cartão de nome do vídeo: batizar personagem que já existe não muda endereço publicado nem assinatura de peça que já saiu. Vem em action=get e list_mine no campo `fullName` (null = ainda sem nome por extenso). TRAÇO (`style`, em que TÉCNICA ele é feito: auto default, realista, anime, manhwa, 3d, custom): irmão da forma, e a divisão é limpa, a forma diz que CORPO é esse e o traço diz como ele é DESENHADO. As mesmas três peças leem daqui. No 'auto' a peça não afirma técnica nenhuma e as imagens de referência mandam; 'realista' é fotografia, 'anime' é cel shading com contorno de tinta, 'manhwa' é pintura de webtoon, '3d' é render com material e oclusão, 'custom' usa a linha escrita em `styleNote` (até 140 caracteres, ex: aquarela sobre papel texturizado). Mande sempre que o personagem tiver técnica definida: até ago/2026 a ficha cravava vocabulário de desenho e devolvia personagem fotográfico virado em ilustração. Conserto sem custo em quem já existe: set_card style=realista. PRESENÇA (set_presence, de graça): o ENDEREÇO do personagem, o que faltava pra ele existir fora da ficha. `socials` é {rede: handle} nas redes instagram, tiktok, youtube, x, threads, bluesky, twitch, spotify, fanvue, patreon, onlyfans, telegram; `websiteUrl` é o site dele e `email` é o email do personagem, o que cadastra e recupera as contas dele nas redes. As redes e o site são PÚBLICOS; o email é PRIVADO e fica só com o dono, mesmo com o personagem publicado, porque email é chave de conta e isca de spam, não canal de contato. Em action=get e list_public de personagem de OUTRA pessoa, o campo simplesmente não vem. É MERGE: mandar {fanvue:'helen'} não apaga o instagram, e pra apagar uma rede se manda ela vazia. O que volta em action=get são as duas formas, `presence` (o handle cru, que é o que se reedita) e `presenceLinks` (o link já montado, pra mostrar sem remontar URL na mão). Vale a pena preencher quando o personagem TEM conta de verdade: é o que separa uma ficha de um ser com endereço, e a página pública dele desenha as redes e o site (nunca o email). FICHA (generate_sheet, COBRA 650 Sinapses, o preço do GPT Image 2.5 Sunburst, que desenha; se ele recusar, a ficha refaz no Nano Banana 2.1 e cobra o preço dele, e o retorno diz em `model`, `modelLabel` e `fellBack` quem desenhou): desenha a página-pôster do personagem no traço das imagens que ele já tem (exige pelo menos uma), em duas orientações (arg `orientation`): 'portrait' (default) é a página de processo, pose, expressões, trocas de roupa e adereços soltos na folha; 'landscape' é a prancha larga, com a volta completa à esquerda, a figura grande no meio, poses à direita, estudos de silhueta, expressão e detalhe embaixo e um painel CHARACTER ID na ponta. A personalidade sai da alma (systemPrompt) e é ela que escolhe roupa e objeto, então personagem com alma escrita rende ficha melhor. Cada geração é um estilo NOVO e elas acumulam no personagem (campo sheetUrls em action=get), nenhuma apaga a anterior, e a ficha já entra na galeria dele como referência das próximas gerações. É geração síncrona: se voltar Timeout, cheque action=get antes de repetir, senão cobra duas vezes. STICKERS (generate_stickers, COBRA 550 Sinapses): desenha 5 stickers do personagem DE UMA VEZ. Uma folha só, em 2K, com as cinco figuras separadas sobre um fundo verde chroma, recortada por código em peças 512x512 transparentes abaixo de 100KB (o teto do WhatsApp). É por isso que sai o preço de UMA imagem em 2K e não de cinco: quem paga é a folha. As peças entram no pack do personagem (um por personagem, os lotes acumulam) e já ficam no picker de expressão do dono, no chat e no Fórum. Arg `moods`: até 5 humores do vocabulário comum por slug (kkkkk, amei, isso, hmm, chega, que, aff, bora, socorro, seinao, valeu, ainao, seila, ideia, calma, contatudo, perfeito, euavisei, naovourir, zzz, somaisum, sextou, merecido, quedia, tudobem); faltando, a casa completa com os mais usados. Arg `hint`: direcionamento curto do autor (roupa, adereço, clima), até 140 caracteres. Arg `stickerTier`: 'folha' (default) é esse lote de cinco, desenhado no Nano Banana 2.1; 'unica' (COBRA 900 Sinapses) desenha UM sticker por vez no Gemini 3 Pro, o motor mais fiel da casa, com a figura sozinha no quadro e o dobro de pixel por peça, e leva só o PRIMEIRO mood da lista. Pra encher o pack, folha; pra traço difícil ou a reação que vira a cara do personagem, a unica. Ao oferecer a escolha, diga o NOME DO MOTOR e o que ele troca: adjetivo vago de acabamento não informa nada a quem vai pagar. Os dois caem no MESMO pack e acumulam. Sem legenda queimada na imagem, de propósito: modelo erra acento em português, então o rótulo fica na row e serve de busca no picker. Exige pelo menos uma imagem no personagem (é dela que sai a cara) e é geração síncrona: se voltar Timeout, cheque sapiens_gallery antes de repetir. Quando a resposta vem com ok=false, a folha foi gerada e paga mas o corte falhou; ela está na galeria e o recorte de novo é de graça, pela web. Publicar o pack na vitrine e o carimbo da casa (que é o que põe no picker de todo mundo) são gestos da web, não desta tool. PLACAS (generate_plates, COBRA 550 Sinapses): seis ângulos do mesmo rosto numa geração só, no Nano Banana 2.1 em 2K, recortados por código em seis referências com PAPEL: perfil, camera-alta, camera-baixa, frontal, cabeca-baixa e outro-lado, cada um com uma expressão diferente. Não é vitrine (isso é a ficha): é a referência feita pra máquina. Referência trava o ângulo da câmera, então quando a cena pede perfil, passe a placa de perfil em referenceImageUrls. As placas ficam FORA de imageUrls de propósito, porque placa de perfil como referência genérica puxa toda peça pro perfil; elas voltam em action=get no campo `plates` ([{role, url}]). As placas ACUMULAM: cada geração soma seis, e a que não serve sai com remove_image. Em `plates` o lote mais novo vem primeiro, cada lote na ordem da grade, então a primeira placa de um papel é a mais nova. Sai das fotos do personagem (exige pelo menos uma) e, quando ele tem passaporte, o descritor dele entra no pedido. Só personagem com rosto de gente (form human ou humanoid): pra bicho, criatura, objeto ou forma abstrata o servidor recusa antes de cobrar. O perfil puro e a câmera alta às vezes voltam amolecidos pro frontal; quando esses dois ângulos importarem, confira as placas antes de usar. Geração síncrona: se voltar Timeout, cheque action=get antes de repetir. Resposta com ok=false quer dizer grade gerada e paga com o corte falho; o recorte de novo é de graça, pela web. CHARACTER VIDEO (a ficha que anda): o personagem atravessa 6 mundos em 12s, trocando de roupa em cada um, com som gerado junto, e o último plano fecha a peça com ele parando e encarando a câmera. A peça volta FECHADA pelas duas portas (tela e agente), sem ninguém pedir: o personagem se apresenta no cartão de nome por cima do fim do último plano, com o LEMA em inglês embaixo (o campo `titleEn`, escrito uma vez em set_card), e só depois a casa assina. Quem não tem lema recebe o subtítulo que o roteiro inventou naquele take, e ele muda no take seguinte. O fecho é asset, não geração: zero Sinapse a mais, e é fail-open, então a resposta traz `closed` e `nameCard` dizendo o que entrou de verdade em vez de prometer um cartão que não está no arquivo. Pra colocar esse cartão num vídeo que JÁ existe, o caminho é a ficha do take na web (gaveta do fecho, 'Apresentação do personagem'), também sem custo. Exige FICHA gerada (é dela que saem a cara e o guarda-roupa), não só imagem. Duas actions, nesta ordem: plan_video (COBRA 50 Sinapses) devolve os 6 planos que a alma do personagem escolheu, com ambiente, ato, enquadramento e roupa. Cada plano vem nas duas línguas: em inglês (setting/act/wardrobe, que é o que vai pro motor) e em português (settingPt/actPt/wardrobePt). MOSTRE a versão em português pro autor, o público da casa é brasileiro e roteiro que ele lê de través não é roteiro aprovado; peça de novo quantas vezes ele quiser, porque trocar sai por 50 e o take errado sai por milhares. replan_shot (COBRA 15 Sinapses) reescreve UM plano e não toca nos outros cinco: os seis são cenas independentes, então quando o autor gosta de quatro e implica com um, troque só aquele em vez de sortear tudo de novo (mande os 6 planos em `shots`, o número em `shotIndex`, e o que ele quer diferente em `shotHint`; volta só o plano trocado, e é você que remonta o roteiro). Depois generate_video (COBRA) renderiza, com os planos aprovados no arg `shots` e o MOTOR no arg `videoTier`: 'draft' (5.400 Sinapses) é o Seedance 2.0 Mini, metade do preço, que tropeça em mão, pouca luz e câmera rápida, e 'final' (8.640 Sinapses) é o Seedance 2.0 Fast, que segura o que o Mini erra. A peça é a MESMA nos dois (mesmos 6 planos, 12s, 720p, com som), então ofereça pelo nome do motor e pelo que ele troca, nunca por adjetivo vago de acabamento. Vertical ou horizontal pelo arg `orientation`. Chamar generate_video SEM `shots` funciona, mas escreve um roteiro novo às cegas e cobra igual: não faça isso sem o autor ter visto o que vai receber. É geração demorada: se voltar Timeout, cheque sapiens_gallery antes de repetir. Resposta com `pending: true` (sem `url`) quer dizer ACEITO E COBRADO: o servidor continua renderizando sozinho (até 25 min) e a peça chega na ficha já fechada; NÃO repita a chamada, isso cobraria de novo, confira depois em sapiens_gallery. Fluxo de criação: create → add_image (1+) → set_card (opcional) → activate. PARE AÍ. O personagem nasce e continua PRIVADO, e é assim que ele serve pro dono: dá pra gerar, referenciar e iterar sem ninguém ver. set_visibility isPublic=true é o gesto de PUBLICAR no Explorar, com slug público, e nÃO é passo de fluxo: só chame quando o dono pedir pra publicar aquele personagem, com essas palavras. Na dúvida, não publique e pergunte. Pra usar um personagem público como referência numa geração, pegue mainImageUrl em list_public/get e passe em sapiens_image referenceImageUrls.",
|
|
169
175
|
schema: characterSchema,
|
|
@@ -325,6 +331,7 @@ const TOOL_TITLES = {
|
|
|
325
331
|
sapiens_stock_audio: "Banco de Áudio",
|
|
326
332
|
sapiens_stock_video: "Banco de Vídeo",
|
|
327
333
|
sapiens_brand: "Brand (Design System)",
|
|
334
|
+
sapiens_collection: "Collections",
|
|
328
335
|
sapiens_character: "Personagens",
|
|
329
336
|
sapiens_profile: "Perfil",
|
|
330
337
|
sapiens_sintetico: "Sintético & Sintonia",
|
package/dist/skills.js
CHANGED
|
@@ -922,6 +922,16 @@ Um carrossel é uma RECEITA: tema (paleta, folha e assinatura) mais slides, cada
|
|
|
922
922
|
|
|
923
923
|
O nome no topo, a marca no canto da capa e o @ do fecho saem de \`theme.signature\` (\`label\`, \`handle\`, \`logoUrl\`). Nasce da marca da pessoa, ou do nome dela quando não tem marca. Se ela pedir pra mudar, mude ali, no update. O @ é o dela no Instagram: não invente, pergunte.
|
|
924
924
|
|
|
925
|
+
## O brand da peça
|
|
926
|
+
|
|
927
|
+
O design system da pessoa veste a peça inteira: a cor (\`theme.brand\`), a fonte do título e do corpo (\`theme.fonts\`), a voz do texto e a assinatura. No \`generate\`, \`brandSlug\` escolhe um brand que não é o padrão da conta. A cor de DESTAQUE (kicker, número, palavra-chave) é a cor do brand que lê no fundo e se separa do texto; pra trocar, ponha o mesmo hex em \`accent\` e \`kicker\` no update.
|
|
928
|
+
|
|
929
|
+
Ela já tem identidade fora daqui (outro app, Figma, o kit de um cliente)? Traga com \`sapiens_brand action=import\`, que grava hex e fonte exatos e é grátis. O \`generate\` cria um brand novo e reinterpreta.
|
|
930
|
+
|
|
931
|
+
## Carrossel que vem de fora
|
|
932
|
+
|
|
933
|
+
Trazendo slides de outro editor: crie o carrossel, leia o catálogo no \`get\` e case cada slide de lá com um layout daqui pelo PAPEL (\`role\`: capa, corpo, cta), pela foto (\`requiresImage\`) e pelos \`slots\`. O texto vai nos slots, o estilo vem do brand, nunca do layout de lá. Slide sem par vira o corpo mais próximo: não force texto num slot que não cabe, corte ou divida em dois slides.
|
|
934
|
+
|
|
925
935
|
## A capa IA
|
|
926
936
|
|
|
927
937
|
\`action=cover\` gera uma capa 16:9 com a manchete QUEIMADA na imagem, um elemento vivo cruzando as letras, e com \`carouselId\` fatia em duas e põe na frente do carrossel, no lugar da capa IA anterior. Cobra pelo motor da capa (o preço sai de \`sapiens_image action=models\`). Confirme o custo com a pessoa antes. Animar a capa é no editor, não por aqui.
|
|
@@ -964,7 +974,34 @@ Baixe e leia ANTES de escrever o código. Num agente com pasta de skills, ele en
|
|
|
964
974
|
- Preço: o de cada modelo está em \`GET /models\`; vídeo cota de graça em \`POST /videos/quote\`. O valor de uma chamada pode variar um pouco com a oferta da hora. O valor de cada chamada volta no header \`x-sapiens-charge-usd\`. Pra pôr o preço da API ao lado de outro provedor (fal.ai, WaveSpeed): \`sapiens_meta action=precos\`, parte \`api\`.
|
|
965
975
|
- Vídeo cota de graça antes (\`POST /videos/quote\`), aceita \`webhook_url\` pra avisar quando termina e devolve a \`url\` do mp4 guardado. Falha do motor volta pro saldo sozinha.
|
|
966
976
|
- Saldo que acabou volta 402 \`insufficient_balance\` com \`recharge_url\` no erro: o código da pessoa deve mostrar esse link (recarga no cartão ou em stablecoin) em vez de só falhar.
|
|
967
|
-
- Uma chamada de teste antes de qualquer lote. Nunca vídeo em laço
|
|
977
|
+
- Uma chamada de teste antes de qualquer lote. Nunca vídeo em laço.
|
|
978
|
+
|
|
979
|
+
## Modelos de texto de preço variável: sapiens-auto e Jev Router
|
|
980
|
+
|
|
981
|
+
Dois modelos de texto que escolhem, a cada pedido, qual modelo responde, e cobram o preço dele. Não são de uso casual: o valor de cada chamada só se sabe depois, e o código da pessoa precisa respeitar isso. O preço está em \`sapiens_meta action=precos\` com \`midia=texto\`, parte \`api.texto.roteadores\` (a faixa do sapiens-auto, do candidato mais barato ao mais caro), e em \`GET /models\`.
|
|
982
|
+
|
|
983
|
+
### sapiens-auto (no POST /chat/completions)
|
|
984
|
+
|
|
985
|
+
- \`model: "sapiens-auto"\` e a casa escolhe o modelo de texto da conversa: código vai pra um modelo forte em código, prova e conta pesada pro topo, conversa e pergunta curta pra um modelo leve, o resto pra um do meio.
|
|
986
|
+
- Cobra o preço do modelo escolhido, sem taxa a mais. O \`model\` da resposta diz qual atendeu, e o \`x-sapiens-charge-usd\` diz quanto custou. A faixa de preço (do candidato mais barato ao mais caro) está na tabela.
|
|
987
|
+
- A escolha vale pra conversa inteira por 2 horas: as falas seguintes ficam no mesmo modelo, mesmo que fiquem triviais. A casa reconhece a conversa pelo começo dela, sem guardar o texto.
|
|
988
|
+
- Cuidado: quem joga no sapiens-auto texto que não escreveu (email, página, mensagem de terceiro) deixa esse texto puxar a conversa pra faixa de topo, que custa dezenas de vezes a leve. Pra agente que lê conteúdo de fora, ponha teto de gasto por dia na API key (Developers) ou fixe o modelo.
|
|
989
|
+
- Quem precisa de um modelo previsível (avaliação, lote com orçamento fechado) manda o id do modelo, não o sapiens-auto.
|
|
990
|
+
|
|
991
|
+
### Jev Router (no POST /chat/completions)
|
|
992
|
+
|
|
993
|
+
- \`model: "jev-router"\`: o router da TypeSafe escolhe o modelo e o esforço de raciocínio de cada pedido. O \`model\` da resposta é o que ele escolheu.
|
|
994
|
+
- Preço variável, o do modelo que ele escolher. Não dá pra cotar antes: leia o \`x-sapiens-charge-usd\` de cada chamada e, em lote, confira o gasto do dia em \`GET /balance\`.
|
|
995
|
+
|
|
996
|
+
## Decisão: o Laya (POST /systemone)
|
|
997
|
+
|
|
998
|
+
Um modelo de decisão responde sem gerar texto: sim ou não, uma opção entre várias ou uma nota numa escala, com a confiança de cada resposta. Serve pra triagem, moderação, filtro, roteamento e leitura de intenção, em volume e barato, antes ou no lugar de chamar um modelo de texto. O preço está em \`sapiens_meta action=precos\` com \`midia=decidir\`, parte \`api.decidir\`, e em \`GET /models\` (\`type: "decision"\`).
|
|
999
|
+
|
|
1000
|
+
- Formato System One (o mesmo do Jev): \`state\` (texto ou objeto) e \`questions\`, cada uma \`noul\` (sim ou não), \`choice\` (uma das opções de \`criteria\`) ou \`score\` (um nível de uma escala). Modelo: \`laya-multilingual\`, aberto, 100+ línguas.
|
|
1001
|
+
- Cobra só a entrada. Até 4 perguntas por chamada. Uma pergunta curta decide em cerca de meio segundo; o tempo cresce com o tamanho do \`state\`.
|
|
1002
|
+
- Uma decisão por vez na máquina: com fila cheia volta 503, sem cobrar. Lote vai em sequência, nunca em paralelo, e o código trata o 503 esperando e tentando de novo.
|
|
1003
|
+
- Sem ajuste fino, o Laya erra mais que um modelo de texto em classificação aberta, às vezes com confiança alta. Serve pra triagem barata e em volume. Antes de pôr no fluxo da pessoa, rode uma amostra do caso dela e confira as respostas.
|
|
1004
|
+
- Quem já usa o SDK do Jev troca a URL base pela da API e o modelo por \`laya-multilingual\`.`,
|
|
968
1005
|
},
|
|
969
1006
|
];
|
|
970
1007
|
export const SKILLS = Object.fromEntries(SKILL_LIST.map((s) => [s.name, s]));
|
package/dist/tools/brand.js
CHANGED
|
@@ -38,6 +38,17 @@ import { convexAction, convexQuery, convexMutation, getSessionToken, } from "../
|
|
|
38
38
|
* (character=null). As refs saem da galeria do personagem. De
|
|
39
39
|
* graça. É o que faz "gera na minha marca, com o meu personagem"
|
|
40
40
|
* funcionar sem anexar referência a cada geração.
|
|
41
|
+
* - import: TRANSCREVE um design system que já existe fora (Figma, outro app,
|
|
42
|
+
* o kit de um cliente): o que veio é o que grava, sem IA no meio,
|
|
43
|
+
* fonte e hex exatos. Grátis (card nasce em SVG). É a porta de
|
|
44
|
+
* quem já tem brand; generate é a de quem quer um.
|
|
45
|
+
* - logo: põe a logo no brand custom pelo ID da imagem na galeria do
|
|
46
|
+
* user (img_…, de sapiens_gallery action=upload), ou tira
|
|
47
|
+
* (handle=null). De graça.
|
|
48
|
+
* - favorite: marca (isFavorite=true, default) ou desmarca um brand custom do
|
|
49
|
+
* user como FAVORITO, o escolhido entre as tentativas de um
|
|
50
|
+
* projeto. Favorito sobe pro topo das listas dele (list, seletores
|
|
51
|
+
* do app). Oficial não se favorita. De graça.
|
|
41
52
|
*
|
|
42
53
|
* Auth: qualquer conta logada (sessionToken). As Sinapses saem do dono do token.
|
|
43
54
|
* NÃO mexe em brand custom de terceiro (só lê o estilo público via list_public e
|
|
@@ -57,11 +68,45 @@ export const brandSchema = z.object({
|
|
|
57
68
|
"list_public",
|
|
58
69
|
"adopt",
|
|
59
70
|
"persona",
|
|
71
|
+
"favorite",
|
|
72
|
+
"import",
|
|
73
|
+
"logo",
|
|
60
74
|
]),
|
|
61
75
|
slug: z
|
|
62
76
|
.string()
|
|
63
77
|
.optional()
|
|
64
|
-
.describe("Slug do brand. Obrigatório em get/refine/reroll/card/delete/set_visibility/adopt/persona. Pegue via action=list (seus) ou action=list_public (da comunidade)."),
|
|
78
|
+
.describe("Slug do brand. Obrigatório em get/refine/reroll/card/delete/set_visibility/adopt/persona/favorite/logo. Pegue via action=list (seus) ou action=list_public (da comunidade)."),
|
|
79
|
+
brand: z
|
|
80
|
+
.any()
|
|
81
|
+
.optional()
|
|
82
|
+
.describe("Pra action=import: o design system que JÁ EXISTE, no shape do Brand da casa (o mesmo que action=get devolve). " +
|
|
83
|
+
"{ name (obrigatório, ≤40), tagline?, description?, " +
|
|
84
|
+
"palette: { primary, secondary?, tertiary?, accent?, background, foreground, swatches?: [{name, hex}] }, " +
|
|
85
|
+
"typography: { headline, body, mono? } (cada uma a família como string, ou {family, note}), " +
|
|
86
|
+
"voice?: { rules (texto ou lista de regras), lexicon?, avoid?, sampleLines? }, " +
|
|
87
|
+
"imageStyle?: { styleBase (em inglês), negative?, backgroundSignature? } }. " +
|
|
88
|
+
"Cor em hex (#RRGGBB, aceita #RGB e sem #). Obrigatórios: name, background, foreground, primary (ou accent), headline e body. " +
|
|
89
|
+
"PAPÉIS DAS CORES na casa: primary é a cor da marca, accent é a que DESTACA a peça (kicker, número, palavra-chave). " +
|
|
90
|
+
"Se no sistema de origem o accent é só cor de botão, mande assim mesmo: o carrossel escolhe sozinho a cor que lê no fundo e se separa do texto, e o editor deixa trocar. " +
|
|
91
|
+
"Mapeie o que a origem chama por outro nome (brand/main → primary, bg/surface → background, text/ink → foreground, cta/highlight → accent). " +
|
|
92
|
+
"Fonte fora do Google Fonts grava como veio (fidelidade) e aparece na fonte da casa onde o navegador não acha a dela; o retorno avisa em `warnings`."),
|
|
93
|
+
source: z
|
|
94
|
+
.string()
|
|
95
|
+
.optional()
|
|
96
|
+
.describe("Pra action=import: de onde o brand veio (o nome do app, 'Figma', 'kit do cliente'). Vai pro briefing e pro aviso. ≤60 chars."),
|
|
97
|
+
handle: z
|
|
98
|
+
.string()
|
|
99
|
+
.nullable()
|
|
100
|
+
.optional()
|
|
101
|
+
.describe("Pra action=logo: o ID citável da imagem da logo na galeria do user (img_…). Suba antes com sapiens_gallery action=upload (PNG com fundo transparente é o ideal) e use o ID que voltar. null tira a logo."),
|
|
102
|
+
wordmark: z
|
|
103
|
+
.string()
|
|
104
|
+
.optional()
|
|
105
|
+
.describe("Pra action=logo: o nome escrito que acompanha a logo, quando ele difere do nome do brand (≤60 chars)."),
|
|
106
|
+
isFavorite: z
|
|
107
|
+
.boolean()
|
|
108
|
+
.optional()
|
|
109
|
+
.describe("Pra action=favorite: true (default) marca o brand como o favorito, o escolhido entre as tentativas; false tira. Só brand custom seu."),
|
|
65
110
|
character: z
|
|
66
111
|
.string()
|
|
67
112
|
.nullable()
|
|
@@ -82,7 +127,7 @@ export const brandSchema = z.object({
|
|
|
82
127
|
name: z
|
|
83
128
|
.string()
|
|
84
129
|
.optional()
|
|
85
|
-
.describe("Pra action=generate: nome
|
|
130
|
+
.describe("Pra action=generate: o nome do brand (opcional, ≤40 chars). Vem exatamente como mandado; sem ele o Gemini sugere um."),
|
|
86
131
|
fromSlug: z
|
|
87
132
|
.string()
|
|
88
133
|
.optional()
|
|
@@ -119,9 +164,29 @@ export async function brand(args) {
|
|
|
119
164
|
typography: b.typography ?? null,
|
|
120
165
|
cardUrl: b.card?.url ?? null,
|
|
121
166
|
persona: b.persona?.label ?? null,
|
|
167
|
+
// Ausente em servidor antigo: aí ninguém é favorito, e a lista segue igual.
|
|
168
|
+
isFavorite: b.isFavorite === true,
|
|
122
169
|
})),
|
|
123
|
-
note: "Oficiais (curados) + os seus custom
|
|
124
|
-
"de imagem): action=get slug=<slug>.
|
|
170
|
+
note: "Oficiais (curados) + os seus custom, os favoritos (isFavorite) primeiro. " +
|
|
171
|
+
"Pra detalhe completo (voz + estilo de imagem): action=get slug=<slug>. " +
|
|
172
|
+
"Pra criar um novo: action=generate. Pra marcar o escolhido: action=favorite.",
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
// -------- favorite: marca/desmarca o escolhido entre as tentativas --------
|
|
176
|
+
if (args.action === "favorite") {
|
|
177
|
+
if (!args.slug)
|
|
178
|
+
throw new Error("action=favorite exige slug de um brand custom SEU (pegue via action=list).");
|
|
179
|
+
const isFavorite = args.isFavorite ?? true;
|
|
180
|
+
const res = await convexMutation("brands:mcpSetBrandFavorite", {
|
|
181
|
+
sessionToken,
|
|
182
|
+
slug: args.slug.trim(),
|
|
183
|
+
isFavorite,
|
|
184
|
+
});
|
|
185
|
+
return {
|
|
186
|
+
...res,
|
|
187
|
+
note: isFavorite
|
|
188
|
+
? "Favorito marcado: ele sobe pro topo das suas listas e dos seletores do app. As outras tentativas ficam guardadas como estão. Pra juntar o projeto inteiro (o brand, as imagens, as notas de escolhida e tentativa), sapiens_collection."
|
|
189
|
+
: "Favorito tirado: o brand volta pra ordem de sempre.",
|
|
125
190
|
};
|
|
126
191
|
}
|
|
127
192
|
// -------- get: brand completo por slug (grátis) --------
|
|
@@ -179,6 +244,57 @@ export async function brand(args) {
|
|
|
179
244
|
" twist='mais bruto'.",
|
|
180
245
|
};
|
|
181
246
|
}
|
|
247
|
+
// -------- import: transcreve brand de fora, sem IA (grátis) --------
|
|
248
|
+
if (args.action === "import") {
|
|
249
|
+
let raw = args.brand;
|
|
250
|
+
// Cliente que serializa `any` manda string JSON.
|
|
251
|
+
if (typeof raw === "string") {
|
|
252
|
+
try {
|
|
253
|
+
raw = JSON.parse(raw);
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
throw new Error("action=import: `brand` chegou como texto que não é JSON. Mande o objeto do design system.");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (!raw || typeof raw !== "object") {
|
|
260
|
+
throw new Error("action=import exige `brand` com o design system que já existe (name, palette, typography; voz e imagem opcionais). " +
|
|
261
|
+
"Pra CRIAR um brand do zero a partir de uma descrição, o caminho é action=generate.");
|
|
262
|
+
}
|
|
263
|
+
const res = await convexAction("customBrandsActions:mcpImportBrand", {
|
|
264
|
+
sessionToken,
|
|
265
|
+
brand: raw,
|
|
266
|
+
source: args.source?.trim() || undefined,
|
|
267
|
+
});
|
|
268
|
+
return {
|
|
269
|
+
...res,
|
|
270
|
+
viewUrl: `${APP}/experimentos/brands`,
|
|
271
|
+
next: "Logo: suba a imagem com sapiens_gallery action=upload e rode action=logo slug=" +
|
|
272
|
+
res.slug +
|
|
273
|
+
" handle=img_…. Card premium (pago): action=card slug=" +
|
|
274
|
+
res.slug +
|
|
275
|
+
". Usar no carrossel: no Tema do editor, ou o carrossel gerado com esse brand como padrão da conta.",
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
// -------- logo: pelo ID da imagem na galeria do user (grátis) --------
|
|
279
|
+
if (args.action === "logo") {
|
|
280
|
+
if (!args.slug)
|
|
281
|
+
throw new Error("action=logo exige slug de um brand custom SEU (pegue via action=list).");
|
|
282
|
+
if (args.handle === undefined) {
|
|
283
|
+
throw new Error("action=logo exige handle=img_… (a imagem da logo na sua galeria; suba com sapiens_gallery action=upload) ou handle=null pra tirar.");
|
|
284
|
+
}
|
|
285
|
+
const res = await convexMutation("brands:mcpSetBrandLogo", {
|
|
286
|
+
sessionToken,
|
|
287
|
+
slug: args.slug.trim(),
|
|
288
|
+
handle: args.handle === null ? null : args.handle.trim(),
|
|
289
|
+
wordmark: args.wordmark?.trim() || undefined,
|
|
290
|
+
});
|
|
291
|
+
return {
|
|
292
|
+
...res,
|
|
293
|
+
note: res.logo
|
|
294
|
+
? "Logo no brand: ela assina o carrossel e entra nas imagens com brandMark=logo."
|
|
295
|
+
: "Logo tirada do brand.",
|
|
296
|
+
};
|
|
297
|
+
}
|
|
182
298
|
// -------- refine: ajusta brand custom existente (cobra Sinapses) --------
|
|
183
299
|
if (args.action === "refine") {
|
|
184
300
|
if (!args.slug)
|
package/dist/tools/carrossel.js
CHANGED
|
@@ -50,6 +50,10 @@ export const carrosselSchema = z.object({
|
|
|
50
50
|
.enum(["4:5", "9:16"])
|
|
51
51
|
.optional()
|
|
52
52
|
.describe("generate: o TAMANHO da folha. '4:5' (padrão, o feed do Instagram mostra inteira) ou '9:16' (tela cheia). Dá pra trocar depois no editor."),
|
|
53
|
+
brandSlug: z
|
|
54
|
+
.string()
|
|
55
|
+
.optional()
|
|
56
|
+
.describe("generate: o brand da peça (slug de sapiens_brand action=list), quando não é o padrão da conta. Veste a cor, a fonte, a voz e a assinatura dele. Sem isso vale o padrão da conta. Brand que a pessoa não pode usar cai no padrão."),
|
|
53
57
|
useSintetico: z
|
|
54
58
|
.boolean()
|
|
55
59
|
.optional()
|
|
@@ -66,7 +70,10 @@ export const carrosselSchema = z.object({
|
|
|
66
70
|
payload: z
|
|
67
71
|
.any()
|
|
68
72
|
.optional()
|
|
69
|
-
.describe("update: o payload INTEIRO, no shape do get: { theme: { palette, sheet, signature }, slides: [{ id, templateId, slots, image }], meta }. Preserve image dos slides que você não mexeu e o theme inteiro (folha e
|
|
73
|
+
.describe("update: o payload INTEIRO, no shape do get: { theme: { palette, sheet, signature, brand?, fonts? }, slides: [{ id, templateId, slots, image }], meta }. Preserve image dos slides que você não mexeu e o theme inteiro (folha, assinatura, cor e fonte do brand): o que faltar volta pro padrão. " +
|
|
74
|
+
"Na peça que veste um brand (palette 'brand'), theme.brand traz os tokens de cor (bg, text, accent, kicker em hex) e theme.fonts a tipografia ({ display: família do título, text: família do corpo }, sem fonts = a da casa). Pra trocar a cor de DESTAQUE, ponha o mesmo hex em accent e kicker. " +
|
|
75
|
+
"Carrossel que vem de FORA (outro app, outro editor): case cada slide de lá com um layout do catálogo do get pelo PAPEL (role: capa, corpo, cta), pela foto (requiresImage) e pelos slots, e ponha o texto nos slots daquele layout; o estilo (cor, fonte, logo) vem do brand, não do layout. Layout de fora sem par no catálogo vira o corpo mais próximo, nunca layout inventado. " +
|
|
76
|
+
"Foto NOVA não entra por URL aqui: use set_image (ID da peça) ou generate_image."),
|
|
70
77
|
title: z.string().optional().describe("update: novo título do carrossel (opcional)."),
|
|
71
78
|
baseUpdatedAt: z
|
|
72
79
|
.number()
|
|
@@ -131,6 +138,7 @@ export async function carrossel(args) {
|
|
|
131
138
|
...(args.userArticleId ? { sourceUserArticleId: args.userArticleId } : {}),
|
|
132
139
|
...(args.model ? { model: args.model } : {}),
|
|
133
140
|
...(args.aspect ? { aspect: args.aspect } : {}),
|
|
141
|
+
...(args.brandSlug?.trim() ? { brandSlug: args.brandSlug.trim() } : {}),
|
|
134
142
|
...(args.useSintetico !== undefined ? { useSintetico: args.useSintetico } : {}),
|
|
135
143
|
...(args.autoPickImages !== undefined ? { autoPickImages: args.autoPickImages } : {}),
|
|
136
144
|
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { convexQuery, convexMutation, getSessionToken } from "../convexClient.js";
|
|
3
|
+
/**
|
|
4
|
+
* Collections: o álbum generalizado do Acervo, operado pelo agente. Uma
|
|
5
|
+
* Collection junta as peças de um mesmo mundo (imagens, vídeos, o design
|
|
6
|
+
* system, o personagem, a música) e cada peça leva uma NOTA do dono. É o lugar
|
|
7
|
+
* do "escolhido entre as tentativas": as tentativas e a escolhida na mesma
|
|
8
|
+
* Collection, cada uma com a sua nota, a escolhida no topo.
|
|
9
|
+
*
|
|
10
|
+
* Sub-actions:
|
|
11
|
+
* - list: as Collections do user (rascunho e publicadas). Grátis.
|
|
12
|
+
* - get: uma Collection (por id ou slug) com as peças, na ordem, e as notas.
|
|
13
|
+
* - create: nasce uma Collection, rascunho e privada (title obrigatório).
|
|
14
|
+
* - update: troca nome e descrição.
|
|
15
|
+
* - add: põe uma peça (itemType + refId, com nota opcional). Peça que já
|
|
16
|
+
* está dentro e chega com nota tem a nota trocada.
|
|
17
|
+
* - remove: tira uma peça (a peça em si não é tocada).
|
|
18
|
+
* - note: troca a nota de uma peça que já está dentro (sem note, apaga).
|
|
19
|
+
* - reorder: nova ordem pelos refId, o primeiro no topo; os não citados vêm
|
|
20
|
+
* depois, na ordem em que estavam.
|
|
21
|
+
*
|
|
22
|
+
* Publicar a Collection e escolher capa são gestos do dono no Acervo, de
|
|
23
|
+
* propósito: Collection publicada não volta a rascunho.
|
|
24
|
+
*
|
|
25
|
+
* Auth: qualquer conta logada (sessionToken). Só mexe nas Collections e nas
|
|
26
|
+
* peças da própria conta.
|
|
27
|
+
*/
|
|
28
|
+
const APP = "https://sapiensinteticos.com";
|
|
29
|
+
export const collectionSchema = z.object({
|
|
30
|
+
action: z.enum(["list", "get", "create", "update", "add", "remove", "note", "reorder"]),
|
|
31
|
+
collection: z
|
|
32
|
+
.string()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("A Collection: o id OU o slug (o nome também serve, ele vira slug). Obrigatório em get/update/add/remove/note/reorder. As suas saem em action=list."),
|
|
35
|
+
title: z
|
|
36
|
+
.string()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Pra action=create (obrigatório) e update: o nome da Collection, até 80 caracteres."),
|
|
39
|
+
description: z
|
|
40
|
+
.string()
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("Pra action=create e update: uma linha sobre o mundo que a Collection junta, até 600 caracteres."),
|
|
43
|
+
itemType: z
|
|
44
|
+
.string()
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("Pra add/remove/note: o tipo da peça. image (imageId de sapiens_image/sapiens_gallery), video (o mesmo id da galeria), brand (o design system: aceita o SLUG, ex 'adult-blue-madrugada'), character (characterId), track (música), article, aula, carousel, comic, sticker, fita, app."),
|
|
47
|
+
refId: z
|
|
48
|
+
.string()
|
|
49
|
+
.optional()
|
|
50
|
+
.describe("Pra add/remove/note: o id da peça (no brand, o slug serve)."),
|
|
51
|
+
note: z
|
|
52
|
+
.string()
|
|
53
|
+
.optional()
|
|
54
|
+
.describe("Pra add e note: a nota do dono sobre a peça DENTRO desta Collection, até 240 caracteres. Pra marcar o escolhido de um projeto: 'escolhida' na que venceu e 'tentativa' nas outras."),
|
|
55
|
+
refIds: z
|
|
56
|
+
.array(z.string())
|
|
57
|
+
.optional()
|
|
58
|
+
.describe("Pra action=reorder: os refId na ordem nova, o primeiro vai pro topo. Basta citar os que mudam de lugar: 'põe a escolhida em primeiro' é mandar só o refId dela."),
|
|
59
|
+
});
|
|
60
|
+
function exigir(valor, mensagem) {
|
|
61
|
+
if (valor === undefined || valor === null || (typeof valor === "string" && !valor.trim())) {
|
|
62
|
+
throw new Error(mensagem);
|
|
63
|
+
}
|
|
64
|
+
return valor;
|
|
65
|
+
}
|
|
66
|
+
export async function collection(args) {
|
|
67
|
+
const sessionToken = getSessionToken();
|
|
68
|
+
const qual = () => exigir(args.collection, `action=${args.action} exige collection (o id ou o slug; as suas saem em action=list).`).trim();
|
|
69
|
+
const peca = () => ({
|
|
70
|
+
itemType: exigir(args.itemType, `action=${args.action} exige itemType (image, video, brand, character...).`).trim(),
|
|
71
|
+
refId: exigir(args.refId, `action=${args.action} exige refId (o id da peça; no brand, o slug serve).`).trim(),
|
|
72
|
+
});
|
|
73
|
+
if (args.action === "list") {
|
|
74
|
+
const cols = await convexQuery("collections:mcpListCollections", { sessionToken });
|
|
75
|
+
return {
|
|
76
|
+
count: Array.isArray(cols) ? cols.length : 0,
|
|
77
|
+
collections: cols,
|
|
78
|
+
note: "No site, as suas Collections moram no seu perfil, nas Criações privadas, aba Collections " +
|
|
79
|
+
`(${APP}/u/<seu @>?tab=private&criacoes=collections). As publicadas também aparecem na aba Collections do Acervo.`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (args.action === "get") {
|
|
83
|
+
return await convexQuery("collections:mcpGetCollection", { sessionToken, collection: qual() });
|
|
84
|
+
}
|
|
85
|
+
if (args.action === "create") {
|
|
86
|
+
const title = exigir(args.title, "action=create exige title (o nome da Collection).").trim();
|
|
87
|
+
const res = await convexMutation("collections:mcpCreateCollection", {
|
|
88
|
+
sessionToken,
|
|
89
|
+
title,
|
|
90
|
+
description: args.description?.trim() || undefined,
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
...res,
|
|
94
|
+
note: "Collection criada, rascunho e privada. Junte as peças com action=add collection=" +
|
|
95
|
+
res.slug +
|
|
96
|
+
". Publicar e escolher capa são no Acervo, pelo dono.",
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (args.action === "update") {
|
|
100
|
+
if (args.title === undefined && args.description === undefined) {
|
|
101
|
+
throw new Error("action=update exige title ou description.");
|
|
102
|
+
}
|
|
103
|
+
return await convexMutation("collections:mcpUpdateCollection", {
|
|
104
|
+
sessionToken,
|
|
105
|
+
collection: qual(),
|
|
106
|
+
title: args.title,
|
|
107
|
+
description: args.description,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
if (args.action === "add") {
|
|
111
|
+
return await convexMutation("collections:mcpAddCollectionItem", {
|
|
112
|
+
sessionToken,
|
|
113
|
+
collection: qual(),
|
|
114
|
+
...peca(),
|
|
115
|
+
note: args.note,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (args.action === "remove") {
|
|
119
|
+
return await convexMutation("collections:mcpRemoveCollectionItem", {
|
|
120
|
+
sessionToken,
|
|
121
|
+
collection: qual(),
|
|
122
|
+
...peca(),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (args.action === "note") {
|
|
126
|
+
return await convexMutation("collections:mcpSetCollectionItemNote", {
|
|
127
|
+
sessionToken,
|
|
128
|
+
collection: qual(),
|
|
129
|
+
...peca(),
|
|
130
|
+
note: args.note,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
if (args.action === "reorder") {
|
|
134
|
+
const refIds = exigir(args.refIds, "action=reorder exige refIds (a ordem nova, o primeiro no topo).");
|
|
135
|
+
return await convexMutation("collections:mcpReorderCollection", {
|
|
136
|
+
sessionToken,
|
|
137
|
+
collection: qual(),
|
|
138
|
+
refIds,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
package/dist/tools/meta.js
CHANGED
|
@@ -65,9 +65,9 @@ export const metaSchema = z.object({
|
|
|
65
65
|
.optional()
|
|
66
66
|
.describe("Código XXXX-XXXX gerado (logado) em sapiensinteticos.com/conectar-claude. Só pra action=login."),
|
|
67
67
|
midia: z
|
|
68
|
-
.enum(["imagem", "video", "musica", "voz", "texto"])
|
|
68
|
+
.enum(["imagem", "video", "musica", "voz", "texto", "decidir"])
|
|
69
69
|
.optional()
|
|
70
|
-
.describe("Só pra action=precos: recorta a tabela numa mídia. Sem ela vêm imagem, vídeo, música e voz (o texto da API é longo e só vem com midia=texto)."),
|
|
70
|
+
.describe("Só pra action=precos: recorta a tabela numa mídia. Sem ela vêm imagem, vídeo, música e voz (o texto da API é longo e só vem com midia=texto, com os modelos de preço variável em `texto.roteadores` e a parte `decidir`). midia=decidir traz só os modelos de decisão da API (o Laya): respondem sim ou não, uma opção ou uma nota, sem gerar texto."),
|
|
71
71
|
busca: z
|
|
72
72
|
.string()
|
|
73
73
|
.optional()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sapiens-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.94.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",
|