ponch-mcp-server 1.0.93 → 1.0.95

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/index.js CHANGED
@@ -12942,7 +12942,9 @@ var brandBriefWriterContract = MartinContractSchema.parse(
12942
12942
  );
12943
12943
  async function save(input) {
12944
12944
  const { db, tenantId, brandId, plan, actor } = input;
12945
- const brandRef = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main");
12945
+ const brandRef = db.doc(
12946
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
12947
+ );
12946
12948
  const brandSnap = await brandRef.get();
12947
12949
  if (!brandSnap.exists) {
12948
12950
  return { ok: false, error: `Brand "${brandId}" no encontrada`, code: "BRAND_NOT_FOUND" };
@@ -12973,7 +12975,9 @@ async function save(input) {
12973
12975
  }
12974
12976
  async function updateField(input) {
12975
12977
  const { db, tenantId, brandId, field, value } = input;
12976
- const brandRef = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main");
12978
+ const brandRef = db.doc(
12979
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
12980
+ );
12977
12981
  const brandSnap = await brandRef.get();
12978
12982
  if (!brandSnap.exists) {
12979
12983
  return { ok: false, error: `Brand "${brandId}" no encontrada`, code: "BRAND_NOT_FOUND" };
@@ -13574,6 +13578,12 @@ var rawContract6 = {
13574
13578
  destructive: false,
13575
13579
  affectsPublication: true,
13576
13580
  affectsExternal: true,
13581
+ martinConfirmationTemplate: (input, locale) => {
13582
+ return getMessage("marketing.seoSuggestion.confirmApply", locale, {
13583
+ suggestionId: input.suggestionId,
13584
+ fieldsCount: String(input.fieldsToApply?.length ?? 0)
13585
+ });
13586
+ },
13577
13587
  martinSummaryTemplate: (_input, output, locale) => {
13578
13588
  if (!output.ok) {
13579
13589
  if (output.code) {
@@ -13851,11 +13861,12 @@ var rawContract7 = {
13851
13861
  after: output.ok ? {
13852
13862
  camposActualizados: output.camposActualizados,
13853
13863
  datosKeys: input.datos ? Object.keys(input.datos) : [],
13854
- fotoId: input.fotoId ?? void 0,
13855
- keyword: input.keyword ?? void 0,
13856
- languageCode: input.languageCode,
13857
- estado: input.estado,
13858
- calendarioItemRef: input.calendarioItemRef ?? void 0
13864
+ // Firestore rechaza undefined — normalizar a null cuando el caller omite.
13865
+ fotoId: input.fotoId ?? null,
13866
+ keyword: input.keyword ?? null,
13867
+ languageCode: input.languageCode ?? null,
13868
+ estado: input.estado ?? null,
13869
+ calendarioItemRef: input.calendarioItemRef ?? null
13859
13870
  } : null
13860
13871
  }),
13861
13872
  quotasConsumed: [],
@@ -13869,7 +13880,7 @@ var contenidoUpdaterContract = MartinContractSchema.parse(
13869
13880
  );
13870
13881
  async function addCalendarSlot(input) {
13871
13882
  const { db, tenantId, brandId, mes, semana, slot } = input;
13872
- const calQuery = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_calendario").where("mes", "==", mes).limit(1).get();
13883
+ const calQuery = await db.collection(collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_calendario")).where("mes", "==", mes).limit(1).get();
13873
13884
  if (calQuery.empty) {
13874
13885
  return {
13875
13886
  ok: false,
@@ -13993,7 +14004,7 @@ async function calendarSlotUpdater(input) {
13993
14004
  if (slotIndex < 0) {
13994
14005
  return { ok: false, error: "slotIndex no puede ser negativo" };
13995
14006
  }
13996
- const calQuery = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_calendario").where("mes", "==", mes).limit(1).get();
14007
+ const calQuery = await db.collection(collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_calendario")).where("mes", "==", mes).limit(1).get();
13997
14008
  if (calQuery.empty) {
13998
14009
  return { ok: false, error: "Calendario no encontrado" };
13999
14010
  }
@@ -14028,7 +14039,7 @@ async function calendarSlotUpdater(input) {
14028
14039
  }
14029
14040
  let contenidosADescartar = [];
14030
14041
  if (oldContenidoRef && (accionContenidoExistente === "descartar" || !tocaSemantica)) {
14031
- const contenidoQuery = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_contenido").where("calendarioItemRef", "==", oldContenidoRef).limit(10).get();
14042
+ const contenidoQuery = await db.collection(collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_contenido")).where("calendarioItemRef", "==", oldContenidoRef).limit(10).get();
14032
14043
  contenidosADescartar = contenidoQuery.docs.filter((c) => {
14033
14044
  const data = c.data();
14034
14045
  return data.estado !== "descartado" && data.estado !== ESTADO_CONTENIDO.PUBLICADO;
@@ -14088,7 +14099,13 @@ async function calendarSlotUpdater(input) {
14088
14099
  let movedSlotEstado = null;
14089
14100
  if (moveTarget && oldContenidoRef) {
14090
14101
  try {
14091
- const contenidoSnap = await db.doc(`tenants/${tenantId}/brands/${brandId}/marketing_contenido/${oldContenidoRef}`).get();
14102
+ const contenidoSnap = await db.doc(
14103
+ docPath(
14104
+ { kind: "brand-nested", tenantId, brandId },
14105
+ "marketing_contenido",
14106
+ oldContenidoRef
14107
+ )
14108
+ ).get();
14092
14109
  if (contenidoSnap.exists) {
14093
14110
  const estadoContenido = contenidoSnap.data().estado;
14094
14111
  movedSlotEstado = mapContenidoEstadoToSlotEstado(estadoContenido);
@@ -14161,7 +14178,13 @@ async function calendarSlotUpdater(input) {
14161
14178
  });
14162
14179
  if (moveTarget && oldContenidoRef) {
14163
14180
  try {
14164
- await db.doc(`tenants/${tenantId}/brands/${brandId}/marketing_contenido/${oldContenidoRef}`).update({
14181
+ await db.doc(
14182
+ docPath(
14183
+ { kind: "brand-nested", tenantId, brandId },
14184
+ "marketing_contenido",
14185
+ oldContenidoRef
14186
+ )
14187
+ ).update({
14165
14188
  calendarioItemRef: moveTarget.targetRef
14166
14189
  });
14167
14190
  } catch (err) {
@@ -14325,7 +14348,7 @@ var calendarSlotUpdaterContract = MartinContractSchema.parse(
14325
14348
  );
14326
14349
  async function getCalendar(input) {
14327
14350
  const { db, tenantId, brandId, mes } = input;
14328
- const snap = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_calendario").where("mes", "==", mes).limit(1).get();
14351
+ const snap = await db.collection(collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_calendario")).where("mes", "==", mes).limit(1).get();
14329
14352
  if (snap.empty) {
14330
14353
  return {
14331
14354
  ok: true,
@@ -14386,6 +14409,21 @@ var rawContract10 = {
14386
14409
  var getCalendarContract = MartinContractSchema.parse(
14387
14410
  rawContract10
14388
14411
  );
14412
+ function normalizeFechaSnapshot(raw) {
14413
+ if (raw === null || raw === void 0) return null;
14414
+ if (typeof raw === "string") return raw;
14415
+ if (raw instanceof Date) return raw.toISOString();
14416
+ const maybeTimestamp = raw;
14417
+ if (typeof maybeTimestamp.toDate === "function") {
14418
+ const d = maybeTimestamp.toDate();
14419
+ if (d instanceof Date) return d.toISOString();
14420
+ }
14421
+ const maybeSerialized = raw;
14422
+ if (typeof maybeSerialized._seconds === "number") {
14423
+ return new Date(maybeSerialized._seconds * 1e3).toISOString();
14424
+ }
14425
+ return null;
14426
+ }
14389
14427
  var DEFAULT_ALERTS_ES = {
14390
14428
  noSeo: () => "No hay snapshot SEO. Genera uno para esta marca.",
14391
14429
  noPlan: () => "No hay plan de marketing. Genera uno desde el snapshot SEO.",
@@ -14394,14 +14432,20 @@ var DEFAULT_ALERTS_ES = {
14394
14432
  };
14395
14433
  async function brandMarketingSummary(input, alerts = DEFAULT_ALERTS_ES) {
14396
14434
  const { db, tenantId, brandId } = input;
14397
- const brandRef = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main");
14435
+ const brandRef = db.doc(
14436
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
14437
+ );
14398
14438
  const brandSnap = await brandRef.get();
14399
14439
  if (!brandSnap.exists) {
14400
14440
  return { ok: false, code: "BRAND_NOT_FOUND" };
14401
14441
  }
14402
14442
  const brand = brandSnap.data() ?? {};
14403
- const contenidoCol = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_contenido");
14404
- const fotosCol = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_fotos");
14443
+ const contenidoCol = db.collection(
14444
+ collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_contenido")
14445
+ );
14446
+ const fotosCol = db.collection(
14447
+ collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_fotos")
14448
+ );
14405
14449
  const [pendientesSnap, publicadosSnap, fotosNuevasSnap] = await Promise.all([
14406
14450
  contenidoCol.where("estado", "==", ESTADO_CONTENIDO.PENDIENTE_APROBACION).get(),
14407
14451
  contenidoCol.where("estado", "==", ESTADO_CONTENIDO.PUBLICADO).limit(50).get(),
@@ -14426,7 +14470,9 @@ async function brandMarketingSummary(input, alerts = DEFAULT_ALERTS_ES) {
14426
14470
  rank: seoSnapshot.rank ?? null,
14427
14471
  totalKeywords: seoSnapshot.totalKeywords ?? null,
14428
14472
  traficoOrganico: seoSnapshot.traficoOrganico ?? null,
14429
- ultimoSnapshot: seoSnapshot.fechaSnapshot ?? null
14473
+ // fechaSnapshot en Firestore es Timestamp. outputSchema declara
14474
+ // string ISO. Normalizamos vía firebase-admin Timestamp check.
14475
+ ultimoSnapshot: normalizeFechaSnapshot(seoSnapshot.fechaSnapshot)
14430
14476
  } : null,
14431
14477
  plan: { existe: planExiste },
14432
14478
  contenido: {
@@ -14527,14 +14573,20 @@ function extractPreview(plataforma, datos) {
14527
14573
  }
14528
14574
  async function brandMarketingPendingActions(input) {
14529
14575
  const { db, tenantId, brandId } = input;
14530
- const brandRef = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main");
14576
+ const brandRef = db.doc(
14577
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
14578
+ );
14531
14579
  const brandSnap = await brandRef.get();
14532
14580
  if (!brandSnap.exists) {
14533
14581
  return { ok: false, code: "BRAND_NOT_FOUND" };
14534
14582
  }
14535
14583
  const brand = brandSnap.data() ?? {};
14536
- const contenidoCol = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_contenido");
14537
- const fotosCol = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_fotos");
14584
+ const contenidoCol = db.collection(
14585
+ collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_contenido")
14586
+ );
14587
+ const fotosCol = db.collection(
14588
+ collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_fotos")
14589
+ );
14538
14590
  const [pendientesSnap, fotosNuevasSnap] = await Promise.all([
14539
14591
  contenidoCol.where("estado", "==", ESTADO_CONTENIDO.PENDIENTE_APROBACION).get(),
14540
14592
  fotosCol.where("estado", "==", ESTADO_FOTO.NUEVA).get()
@@ -14624,7 +14676,7 @@ var DEFAULT_ALERTS_ES2 = {
14624
14676
  };
14625
14677
  async function tenantMarketingSummary(input, alerts = DEFAULT_ALERTS_ES2) {
14626
14678
  const { db, tenantId } = input;
14627
- const brandsSnap = await db.collection("tenants").doc(tenantId).collection("brands").get();
14679
+ const brandsSnap = await db.collection(collectionPath({ kind: "tenant-nested", tenantId }, "brands")).get();
14628
14680
  const totalBrandsInTenant = brandsSnap.size;
14629
14681
  if (totalBrandsInTenant === 0) {
14630
14682
  return { ok: false, code: "TENANT_HAS_NO_BRANDS" };
@@ -14758,7 +14810,7 @@ var tenantMarketingSummaryContract = MartinContractSchema.parse(
14758
14810
  var TENANT_PENDING_CAP = 20;
14759
14811
  async function tenantMarketingPendingActions(input) {
14760
14812
  const { db, tenantId } = input;
14761
- const brandsSnap = await db.collection("tenants").doc(tenantId).collection("brands").get();
14813
+ const brandsSnap = await db.collection(collectionPath({ kind: "tenant-nested", tenantId }, "brands")).get();
14762
14814
  const totalBrandsInTenant = brandsSnap.size;
14763
14815
  if (totalBrandsInTenant === 0) {
14764
14816
  return { ok: false, code: "TENANT_HAS_NO_BRANDS" };
@@ -14774,7 +14826,9 @@ async function tenantMarketingPendingActions(input) {
14774
14826
  const brandIds = brandsSnap.docs.map((d) => d.id);
14775
14827
  const nombrePorBrand = /* @__PURE__ */ new Map();
14776
14828
  const configReads = brandIds.map(
14777
- (brandId) => db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main").get().then((snap) => {
14829
+ (brandId) => db.doc(
14830
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
14831
+ ).get().then((snap) => {
14778
14832
  const nombre = snap.exists ? snap.data()?.nombre ?? null : null;
14779
14833
  nombrePorBrand.set(brandId, nombre);
14780
14834
  })
@@ -14886,12 +14940,16 @@ var tenantMarketingPendingActionsContract = MartinContractSchema.parse(
14886
14940
  );
14887
14941
  async function seoSnapshotReader(input) {
14888
14942
  const { db, tenantId, brandId } = input;
14889
- const brandRef = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main");
14943
+ const brandRef = db.doc(
14944
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
14945
+ );
14890
14946
  const brandSnap = await brandRef.get();
14891
14947
  if (!brandSnap.exists) {
14892
14948
  return { ok: false, code: "BRAND_NOT_FOUND" };
14893
14949
  }
14894
- const snap = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_snapshots_semrush").orderBy("mes", "desc").limit(1).get();
14950
+ const snap = await db.collection(
14951
+ collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_snapshots_semrush")
14952
+ ).orderBy("mes", "desc").limit(1).get();
14895
14953
  if (snap.empty) {
14896
14954
  return { ok: false, code: "SEO_SNAPSHOT_MISSING" };
14897
14955
  }
@@ -14998,12 +15056,14 @@ var SCHEMA_FOR_SAVE_EN = {
14998
15056
  };
14999
15057
  async function collectionsReader(input) {
15000
15058
  const { db, tenantId, brandId } = input;
15001
- const brandRef = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main");
15059
+ const brandRef = db.doc(
15060
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
15061
+ );
15002
15062
  const brandSnap = await brandRef.get();
15003
15063
  if (!brandSnap.exists) {
15004
15064
  return { ok: false, code: "BRAND_NOT_FOUND" };
15005
15065
  }
15006
- const collSnap = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_snapshots").doc("main").collection("collections").get();
15066
+ const collSnap = await db.doc(docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_snapshots", "main")).collection("collections").get();
15007
15067
  if (collSnap.empty) {
15008
15068
  return { ok: false, code: "NO_COLLECTIONS_NESTED" };
15009
15069
  }
@@ -15011,7 +15071,9 @@ async function collectionsReader(input) {
15011
15071
  const brand = brandSnap.data() ?? {};
15012
15072
  const plan = brand.plan ?? {};
15013
15073
  const keywordsPrioritarios = Array.isArray(plan.keywordsPrioritarios) ? plan.keywordsPrioritarios : [];
15014
- const seoSuggestionsCol = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_suggestions_seo");
15074
+ const seoSuggestionsCol = db.collection(
15075
+ collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_suggestions_seo")
15076
+ );
15015
15077
  const seoSnap = await seoSuggestionsCol.where("intent", "==", "seo_meta").where("target.category", "==", "collection").where("estado", "in", ["pendiente", "aplicada"]).get();
15016
15078
  const existingSuggestions = {};
15017
15079
  for (const d of seoSnap.docs) {
@@ -15132,7 +15194,9 @@ var collectionsReaderContract = MartinContractSchema.parse(
15132
15194
  var QUERY_LIMIT = 50;
15133
15195
  async function photoGalleryReader(input) {
15134
15196
  const { db, tenantId, brandId, estado } = input;
15135
- let query = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_fotos");
15197
+ let query = db.collection(
15198
+ collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_fotos")
15199
+ );
15136
15200
  if (estado) {
15137
15201
  query = query.where("estado", "==", estado);
15138
15202
  }
@@ -15340,7 +15404,9 @@ async function photoEditStatusReader(input) {
15340
15404
  const { db, tenantId, brandId, fotoId } = input;
15341
15405
  const maxPerDay = MARKETING_CONSTRAINTS.photoEdit.maxIterationsPerPhotoPerDay;
15342
15406
  const costoPhotoEdit = CREDIT_COSTS.photo_edit;
15343
- const fotoRef = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_fotos").doc(fotoId);
15407
+ const fotoRef = db.doc(
15408
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_fotos", fotoId)
15409
+ );
15344
15410
  const fotoSnap = await fotoRef.get();
15345
15411
  if (!fotoSnap.exists) {
15346
15412
  return { ok: false, code: "FOTO_NOT_FOUND" };
@@ -15354,7 +15420,9 @@ async function photoEditStatusReader(input) {
15354
15420
  const estado = typeof foto.estado === "string" ? foto.estado : "desconocido";
15355
15421
  const ultimoError = typeof foto.ultimoError === "string" ? foto.ultimoError : null;
15356
15422
  const lockId = `photo_edit_${fotoId}`;
15357
- const lockSnap = await db.collection("tenants").doc(tenantId).collection("locks").doc(lockId).get();
15423
+ const lockSnap = await db.doc(
15424
+ docPath({ kind: "tenant-nested", tenantId }, "locks", lockId)
15425
+ ).get();
15358
15426
  let lock = { held: false };
15359
15427
  if (lockSnap.exists) {
15360
15428
  const lockData = lockSnap.data();
@@ -15376,7 +15444,9 @@ async function photoEditStatusReader(input) {
15376
15444
  periodEnd: null,
15377
15445
  costoPhotoEdit
15378
15446
  };
15379
- const creditsSnap = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("credits").doc("main").get();
15447
+ const creditsSnap = await db.doc(
15448
+ docPath({ kind: "brand-nested", tenantId, brandId }, "credits", "main")
15449
+ ).get();
15380
15450
  if (creditsSnap.exists) {
15381
15451
  const creditsData = creditsSnap.data();
15382
15452
  credits = {
@@ -15502,13 +15572,18 @@ var photoEditStatusReaderContract = MartinContractSchema.parse(
15502
15572
  rawContract18
15503
15573
  );
15504
15574
  async function contenidoApprover(input) {
15505
- const { db, tenantId, contenidoId, actor } = input;
15506
- const ref = db.collection("tenants").doc(tenantId).collection("marketing_contenido").doc(contenidoId);
15575
+ const { db, tenantId, brandId, contenidoId, actor } = input;
15576
+ const ref = db.doc(
15577
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_contenido", contenidoId)
15578
+ );
15507
15579
  const snap = await ref.get();
15508
15580
  if (!snap.exists) {
15509
15581
  return { ok: false, code: "CONTENT_NOT_FOUND" };
15510
15582
  }
15511
15583
  const data = snap.data();
15584
+ if (typeof data.brandId === "string" && data.brandId !== brandId) {
15585
+ return { ok: false, code: "CONTENT_BRAND_MISMATCH" };
15586
+ }
15512
15587
  const estadoActual = typeof data.estado === "string" ? data.estado : "";
15513
15588
  if (!esTransicionValida(estadoActual, ESTADO_CONTENIDO.APROBADO)) {
15514
15589
  return {
@@ -15534,6 +15609,7 @@ async function contenidoApprover(input) {
15534
15609
  }
15535
15610
  var ParamsSchema19 = import_zod54.z.object({
15536
15611
  tenantId: import_zod54.z.string().min(1).describe('Tenant identifier (the business account). Example: "your-tenant-id".'),
15612
+ brandId: import_zod54.z.string().min(1).describe('Brand identifier within the tenant. Example: "your-brand-id".'),
15537
15613
  contenidoId: import_zod54.z.string().min(1).describe('Marketing content document id. Example: "BgAeOYTcPaQ0gglhK4PU".'),
15538
15614
  actor: import_zod54.z.object({
15539
15615
  uid: import_zod54.z.string().min(1).describe('User id who is approving. Example: "your-user-uid".'),
@@ -15550,7 +15626,7 @@ var SuccessSchema8 = import_zod54.z.object({
15550
15626
  });
15551
15627
  var FailureSchema8 = import_zod54.z.object({
15552
15628
  ok: import_zod54.z.literal(false),
15553
- code: import_zod54.z.enum(["CONTENT_NOT_FOUND", "INVALID_STATE_TRANSITION"]),
15629
+ code: import_zod54.z.enum(["CONTENT_NOT_FOUND", "INVALID_STATE_TRANSITION", "CONTENT_BRAND_MISMATCH"]),
15554
15630
  estadoActual: import_zod54.z.string().optional()
15555
15631
  });
15556
15632
  var OutputSchema19 = import_zod54.z.discriminatedUnion("ok", [SuccessSchema8, FailureSchema8]);
@@ -15585,7 +15661,7 @@ var rawContract19 = {
15585
15661
  });
15586
15662
  },
15587
15663
  auditAction: "marketing.contenido.aprobar",
15588
- extractTargetPath: (input) => `tenants/${input.tenantId}/marketing_contenido/${input.contenidoId}`,
15664
+ extractTargetPath: (input) => `tenants/${input.tenantId}/brands/${input.brandId}/marketing_contenido/${input.contenidoId}`,
15589
15665
  extractChanges: (_input, output) => ({
15590
15666
  before: output.ok ? { estado: output.estadoAnterior } : null,
15591
15667
  after: output.ok ? { estado: output.nuevoEstado } : null
@@ -15600,13 +15676,18 @@ var contenidoApproverContract = MartinContractSchema.parse(
15600
15676
  rawContract19
15601
15677
  );
15602
15678
  async function contenidoRejecter(input) {
15603
- const { db, tenantId, contenidoId, motivo, actor } = input;
15604
- const ref = db.collection("tenants").doc(tenantId).collection("marketing_contenido").doc(contenidoId);
15679
+ const { db, tenantId, brandId, contenidoId, motivo, actor } = input;
15680
+ const ref = db.doc(
15681
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_contenido", contenidoId)
15682
+ );
15605
15683
  const snap = await ref.get();
15606
15684
  if (!snap.exists) {
15607
15685
  return { ok: false, code: "CONTENT_NOT_FOUND" };
15608
15686
  }
15609
15687
  const data = snap.data();
15688
+ if (typeof data.brandId === "string" && data.brandId !== brandId) {
15689
+ return { ok: false, code: "CONTENT_BRAND_MISMATCH" };
15690
+ }
15610
15691
  const estadoActual = typeof data.estado === "string" ? data.estado : "";
15611
15692
  if (!esTransicionValida(estadoActual, ESTADO_CONTENIDO.RECHAZADO)) {
15612
15693
  return {
@@ -15634,6 +15715,7 @@ async function contenidoRejecter(input) {
15634
15715
  }
15635
15716
  var ParamsSchema20 = import_zod55.z.object({
15636
15717
  tenantId: import_zod55.z.string().min(1).describe('Tenant identifier (the business account). Example: "your-tenant-id".'),
15718
+ brandId: import_zod55.z.string().min(1).describe('Brand identifier within the tenant. Example: "your-brand-id".'),
15637
15719
  contenidoId: import_zod55.z.string().min(1).describe('Marketing content document id. Example: "BgAeOYTcPaQ0gglhK4PU".'),
15638
15720
  motivo: import_zod55.z.string().min(1).describe('Reason for rejection (required, surfaced to tenant in UI). Example: "tono no encaja con la marca".'),
15639
15721
  actor: import_zod55.z.object({
@@ -15652,7 +15734,7 @@ var SuccessSchema9 = import_zod55.z.object({
15652
15734
  });
15653
15735
  var FailureSchema9 = import_zod55.z.object({
15654
15736
  ok: import_zod55.z.literal(false),
15655
- code: import_zod55.z.enum(["CONTENT_NOT_FOUND", "INVALID_STATE_TRANSITION"]),
15737
+ code: import_zod55.z.enum(["CONTENT_NOT_FOUND", "INVALID_STATE_TRANSITION", "CONTENT_BRAND_MISMATCH"]),
15656
15738
  estadoActual: import_zod55.z.string().optional()
15657
15739
  });
15658
15740
  var OutputSchema20 = import_zod55.z.discriminatedUnion("ok", [SuccessSchema9, FailureSchema9]);
@@ -15689,7 +15771,7 @@ var rawContract20 = {
15689
15771
  });
15690
15772
  },
15691
15773
  auditAction: "marketing.contenido.rechazar",
15692
- extractTargetPath: (input) => `tenants/${input.tenantId}/marketing_contenido/${input.contenidoId}`,
15774
+ extractTargetPath: (input) => `tenants/${input.tenantId}/brands/${input.brandId}/marketing_contenido/${input.contenidoId}`,
15693
15775
  extractChanges: (_input, output) => ({
15694
15776
  before: output.ok ? { estado: output.estadoAnterior } : null,
15695
15777
  after: output.ok ? { estado: output.nuevoEstado, rechazadoMotivo: output.motivo } : null
@@ -15706,7 +15788,9 @@ var contenidoRejecterContract = MartinContractSchema.parse(
15706
15788
  async function photoBriefingWriter(input) {
15707
15789
  const { db, tenantId, brandId, semana, necesidades, actor } = input;
15708
15790
  const docId = `${tenantId}_${brandId}_${semana}`;
15709
- const ref = db.collection("tenants").doc(tenantId).collection("marketing_fotobriefings").doc(docId);
15791
+ const ref = db.doc(
15792
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_fotobriefings", docId)
15793
+ );
15710
15794
  const snap = await ref.get();
15711
15795
  const existing = snap.exists ? snap.data() : null;
15712
15796
  const prevNecesidades = Array.isArray(existing?.necesidades) ? existing.necesidades : [];
@@ -15769,7 +15853,7 @@ async function photoBriefingWriter(input) {
15769
15853
  return {
15770
15854
  ok: true,
15771
15855
  briefingId: docId,
15772
- briefingPath: `tenants/${tenantId}/marketing_fotobriefings/${docId}`,
15856
+ briefingPath: docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_fotobriefings", docId),
15773
15857
  totalNecesidades,
15774
15858
  totalCubiertas,
15775
15859
  necesidadesAgregadas: necesidades.length,
@@ -15822,7 +15906,7 @@ var rawContract21 = {
15822
15906
  });
15823
15907
  },
15824
15908
  auditAction: "marketing.foto_briefing.agregar",
15825
- extractTargetPath: (input) => `tenants/${input.tenantId}/marketing_fotobriefings/${input.tenantId}_${input.brandId}_${input.semana}`,
15909
+ extractTargetPath: (input) => `tenants/${input.tenantId}/brands/${input.brandId}/marketing_fotobriefings/${input.tenantId}_${input.brandId}_${input.semana}`,
15826
15910
  extractChanges: (input, output) => ({
15827
15911
  before: null,
15828
15912
  // Helper no captura snapshot pre-merge.
@@ -15844,7 +15928,9 @@ var photoBriefingWriterContract = MartinContractSchema.parse(
15844
15928
  );
15845
15929
  async function tenantContextSetter(input) {
15846
15930
  const { db, tenantId, brandId } = input;
15847
- const brandConfigRef = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main");
15931
+ const brandConfigRef = db.doc(
15932
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
15933
+ );
15848
15934
  const brandSnap = await brandConfigRef.get();
15849
15935
  if (brandSnap.exists) {
15850
15936
  const data = brandSnap.data();
@@ -15856,7 +15942,7 @@ async function tenantContextSetter(input) {
15856
15942
  brandDominio: typeof data.dominio === "string" ? data.dominio : null
15857
15943
  };
15858
15944
  }
15859
- const allBrandsSnap = await db.collection("tenants").doc(tenantId).collection("brands").get();
15945
+ const allBrandsSnap = await db.collection(collectionPath({ kind: "tenant-nested", tenantId }, "brands")).get();
15860
15946
  if (allBrandsSnap.empty) {
15861
15947
  return {
15862
15948
  ok: false,
@@ -15947,13 +16033,18 @@ var tenantContextSetterContract = MartinContractSchema.parse(
15947
16033
  rawContract22
15948
16034
  );
15949
16035
  async function photoDescarter(input) {
15950
- const { db, tenantId, fotoId, actor } = input;
15951
- const ref = db.collection("tenants").doc(tenantId).collection("marketing_fotos").doc(fotoId);
16036
+ const { db, tenantId, brandId, fotoId, actor } = input;
16037
+ const ref = db.doc(
16038
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_fotos", fotoId)
16039
+ );
15952
16040
  const snap = await ref.get();
15953
16041
  if (!snap.exists) {
15954
16042
  return { ok: false, code: "FOTO_NOT_FOUND" };
15955
16043
  }
15956
16044
  const data = snap.data();
16045
+ if (typeof data.brandId === "string" && data.brandId !== brandId) {
16046
+ return { ok: false, code: "FOTO_BRAND_MISMATCH" };
16047
+ }
15957
16048
  const estadoActual = typeof data.estado === "string" ? data.estado : "";
15958
16049
  if (!validarTransicionFoto(estadoActual, ESTADO_FOTO.DESCARTADA)) {
15959
16050
  return {
@@ -15979,6 +16070,7 @@ async function photoDescarter(input) {
15979
16070
  }
15980
16071
  var ParamsSchema23 = import_zod58.z.object({
15981
16072
  tenantId: import_zod58.z.string().min(1).describe('Tenant identifier (the business account). Example: "your-tenant-id".'),
16073
+ brandId: import_zod58.z.string().min(1).describe('Brand identifier within the tenant. Example: "your-brand-id".'),
15982
16074
  fotoId: import_zod58.z.string().min(1).describe('Photo identifier. Example: "your-tenant_your-brand_1234567890_abcdef".'),
15983
16075
  actor: import_zod58.z.object({
15984
16076
  uid: import_zod58.z.string().min(1).describe('User id who is discarding. Example: "your-user-uid".'),
@@ -15995,7 +16087,7 @@ var SuccessSchema11 = import_zod58.z.object({
15995
16087
  });
15996
16088
  var FailureSchema11 = import_zod58.z.object({
15997
16089
  ok: import_zod58.z.literal(false),
15998
- code: import_zod58.z.enum(["FOTO_NOT_FOUND", "INVALID_STATE_TRANSITION"]),
16090
+ code: import_zod58.z.enum(["FOTO_NOT_FOUND", "INVALID_STATE_TRANSITION", "FOTO_BRAND_MISMATCH"]),
15999
16091
  estadoActual: import_zod58.z.string().optional()
16000
16092
  });
16001
16093
  var OutputSchema23 = import_zod58.z.discriminatedUnion("ok", [SuccessSchema11, FailureSchema11]);
@@ -16030,7 +16122,7 @@ var rawContract23 = {
16030
16122
  });
16031
16123
  },
16032
16124
  auditAction: "marketing.foto.descartar",
16033
- extractTargetPath: (input) => `tenants/${input.tenantId}/marketing_fotos/${input.fotoId}`,
16125
+ extractTargetPath: (input) => `tenants/${input.tenantId}/brands/${input.brandId}/marketing_fotos/${input.fotoId}`,
16034
16126
  extractChanges: (_input, output) => ({
16035
16127
  before: output.ok ? { estado: output.estadoAnterior } : null,
16036
16128
  after: output.ok ? { estado: output.nuevoEstado } : null
@@ -16045,13 +16137,18 @@ var photoDescarterContract = MartinContractSchema.parse(
16045
16137
  rawContract23
16046
16138
  );
16047
16139
  async function photoEditadaMarker(input) {
16048
- const { db, tenantId, fotoId, actor } = input;
16049
- const ref = db.collection("tenants").doc(tenantId).collection("marketing_fotos").doc(fotoId);
16140
+ const { db, tenantId, brandId, fotoId, actor } = input;
16141
+ const ref = db.doc(
16142
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_fotos", fotoId)
16143
+ );
16050
16144
  const snap = await ref.get();
16051
16145
  if (!snap.exists) {
16052
16146
  return { ok: false, code: "FOTO_NOT_FOUND" };
16053
16147
  }
16054
16148
  const data = snap.data();
16149
+ if (typeof data.brandId === "string" && data.brandId !== brandId) {
16150
+ return { ok: false, code: "FOTO_BRAND_MISMATCH" };
16151
+ }
16055
16152
  const estadoActual = typeof data.estado === "string" ? data.estado : "";
16056
16153
  const archivoOriginal = typeof data.archivoOriginal === "string" ? data.archivoOriginal : null;
16057
16154
  if (!validarTransicionFoto(estadoActual, ESTADO_FOTO.EDITADA)) {
@@ -16083,6 +16180,7 @@ async function photoEditadaMarker(input) {
16083
16180
  }
16084
16181
  var ParamsSchema24 = import_zod59.z.object({
16085
16182
  tenantId: import_zod59.z.string().min(1).describe('Tenant identifier. Example: "your-tenant-id".'),
16183
+ brandId: import_zod59.z.string().min(1).describe('Brand identifier within the tenant. Example: "your-brand-id".'),
16086
16184
  fotoId: import_zod59.z.string().min(1).describe('Photo identifier. Example: "your-tenant_your-brand_1234567890_abcdef".'),
16087
16185
  actor: import_zod59.z.object({
16088
16186
  uid: import_zod59.z.string().min(1).describe('User id who marks the photo. Example: "your-user-uid".'),
@@ -16100,7 +16198,7 @@ var SuccessSchema12 = import_zod59.z.object({
16100
16198
  });
16101
16199
  var FailureSchema12 = import_zod59.z.object({
16102
16200
  ok: import_zod59.z.literal(false),
16103
- code: import_zod59.z.enum(["FOTO_NOT_FOUND", "INVALID_STATE_TRANSITION", "FOTO_SIN_ARCHIVO_ORIGINAL"]),
16201
+ code: import_zod59.z.enum(["FOTO_NOT_FOUND", "INVALID_STATE_TRANSITION", "FOTO_SIN_ARCHIVO_ORIGINAL", "FOTO_BRAND_MISMATCH"]),
16104
16202
  estadoActual: import_zod59.z.string().optional()
16105
16203
  });
16106
16204
  var OutputSchema24 = import_zod59.z.discriminatedUnion("ok", [SuccessSchema12, FailureSchema12]);
@@ -16135,7 +16233,7 @@ var rawContract24 = {
16135
16233
  });
16136
16234
  },
16137
16235
  auditAction: "marketing.foto.usar_tal_cual",
16138
- extractTargetPath: (input) => `tenants/${input.tenantId}/marketing_fotos/${input.fotoId}`,
16236
+ extractTargetPath: (input) => `tenants/${input.tenantId}/brands/${input.brandId}/marketing_fotos/${input.fotoId}`,
16139
16237
  extractChanges: (_input, output) => ({
16140
16238
  before: output.ok ? { estado: output.estadoAnterior } : null,
16141
16239
  after: output.ok ? { estado: output.nuevoEstado, archivoEditado: output.archivoEditado } : null
@@ -16151,7 +16249,9 @@ var photoEditadaMarkerContract = MartinContractSchema.parse(
16151
16249
  );
16152
16250
  async function brandContextSetter(input) {
16153
16251
  const { db, tenantId, brandId } = input;
16154
- const brandRef = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main");
16252
+ const brandRef = db.doc(
16253
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
16254
+ );
16155
16255
  const brandSnap = await brandRef.get();
16156
16256
  if (!brandSnap.exists) {
16157
16257
  return {
@@ -16634,7 +16734,9 @@ var brandBriefBuilderContract = MartinContractSchema.parse(
16634
16734
  rawContract27
16635
16735
  );
16636
16736
  async function resolveLastImportId(db, tenantId, brandId) {
16637
- const configSnap = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main").get();
16737
+ const configSnap = await db.doc(
16738
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
16739
+ ).get();
16638
16740
  const brand = configSnap.data();
16639
16741
  const canalId = brand?.canalId;
16640
16742
  if (!canalId) return null;
@@ -16645,7 +16747,9 @@ async function resolveLastImportId(db, tenantId, brandId) {
16645
16747
  }
16646
16748
  async function marketingPlanBuilder(input) {
16647
16749
  const { db, tenantId, brandId } = input;
16648
- const configSnap = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main").get();
16750
+ const configSnap = await db.doc(
16751
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
16752
+ ).get();
16649
16753
  if (!configSnap.exists) {
16650
16754
  return { ok: false, error: `Brand "${brandId}" no encontrada`, code: "BRAND_NOT_FOUND" };
16651
16755
  }
@@ -16659,7 +16763,9 @@ async function marketingPlanBuilder(input) {
16659
16763
  }
16660
16764
  const productosQ = await db.collection("productos").where("tenantId", "==", tenantId).limit(100).get();
16661
16765
  const productos = productosQ.docs.map((d) => d.data());
16662
- const histQ = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_contenido").where("estado", "==", ESTADO_CONTENIDO.PUBLICADO).limit(20).get();
16766
+ const histQ = await db.collection(
16767
+ collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_contenido")
16768
+ ).where("estado", "==", ESTADO_CONTENIDO.PUBLICADO).limit(20).get();
16663
16769
  const historial = histQ.docs.map((d) => d.data());
16664
16770
  const lastImportId = await resolveLastImportId(db, tenantId, brandId);
16665
16771
  let colecciones = [];
@@ -16704,7 +16810,9 @@ async function marketingPlanBuilder(input) {
16704
16810
  shopifyBlogs = Object.values(blogMap);
16705
16811
  }
16706
16812
  const existingBlogStrategy = brand.blogStrategy;
16707
- const seoSuggestionsCol = db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_suggestions_seo");
16813
+ const seoSuggestionsCol = db.collection(
16814
+ collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_suggestions_seo")
16815
+ );
16708
16816
  const appliedSnap = await seoSuggestionsCol.where("intent", "==", "seo_meta").where("target.category", "==", "collection").where("estado", "==", "aplicada").get();
16709
16817
  const appliedChanges = [];
16710
16818
  for (const d of appliedSnap.docs) {
@@ -17197,10 +17305,11 @@ var rawContract29 = {
17197
17305
  after: output.ok ? {
17198
17306
  contenidoId: output.contenidoId,
17199
17307
  plataforma: output.plataforma,
17200
- tipo: input.tipo,
17201
- keyword: input.keyword,
17202
- fotoId: input.fotoId,
17203
- calendarioItemRef: input.calendarioItemRef,
17308
+ // Firestore rechaza undefined — normalizamos a null cuando el caller omite.
17309
+ tipo: input.tipo ?? null,
17310
+ keyword: input.keyword ?? null,
17311
+ fotoId: input.fotoId ?? null,
17312
+ calendarioItemRef: input.calendarioItemRef ?? null,
17204
17313
  descartados: output.descartados,
17205
17314
  pipelineLinked: output.pipelineLinked
17206
17315
  } : null
@@ -17874,7 +17983,9 @@ async function slotAssetFinder(input) {
17874
17983
  const limit = input.limit ?? 5;
17875
17984
  const photoThreshold = deps.thresholds?.photos ?? DEFAULT_PHOTO_THRESHOLD;
17876
17985
  const shopifyThreshold = deps.thresholds?.shopify ?? DEFAULT_SHOPIFY_THRESHOLD;
17877
- const configSnap = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main").get();
17986
+ const configSnap = await db.doc(
17987
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
17988
+ ).get();
17878
17989
  if (!configSnap.exists) {
17879
17990
  return { ok: false, error: `Brand "${brandId}" no encontrada`, code: "BRAND_NOT_FOUND" };
17880
17991
  }
@@ -18079,7 +18190,9 @@ function cosineSimilarity(a, b) {
18079
18190
  async function canvaTemplateSelector(input) {
18080
18191
  const { db, tenantId, brandId, plataforma, tipoContenido, keyword, deps } = input;
18081
18192
  const threshold = deps.threshold ?? DEFAULT_SIMILARITY_THRESHOLD;
18082
- const configSnap = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main").get();
18193
+ const configSnap = await db.doc(
18194
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
18195
+ ).get();
18083
18196
  if (!configSnap.exists) {
18084
18197
  return { plantillaId: null, motivo: "brand_no_encontrada" };
18085
18198
  }
@@ -18314,12 +18427,14 @@ function instruccionesParaError(code, details, lang) {
18314
18427
  }
18315
18428
  async function photoDirectorPlan(input) {
18316
18429
  const { db, tenantId, brandId, fotoId, deps } = input;
18317
- const fotoQ = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_fotos").where("id", "==", fotoId).limit(1).get();
18430
+ const fotoQ = await db.collection(collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_fotos")).where("id", "==", fotoId).limit(1).get();
18318
18431
  if (fotoQ.empty) {
18319
18432
  return { ok: false, code: "FOTO_NOT_FOUND", error: `Foto ${fotoId} no encontrada` };
18320
18433
  }
18321
18434
  const foto = fotoQ.docs[0].data();
18322
- const configSnap = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main").get();
18435
+ const configSnap = await db.doc(
18436
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
18437
+ ).get();
18323
18438
  const brand = configSnap.exists ? configSnap.data() : null;
18324
18439
  const brandBrief = (brand?.brandBrief ?? null) || {};
18325
18440
  const visualRules = brandBrief.visualRules ?? {};
@@ -18895,12 +19010,16 @@ Campos comunes de save_generated_content:
18895
19010
  tipo: string (del slot)
18896
19011
  `;
18897
19012
  async function buildTenantContext(db, tenantId, brandId) {
18898
- const configSnap = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main").get();
19013
+ const configSnap = await db.doc(
19014
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
19015
+ ).get();
18899
19016
  if (!configSnap.exists) return "";
18900
19017
  const brand = configSnap.data();
18901
19018
  const [fotosQ, contenidoQ, productosQ] = await Promise.all([
18902
- db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_fotos").where("estado", "==", ESTADO_FOTO.EDITADA).limit(10).get(),
18903
- db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_contenido").where("estado", "==", ESTADO_CONTENIDO.PUBLICADO).limit(10).get(),
19019
+ db.collection(collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_fotos")).where("estado", "==", ESTADO_FOTO.EDITADA).limit(10).get(),
19020
+ db.collection(
19021
+ collectionPath({ kind: "brand-nested", tenantId, brandId }, "marketing_contenido")
19022
+ ).where("estado", "==", ESTADO_CONTENIDO.PUBLICADO).limit(10).get(),
18904
19023
  db.collection("productos").where("tenantId", "==", tenantId).limit(50).get()
18905
19024
  ]);
18906
19025
  const fotosEditadas = fotosQ.docs.map((d) => d.data());
@@ -19109,7 +19228,9 @@ async function buildSystemPrompt(input) {
19109
19228
  const p2 = PARTE_2_REGLAS.replace("{{SHAPES_BLOCK}}", shapesBlock);
19110
19229
  return p1 + p2;
19111
19230
  }
19112
- const configSnap = await db.collection("tenants").doc(tenantId).collection("brands").doc(brandId).collection("marketing_config").doc("main").get();
19231
+ const configSnap = await db.doc(
19232
+ docPath({ kind: "brand-nested", tenantId, brandId }, "marketing_config", "main")
19233
+ ).get();
19113
19234
  const brand = configSnap.exists ? configSnap.data() : null;
19114
19235
  let prompt = PARTE_1_IDENTIDAD.replace(
19115
19236
  "{{brand_nombre}}",
@@ -20244,7 +20365,7 @@ IMPORTANT \u2014 'razon' field: the tenant sees this in the app. Write in friend
20244
20365
  contract: photoDescarterContract,
20245
20366
  helper: photoDescarter,
20246
20367
  callable: callPhotoDescarter,
20247
- input: { tenantId, fotoId, actor },
20368
+ input: { tenantId, brandId, fotoId, actor },
20248
20369
  ctx
20249
20370
  });
20250
20371
  let payload;
@@ -20282,7 +20403,7 @@ IMPORTANT \u2014 'razon' field: the tenant sees this in the app. Write in friend
20282
20403
  contract: photoEditadaMarkerContract,
20283
20404
  helper: photoEditadaMarker,
20284
20405
  callable: callPhotoEditadaMarker,
20285
- input: { tenantId, fotoId, actor },
20406
+ input: { tenantId, brandId, fotoId, actor },
20286
20407
  ctx
20287
20408
  });
20288
20409
  let payload;
@@ -21094,7 +21215,7 @@ NUNCA uses update_calendar_slot para asignar fotos \u2014 eso escribe en la agen
21094
21215
  contract: contenidoApproverContract,
21095
21216
  helper: contenidoApprover,
21096
21217
  callable: callContenidoApprover,
21097
- input: { tenantId, contenidoId, actor },
21218
+ input: { tenantId, brandId, contenidoId, actor },
21098
21219
  ctx
21099
21220
  });
21100
21221
  let payload;
@@ -21135,7 +21256,7 @@ NUNCA uses update_calendar_slot para asignar fotos \u2014 eso escribe en la agen
21135
21256
  contract: contenidoRejecterContract,
21136
21257
  helper: contenidoRejecter,
21137
21258
  callable: callContenidoRejecter,
21138
- input: { tenantId, contenidoId, motivo, actor },
21259
+ input: { tenantId, brandId, contenidoId, motivo, actor },
21139
21260
  ctx
21140
21261
  });
21141
21262
  let payload;