sapiens-mcp 1.43.3 → 1.43.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/registry.js CHANGED
@@ -42,7 +42,7 @@ import { skillMenuLine } from "./skills.js";
42
42
  */
43
43
  export const TOOLS = {
44
44
  sapiens_pipeline: {
45
- description: "CRUD do content pipeline Sapiens (sources/productions/publishables). Sub-actions: list_sources, list_articles (use includeDrafts pra incluir drafts; onlyAvailable pra esconder os já virados em source), get_source, get_production, list_versions, add_article_as_source, create_draft_article_and_source (seed), create_production (sourceId+format → productionId draft), update_production (substitui payload, opcionalmente muda status), finalize_production (cria publishable v1, v2... com snapshot), remove_production, remove_source, set_source_done, update_source_notes, restore_version (volta payload duma versão antiga), set_publishable_title (renomeia um publishable), backfill_via (rotula em lote o campo 'via' das productions antigas; dryRun=true só lista), propose_mega_grafico_plan (granular: só gera plano via Gemini, devolve fullPrompt+spec), run_mega_grafico_full (ONE-SHOT, recomendado: cria production+propõe plano+gera imagem+aplica selo Sapiens+finaliza publishable numa chamada só), generate_carousel (gera um carrossel editorial standalone a partir de brief OU articleId — 7-9 slides na voz Sapiens + imagens do banco; devolve id + url do editor pro humano abrir, ajustar e exportar; admin-only, cobra Sinapses, reembolsa se falhar). generate_carousel_production (o IRMÃO PIPELINE do generate_carousel: recebe um sourceId de artigo, CRIA a production carrossel_ig e a preenche pelo MESMO motor da casa, já no shape editorial (templateId+slots) que o editor de pipeline renderiza — prefira ESTE a create_production+payload cru pro carrossel, que abre VAZIO no editor; devolve productionId + url do editor de pipeline; admin-only, cobra Sinapses, reembolsa se falhar). CARROSSEL FINO (edição sem tela, admin-only): list_carousels (seus carrosséis standalone), get_carousel (payload completo + catálogo de templates com slots/limites + paletas — tudo pra VOCÊ escrever os slides), update_carousel (payload inteiro de volta, sanitizado no servidor; preserve os campos image dos slides que não mexeu), carousel_auto_images (IA escolhe imagens do banco pros slides de foto, 60 Sinapses; makeVisual=true converte slides de texto pra layouts de foto antes — ritmo visual; generateMissing=true gera imagem nova pros que o banco não cobrir, ~450 Sinapses cada, até 4, avise o custo antes), carousel_generate_image (imagem nova pra UM slide, ~450 Sinapses, customPrompt opcional). Fluxo confortável (standalone): generate_carousel → get_carousel → update_carousel (afia os textos) → carousel_auto_images → humano abre a url pra exportar. Fluxo pipeline (carrossel atrelado a um artigo/source): generate_carousel_production (sourceId) → humano abre a url do editor de pipeline pra revisar e finalizar (finalize_production). Pra mega_grafico, SEMPRE prefira run_mega_grafico_full em vez de sequenciar manualmente (menos drift). Idempotente só na production (passa productionId pra reusar a MESMA row), mas cada run RE-GERA a imagem e cobra de novo (~900 Sinapses): não é grátis re-rodar. OBRIGATÓRIO perguntar ao user antes se withHelen=true (cartoon Helen interage com tema, ~15-25% do poster) ou false (poster 100% diagramático). Custo ~900-1000 sinapses por geração. run_mega_grafico_full, generate_carousel, generate_carousel_production, carousel_auto_images (com generateMissing) e carousel_generate_image são SÍNCRONAS e pesadas: vale a REGRA DO TIMEOUT (podem cobrar mesmo voltando 'Timeout'; cheque get_carousel/dashboard antes de repetir). Use skipFinalize=true se quiser deixar production em 'ready' pro admin revisar antes de publishable. Payload livre por formato — chame sapiens_meta action=formats pra ver schemas sugeridos.",
45
+ description: "CRUD do content pipeline Sapiens (sources/productions/publishables). Sub-actions: list_sources, list_articles (use includeDrafts pra incluir drafts; onlyAvailable pra esconder os já virados em source), get_source, get_production, list_versions, add_article_as_source, create_draft_article_and_source (seed), create_production (sourceId+format → productionId draft), update_production (substitui payload, opcionalmente muda status), finalize_production (cria publishable v1, v2... com snapshot), remove_production, remove_source, set_source_done, update_source_notes, restore_version (volta payload duma versão antiga), set_publishable_title (renomeia um publishable), backfill_via (rotula em lote o campo 'via' das productions antigas; dryRun=true só lista), propose_mega_grafico_plan (granular: só gera plano via Gemini, devolve fullPrompt+spec), run_mega_grafico_full (ONE-SHOT, recomendado: cria production+propõe plano+gera imagem+aplica selo Sapiens+finaliza publishable numa chamada só), generate_carousel (gera um carrossel editorial standalone a partir de brief OU articleId — 7-9 slides na voz Sapiens + imagens do banco; devolve id + url do editor pro humano abrir, ajustar e exportar; admin-only, cobra Sinapses, reembolsa se falhar; model opcional trava uma folha registrada da casa, ex 'feed' ou 'reflexao', em vez do caminho editorial livre). generate_carousel_production (o IRMÃO PIPELINE do generate_carousel: recebe um sourceId de artigo, CRIA a production carrossel_ig e a preenche pelo MESMO motor da casa, já no shape editorial (templateId+slots) que o editor de pipeline renderiza — prefira ESTE a create_production+payload cru pro carrossel, que abre VAZIO no editor; devolve productionId + url do editor de pipeline; admin-only, cobra Sinapses, reembolsa se falhar; model opcional trava a folha registrada; beats opcional entrega copy PRONTO por beat da espinha, [{n, slots}], quando o agente JÁ escreveu e validou os blocos: com beats o Gemini de escrita NÃO roda e a rodada cobra só o picker de imagens, zero com autoPickImages=false; caption opcional vira a legenda do meta). CARROSSEL FINO (edição sem tela, admin-only): list_carousels (seus carrosséis standalone), get_carousel (payload completo + catálogo de templates com slots/limites + paletas — tudo pra VOCÊ escrever os slides), update_carousel (payload inteiro de volta, sanitizado no servidor; preserve os campos image dos slides que não mexeu), carousel_auto_images (IA escolhe imagens do banco pros slides de foto, 60 Sinapses; aceita carouselId standalone OU productionId de carrossel da pipeline; makeVisual=true converte slides de texto pra layouts de foto antes — ritmo visual; generateMissing=true gera imagem nova pros que o banco não cobrir, ~450 Sinapses cada, até 4, avise o custo antes), carousel_generate_image (imagem nova pra UM slide, ~450 Sinapses, customPrompt opcional). Fluxo confortável (standalone): generate_carousel → get_carousel → update_carousel (afia os textos) → carousel_auto_images → humano abre a url pra exportar. Fluxo pipeline (carrossel atrelado a um artigo/source): generate_carousel_production (sourceId) → humano abre a url do editor de pipeline pra revisar e finalizar (finalize_production). Fluxo pipeline com copy do agente (a skill escreveu os blocos): generate_carousel_production (sourceId + model + beats) → carousel_auto_images (productionId) se faltar foto → humano revisa e finaliza. Pra mega_grafico, SEMPRE prefira run_mega_grafico_full em vez de sequenciar manualmente (menos drift). Idempotente só na production (passa productionId pra reusar a MESMA row), mas cada run RE-GERA a imagem e cobra de novo (~900 Sinapses): não é grátis re-rodar. OBRIGATÓRIO perguntar ao user antes se withHelen=true (cartoon Helen interage com tema, ~15-25% do poster) ou false (poster 100% diagramático). Custo ~900-1000 sinapses por geração. run_mega_grafico_full, generate_carousel, generate_carousel_production, carousel_auto_images (com generateMissing) e carousel_generate_image são SÍNCRONAS e pesadas: vale a REGRA DO TIMEOUT (podem cobrar mesmo voltando 'Timeout'; cheque get_carousel/dashboard antes de repetir). Use skipFinalize=true se quiser deixar production em 'ready' pro admin revisar antes de publishable. Payload livre por formato — chame sapiens_meta action=formats pra ver schemas sugeridos.",
46
46
  schema: pipelineSchema,
47
47
  handler: pipeline,
48
48
  },
@@ -107,7 +107,7 @@ export const TOOLS = {
107
107
  handler: persona,
108
108
  },
109
109
  sapiens_helen: {
110
- description: "Helen Voice TTS via ElevenLabs ou Google Gemini (qualquer logado; cobra Sinapses). Sub-actions: list_presets (catálogo de voiceIds/voiceNames recomendados + stylePreambles), speak (sintetiza, retorna audioBase64+mimeType+sizeBytes). BYOK suportado via clientApiKey, senão usa env do deploy. Custo: ElevenLabs ~$0.30/500c, Google ~$0.01/500c. Max 5000 chars (quebra antes em chunks). text pré-processado pelo caller (sem em-dash, sem markdown).",
110
+ description: "Helen Voice TTS via ElevenLabs, Google Gemini ou Fish Audio (qualquer logado; cobra Sinapses). Sub-actions: list_presets (catálogo de voiceIds/voiceNames recomendados + stylePreambles), speak (sintetiza, retorna audioBase64+mimeType+sizeBytes). provider=fish é o S2.1 Pro, o mais forte em japonês, mandarim e coreano: fala de personagem e cena de anime pedem ele. `language` escolhe o idioma (auto|pt|en|ja|es|ko|zh|fr|it|de). BYOK suportado via clientApiKey, senão usa env do deploy. Custo: ElevenLabs ~$0.30/500c, Google ~$0.01/500c, Fish grátis via API até 31/08/2026. Max 5000 chars (quebra antes em chunks). text pré-processado pelo caller (sem em-dash, sem markdown).",
111
111
  schema: helenSchema,
112
112
  handler: helen,
113
113
  },
@@ -127,7 +127,7 @@ export const TOOLS = {
127
127
  handler: video,
128
128
  },
129
129
  sapiens_stock_audio: {
130
- description: "Banco de som da casa: trilha pronta E efeito sonoro (tabela stockAudio). Sub-actions de leitura (públicas, sem auth): categories (lista os moods/usos), list (busca com filtros mood/albumSlug/durationMax/search; kind='sfx' traz os EFEITOS: whoosh, clique, impacto, ambiência, foley — devolve {count, items} com title/url/durationSeconds/tags), get (1 item por audioId). Use pra puxar trilha/efeito pronto: pega a `url` e usa direto no ffmpeg. Não achou o efeito? generate (COBRA Sinapses, exige login) cria um novo por texto: prompt + durationSeconds (1-15, default 5) + provider ('mirelo' padrão 30 Sinapses/s mín 60 | 'elevenlabs' premium 60/s mín 120) + promptInfluence opcional (0..1, só elevenlabs: fidelidade ao texto, default 0.3), assíncrono — acompanhe com generation-status (generationId) até 'ready' (audioUrl; o efeito também entra no acervo kind=sfx) ou 'failed' (Sinapses reembolsadas). Efeito é CURTO (1-15s): música/trilha nova é no sapiens_musicator. Mood disponíveis: calmo, intenso, narrativo, épico, sombrio.",
130
+ description: "Banco de som da casa: trilha pronta E efeito sonoro (tabela stockAudio). Sub-actions de leitura (públicas, sem auth): categories (lista os moods/usos), list (busca com filtros mood/albumSlug/durationMax/search; kind='sfx' traz os EFEITOS: whoosh, clique, impacto, ambiência, foley — devolve {count, items} com title/url/durationSeconds/tags), get (1 item por audioId). Use pra puxar trilha/efeito pronto: pega a `url` e usa direto no ffmpeg. Não achou o efeito? generate (COBRA Sinapses, exige login) cria um novo por texto: prompt + durationSeconds (1-15, default 5) + provider ('mirelo' padrão 35 Sinapses/s mín 70 | 'elevenlabs' premium 60/s mín 120) + promptInfluence opcional (0..1, só elevenlabs: fidelidade ao texto, default 0.3), assíncrono — acompanhe com generation-status (generationId) até 'ready' (audioUrl; o efeito também entra no acervo kind=sfx) ou 'failed' (Sinapses reembolsadas). Efeito é CURTO (1-15s): música/trilha nova é no sapiens_musicator. Mood disponíveis: calmo, intenso, narrativo, épico, sombrio.",
131
131
  schema: stockAudioSchema,
132
132
  handler: stockAudio,
133
133
  },
package/dist/skills.js CHANGED
@@ -144,7 +144,7 @@ Depois de pronta: \`action=publish\` põe a faixa no Acervo da Comunidade (só a
144
144
  Efeito é CURTO. Música inteira é no Musicator, não aqui.
145
145
 
146
146
  1. Procure pronto primeiro: \`sapiens_stock_audio action=list kind=sfx\`. Grátis.
147
- 2. Não achou: \`action=generate\` com \`prompt\` e \`durationSeconds\` (1 a 15, default 5) e \`provider\` ('mirelo' padrão 30 Sinapses/s mín 60, 'elevenlabs' premium 60/s mín 120). \`promptInfluence\` (0..1) só vale no elevenlabs.
147
+ 2. Não achou: \`action=generate\` com \`prompt\` e \`durationSeconds\` (1 a 15, default 5) e \`provider\` ('mirelo' padrão 35 Sinapses/s mín 70, 'elevenlabs' premium 60/s mín 120). \`promptInfluence\` (0..1) só vale no elevenlabs.
148
148
  3. É assíncrono: devolve \`generationId\`, acompanhe com \`action=generation-status\` até 'ready' (traz audioUrl) ou 'failed' (reembolsa sozinho).
149
149
 
150
150
  ## Sonorizar um clipe
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { convexAction, getSessionToken } from "../convexClient.js";
3
3
  /**
4
- * Helen Voice — Text-to-Speech via ElevenLabs ou Google Gemini.
4
+ * Helen Voice — Text-to-Speech via ElevenLabs, Google Gemini ou Fish Audio.
5
5
  *
6
6
  * Sub-actions:
7
7
  * - speak: sintetiza fala. Retorna audioBase64 + mimeType + sizeBytes.
@@ -18,13 +18,13 @@ export const helenSchema = z.object({
18
18
  .optional()
19
19
  .describe("Texto a falar (action=speak). Max 5000 chars."),
20
20
  provider: z
21
- .enum(["elevenlabs", "google"])
21
+ .enum(["elevenlabs", "google", "fish"])
22
22
  .optional()
23
- .describe("elevenlabs (melhor natural, mais caro) ou google (Gemini TTS, mais barato). Default elevenlabs."),
23
+ .describe("elevenlabs (inglês expressivo, mais caro), google (Gemini TTS, mais barato) ou fish (S2.1 Pro, o melhor em japonês e nas línguas asiáticas). Default elevenlabs."),
24
24
  modelId: z
25
25
  .string()
26
26
  .optional()
27
- .describe("ElevenLabs: 'eleven_v3' (default) ou 'eleven_multilingual_v2'. Google: 'gemini-3.1-flash-tts-preview' (default)."),
27
+ .describe("ElevenLabs: 'eleven_v3' (default) ou 'eleven_multilingual_v2'. Google: 'gemini-3.1-flash-tts-preview' (default). Fish: escolhido no servidor, o que vier aqui é ignorado."),
28
28
  voiceId: z
29
29
  .string()
30
30
  .optional()
@@ -33,10 +33,18 @@ export const helenSchema = z.object({
33
33
  .string()
34
34
  .optional()
35
35
  .describe("Google prebuilt voice (default Kore). Use list_presets pra ver opções."),
36
+ fishReferenceId: z
37
+ .string()
38
+ .optional()
39
+ .describe("Fish: id da voz no acervo do fish.audio. Sem ele vai a voz default do modelo."),
40
+ language: z
41
+ .enum(["auto", "pt", "en", "ja", "es", "ko", "zh", "fr", "it", "de"])
42
+ .optional()
43
+ .describe("Idioma da fala. Cada motor recebe no dialeto dele; fish e google detectam pelo texto, então lá isto é informativo. Default pt."),
36
44
  languageCode: z
37
45
  .string()
38
46
  .optional()
39
- .describe("ElevenLabs: ex 'pt' pra PT-BR otimizado."),
47
+ .describe("ElevenLabs: ex 'pt' pra PT-BR otimizado. Vence o `language` quando os dois vêm."),
40
48
  stylePreamble: z
41
49
  .string()
42
50
  .optional()
@@ -85,6 +93,16 @@ const PRESETS = {
85
93
  "Read this slow and contemplative, with pauses:",
86
94
  ],
87
95
  },
96
+ fish: {
97
+ note: "Fish Audio S2.1 Pro. Não precisa de modelId (o servidor escolhe) e detecta o idioma pelo texto. É o motor mais forte da casa em japonês, mandarim e coreano: cena de anime, fala de personagem, narração em ja pedem ele. Voz opcional em fishReferenceId; sem ela, a voz default.",
98
+ voices: [
99
+ {
100
+ fishReferenceId: null,
101
+ name: "Voz default do modelo",
102
+ use: "o caminho normal: escreve o texto no idioma que quer e manda",
103
+ },
104
+ ],
105
+ },
88
106
  };
89
107
  export async function helen(args) {
90
108
  if (args.action === "list_presets") {
@@ -98,9 +116,13 @@ export async function helen(args) {
98
116
  const modelId = args.modelId ??
99
117
  (provider === "elevenlabs"
100
118
  ? "eleven_v3"
101
- // 3.1 Flash é o único TTS do Google que gera nas contas free da casa
102
- // (o 2.5 responde 200 sem áudio nelas e só sai na conta central).
103
- : "gemini-3.1-flash-tts-preview");
119
+ : provider === "fish"
120
+ // O motor real do Fish é o header `model` do servidor. Este id viaja só
121
+ // pra log e pra chave de cache não misturar os motores.
122
+ ? "s2.1-pro-free"
123
+ // 3.1 Flash é o único TTS do Google que gera nas contas free da casa
124
+ // (o 2.5 responde 200 sem áudio nelas e só sai na conta central).
125
+ : "gemini-3.1-flash-tts-preview");
104
126
  if (provider === "elevenlabs" && !args.voiceId) {
105
127
  throw new Error("provider=elevenlabs exige voiceId. Use action=list_presets pra ver opções.");
106
128
  }
@@ -112,6 +134,8 @@ export async function helen(args) {
112
134
  modelId,
113
135
  voiceId: args.voiceId,
114
136
  googleVoiceName: args.googleVoiceName,
137
+ fishReferenceId: args.fishReferenceId,
138
+ language: args.language,
115
139
  languageCode: args.languageCode,
116
140
  stylePreamble: args.stylePreamble,
117
141
  voiceSettings: args.voiceSettings,
@@ -89,11 +89,33 @@ export const pipelineSchema = z.object({
89
89
  .boolean()
90
90
  .optional()
91
91
  .describe("generate_carousel: se TRUE, a voz do Sintético do dono dirige o copy (amplificar). Default false = estilo do dono."),
92
+ model: z
93
+ .string()
94
+ .optional()
95
+ .describe("generate_carousel / generate_carousel_production: folha travada registrada da casa (ex: 'feed', 'reflexao'). A espinha de slides vem do servidor; a IA (ou seus beats) só escreve a prosa. Ausente = caminho editorial livre."),
96
+ beats: z
97
+ .union([
98
+ z.array(z.object({
99
+ n: z.number().describe("número do beat na espinha (1 a 9)"),
100
+ slots: z
101
+ .record(z.string())
102
+ .describe("só os slots de PROSA daquele beat (title/body/punch/tagline...), já contados e na voz"),
103
+ })),
104
+ // Defesa: cliente que serializa o array como string JSON (mesmo caso do
105
+ // arg payload). O handler normaliza.
106
+ z.string(),
107
+ ])
108
+ .optional()
109
+ .describe("generate_carousel_production: copy PRONTO por beat da espinha, [{n, slots}]. Exige model. Com beats o Gemini de escrita NÃO roda e a rodada cobra só o picker de imagens (zero com autoPickImages=false). Use quando a skill do carrossel já escreveu e validou os blocos (o contador emite este JSON com --beats)."),
110
+ keyword: z
111
+ .string()
112
+ .optional()
113
+ .describe("generate_carousel_production (com beats): a palavra única do comment-to-DM, vira meta.keyword do pacote de publicação."),
92
114
  // carrossel standalone (get/update/imagens)
93
115
  carouselId: z
94
116
  .string()
95
117
  .optional()
96
- .describe("carousels_standalone:_id — get_carousel/update_carousel/carousel_auto_images/carousel_generate_image. Vem de generate_carousel ou list_carousels."),
118
+ .describe("carousels_standalone:_id — get_carousel/update_carousel/carousel_auto_images/carousel_generate_image. Vem de generate_carousel ou list_carousels. No carousel_auto_images, alternativa: productionId (carrossel da pipeline)."),
97
119
  slideId: z
98
120
  .string()
99
121
  .optional()
@@ -129,7 +151,7 @@ export const pipelineSchema = z.object({
129
151
  productionId: z
130
152
  .string()
131
153
  .optional()
132
- .describe("contentProductions:_id — get_production/update_production/finalize_production/remove_production/list_versions. Vem de create_production."),
154
+ .describe("contentProductions:_id — get_production/update_production/finalize_production/remove_production/list_versions/carousel_auto_images (carrossel da pipeline). Vem de create_production ou generate_carousel_production."),
133
155
  publishableId: z
134
156
  .string()
135
157
  .optional()
@@ -191,6 +213,25 @@ function coercePayloadArg(raw) {
191
213
  throw new Error('Arg "payload" veio como string mas não é JSON válido. Mande o objeto do payload.');
192
214
  }
193
215
  }
216
+ // Mesma defesa pro `beats` do generate_carousel_production: aceita o array já
217
+ // estruturado ou a string JSON dele, e valida o shape mínimo antes de mandar.
218
+ function coerceBeatsArg(raw) {
219
+ if (raw == null)
220
+ return undefined;
221
+ let val = raw;
222
+ if (typeof val === "string") {
223
+ try {
224
+ val = JSON.parse(val);
225
+ }
226
+ catch {
227
+ throw new Error('Arg "beats" veio como string mas não é JSON válido. Mande o array [{n, slots}].');
228
+ }
229
+ }
230
+ if (!Array.isArray(val) || val.some((b) => typeof b?.n !== "number" || !b?.slots)) {
231
+ throw new Error('Arg "beats" deve ser um array de { n: número do beat, slots: {slot: texto} }.');
232
+ }
233
+ return val;
234
+ }
194
235
  export async function pipeline(args) {
195
236
  const sessionToken = getSessionToken();
196
237
  switch (args.action) {
@@ -382,6 +423,7 @@ export async function pipeline(args) {
382
423
  ...(articleId ? { publishedArticleId: articleId } : {}),
383
424
  autoPickImages: args.autoPickImages,
384
425
  useSintetico: args.useSintetico,
426
+ ...(args.model ? { model: args.model } : {}),
385
427
  });
386
428
  }
387
429
  case "generate_carousel_production": {
@@ -393,11 +435,20 @@ export async function pipeline(args) {
393
435
  // Sinapses (reembolsa se falhar). Devolve productionId + url do editor.
394
436
  // SÍNCRONA e pesada (Gemini + imagens): vale a REGRA DO TIMEOUT — se voltar
395
437
  // Timeout, confira em get_production/list_sources antes de repetir.
438
+ //
439
+ // model trava a folha registrada; beats = copy pronto do agente (pula o
440
+ // Gemini de escrita, cobra só o picker). beats pode chegar serializado
441
+ // como string JSON (mesmo caso do payload): normaliza antes de mandar.
442
+ const beats = coerceBeatsArg(args.beats);
396
443
  return await convexAction("carouselAutofill:mcpFillCarouselFromArticle", {
397
444
  sessionToken,
398
445
  sourceId: need(args.sourceId, "sourceId"),
399
446
  autoPickImages: args.autoPickImages,
400
447
  useSintetico: args.useSintetico,
448
+ ...(args.model ? { model: args.model } : {}),
449
+ ...(beats ? { beats } : {}),
450
+ ...(args.caption ? { caption: args.caption } : {}),
451
+ ...(args.keyword ? { keyword: args.keyword } : {}),
401
452
  });
402
453
  }
403
454
  case "list_carousels":
@@ -420,18 +471,24 @@ export async function pipeline(args) {
420
471
  payload: coercePayloadArg(need(args.payload, "payload")),
421
472
  ...(args.title ? { title: args.title } : {}),
422
473
  });
423
- case "carousel_auto_images":
474
+ case "carousel_auto_images": {
424
475
  // IA escolhe imagens do banco pros slides de foto sem imagem (60 Sinapses).
476
+ // Aceita carouselId (standalone) OU productionId (carrossel da pipeline).
425
477
  // makeVisual dá ritmo a carrossel texto-pesado; generateMissing gera as que
426
478
  // o banco não cobriu (custo por imagem). SÍNCRONA e pesada com
427
479
  // generateMissing: vale a REGRA DO TIMEOUT.
480
+ if (!args.carouselId && !args.productionId) {
481
+ throw new Error("carousel_auto_images: passe carouselId (standalone) OU productionId (carrossel da pipeline).");
482
+ }
428
483
  return await convexAction("carouselAutofill:mcpCarouselAutoImages", {
429
484
  sessionToken,
430
- carouselId: need(args.carouselId, "carouselId"),
485
+ ...(args.carouselId ? { carouselId: args.carouselId } : {}),
486
+ ...(!args.carouselId && args.productionId ? { productionId: args.productionId } : {}),
431
487
  force: args.force,
432
488
  makeVisual: args.makeVisual,
433
489
  generateMissing: args.generateMissing,
434
490
  });
491
+ }
435
492
  case "carousel_generate_image":
436
493
  // Gera imagem NOVA pra UM slide (nanoBanana, ~450 Sinapses) e salva no
437
494
  // carrossel. SÍNCRONA (~20-60s): vale a REGRA DO TIMEOUT.
@@ -67,7 +67,7 @@ export const stockAudioSchema = z.object({
67
67
  provider: z
68
68
  .enum(["mirelo", "elevenlabs"])
69
69
  .optional()
70
- .describe("action=generate: 'mirelo' (padrão, 30 Sinapses/s, mín 60) | 'elevenlabs' (premium, 60/s, mín 120)."),
70
+ .describe("action=generate: 'mirelo' (padrão, 35 Sinapses/s, mín 70) | 'elevenlabs' (premium, 60/s, mín 120)."),
71
71
  promptInfluence: z
72
72
  .number()
73
73
  .min(0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sapiens-mcp",
3
- "version": "1.43.3",
3
+ "version": "1.43.5",
4
4
  "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.",
5
5
  "type": "module",
6
6
  "bin": {