sapiens-mcp 1.74.0 → 1.76.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/prompts.js +1 -1
- package/dist/registry.js +3 -3
- package/dist/skills.js +6 -2
- package/dist/tools/character.js +22 -1
- package/dist/tools/distribution.js +68 -3
- package/dist/tools/gallery.js +41 -6
- package/dist/tools/repertorio.js +2 -2
- package/package.json +1 -1
package/dist/prompts.js
CHANGED
|
@@ -19,7 +19,7 @@ export const SAPIENS_PROMPTS = [
|
|
|
19
19
|
{
|
|
20
20
|
name: "capturar-repertorio",
|
|
21
21
|
title: "Capturar obra no Repertório",
|
|
22
|
-
description: "Grava um filme/série/anime/jogo/livro/música no seu acervo pessoal (grátis).",
|
|
22
|
+
description: "Grava um filme/série/anime/jogo/jogo de tabuleiro/livro/música no seu acervo pessoal (grátis).",
|
|
23
23
|
arguments: [
|
|
24
24
|
{
|
|
25
25
|
name: "obra",
|
package/dist/registry.js
CHANGED
|
@@ -71,7 +71,7 @@ export const TOOLS = {
|
|
|
71
71
|
handler: skill,
|
|
72
72
|
},
|
|
73
73
|
sapiens_repertorio: {
|
|
74
|
-
description: "Acervo pessoal de filme/série/anime/jogo/livro/música/pessoa (Repertório, o segundo cérebro do user). Reads: list (filtros mediaType/status), search (texto em title/genres/tags), get (detalhe), popArticles, resolve (busca capa/ano/id nos providers server-side: OMDb/IGDB-Twitch/AniList/Google Books/iTunes/Wikidata). Mutations (qualquer logado, mexem no PRÓPRIO acervo): add_item, update_item (status/rating/tags/note/isPublic), remove_item. CAPTURA ONE-SHOT travada na lista de providers: quando o user fala natural ('acabei de ver Duna 2, nota 9', 'tô jogando Hollow Knight', 'li tal livro'), (1) infira mediaType e status (assisti/zerei/li=completed, quero=backlog, tô jogando/vendo=active, dropei=dropped) e rating se citado; (2) chame action=resolve {mediaType, query}, escolha o candidato certo e faça add_item passando SÓ o source + externalId DELE + os campos pessoais (status/rating/tags/note). O servidor re-resolve no provider e grava título/capa/ano canônicos — você NÃO manda título/capa nem inventa externalId. (3) Se o resolve não achar (lista vazia/providerKeyMissing), NÃO dá pra adicionar: diga ao user que não encontrou nos providers (não fabrique entry manual). Upsert/dedup por (userId, source, externalId). Só pergunte se ambíguo entre candidatos. FERRAMENTAS DE IA (Repertório de Ferramentas): fluxo separado (vêm do catálogo aitag, não dos providers de mídia). action=search_tools {query} acha a ferramenta no catálogo e devolve o toolId; action=add_tool {toolId, favorite?, rating?, note?} grava como mediaType 'tool' (estar no acervo já é 'usei'; favorite=true liga a estrela). Use quando o user fala 'adiciona o Midjourney/Cursor no meu repertório de ferramentas' ou 'uso tal ferramenta de IA'. Cada ferramenta aponta pra página dela no aitag. SAPIENS (pessoas, ombros de gigantes; a aba no site chama Sapiens): mediaType 'person' cataloga gente que inspira (cineasta, mangaká, cientista, escritor) via Wikidata. Mesmo fluxo de mídia: resolve {mediaType:'person', query:'Osamu Tezuka'} → add_item com source 'wikidata' + o QID do candidato. O servidor grava foto, ocupações, bio e o link da Wikipedia; entra como completed (estar no acervo = me inspira). Use quando o user fala 'adiciona o Miyazaki nas minhas inspirações/nos meus sapiens/no meu repertório'.",
|
|
74
|
+
description: "Acervo pessoal de filme/série/anime/jogo/jogo de tabuleiro/livro/música/pessoa (Repertório, o segundo cérebro do user). Reads: list (filtros mediaType/status), search (texto em title/genres/tags), get (detalhe), popArticles, resolve (busca capa/ano/id nos providers server-side: OMDb/IGDB-Twitch/AniList/Google Books/iTunes/Wikidata). Mutations (qualquer logado, mexem no PRÓPRIO acervo): add_item, update_item (status/rating/tags/note/isPublic), remove_item. CAPTURA ONE-SHOT travada na lista de providers: quando o user fala natural ('acabei de ver Duna 2, nota 9', 'tô jogando Hollow Knight', 'li tal livro'), (1) infira mediaType e status (assisti/zerei/li=completed, quero=backlog, tô jogando/vendo=active, dropei=dropped) e rating se citado; (2) chame action=resolve {mediaType, query}, escolha o candidato certo e faça add_item passando SÓ o source + externalId DELE + os campos pessoais (status/rating/tags/note). O servidor re-resolve no provider e grava título/capa/ano canônicos — você NÃO manda título/capa nem inventa externalId. (3) Se o resolve não achar (lista vazia/providerKeyMissing), NÃO dá pra adicionar: diga ao user que não encontrou nos providers (não fabrique entry manual). Upsert/dedup por (userId, source, externalId). Só pergunte se ambíguo entre candidatos. FERRAMENTAS DE IA (Repertório de Ferramentas): fluxo separado (vêm do catálogo aitag, não dos providers de mídia). action=search_tools {query} acha a ferramenta no catálogo e devolve o toolId; action=add_tool {toolId, favorite?, rating?, note?} grava como mediaType 'tool' (estar no acervo já é 'usei'; favorite=true liga a estrela). Use quando o user fala 'adiciona o Midjourney/Cursor no meu repertório de ferramentas' ou 'uso tal ferramenta de IA'. Cada ferramenta aponta pra página dela no aitag. SAPIENS (pessoas, ombros de gigantes; a aba no site chama Sapiens): mediaType 'person' cataloga gente que inspira (cineasta, mangaká, cientista, escritor) via Wikidata. Mesmo fluxo de mídia: resolve {mediaType:'person', query:'Osamu Tezuka'} → add_item com source 'wikidata' + o QID do candidato. O servidor grava foto, ocupações, bio e o link da Wikipedia; entra como completed (estar no acervo = me inspira). Use quando o user fala 'adiciona o Miyazaki nas minhas inspirações/nos meus sapiens/no meu repertório'. JOGO DE TABULEIRO (e de cartas; a aba no site chama Tabuleiro): mediaType 'boardgame', porque 'game' é videogame e o IGDB não conhece tabuleiro. Também via Wikidata: resolve {mediaType:'boardgame', query:'Galactic Cruise'} → add_item com source 'wikidata' + o QID do candidato. Busque pelo título em inglês, que é como a Wikidata cadastra; o designer vem no campo authors do candidato e separa homônimos. O servidor grava designers, editoras e o link da página do jogo no BGG. Lançamento muito recente pode ainda não estar na Wikidata: resolve vazio = diga isso à pessoa, não fabrique. Use quando o user fala 'joguei Catan', 'quero jogar Rebirth', 'adiciona esse jogo de tabuleiro'.",
|
|
75
75
|
schema: repertorioSchema,
|
|
76
76
|
handler: repertorio,
|
|
77
77
|
},
|
|
@@ -146,7 +146,7 @@ export const TOOLS = {
|
|
|
146
146
|
handler: brand,
|
|
147
147
|
},
|
|
148
148
|
sapiens_character: {
|
|
149
|
-
description: "Personagens (character sheets) do Sapiens — a tabela `influencers`: personagem reutilizável com imagens (pra character-lock em geração) + alma (systemPrompt), tudo amarrado à conta do dono do token (sem admin). Sub-actions: list_public (catálogo global de personagens públicos do Explorar; cada um traz mainImageUrl/imageUrls usáveis direto como referenceImageUrls em sapiens_image; sem custo, sem login), get (detalhe de 1 por characterId — público+ativo qualquer um vê, draft/privado só o dono; systemPrompt só volta pro dono), list_mine (os personagens do próprio user, inclui drafts/privados), create (cria rascunho na conta: name + gender + opcional form/title/systemPrompt), add_image (adiciona imagem ao próprio personagem via imageUrl público OU sourceImageId da galeria; 1ª vira principal), set_card (edita alma/título/lema em inglês/nome/nome por extenso/forma do próprio), set_presence (escreve onde o personagem existe FORA da casa: redes sociais por handle, Fanvue, site e o email dele, este último privado), set_passport (escreve o PASSAPORTE dele: como ACERTAR este ser, que é outra coisa de quem ele é. Guarda o `descriptor` de geração em inglês pra colar verbatim, o `negative` travado que fica sempre ligado, os `locks` (o que TRAVA nele, um por entrada, que é a lista que vira as perguntas da conferência), as `refs` por PAPEL (proporção, rosto, tatuagem: ref sem papel ninguém sabe usar), as `recipes` medidas por tipo de peça (com o prompt VERBATIM, porque o valor está na palavra exata que funcionou) e o `discarded`, o que já foi recusado. Esse último não é enfeite: o motor traz de volta o que ninguém proibiu, então ideia descartada sem negativo escrito volta sozinha na leva seguinte. É MERGE por campo, então mandar só o negative não apaga o descritor, e campo vazio apaga. Só o DONO lê: receita de produção não sai em personagem público. Em action=get ele volta em duas formas, `passport` (campo a campo, o que se reedita) e `passportPrompt` (o bloco já montado pra colar). Preencha sempre que uma rodada medir algo que a próxima não deve redescobrir: é o que faz a receita viajar pra fora do repo), activate (tira do rascunho e libera o personagem pra USAR nas gerações do dono; exige ≥1 imagem. NÃO torna público: quem publica é set_visibility, e ativar é gesto reversível e privado), set_visibility (isPublic true=Explorar+slug / false=privado), set_hidden (hidden true tira o personagem da TELA do dono: some da lista de personagens e as peças geradas com ele saem da galeria de fotos, vídeos e rascunhos do dono, voltando pelo filtro Ocultos; hidden false devolve. É o gesto de quem vai compartilhar tela. Não despublica, não apaga, não sai dos seletores de geração, e personagem oculto não publica até ser mostrado. Grátis. O list_mine traz `hidden` em cada personagem). GESTÃO de imagem (por url, pegue as urls atuais em action=get campo imageUrls): remove_image (tira uma), set_main_image (define a principal), reorder_images (nova ordem via orderedUrls, posição 0=principal), e delete (apaga o personagem, permanente). FORMA (`form`, o que tipo de CORPO ele tem: human default, humanoid, animal (bicho real), creature (ser inventado), object, abstract): não é enfeite de cadastro, é o que a ficha, as figurinhas e o vídeo leem pra decidir se descrevem rosto e mãos ou silhueta e postura, e é ele que impede uma criatura de voltar desenhada como pessoa. Mande no create sempre que o personagem não for gente; personagem que já existe sem ele conserta com set_card form=creature. Ausente = human, que é o que a casa desenhava antes do campo existir. NOME E NOME POR EXTENSO (`name` e `fullName`): `name` é como o personagem é CHAMADO e é o que toda tela desenha; `fullName` é o nome inteiro, pra quem tem apelido ('Crow Girl' na tela, 'Kaia Crowe' por extenso). Um não substitui o outro, e o extenso não mexe no slug da página nem no cartão de nome do vídeo: batizar personagem que já existe não muda endereço publicado nem assinatura de peça que já saiu. Vem em action=get e list_mine no campo `fullName` (null = ainda sem nome por extenso). TRAÇO (`style`, em que TÉCNICA ele é feito: auto default, realista, anime, manhwa, 3d, custom): irmão da forma, e a divisão é limpa, a forma diz que CORPO é esse e o traço diz como ele é DESENHADO. As mesmas três peças leem daqui. No 'auto' a peça não afirma técnica nenhuma e as imagens de referência mandam; 'realista' é fotografia, 'anime' é cel shading com contorno de tinta, 'manhwa' é pintura de webtoon, '3d' é render com material e oclusão, 'custom' usa a linha escrita em `styleNote` (até 140 caracteres, ex: aquarela sobre papel texturizado). Mande sempre que o personagem tiver técnica definida: até ago/2026 a ficha cravava vocabulário de desenho e devolvia personagem fotográfico virado em ilustração. Conserto sem custo em quem já existe: set_card style=realista. PRESENÇA (set_presence, de graça): o ENDEREÇO do personagem, o que faltava pra ele existir fora da ficha. `socials` é {rede: handle} nas redes instagram, tiktok, youtube, x, threads, bluesky, twitch, spotify, fanvue, patreon, onlyfans, telegram; `websiteUrl` é o site dele e `email` é o email do personagem, o que cadastra e recupera as contas dele nas redes. As redes e o site são PÚBLICOS; o email é PRIVADO e fica só com o dono, mesmo com o personagem publicado, porque email é chave de conta e isca de spam, não canal de contato. Em action=get e list_public de personagem de OUTRA pessoa, o campo simplesmente não vem. É MERGE: mandar {fanvue:'helen'} não apaga o instagram, e pra apagar uma rede se manda ela vazia. O que volta em action=get são as duas formas, `presence` (o handle cru, que é o que se reedita) e `presenceLinks` (o link já montado, pra mostrar sem remontar URL na mão). Vale a pena preencher quando o personagem TEM conta de verdade: é o que separa uma ficha de um ser com endereço, e a página pública dele desenha as redes e o site (nunca o email). FICHA (generate_sheet, COBRA 450 Sinapses): desenha a página-pôster do personagem no traço das imagens que ele já tem (exige pelo menos uma), em duas orientações (arg `orientation`): 'portrait' (default) é a página de processo, pose, expressões, trocas de roupa e adereços soltos na folha; 'landscape' é a prancha larga, com a volta completa à esquerda, a figura grande no meio, poses à direita, estudos de silhueta, expressão e detalhe embaixo e um painel CHARACTER ID na ponta. A personalidade sai da alma (systemPrompt) e é ela que escolhe roupa e objeto, então personagem com alma escrita rende ficha melhor. Cada geração é um estilo NOVO e elas acumulam no personagem (campo sheetUrls em action=get), nenhuma apaga a anterior, e a ficha já entra na galeria dele como referência das próximas gerações. É geração síncrona: se voltar Timeout, cheque action=get antes de repetir, senão cobra duas vezes. STICKERS (generate_stickers, COBRA 550 Sinapses): desenha 5 stickers do personagem DE UMA VEZ. Uma folha só, em 2K, com as cinco figuras separadas sobre um fundo verde chroma, recortada por código em peças 512x512 transparentes abaixo de 100KB (o teto do WhatsApp). É por isso que sai o preço de UMA imagem em 2K e não de cinco: quem paga é a folha. As peças entram no pack do personagem (um por personagem, os lotes acumulam) e já ficam no picker de expressão do dono, no chat e no Fórum. Arg `moods`: até 5 humores do vocabulário comum por slug (kkkkk, amei, isso, hmm, chega, que, aff, bora, socorro, seinao, valeu, ainao, seila, ideia, calma, contatudo, perfeito, euavisei, naovourir, zzz, somaisum, sextou, merecido, quedia, tudobem); faltando, a casa completa com os mais usados. Arg `hint`: direcionamento curto do autor (roupa, adereço, clima), até 140 caracteres. Arg `stickerTier`: 'folha' (default) é esse lote de cinco, desenhado no Gemini 3.1 Flash; 'unica' (COBRA 900 Sinapses) desenha UM sticker por vez no Gemini 3 Pro, o motor mais fiel da casa, com a figura sozinha no quadro e o dobro de pixel por peça, e leva só o PRIMEIRO mood da lista. Pra encher o pack, folha; pra traço difícil ou a reação que vira a cara do personagem, a unica. Ao oferecer a escolha, diga o NOME DO MOTOR e o que ele troca: adjetivo vago de acabamento não informa nada a quem vai pagar. Os dois caem no MESMO pack e acumulam. Sem legenda queimada na imagem, de propósito: modelo erra acento em português, então o rótulo fica na row e serve de busca no picker. Exige pelo menos uma imagem no personagem (é dela que sai a cara) e é geração síncrona: se voltar Timeout, cheque sapiens_gallery antes de repetir. Quando a resposta vem com ok=false, a folha foi gerada e paga mas o corte falhou; ela está na galeria e o recorte de novo é de graça, pela web. Publicar o pack na vitrine e o carimbo da casa (que é o que põe no picker de todo mundo) são gestos da web, não desta tool. 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.",
|
|
149
|
+
description: "Personagens (character sheets) do Sapiens — a tabela `influencers`: personagem reutilizável com imagens (pra character-lock em geração) + alma (systemPrompt), tudo amarrado à conta do dono do token (sem admin). Sub-actions: list_public (catálogo global de personagens públicos do Explorar; cada um traz mainImageUrl/imageUrls usáveis direto como referenceImageUrls em sapiens_image; sem custo, sem login), get (detalhe de 1 por characterId — público+ativo qualquer um vê, draft/privado só o dono; systemPrompt só volta pro dono), list_mine (os personagens do próprio user, inclui drafts/privados), create (cria rascunho na conta: name + gender + opcional form/title/systemPrompt), add_image (adiciona imagem ao próprio personagem via imageUrl público OU sourceImageId da galeria; 1ª vira principal), set_card (edita alma/título/lema em inglês/nome/nome por extenso/forma do próprio), set_presence (escreve onde o personagem existe FORA da casa: redes sociais por handle, Fanvue, site e o email dele, este último privado), set_passport (escreve o PASSAPORTE dele: como ACERTAR este ser, que é outra coisa de quem ele é. Guarda o `descriptor` de geração em inglês pra colar verbatim, o `negative` travado que fica sempre ligado, os `locks` (o que TRAVA nele, um por entrada, que é a lista que vira as perguntas da conferência), as `refs` por PAPEL (proporção, rosto, tatuagem: ref sem papel ninguém sabe usar), as `recipes` medidas por tipo de peça (com o prompt VERBATIM, porque o valor está na palavra exata que funcionou) e o `discarded`, o que já foi recusado. Esse último não é enfeite: o motor traz de volta o que ninguém proibiu, então ideia descartada sem negativo escrito volta sozinha na leva seguinte. É MERGE por campo, então mandar só o negative não apaga o descritor, e campo vazio apaga. Só o DONO lê: receita de produção não sai em personagem público. Em action=get ele volta em duas formas, `passport` (campo a campo, o que se reedita) e `passportPrompt` (o bloco já montado pra colar). Preencha sempre que uma rodada medir algo que a próxima não deve redescobrir: é o que faz a receita viajar pra fora do repo), activate (tira do rascunho e libera o personagem pra USAR nas gerações do dono; exige ≥1 imagem. NÃO torna público: quem publica é set_visibility, e ativar é gesto reversível e privado), set_visibility (isPublic true=Explorar+slug / false=privado), set_hidden (hidden true tira o personagem da TELA do dono: some da lista de personagens e as peças geradas com ele saem da galeria de fotos, vídeos e rascunhos do dono, voltando pelo filtro Ocultos; hidden false devolve. É o gesto de quem vai compartilhar tela. Não despublica, não apaga, não sai dos seletores de geração, e personagem oculto não publica até ser mostrado. Grátis. O list_mine traz `hidden` em cada personagem). GESTÃO de imagem (por url, pegue as urls atuais em action=get campo imageUrls): remove_image (tira uma), set_main_image (define a principal), reorder_images (nova ordem via orderedUrls, posição 0=principal), e delete (apaga o personagem, permanente). FORMA (`form`, o que tipo de CORPO ele tem: human default, humanoid, animal (bicho real), creature (ser inventado), object, abstract): não é enfeite de cadastro, é o que a ficha, as figurinhas e o vídeo leem pra decidir se descrevem rosto e mãos ou silhueta e postura, e é ele que impede uma criatura de voltar desenhada como pessoa. Mande no create sempre que o personagem não for gente; personagem que já existe sem ele conserta com set_card form=creature. Ausente = human, que é o que a casa desenhava antes do campo existir. NOME E NOME POR EXTENSO (`name` e `fullName`): `name` é como o personagem é CHAMADO e é o que toda tela desenha; `fullName` é o nome inteiro, pra quem tem apelido ('Crow Girl' na tela, 'Kaia Crowe' por extenso). Um não substitui o outro, e o extenso não mexe no slug da página nem no cartão de nome do vídeo: batizar personagem que já existe não muda endereço publicado nem assinatura de peça que já saiu. Vem em action=get e list_mine no campo `fullName` (null = ainda sem nome por extenso). TRAÇO (`style`, em que TÉCNICA ele é feito: auto default, realista, anime, manhwa, 3d, custom): irmão da forma, e a divisão é limpa, a forma diz que CORPO é esse e o traço diz como ele é DESENHADO. As mesmas três peças leem daqui. No 'auto' a peça não afirma técnica nenhuma e as imagens de referência mandam; 'realista' é fotografia, 'anime' é cel shading com contorno de tinta, 'manhwa' é pintura de webtoon, '3d' é render com material e oclusão, 'custom' usa a linha escrita em `styleNote` (até 140 caracteres, ex: aquarela sobre papel texturizado). Mande sempre que o personagem tiver técnica definida: até ago/2026 a ficha cravava vocabulário de desenho e devolvia personagem fotográfico virado em ilustração. Conserto sem custo em quem já existe: set_card style=realista. PRESENÇA (set_presence, de graça): o ENDEREÇO do personagem, o que faltava pra ele existir fora da ficha. `socials` é {rede: handle} nas redes instagram, tiktok, youtube, x, threads, bluesky, twitch, spotify, fanvue, patreon, onlyfans, telegram; `websiteUrl` é o site dele e `email` é o email do personagem, o que cadastra e recupera as contas dele nas redes. As redes e o site são PÚBLICOS; o email é PRIVADO e fica só com o dono, mesmo com o personagem publicado, porque email é chave de conta e isca de spam, não canal de contato. Em action=get e list_public de personagem de OUTRA pessoa, o campo simplesmente não vem. É MERGE: mandar {fanvue:'helen'} não apaga o instagram, e pra apagar uma rede se manda ela vazia. O que volta em action=get são as duas formas, `presence` (o handle cru, que é o que se reedita) e `presenceLinks` (o link já montado, pra mostrar sem remontar URL na mão). Vale a pena preencher quando o personagem TEM conta de verdade: é o que separa uma ficha de um ser com endereço, e a página pública dele desenha as redes e o site (nunca o email). FICHA (generate_sheet, COBRA 450 Sinapses): desenha a página-pôster do personagem no traço das imagens que ele já tem (exige pelo menos uma), em duas orientações (arg `orientation`): 'portrait' (default) é a página de processo, pose, expressões, trocas de roupa e adereços soltos na folha; 'landscape' é a prancha larga, com a volta completa à esquerda, a figura grande no meio, poses à direita, estudos de silhueta, expressão e detalhe embaixo e um painel CHARACTER ID na ponta. A personalidade sai da alma (systemPrompt) e é ela que escolhe roupa e objeto, então personagem com alma escrita rende ficha melhor. Cada geração é um estilo NOVO e elas acumulam no personagem (campo sheetUrls em action=get), nenhuma apaga a anterior, e a ficha já entra na galeria dele como referência das próximas gerações. É geração síncrona: se voltar Timeout, cheque action=get antes de repetir, senão cobra duas vezes. STICKERS (generate_stickers, COBRA 550 Sinapses): desenha 5 stickers do personagem DE UMA VEZ. Uma folha só, em 2K, com as cinco figuras separadas sobre um fundo verde chroma, recortada por código em peças 512x512 transparentes abaixo de 100KB (o teto do WhatsApp). É por isso que sai o preço de UMA imagem em 2K e não de cinco: quem paga é a folha. As peças entram no pack do personagem (um por personagem, os lotes acumulam) e já ficam no picker de expressão do dono, no chat e no Fórum. Arg `moods`: até 5 humores do vocabulário comum por slug (kkkkk, amei, isso, hmm, chega, que, aff, bora, socorro, seinao, valeu, ainao, seila, ideia, calma, contatudo, perfeito, euavisei, naovourir, zzz, somaisum, sextou, merecido, quedia, tudobem); faltando, a casa completa com os mais usados. Arg `hint`: direcionamento curto do autor (roupa, adereço, clima), até 140 caracteres. Arg `stickerTier`: 'folha' (default) é esse lote de cinco, desenhado no Gemini 3.1 Flash; 'unica' (COBRA 900 Sinapses) desenha UM sticker por vez no Gemini 3 Pro, o motor mais fiel da casa, com a figura sozinha no quadro e o dobro de pixel por peça, e leva só o PRIMEIRO mood da lista. Pra encher o pack, folha; pra traço difícil ou a reação que vira a cara do personagem, a unica. Ao oferecer a escolha, diga o NOME DO MOTOR e o que ele troca: adjetivo vago de acabamento não informa nada a quem vai pagar. Os dois caem no MESMO pack e acumulam. Sem legenda queimada na imagem, de propósito: modelo erra acento em português, então o rótulo fica na row e serve de busca no picker. Exige pelo menos uma imagem no personagem (é dela que sai a cara) e é geração síncrona: se voltar Timeout, cheque sapiens_gallery antes de repetir. Quando a resposta vem com ok=false, a folha foi gerada e paga mas o corte falhou; ela está na galeria e o recorte de novo é de graça, pela web. Publicar o pack na vitrine e o carimbo da casa (que é o que põe no picker de todo mundo) são gestos da web, não desta tool. PLACAS (generate_plates, COBRA 550 Sinapses): seis ângulos do mesmo rosto numa geração só, no Gemini 3.1 Flash em 2K, recortados por código em seis referências com PAPEL: perfil, camera-alta, camera-baixa, frontal, cabeca-baixa e outro-lado, cada um com uma expressão diferente. Não é vitrine (isso é a ficha): é a referência feita pra máquina. Referência trava o ângulo da câmera, então quando a cena pede perfil, passe a placa de perfil em referenceImageUrls. As placas ficam FORA de imageUrls de propósito, porque placa de perfil como referência genérica puxa toda peça pro perfil; elas voltam em action=get no campo `plates` ([{role, url}], na ordem da grade). Cada lote substitui o anterior, porque é o kit vigente. Sai das fotos do personagem (exige pelo menos uma) e, quando ele tem passaporte, o descritor dele entra no pedido. Só personagem com rosto de gente (form human ou humanoid): pra bicho, criatura, objeto ou forma abstrata o servidor recusa antes de cobrar. O perfil puro e a câmera alta às vezes voltam amolecidos pro frontal; quando esses dois ângulos importarem, confira as placas antes de usar. Geração síncrona: se voltar Timeout, cheque action=get antes de repetir. Resposta com ok=false quer dizer grade gerada e paga com o corte falho; o recorte de novo é de graça, pela web. CHARACTER VIDEO (a ficha que anda): o personagem atravessa 6 mundos em 12s, trocando de roupa em cada um, com som gerado junto, e o último plano fecha a peça com ele parando e encarando a câmera. A peça volta FECHADA pelas duas portas (tela e agente), sem ninguém pedir: o personagem se apresenta no cartão de nome por cima do fim do último plano, com o LEMA em inglês embaixo (o campo `titleEn`, escrito uma vez em set_card), e só depois a casa assina. Quem não tem lema recebe o subtítulo que o roteiro inventou naquele take, e ele muda no take seguinte. O fecho é asset, não geração: zero Sinapse a mais, e é fail-open, então a resposta traz `closed` e `nameCard` dizendo o que entrou de verdade em vez de prometer um cartão que não está no arquivo. Pra colocar esse cartão num vídeo que JÁ existe, o caminho é a ficha do take na web (gaveta do fecho, 'Apresentação do personagem'), também sem custo. Exige FICHA gerada (é dela que saem a cara e o guarda-roupa), não só imagem. Duas actions, nesta ordem: plan_video (COBRA 50 Sinapses) devolve os 6 planos que a alma do personagem escolheu, com ambiente, ato, enquadramento e roupa. Cada plano vem nas duas línguas: em inglês (setting/act/wardrobe, que é o que vai pro motor) e em português (settingPt/actPt/wardrobePt). MOSTRE a versão em português pro autor, o público da casa é brasileiro e roteiro que ele lê de través não é roteiro aprovado; peça de novo quantas vezes ele quiser, porque trocar sai por 50 e o take errado sai por milhares. replan_shot (COBRA 15 Sinapses) reescreve UM plano e não toca nos outros cinco: os seis são cenas independentes, então quando o autor gosta de quatro e implica com um, troque só aquele em vez de sortear tudo de novo (mande os 6 planos em `shots`, o número em `shotIndex`, e o que ele quer diferente em `shotHint`; volta só o plano trocado, e é você que remonta o roteiro). Depois generate_video (COBRA) renderiza, com os planos aprovados no arg `shots` e o MOTOR no arg `videoTier`: 'draft' (5.400 Sinapses) é o Seedance 2.0 Mini, metade do preço, que tropeça em mão, pouca luz e câmera rápida, e 'final' (8.640 Sinapses) é o Seedance 2.0 Fast, que segura o que o Mini erra. A peça é a MESMA nos dois (mesmos 6 planos, 12s, 720p, com som), então ofereça pelo nome do motor e pelo que ele troca, nunca por adjetivo vago de acabamento. Vertical ou horizontal pelo arg `orientation`. Chamar generate_video SEM `shots` funciona, mas escreve um roteiro novo às cegas e cobra igual: não faça isso sem o autor ter visto o que vai receber. É geração demorada: se voltar Timeout, cheque sapiens_gallery antes de repetir. Resposta com `pending: true` (sem `url`) quer dizer ACEITO E COBRADO: o servidor continua renderizando sozinho (até 25 min) e a peça chega na ficha já fechada; NÃO repita a chamada, isso cobraria de novo, confira depois em sapiens_gallery. Fluxo de criação: create → add_image (1+) → set_card (opcional) → activate. PARE AÍ. O personagem nasce e continua PRIVADO, e é assim que ele serve pro dono: dá pra gerar, referenciar e iterar sem ninguém ver. set_visibility isPublic=true é o gesto de PUBLICAR no Explorar, com slug público, e nÃO é passo de fluxo: só chame quando o dono pedir pra publicar aquele personagem, com essas palavras. Na dúvida, não publique e pergunte. Pra usar um personagem público como referência numa geração, pegue mainImageUrl em list_public/get e passe em sapiens_image referenceImageUrls.",
|
|
150
150
|
schema: characterSchema,
|
|
151
151
|
handler: character,
|
|
152
152
|
},
|
|
@@ -206,7 +206,7 @@ export const TOOLS = {
|
|
|
206
206
|
handler: trilhas,
|
|
207
207
|
},
|
|
208
208
|
sapiens_distribution: {
|
|
209
|
-
description: "Despachar peças (Distribution Workflow) — a peça pronta entra na FILA com canal, copy e a aba de quem ela é. ADMIN-ONLY: despacho depende de rede conectada e hoje só a casa tem. NADA aqui posta em rede nenhuma; quem despacha é a tela em /experimentos/distribution-workflow, que é onde a conta conectada mora. A ABA (`voice`) responde de quem é a peça: sapiens (a casa), helen-ailith, borderless, ou o slug de um personagem seu — slug livre, então personagem novo ganha aba sozinho na primeira peça dele, sem deploy. Em queue, aba ausente = o servidor deriva do personagem da peça e cai em sapiens quando não há. Sub-actions: queue (põe na fila: title e channel obrigatórios, mais copy/hashtags/assetUrl/notes). Em queue, DOIS campos que parecem opcionais e não são: `lang` diz em que língua a copy já está, e sem ele canal de rede internacional reescreve a sua copy sozinho, dois segundos depois, na voz da casa; `assetPageAssetId` é o imageId da obra da casa, e sem ele a peça só encontra a obra se a assetUrl bater letra a letra, então versão web de um master nasce órfã de ficha, list (o que está na fila, filtrável por status e por aba), voices (as abas que existem hoje com a contagem de cada uma), status (move a peça de lane: fila, agendado, postado, descartado; postado aceita postUrl e fecha o rastro, agendado aceita scheduledFor em ms), bio (lê ou troca a BIO de uma conta da casa no Bluesky, channel bluesky ou bluesky-2 a bluesky-5: sem texto só lê; bioLine acrescenta uma linha no fim sem mexer no resto e não repete linha que já está lá; bio reescreve a inteira; teto de 256; ESTA sub-action muda o perfil público na hora, e bio não aceita hiperlink, o endereço aparece escrito), lineages (o CADASTRO das linhagens, o mesmo da aba Teia: de cada personagem, produto e da casa, a mãe no Bluesky onde a peça nasce, as contas próprias, as galerias que a mãe alimenta sozinha, as fichas e o post do dia, mais as regras que a teia desdobra e as vagas de conta ainda sem dona), lineage-new (nasce uma linhagem: nome obrigatório, grupo, artigo, lang; nasce sem mãe, então nada viaja sozinho até o lineage-save dar a mãe), lineage-save (slug e só o que muda: mae, contas rede->canal, galerias, fichaIds, rotina, contaNaCasa; o servidor recusa conta que já é de outra linhagem e canal de outra rede, e a vaga assumida ganha o nome da linhagem), lineage-archive (slug + arquivada). Sem custo em Sinapses: isto organiza, não gera. A copy segue a voz de QUEM ASSINA a peça, que não é sempre a voz da casa: peça da Helen fala como a Helen. E a peça sai pela CONTA de quem assina ela: conta extra da mesma rede é um canal próprio (bluesky-2 é outra conta no Bluesky, instagram-pro outra no Instagram), então case o canal com a aba antes de despachar.",
|
|
209
|
+
description: "Despachar peças (Distribution Workflow) — a peça pronta entra na FILA com canal, copy e a aba de quem ela é. ADMIN-ONLY: despacho depende de rede conectada e hoje só a casa tem. NADA aqui posta em rede nenhuma; quem despacha é a tela em /experimentos/distribution-workflow, que é onde a conta conectada mora. A ABA (`voice`) responde de quem é a peça: sapiens (a casa), helen-ailith, borderless, ou o slug de um personagem seu — slug livre, então personagem novo ganha aba sozinho na primeira peça dele, sem deploy. Em queue, aba ausente = o servidor deriva do personagem da peça e cai em sapiens quando não há. Sub-actions: queue (põe na fila: title e channel obrigatórios, mais copy/hashtags/assetUrl/notes). Em queue, DOIS campos que parecem opcionais e não são: `lang` diz em que língua a copy já está, e sem ele canal de rede internacional reescreve a sua copy sozinho, dois segundos depois, na voz da casa; `assetPageAssetId` é o imageId da obra da casa, e sem ele a peça só encontra a obra se a assetUrl bater letra a letra, então versão web de um master nasce órfã de ficha, list (o que está na fila, filtrável por status e por aba), voices (as abas que existem hoje com a contagem de cada uma), status (move a peça de lane: fila, agendado, postado, descartado; postado aceita postUrl e fecha o rastro, agendado aceita scheduledFor em ms), sign (liga ou desliga a ASSINATURA do vídeo da peça: sint.fyi/<dono> no canto e o fecho de 2s, o mesmo carimbo do download; itemId, e signed com true de default; só vídeo do CDN da casa; o servidor recusa peça +18 e praça adulta; o carimbo leva uns 30s e até lá a peça não sai; em queue, signed:true já enfileira assinada), bio (lê ou troca a BIO de uma conta da casa no Bluesky, channel bluesky ou bluesky-2 a bluesky-5: sem texto só lê; bioLine acrescenta uma linha no fim sem mexer no resto e não repete linha que já está lá; bio reescreve a inteira; teto de 256; ESTA sub-action muda o perfil público na hora, e bio não aceita hiperlink, o endereço aparece escrito), lineages (o CADASTRO das linhagens, o mesmo da aba Teia: de cada personagem, produto e da casa, a mãe no Bluesky onde a peça nasce, as contas próprias, as galerias que a mãe alimenta sozinha, as fichas e o post do dia, mais as regras que a teia desdobra e as vagas de conta ainda sem dona), lineage-new (nasce uma linhagem: nome obrigatório, grupo, artigo, lang; nasce sem mãe, então nada viaja sozinho até o lineage-save dar a mãe), lineage-save (slug e só o que muda: mae, contas rede->canal, galerias, fichaIds, rotina, contaNaCasa; o servidor recusa conta que já é de outra linhagem e canal de outra rede, e a vaga assumida ganha o nome da linhagem), lineage-archive (slug + arquivada). Sem custo em Sinapses: isto organiza, não gera. A copy segue a voz de QUEM ASSINA a peça, que não é sempre a voz da casa: peça da Helen fala como a Helen. E a peça sai pela CONTA de quem assina ela: conta extra da mesma rede é um canal próprio (bluesky-2 é outra conta no Bluesky, instagram-pro outra no Instagram), então case o canal com a aba antes de despachar.",
|
|
210
210
|
schema: distributionSchema,
|
|
211
211
|
handler: distribution,
|
|
212
212
|
},
|
package/dist/skills.js
CHANGED
|
@@ -137,7 +137,7 @@ Condensa a informação num framework com nome, ancora numa analogia concreta, e
|
|
|
137
137
|
{
|
|
138
138
|
name: "repertorio",
|
|
139
139
|
title: "Repertório: guardar obra e ferramenta no acervo",
|
|
140
|
-
description: "Guardar filme, série, anime, jogo, livro, música, pessoa ou ferramenta de IA no Repertório. Puxe ANTES de qualquer sapiens_repertorio: a gravação é travada num fluxo de dois passos e tentar guardar direto falha sempre.",
|
|
140
|
+
description: "Guardar filme, série, anime, jogo, jogo de tabuleiro, livro, música, pessoa ou ferramenta de IA no Repertório. Puxe ANTES de qualquer sapiens_repertorio: a gravação é travada num fluxo de dois passos e tentar guardar direto falha sempre.",
|
|
141
141
|
body: `## A regra que faz este fluxo falhar
|
|
142
142
|
|
|
143
143
|
Você NÃO grava uma obra passando o título. O servidor não aceita metadado vindo de você (título, capa, ano) de propósito: é anti-fabricação. Você identifica a obra num provider, e o servidor re-resolve por id e grava o canônico.
|
|
@@ -150,9 +150,11 @@ São sempre DOIS passos. Pular o primeiro falha sempre, e é o erro número um d
|
|
|
150
150
|
2. Escolha o candidato certo. Em dúvida entre dois, PERGUNTE em vez de chutar: obra errada no acervo é a pessoa quem vai ter que apagar.
|
|
151
151
|
3. **\`action=add_item\`** com o \`source\` e o \`externalId\` **exatos** daquele candidato, mais o que é pessoal: \`status\`, \`rating\` (0-10), \`tags\`, \`note\`, \`containsSpoilers\`, \`isPublic\`.
|
|
152
152
|
|
|
153
|
-
\`mediaType\`: movie, series, anime, game, book, music, tool, person.
|
|
153
|
+
\`mediaType\`: movie, series, anime, game, book, music, tool, person, boardgame.
|
|
154
154
|
\`status\`: backlog (quer ver), active (vendo agora), completed (viu), paused, dropped.
|
|
155
155
|
|
|
156
|
+
Jogo de tabuleiro (e de cartas) é \`boardgame\`: \`game\` é videogame, e o provider dele não conhece tabuleiro. A busca do tabuleiro é na Wikidata e acha pelo título em inglês; o designer vem em \`authors\` no candidato e separa homônimos. Lançamento muito recente pode ainda não estar cadastrado: resolve vazio quer dizer que o jogo não está na base, então diga isso à pessoa.
|
|
157
|
+
|
|
156
158
|
Nunca invente um \`externalId\` nem reaproveite um de outra busca: id que não resolve no provider é rejeitado. A dedup é por (você, source, externalId), então repetir o mesmo par não duplica.
|
|
157
159
|
|
|
158
160
|
## Guardar uma ferramenta de IA
|
|
@@ -429,6 +431,8 @@ Redes aceitas: instagram, tiktok, youtube, x, threads, bluesky, twitch, spotify,
|
|
|
429
431
|
|
|
430
432
|
A partir daí ele é personagem reutilizável: entra como referência nas gerações daqui (\`referenceImageUrls\` em \`sapiens_image\`), rende ficha oficial desenhada no traço dele (\`generate_sheet\`) e aparece no perfil. Personagem que fica solto na galeria vira imagem bonita e some.
|
|
431
433
|
|
|
434
|
+
**As placas são a referência de ÂNGULO.** Referência trava a posição da câmera: com foto de frente, o motor devolve rosto de frente por mais que o texto peça perfil. \`sapiens_character action=generate_plates characterId="<id>"\` gera seis ângulos do mesmo rosto numa geração só (perfil, camera-alta, camera-baixa, frontal, cabeca-baixa, outro-lado), cada um com uma expressão, e grava as seis no personagem com o papel de cada uma. Elas voltam em \`action=get\` no campo \`plates\` e NÃO entram em \`imageUrls\`: quando a cena pede perfil, passe a placa de perfil em \`referenceImageUrls\`; no resto, as fotos de sempre. Só pra personagem com rosto de gente (form human ou humanoid), e cada lote substitui o anterior.
|
|
435
|
+
|
|
432
436
|
Duas notas honestas: a imagem adicionada por link continua morando no servidor de onde veio, então quem quiser os bytes guardados aqui gera uma peça a partir dela; e trazer arquivo de fora direto pra galeria (\`sapiens_gallery\` upload/ingest) é porta de admin hoje, então em conta comum isso recusa e o caminho é o de cima.
|
|
433
437
|
|
|
434
438
|
## Fronteira
|
package/dist/tools/character.js
CHANGED
|
@@ -36,6 +36,12 @@ import { getClientName } from "../clientIdentity.js";
|
|
|
36
36
|
* - generate_sheet: desenha a ficha do personagem (COBRA), vertical ou
|
|
37
37
|
* horizontal (`orientation`). A ficha entra na galeria dele e
|
|
38
38
|
* já serve de referência; elas acumulam.
|
|
39
|
+
* - generate_plates: as PLACAS de ângulo (COBRA): uma grade 2K com seis
|
|
40
|
+
* ângulos do mesmo rosto, recortada em seis referências com
|
|
41
|
+
* papel (perfil, camera-alta, camera-baixa, frontal,
|
|
42
|
+
* cabeca-baixa, outro-lado). Voltam em action=get no campo
|
|
43
|
+
* `plates`; cada lote substitui o anterior. Só personagem com
|
|
44
|
+
* rosto de gente (form human ou humanoid).
|
|
39
45
|
* - generate_stickers: desenha stickers do personagem (COBRA), em dois
|
|
40
46
|
* caminhos (`stickerTier`): 'folha' (default) traz 5 de uma
|
|
41
47
|
* vez no Gemini 3.1 Flash, 'unica' desenha 1 no Gemini 3 Pro.
|
|
@@ -81,6 +87,7 @@ export const characterSchema = z.object({
|
|
|
81
87
|
"set_main_image",
|
|
82
88
|
"reorder_images",
|
|
83
89
|
"generate_sheet",
|
|
90
|
+
"generate_plates",
|
|
84
91
|
"generate_stickers",
|
|
85
92
|
"plan_video",
|
|
86
93
|
"replan_shot",
|
|
@@ -222,7 +229,7 @@ export const characterSchema = z.object({
|
|
|
222
229
|
"é ignorada em silêncio."),
|
|
223
230
|
imageUrl: httpUrl()
|
|
224
231
|
.optional()
|
|
225
|
-
.describe("Pra add_image: URL pública da imagem (Bunny CDN / Convex storage). Use a `url` que sapiens_image/sapiens_gallery devolvem. Pra remove_image/set_main_image: a url da imagem JÁ no personagem (pegue via action=get, campo imageUrls, ou sheetUrls pra apagar uma ficha)."),
|
|
232
|
+
.describe("Pra add_image: URL pública da imagem (Bunny CDN / Convex storage). Use a `url` que sapiens_image/sapiens_gallery devolvem. Pra remove_image/set_main_image: a url da imagem JÁ no personagem (pegue via action=get, campo imageUrls, ou sheetUrls pra apagar uma ficha, ou plates[].url pra apagar uma placa)."),
|
|
226
233
|
orderedUrls: z
|
|
227
234
|
.array(z.string())
|
|
228
235
|
.optional()
|
|
@@ -530,6 +537,20 @@ export async function character(args) {
|
|
|
530
537
|
orientation: args.orientation,
|
|
531
538
|
});
|
|
532
539
|
}
|
|
540
|
+
// -------- generate_plates: seis ângulos do mesmo rosto --------
|
|
541
|
+
// Geração SÍNCRONA que COBRA: cai na regra do timeout. Se voltar Timeout, NÃO
|
|
542
|
+
// repita às cegas: confira em action=get (campo plates) se as placas caíram.
|
|
543
|
+
// Quando o corte falha depois da geração, a resposta volta com ok=false e o
|
|
544
|
+
// gridImageId da grade paga; o recorte de novo é grátis e mora na web.
|
|
545
|
+
if (args.action === "generate_plates") {
|
|
546
|
+
if (!args.characterId)
|
|
547
|
+
throw new Error("action=generate_plates exige characterId.");
|
|
548
|
+
const sessionToken = getSessionToken();
|
|
549
|
+
return await convexAction("characterPlatesActions:mcpGenerateCharacterPlates", {
|
|
550
|
+
sessionToken,
|
|
551
|
+
influencerId: args.characterId,
|
|
552
|
+
});
|
|
553
|
+
}
|
|
533
554
|
// -------- generate_stickers: o lote de 5, ou 1 no motor mais fiel --------
|
|
534
555
|
// Geração SÍNCRONA que COBRA: cai na regra do timeout. Se voltar Timeout, NÃO
|
|
535
556
|
// repita às cegas: a folha pode ter saído (confira em sapiens_gallery).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { convexQuery, convexMutation, convexAction, getSessionToken } from "../convexClient.js";
|
|
2
|
+
import { convexQuery, convexMutation, convexAction, describeConvexError, getSessionToken, } from "../convexClient.js";
|
|
3
3
|
/**
|
|
4
4
|
* sapiens_distribution: o Despachar peças pelo agente.
|
|
5
5
|
*
|
|
@@ -22,6 +22,9 @@ import { convexQuery, convexMutation, convexAction, getSessionToken } from "../c
|
|
|
22
22
|
* - list: o que está na fila, filtrável por status e por aba.
|
|
23
23
|
* - voices: as abas que existem hoje, com a contagem de cada uma.
|
|
24
24
|
* - status: move a peça de lane (fila, agendado, postado, descartado).
|
|
25
|
+
* - sign: liga ou desliga a ASSINATURA do vídeo da peça (sint.fyi no canto
|
|
26
|
+
* e o fecho de 2s, o carimbo do download). A mesma chave da ficha, com as
|
|
27
|
+
* mesmas travas no servidor. Em queue, `signed: true` já enfileira assinada.
|
|
25
28
|
* - lineages / lineage-new / lineage-save / lineage-archive: o CADASTRO das
|
|
26
29
|
* linhagens (personagens, produtos, a casa), o mesmo que a aba Teia edita:
|
|
27
30
|
* a mãe no Bluesky, as contas próprias, as galerias que a mãe alimenta, as
|
|
@@ -129,6 +132,7 @@ export const distributionSchema = z.object({
|
|
|
129
132
|
"list",
|
|
130
133
|
"voices",
|
|
131
134
|
"status",
|
|
135
|
+
"sign",
|
|
132
136
|
"bio",
|
|
133
137
|
"lineages",
|
|
134
138
|
"lineage-new",
|
|
@@ -193,7 +197,11 @@ export const distributionSchema = z.object({
|
|
|
193
197
|
.optional()
|
|
194
198
|
.describe("queue, só canais fanvue e fanvue-dm: preço em CENTAVOS de dólar (pay-per-view). 1200 = $12. Piso de 300 na API deles, e peça paga EXIGE mídia. Ausente ou 0 = de graça pra quem já tem acesso."),
|
|
195
199
|
notes: z.string().optional().describe("queue: recado pra você mesmo na hora de postar."),
|
|
196
|
-
|
|
200
|
+
signed: z
|
|
201
|
+
.boolean()
|
|
202
|
+
.optional()
|
|
203
|
+
.describe("sign: true liga (o default), false desliga. queue: true enfileira a peça já com a assinatura ligada. A ASSINATURA carimba o VÍDEO da peça com sint.fyi/<dono> no canto e o fecho de 2 segundos no fim (o vídeo fica 2s mais longo), o mesmo carimbo do download. Só vale pra vídeo do CDN da casa (o que sapiens_video/sapiens_gallery devolvem); imagem sai como veio. O servidor RECUSA em peça +18 e em praça com teto adulto (fanvue, fanvue-dm, pornhub, civitai-red), porque sint.fyi é endereço da casa. O carimbo leva uns 30s na primeira vez (na hora quando o vídeo já foi assinado antes), e enquanto ele não fica pronto a peça NÃO sai: o botão de postar da tela devolve o motivo."),
|
|
204
|
+
itemId: z.string().optional().describe("status/sign: o id da peça (vem do list ou do queue)."),
|
|
197
205
|
status: z
|
|
198
206
|
.enum(["fila", "agendado", "postado", "descartado"])
|
|
199
207
|
.optional()
|
|
@@ -270,6 +278,25 @@ export const distributionSchema = z.object({
|
|
|
270
278
|
.optional()
|
|
271
279
|
.describe("lineage-archive: true arquiva, false devolve. A casa e o dono não se arquivam."),
|
|
272
280
|
});
|
|
281
|
+
/** O recado que volta pro agente depois de ligar ou desligar a assinatura. */
|
|
282
|
+
function recadoDaAssinatura(signed, r) {
|
|
283
|
+
if (!signed)
|
|
284
|
+
return "Assinatura desligada: o vídeo sai como veio do gerador, sem marca.";
|
|
285
|
+
const fila = r.videos - r.prontos - r.faltam;
|
|
286
|
+
const partes = [
|
|
287
|
+
"Assinatura ligada: o vídeo vai ao ar com sint.fyi/<dono> no canto e o fecho de 2s no fim.",
|
|
288
|
+
r.prontos === r.videos
|
|
289
|
+
? "O carimbo já estava pronto (o vídeo tinha sido assinado antes)."
|
|
290
|
+
: fila > 0
|
|
291
|
+
? `${fila} vídeo(s) na fila do carimbo, uns 30 segundos. Até lá a peça NÃO sai: o botão de postar da tela devolve o motivo.`
|
|
292
|
+
: "",
|
|
293
|
+
r.faltam > 0
|
|
294
|
+
? `A fila carimba 6 vídeos por minuto e ${r.faltam} ficaram pra depois: chame action=sign de novo daqui a um minuto (os que já entraram estão em cache).`
|
|
295
|
+
: "",
|
|
296
|
+
"A peça continua na fila do Distribution: quem posta é a tela.",
|
|
297
|
+
];
|
|
298
|
+
return partes.filter(Boolean).join(" ");
|
|
299
|
+
}
|
|
273
300
|
export async function distribution(args) {
|
|
274
301
|
const sessionToken = getSessionToken();
|
|
275
302
|
if (args.action === "queue") {
|
|
@@ -295,12 +322,50 @@ export async function distribution(args) {
|
|
|
295
322
|
priceCents: args.priceCents,
|
|
296
323
|
notes: args.notes,
|
|
297
324
|
});
|
|
298
|
-
|
|
325
|
+
const fila = {
|
|
299
326
|
itemId: r.itemId,
|
|
300
327
|
status: "fila",
|
|
301
328
|
voice: args.voice ?? "(derivado no servidor)",
|
|
302
329
|
instruction: "A peça está na fila do Distribution Workflow, não foi ao ar. Quem posta é a tela, em /experimentos/distribution-workflow.",
|
|
303
330
|
};
|
|
331
|
+
if (!args.signed)
|
|
332
|
+
return fila;
|
|
333
|
+
// A assinatura é um segundo passo de propósito: a peça JÁ está na fila, e
|
|
334
|
+
// recusa aqui (peça +18, praça adulta, sem vídeo da casa) não pode desfazer
|
|
335
|
+
// o enfileiramento. Volta o motivo e a peça fica sem marca.
|
|
336
|
+
try {
|
|
337
|
+
const a = await convexMutation("distributionMcp:mcpAssinarPeca", {
|
|
338
|
+
sessionToken,
|
|
339
|
+
itemId: r.itemId,
|
|
340
|
+
signed: true,
|
|
341
|
+
});
|
|
342
|
+
return {
|
|
343
|
+
...fila,
|
|
344
|
+
signed: true,
|
|
345
|
+
assinatura: a,
|
|
346
|
+
instruction: `${fila.instruction} ${recadoDaAssinatura(true, a)}`,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
catch (e) {
|
|
350
|
+
const motivo = describeConvexError(e);
|
|
351
|
+
return {
|
|
352
|
+
...fila,
|
|
353
|
+
signed: false,
|
|
354
|
+
assinatura: { ok: false, motivo },
|
|
355
|
+
instruction: `${fila.instruction} A assinatura NÃO foi ligada: ${motivo} A peça ficou na fila sem marca.`,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (args.action === "sign") {
|
|
360
|
+
if (!args.itemId)
|
|
361
|
+
throw new Error("action=sign exige itemId.");
|
|
362
|
+
const signed = args.signed ?? true;
|
|
363
|
+
const r = await convexMutation("distributionMcp:mcpAssinarPeca", {
|
|
364
|
+
sessionToken,
|
|
365
|
+
itemId: args.itemId,
|
|
366
|
+
signed,
|
|
367
|
+
});
|
|
368
|
+
return { ok: true, itemId: args.itemId, signed, ...r, instruction: recadoDaAssinatura(signed, r) };
|
|
304
369
|
}
|
|
305
370
|
if (args.action === "list") {
|
|
306
371
|
return await convexQuery("distributionMcp:mcpListItems", {
|
package/dist/tools/gallery.js
CHANGED
|
@@ -154,6 +154,11 @@ export const gallerySchema = z.object({
|
|
|
154
154
|
.boolean()
|
|
155
155
|
.optional()
|
|
156
156
|
.describe("action=refs: false (default) SOMA às referências que já existem; true troca a lista inteira. Pra limpar tudo, mande a lista vazia com replace=true."),
|
|
157
|
+
// Mesma tolerância a lista serializada como texto do referenceImageIds.
|
|
158
|
+
skills: z
|
|
159
|
+
.union([z.array(z.string()), z.string()])
|
|
160
|
+
.optional()
|
|
161
|
+
.describe("action=ingest: COMO A PEÇA FOI FEITA, os slugs das skills da casa que dirigiram o take (ex: ['clipe'], ['rasante','storyboard']). Aparece como 'Feito com /clipe' na ficha e na página pública da peça. No Claude Code com o plugin Sapiens o hook carimba sozinho; em outro cliente, passe sempre que o take saiu de uma skill da casa. Re-ingerir SOMA às que já estavam, nunca apaga. Metadado puro: não muda custo nem alcance."),
|
|
157
162
|
uploadedSource: z
|
|
158
163
|
.string()
|
|
159
164
|
.optional()
|
|
@@ -343,7 +348,7 @@ async function ingestPelaPonte(sessionToken, args, ficha) {
|
|
|
343
348
|
throw new Error(`Subir a peça pro storage falhou: HTTP ${subida.status}. Tente de novo; se repetir, peça outra uploadUrl (ela vale 1h).`);
|
|
344
349
|
}
|
|
345
350
|
const { storageId } = (await subida.json());
|
|
346
|
-
return await convexAction("externalIngestNode:mcpIngestFromStorage", {
|
|
351
|
+
return await semSkillsSeRecusar(ficha, (f) => convexAction("externalIngestNode:mcpIngestFromStorage", {
|
|
347
352
|
sessionToken,
|
|
348
353
|
storageId,
|
|
349
354
|
mimeType,
|
|
@@ -352,8 +357,24 @@ async function ingestPelaPonte(sessionToken, args, ficha) {
|
|
|
352
357
|
// mover ou apagar qualquer byte.
|
|
353
358
|
contentSha256: sha256,
|
|
354
359
|
unfiltered: args.unfiltered,
|
|
355
|
-
...
|
|
356
|
-
});
|
|
360
|
+
...f,
|
|
361
|
+
}));
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Servidor de antes do carimbo de skill (deploy atrasado em relação ao pacote)
|
|
365
|
+
* recusa o arg que não conhece, e recusa ANTES do handler, sem tocar em nada.
|
|
366
|
+
* A peça vale mais que o carimbo: tenta de novo sem ele.
|
|
367
|
+
*/
|
|
368
|
+
async function semSkillsSeRecusar(ficha, chamar) {
|
|
369
|
+
try {
|
|
370
|
+
return await chamar(ficha);
|
|
371
|
+
}
|
|
372
|
+
catch (e) {
|
|
373
|
+
if (!ficha.skills || !/extra field `?skills`?/i.test(describeConvexError(e)))
|
|
374
|
+
throw e;
|
|
375
|
+
const { skills: _semCarimbo, ...resto } = ficha;
|
|
376
|
+
return await chamar(resto);
|
|
377
|
+
}
|
|
357
378
|
}
|
|
358
379
|
/**
|
|
359
380
|
* Normaliza `referenceImageIds` pro formato que o Convex espera (lista).
|
|
@@ -400,6 +421,19 @@ function normalizePartIds(raw) {
|
|
|
400
421
|
.filter(Boolean)
|
|
401
422
|
.slice(0, 16);
|
|
402
423
|
}
|
|
424
|
+
/**
|
|
425
|
+
* E pras skills: lista ou texto com vírgula vira lista, vazio vira undefined
|
|
426
|
+
* (campo que o servidor nem recebe). Quem limpa o slug é o servidor
|
|
427
|
+
* (convex/shared/skillSlugs.ts), aqui só o formato.
|
|
428
|
+
*/
|
|
429
|
+
function normalizeSkills(raw) {
|
|
430
|
+
if (!raw)
|
|
431
|
+
return undefined;
|
|
432
|
+
const lista = (Array.isArray(raw) ? raw : raw.split(","))
|
|
433
|
+
.map((s) => s.trim().replace(/^["'[]+|["'\]]+$/g, ""))
|
|
434
|
+
.filter(Boolean);
|
|
435
|
+
return lista.length ? lista.slice(0, 8) : undefined;
|
|
436
|
+
}
|
|
403
437
|
export async function gallery(args) {
|
|
404
438
|
const sessionToken = getSessionToken();
|
|
405
439
|
if (args.action === "list") {
|
|
@@ -492,14 +526,15 @@ export async function gallery(args) {
|
|
|
492
526
|
characterIds: normalizeCastIds(args.characterIds).length
|
|
493
527
|
? normalizeCastIds(args.characterIds)
|
|
494
528
|
: undefined,
|
|
529
|
+
skills: normalizeSkills(args.skills),
|
|
495
530
|
};
|
|
496
531
|
if (args.sourceUrl && !temBytesLocais) {
|
|
497
532
|
try {
|
|
498
|
-
const result = await convexAction("externalIngest:mcpIngestExternal", {
|
|
533
|
+
const result = await semSkillsSeRecusar(ficha, (f) => convexAction("externalIngest:mcpIngestExternal", {
|
|
499
534
|
sessionToken,
|
|
500
535
|
sourceUrl: args.sourceUrl,
|
|
501
|
-
...
|
|
502
|
-
});
|
|
536
|
+
...f,
|
|
537
|
+
}));
|
|
503
538
|
return { ...result, aviso: AVISO_INGEST };
|
|
504
539
|
}
|
|
505
540
|
catch (e) {
|
package/dist/tools/repertorio.js
CHANGED
|
@@ -24,7 +24,7 @@ import { need } from "../schema.js";
|
|
|
24
24
|
*
|
|
25
25
|
* Action-based design (memoria: consolidar tools via args).
|
|
26
26
|
*/
|
|
27
|
-
const mediaTypeEnum = z.enum(["movie", "series", "anime", "game", "book", "music", "tool", "person"]);
|
|
27
|
+
const mediaTypeEnum = z.enum(["movie", "series", "anime", "game", "book", "music", "tool", "person", "boardgame"]);
|
|
28
28
|
const statusEnum = z.enum([
|
|
29
29
|
"backlog",
|
|
30
30
|
"active",
|
|
@@ -62,7 +62,7 @@ export const repertorioSchema = z.object({
|
|
|
62
62
|
// add_item — só identifica o item; o servidor re-resolve e grava o metadado.
|
|
63
63
|
source: sourceEnum
|
|
64
64
|
.optional()
|
|
65
|
-
.describe("Pra add_item: o source EXATO de um candidato do action=resolve (manual=filme/série via OMDb, anilist, twitch=jogo, googlebooks, itunes, wikidata=pessoa). Não invente."),
|
|
65
|
+
.describe("Pra add_item: o source EXATO de um candidato do action=resolve (manual=filme/série via OMDb, anilist, twitch=jogo, googlebooks, itunes, wikidata=pessoa ou jogo de tabuleiro). Não invente."),
|
|
66
66
|
externalId: z
|
|
67
67
|
.string()
|
|
68
68
|
.optional()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sapiens-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.76.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",
|