hermoso 0.1.181 → 0.1.183

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.
Files changed (2) hide show
  1. package/mcp/tools.mjs +21 -13
  2. package/package.json +1 -1
package/mcp/tools.mjs CHANGED
@@ -460,7 +460,9 @@ const geoLine = (r) => {
460
460
  const dims = d.deliveredWidth && d.deliveredHeight ? `${d.deliveredWidth}x${d.deliveredHeight}` : '';
461
461
  const bits = [dims, d.deliveredAspect, d.deliveredResolution].filter(Boolean);
462
462
  if (!bits.length && !d.deliveryNote) return '';
463
- return (bits.length ? `\nDelivered: ${bits.join(' · ')} (measured off the file)` : '') + (d.deliveryNote ? `\n⚠ ${d.deliveryNote}` : '');
463
+ // A worker's own disclosure rides the read-back (2026-09-01): the Multiplier reports `mechanism` (motion-transfer vs the
464
+ // video-edit fallback) and a `note` saying why — a caller who never sees them reads an edited take as a driven one.
465
+ return (bits.length ? `\nDelivered: ${bits.join(' · ')} (measured off the file)` : '') + (d.deliveryNote ? `\n⚠ ${d.deliveryNote}` : '') + (d.mechanism ? `\nMechanism: ${d.mechanism}` : '') + (d.note && d.note !== d.deliveryNote ? `\nℹ ${d.note}` : '');
464
466
  };
465
467
 
466
468
  // Shared outputSchema fields for the job-based render tools (the renderJob result that becomes structuredContent).
@@ -2901,17 +2903,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
2901
2903
  return ok(`Brands on this account:\n${lines}\n\nSwitch with use_brand.${sharedTxt}`, { ...d, sharedWorkspaces: shared });
2902
2904
  }));
2903
2905
 
2904
- server.registerTool('use_brand', {
2905
- title: 'Switch brand',
2906
- description: "Pin which brand this connection acts on (multi-brand accounts). Pass the brand id or exact name from list_brands. Works for a brand on your own account AND for a workspace another account SHARED with you — for a shared one pass its name or the profile id list_brands prints, and access is verified against your real membership before it is pinned. Persists for this API key until changed, on every surface (hosted connector included — no environment variables, no restart).",
2907
- inputSchema: { brand: z.string().describe('brand id (e.g. default / p_xxx), its exact name from list_brands, or the profile id of a workspace shared with you') },
2908
- outputSchema: {
2909
- ok: z.boolean().optional().describe('true when the brand switch persisted'),
2910
- brand: z.any().optional().describe('the now-active brand ({id, name})'),
2911
- shared: z.boolean().optional().describe('true when the pinned workspace is owned by another account'),
2912
- },
2913
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
2914
- }, wrap(async ({ brand }) => {
2906
+ const makeUseBrandHandler = (ctx) => wrap(async ({ brand }) => {
2915
2907
  const d = await apiGet('/api/brands');
2916
2908
  const want = String(brand || '').trim().toLowerCase();
2917
2909
  const hit = (d.brands || []).find(b => b.id.toLowerCase() === want || String(b.name || '').toLowerCase() === want);
@@ -2947,7 +2939,23 @@ function buildTools(rawServer, opts = {}, sink = null) {
2947
2939
  const own = (d.brands || []).map(b => `• ${b.name} (id: ${b.id})`).join('\n');
2948
2940
  const shr = shared.map(w => `• ${w.name || 'Shared workspace'}${w.ownerName ? ` — ${w.ownerName}` : ''} (id: ${w.profileUuid}, shared with you)`).join('\n');
2949
2941
  return { content: [{ type: 'text', text: `No brand matching "${brand}". Available:\n${own}${shr ? `\n${shr}` : ''}` }], isError: true };
2950
- }));
2942
+ });
2943
+ server.registerTool('use_brand', {
2944
+ title: 'Switch brand',
2945
+ description: "Pin which brand this connection acts on (multi-brand accounts). Pass the brand id or exact name from list_brands. Works for a brand on your own account AND for a workspace another account SHARED with you — for a shared one pass its name or the profile id list_brands prints, and access is verified against your real membership before it is pinned. Persists for this API key until changed, on every surface (hosted connector included — no environment variables, no restart).",
2946
+ inputSchema: { brand: z.string().describe('brand id (e.g. default / p_xxx), its exact name from list_brands, or the profile id of a workspace shared with you') },
2947
+ outputSchema: {
2948
+ ok: z.boolean().optional().describe('true when the brand switch persisted'),
2949
+ brand: z.any().optional().describe('the now-active brand ({id, name})'),
2950
+ shared: z.boolean().optional().describe('true when the pinned workspace is owned by another account'),
2951
+ },
2952
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
2953
+ // PER-SESSION, LIKE enable_tools (2026-09-01): this handler re-gates `ctx.handleOf` — the roster of THE SESSION THAT
2954
+ // CALLED IT. A plain closure captured the ctx of the session that BUILT the canon (the first in the process), so every
2955
+ // later session got the right sentence ("re-scoped to 28 connectors") and the wrong roster: measured on prod, a
2956
+ // session pinned to a 0-connector brand switched to one with 28 and still listed 113 tools; `enable_tools` in the
2957
+ // same session said "3 more". It "worked" the morning it shipped because that measurement WAS the first session.
2958
+ }, sessionBound(makeUseBrandHandler, ctx));
2951
2959
 
2952
2960
  // ── BRAND WORKSPACE LIFECYCLE. draft_brand OVERWRITES the active workspace's brand; it does not mint one — so
2953
2961
  // without these an agency running Hermoso purely through an agent could never take on a second client.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hermoso",
3
- "version": "0.1.181",
3
+ "version": "0.1.183",
4
4
  "mcpName": "io.github.hermoso-ai/hermoso",
5
5
  "description": "AI ad studio and marketing MCP server with 745 tools. Research the ads already running in any market, generate finished image, video and UGC avatar ads, publish and schedule them to your own channels, build and manage the ad campaigns behind them, and read what they achieved. AD PLATFORMS: Meta, Google Ads, TikTok Ads, LinkedIn Ads, Reddit Ads, X Ads, Pinterest Ads, Snapchat Ads, Microsoft Advertising, Apple Search Ads and ChatGPT Ads, plus product feeds in Google Merchant Center. PUBLISHING AND SCHEDULING: Facebook, Instagram, Threads, TikTok, YouTube, X, LinkedIn, Pinterest, Bluesky and Telegram. AD RESEARCH: the Meta, Google and LinkedIn ad libraries plus organic TikTok, Instagram, YouTube, Threads and Reddit. ANALYTICS: Google Analytics 4, Google Search Console and every connected platform's own post and campaign insights. Also brand onboarding, 50+ image and video generation models, ad scoring, competitor teardowns, Google Drive and OneDrive, a CLI and installable Claude skills.",
6
6
  "type": "module",