@seldonframe/mcp 1.40.13 → 1.40.14

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/package.json +2 -1
  2. package/src/welcome.js +2 -2
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@seldonframe/mcp",
3
- "version": "1.40.13",
3
+ "version": "1.40.14",
4
+ "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.40.14: GOOGLE ANALYTICS — host-aware install. Wires gtag.js (Google Analytics 4) into the seldonframe.com marketing site + app.seldonframe.com operator dashboard, but DELIBERATELY skips it on every operator workspace subdomain (*.app.seldonframe.com). The privacy reasoning: operator workspace subdomains serve THE OPERATOR'S customers, not SeldonFrame's. Aggregating their browsing data under SeldonFrame's measurement ID would violate Google's TOS, be ethically wrong (thousands of unrelated businesses' customers tracked under one ID without their consent), and confuse operator-owned GA setups (a future product feature where operators paste their own measurement ID into workspace settings — we don't want double-hits). NEW components/analytics/google-analytics.tsx with two exports: GoogleAnalytics (the Script tags using next/script afterInteractive strategy) and shouldRenderGoogleAnalytics(host) helper that returns true only for explicit allowlist entries (seldonframe.com, www.seldonframe.com, app.seldonframe.com). Root app/layout.tsx now async, reads headers().get('host') server-side, conditionally renders <GoogleAnalytics> when both NEXT_PUBLIC_GA_MEASUREMENT_ID env var is set AND the host passes the allowlist. Workspace subdomains, Vercel preview deploys, localhost dev, anything not explicitly allowlisted → no GA. NEW env var REQUIRED: NEXT_PUBLIC_GA_MEASUREMENT_ID (e.g. G-66W62KE0ST). Set in Vercel Project Settings → Environment Variables for Production + Preview + Development. NO migrations, NO new MCP tools. Backend redeploy required for the layout change. EXPECTED IMPACT: pageview tracking lights up on the marketing site + operator dashboard within minutes of Vercel redeploy + env var set. Operator workspace visitors generate zero tracking pings under SeldonFrame's measurement ID — clean privacy boundary, clean TOS compliance, future-proof for per-workspace operator-owned GA tags.",
4
5
  "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.40.13: BOOKINGS CALENDAR — CARDS POSITIONED AT THEIR ACTUAL TIME. Pre-1.40.13 the /bookings dashboard week-view rendered booking cards as a flexbox column at the TOP of each day cell, regardless of the appointment's start time. The 12-hour time column on the left (8:00 → 19:00) was purely decorative. A 1 PM booking appeared visually at the 8 AM row with '1:00 PM' printed inside the card — confusing for any operator scanning the calendar. v1.40.13 wires each booking card to the time grid via absolute positioning. NEW constants WEEK_VIEW_START_HOUR=8, WEEK_VIEW_END_HOUR=20, HOUR_HEIGHT_PX=80 (matches the existing h-20 time-column rows) anchor the grid. NEW timeInZone(date, tz) helper extracts hours/minutes from a Date in the workspace timezone via Intl.DateTimeFormat round-trip (browser-local getHours() drifts when viewer TZ ≠ workspace TZ — same fix pattern as v1.40.2's utcMomentForLocalTime helper). NEW bookingTopPx(startsAt, tz) computes the vertical pixel offset from the grid's start (clamped so off-hours bookings render at the boundary, not off-screen). Day columns now use `relative` positioning + fixed `height: 960px` (12 hours × 80px) matching the time column. Booking cards are `absolute` with `top: bookingTopPx(...)` + `height: 76px` (60 minute default duration). Hour grid lines added to each day column so the visual rhythm aligns with the time labels on the left. NO timezone bug: the position uses the workspace timezone consistently with v1.40.9's display fix — a 1 PM workspace-local booking lands at the 13:00 row, regardless of where the operator is viewing from. NO data shape change: bookings already include startsAt as ISO; the fix is pure layout. NO migrations, NO new MCP tools, NO new env vars. Backend redeploy required. EXPECTED IMPACT: the operator dashboard's calendar visually matches reality — a 1pm appointment appears at the 1pm row, not at the top of the day column. Multi-booking days stack visually correctly. Off-hours bookings (e.g. 6 AM emergency or 9 PM after-hours) clamp to the visible boundary with their real time visible inside. Caps the v1.40.12 ship.",
5
6
  "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.40.12: VALIDATOR + BOOKING-FLOW PROMPT FIX. Real conversation in production exposed two intertwined bugs in the website-chatbot booking flow. Transcript: user gave name/email/phone in one message → agent immediately called book_appointment (premature) AND asked 'go ahead?' (contradictory — booking already happened) → user replied 'go ahead' → agent tried to acknowledge ('You're booked for Monday at 1pm') → no_hallucinated_state_change validator blocked it → regen blocked → fallback ('Let me look into that for you') → conversation stalled, user confused. (1) VALIDATOR FIX. noHallucinatedStateChange in lib/agents/validators.ts was scoped to THIS TURN's tool calls only. When the agent legitimately acknowledged a booking that succeeded in the PREVIOUS turn, the validator saw no book_appointment in the current turn and flagged it as hallucination. v1.40.12 adds a `recentSuccessfulTools: string[]` field to ValidatorContext. The runtime scans all assistant turns in the conversation history and builds a set of tool names that succeeded with ok=true. The validator unions this with the current-turn set before checking action-pattern matches. Legitimate follow-up acknowledgments (turn N+1 saying 'you're booked' after turn N's successful book_appointment) now pass. Hallucinations (turn N saying 'you're booked' when no book_appointment was ever called in this conversation) still fail. (2) PROMPT FIX. be-smart-by-default.ts rule 5 'Confirm before destructive actions' was too vague — said 'wait for explicit yes' but didn't enforce the strict ORDER of operations. The agent in production interpreted 'confirm before destructive actions' as 'present details AND book in the same turn, then ask for confirmation' — leading to the contradictory 'go ahead?' question about something already done. v1.40.12 rewrites rule 5 with explicit 5-step ordered sequence: gather info → present summary (one sentence ending in '?') → WAIT → on explicit 'yes', call tool → on tool ok=true, acknowledge in one sentence. Also added explicit text: 'DO NOT call the tool in the same turn as the confirmation summary. The confirmation summary turn does NOT include a tool call.' Rule 6 also extended with: 'IT IS OK to acknowledge a tool that succeeded in a PREVIOUS turn' to dovetail with the validator fix. NO new env vars, NO migrations, NO new MCP tools. Backend redeploy required. EXPECTED IMPACT: bookings flow correctly — agent presents details, waits, gets explicit confirmation, calls the tool, acknowledges the result. No more 'go ahead?' contradiction. No more fallback after the user said go ahead. Both the prompt-side (prevent the antecedent confusion) and validator-side (don't punish legitimate follow-up acknowledgments) covered.",
6
7
  "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.40.11: MOBILE CHATBOT UX HOTFIX. Two real bugs surfaced on first published-agent test: (1) Mobile horizontal scroll inside the chat panel — long URLs/tokens in assistant replies pushed the panel into horizontal overflow on mobile widths. The .sf-agent-messages container had overflow-y:auto but no overflow-x:hidden, and the .sf-agent-msg bubbles had word-wrap:break-word but no overflow-wrap:anywhere. v1.40.11 adds overflow-x:hidden + min-width:0 to .sf-agent-messages and overflow-wrap:anywhere + min-width:0 to .sf-agent-msg. The min-width:0 is required to override the flex-default min-width:auto which would otherwise refuse to shrink the message bubble below its content size. Long URLs, long emails, long IDs now wrap inside the bubble instead of pushing the panel sideways. (2) Input loses focus + keyboard dismisses after send on iOS. Pre-1.40.11 the submit handler set inputEl.disabled=true during send and inputEl.disabled=false + inputEl.focus() after. On iOS Safari, setting disabled=true dismisses the soft keyboard. The later .focus() call — running outside a user-gesture event handler because of the async fetch + SSE chain — cannot reopen the keyboard. Result: user sends message, keyboard dismisses, has to tap the input again to send the next message. Mobile UX death. v1.40.11 reframes: input stays ENABLED throughout the send (keyboard stays up, focus preserved), the SEND BUTTON gets disabled instead (visual feedback that the turn is in flight). New `sending` flag in the handler closure prevents double-submit without touching the input element. The .focus() call after send is removed — input was never blurred, so focus is preserved naturally. NO new env vars, NO migrations, NO new MCP tools. Backend redeploy required for both fixes (embed.js is server-rendered per request — bubble auto-pulls new CSS + new handler on next page load, no re-embed needed by operators). EXPECTED IMPACT: chat conversations on mobile feel like native messaging apps — keyboard stays up across multiple sends, no horizontal scroll regardless of how chatty the agent gets. Caps the v1.40.10 ship. NOT INCLUDED IN THIS SHIP: validator no_hallucinated_state_change failures (needs conversation transcript to diagnose per-case), AUTH_SECRET missing in Vercel env (operator env config, unrelated to chatbot code).",
package/src/welcome.js CHANGED
@@ -8,7 +8,7 @@
8
8
  // stripped. `create_full_workspace` is the only workspace-creation
9
9
  // path mentioned anywhere in this briefing.
10
10
 
11
- export const VERSION = "1.40.13";
11
+ export const VERSION = "1.40.14";
12
12
 
13
13
  export const WELCOME_MARKDOWN = `# SeldonFrame — create a real Business OS in one conversation
14
14
 
@@ -485,4 +485,4 @@ admin dashboard. Pre-fills their email automatically.
485
485
  <https://seldonframe.com> · **Discord:** <https://discord.gg/sbVUu976NW>
486
486
  `;
487
487
 
488
- export const FIRST_CALL_BANNER = `🚀 SeldonFrame v1.40.13 is connected. STEP ZERO: for any workspace task call get_workspace_state({workspace_id}) FIRST — returns workspace identity + integrations status + agents with inline health stats + counts + tailored next_steps in ONE round-trip. Replaces 4-6 progressive discovery calls. ANTI-PATTERNS: don't ls/cat/read .env (SF is hosted, not local files); don't check node/npm versions (irrelevant); don't ask 'is Anthropic key configured?' (state response tells you); don't create a duplicate agent (state response shows existing ones — use update_website_chatbot instead of build_website_chatbot when one exists). Token-budget concept removed in v1.27.9 — ignore stale references. CAPABILITY MAP — pick the right primitive BEFORE exploring tools: (a) "build me a website" → WORKSPACE → create_workspace_v2 + block tools. (b) "add a chatbot to my website / landing page" → AGENT (web chat) → build_website_chatbot (v1.28+ skill bundle: configure_llm + create_agent + publish-test in 1 call; auto-detects ANTHROPIC_API_KEY from env). Drop to primitives (configure_llm_provider + create_agent + publish_agent) only for custom flows. CRITICAL ANTI-PATTERN: chat widgets are NOT blocks. Don't search list_blocks for chat — chat agents are a separate primitive (v1.26+). (c) "auto-reply to inbound SMS/email" → CONVERSATION → send_conversation_turn (one-shot Soul-aware reply, not a website widget). (d) "add hero/services/faq/cta section" → BLOCK → get_block_skill + persist_block. (e) "send campaign email/sms" → MESSAGING. (f) CRM ops → list_contacts/create_deal/etc. CHATBOT CANONICAL FLOW (v1.28+ — 1 call instead of 5): build_website_chatbot({workspace_id, name, faq, pricing_facts, greeting}) → wraps configure_llm + create_agent + publish-test in one call, auto-detects ANTHROPIC_API_KEY from env. Then: operator sandbox-tests at /agents/[id]/test → publish_agent({status:"live"}) auto-runs 8-scenario eval gate (≥87.5% pass required). Observability tools after publish: list_agents, tail_agent_conversations, get_agent_conversation, get_agent_metrics, run_agent_evals, replay_conversation. Dashboard surfaces /agents, /agents/[id]/test, /agents/[id]/settings, /agents/[id]/evals, /agents/[id]/conversations let operators iterate without leaving the browser. WORKSPACE FLOW (legacy reference): create_workspace_v2 → IN PARALLEL for all 7 recommended_blocks (hero, services, about, faq, cta, booking, intake): get_block_skill + persist_block → complete_workspace_v2 → finalize_workspace({workspace_id, email}). Run blocks in PARALLEL (Promise.all) — sequential takes 60+ seconds. v1.10+ CUSTOMIZE: regenerate_block, upload_workspace_image (image_url preferred over base64). v1.11+ STRUCTURAL: get_landing_structure, move_section, delete_section. v1.12+ COMPOSITES: add_composite_section / update_composite_section — manifest ANY block from 12 low-level primitives. Skipping finalize_workspace leaves the operator with no admin login. Every URL is real. NEVER create local files. v1.34+ UI POLISH (smart defaults + optional levers): every published page ships with motion baked in (RevealOnScroll on sections, Stagger on grid blocks, HoverLift on CTAs) — no operator action required. For operators who want to tune intensity: apply_motion_preset({preset: "minimal"|"subtle"|"balanced"|"editorial"}). For operators bringing their own design system: apply_design_md({design_md_content}) accepts the Google Labs DESIGN.md format; import_claude_design_handoff({bundle}) accepts Claude Design's handoff bundle. All three tools are OPTIONAL — surface them only when the operator's vibe asks for it ("make it feel premium", "I have a brand kit", "I just exported from Claude Design"). Never push these on a user who just wants a working site. v1.36+ PROMPT-FATIGUE GUIDANCE: workspace creation typically fires 8–18 MCP tool calls (workspace + blocks + agent + booking + intake + landing). Claude Code prompts on each by default and create_landing_page's Puck schema is verbose JSON the operator should NOT read. DO TWO THINGS to avoid spamming: (1) RECOMMEND THE OPERATOR pick option 2 ('Yes, and do not ask again for seldonframe – <tool>') the FIRST time create_landing_page / persist_block / build_website_chatbot fire — Claude Code caches per-tool autoapprove for the project, the rest of workspace creation runs without interrupting them. Mention this proactively before the flow starts. (2) BUNDLE TOOL CALLS in parallel via Promise.all — never call get_block_skill + persist_block sequentially for 7 blocks; fire them all at once. v1.36+ LANDING-PAGE COMPOSITION FOR LOCAL-SERVICE BUSINESSES: for trades/service businesses (plumbing, HVAC, locksmith, electrician, roofing, towing, dental, salon, mobile mechanic), default to a 10-section composition: navbar → hero (branded gradient empty-state when no photo) → emergencyStrip (sticky brand-colored banner with phone) → trust strip via the Grid block → servicesGrid (per-service cards with price + duration + Book CTA — replaces the SaaS-style pricing block) → benefits (3 differentiators) → process (3-step what-happens-after-booking) → serviceArea (chip cloud of cities/neighborhoods) → testimonials (5+ quotes — placeholder OK pre-launch) → faq → cta → footer. The Pricing block belongs to TIER-based businesses (SaaS, coaches, gyms) NOT per-service businesses — substitute servicesGrid. v1.36.0 added the servicesGrid, emergencyStrip, serviceArea block types — use them when persisting landing pages for trades verticals. The hero block ships a branded-gradient empty state when heroImage is missing; if the operator does not provide a photo, suggest a stock-photo prompt ('your tech truck on a job, sunset shot in Phoenix') so they can drop one in via /landing → Edit later. v1.37.0 GOOGLE MAPS PASTE → WORKSPACE: when the operator pastes a Google Maps business listing (Top Plumbing Experts, ABC Plumbing of San Antonio, etc.), use create_workspace_from_google_paste — NOT create_full_workspace. Same backend pipeline + same finalize_workspace follow-up; the paste-tool's docstring documents the extraction rules (name → bold title, phone → phone-icon row, address → location-pin line, services → categories chip row + 'Services' section deduped, rating + count → '4.7 ★ (950)' element, weekly_hours → 'Monday: 9 AM-5 PM, Tuesday: closed' parsed into canonical {monday:{enabled:true,start:'09:00',end:'17:00'},...} shape). The weekly_hours field flows DIRECTLY into the booking template's metadata.availability, so the operator's actual hours appear on the public /book page on first GET — no separate configure_booking call. 'Open 24 hours' → start:'00:00', end:'23:59'. 'Closed' day → enabled:false. Wrong key shape (e.g. 'mon' instead of 'monday') is silently dropped by the backend and falls back to Mon-Fri 9-5 defaults. NO Google Places API key required — the paste IS the source of truth. v1.36.4 paired fix: the booking page's normalizeAvailability now accepts both 3-letter and full-name day keys, so any legacy rows already in the DB also hydrate correctly. v1.38.3 ADDITIONAL QUALITY BLOCKS: (a) projectGallery — 6-photo masonry of stock photos auto-fetched per service via Unsplash from queries Claude generates inside enhance-blocks; closes the "feels populated" gap that's the single biggest visible difference between a generic SF workspace and a real-business landing page. (b) stickyMobileCTA — fixed bottom-of-screen Call/Book bar, MOBILE ONLY (md:hidden), industry-standard for trades sites with 2-3x mobile booking lift; auto-included whenever a phone is set. (c) testimonial synthesis from Maps paste — when the operator pastes a Google Maps listing that contains review excerpts, Claude Code extracts them VERBATIM into a 'testimonials' field on create_workspace_from_google_paste; backend renders them as-is. NEVER fabricated — if the paste has no review text, the testimonials block is OMITTED from the page entirely (better empty than fake). The testimonials field in the MCP tool input is OPTIONAL — Claude must omit it when the paste doesn't contain real review text. v1.38.0 ATOMIC HORMOZI-QUALITY OUTPUT: every workspace produced by create_full_workspace OR create_workspace_from_google_paste now ships with Hormozi value-equation hero copy + per-business Unsplash photos + scroll-triggered motion baked in — atomically, no follow-up tool calls, no operator action. Pre-1.38.0 the atomic create produced canned 'Welcome to X' copy from personality content templates; only workspaces where Claude Code did the BLOCK-AS-SKILL flow afterward (get_block_skill + persist_block per block) got the rich output. Tirionforge HVAC happened to look great because of that follow-up; atlantic-plumbing didn't get it. v1.38.0 closes the gap by adding Step 12.7 inside the orchestrator: ONE Claude Opus 4.7 call generates hero + servicesGrid + about + benefits + process + faq + cta from src/blocks/*/SKILL.md (the exact same fat skill files Claude Code reads via get_block_skill — single source of truth, no duplication). Output writes to landingPages.sections (LandingPageSection[]), contentHtml/Css get NULLED, route /l/[orgSlug]/[slug] falls through to <PageRenderer> which auto-wraps below-fold sections in <RevealOnScroll>. Hormozi copy + per-business Unsplash + motion all light up at once. EMAIL-FIRST GUARDRAIL UNCHANGED: create_full_workspace + create_workspace_from_google_paste both still RETURN BEFORE THE EMAIL DANCE — operator can't see URLs until they reply with email, finalize_workspace mints the magic admin link + sends the welcome email via Resend. BYOK pass-through: getAIClient checks organizations.integrations.anthropic.apiKey first, falls back to platform ANTHROPIC_API_KEY env. Cost ~$0.10 per workspace, irrelevant since the operator typically supplies their own key. SOFT-FAIL: any error in Step 12.7 leaves the workspace valid (canned-copy Path A intact); never blocks creation. EXPECTED IMPACT: every fresh workspace looks $10k-tier first-shot, no "create workspace then enhance blocks" two-step dance. v1.40.3 IMAGE PIPELINE FOOLPROOFING: removed the deprecated source.unsplash.com keyless fallback from BOTH resolveHeroImageUrlForQuery and resolveGalleryImageUrlsForQueries. Pre-1.40.3 when the official Unsplash API errored or returned no results we fell back to https://source.unsplash.com/{w}x{h}/?{query} — that endpoint is deprecated and now frequently returns broken responses, which were stored verbatim in landingPages.sections and rendered as broken-image icons before the v1.40.2 onError handler eventually flipped state. The HERO Aesthetic Co. test exposed this: hero showed a broken-image icon despite the gallery rendering 4 medspa photos correctly (proving onError wasn't reliably catching deprecated-CDN failures). Fix: never store a known-broken URL. Hero query miss → empty string → branded-gradient empty-state from frame zero (no broken icon ever). Gallery query miss → SKIP that slot → grid auto-reflows to a clean 4-tile composition instead of 6 tiles with 2 broken. Trade-off: workspaces without UNSPLASH_ACCESS_KEY now ship gradient hero + empty gallery (better than broken images). Operators get full control by setting the env var or replacing images post-launch via update_landing_section. ws1-webhook-pricing-fixes branch ships this; redeploy required.`;
488
+ export const FIRST_CALL_BANNER = `🚀 SeldonFrame v1.40.14 is connected. STEP ZERO: for any workspace task call get_workspace_state({workspace_id}) FIRST — returns workspace identity + integrations status + agents with inline health stats + counts + tailored next_steps in ONE round-trip. Replaces 4-6 progressive discovery calls. ANTI-PATTERNS: don't ls/cat/read .env (SF is hosted, not local files); don't check node/npm versions (irrelevant); don't ask 'is Anthropic key configured?' (state response tells you); don't create a duplicate agent (state response shows existing ones — use update_website_chatbot instead of build_website_chatbot when one exists). Token-budget concept removed in v1.27.9 — ignore stale references. CAPABILITY MAP — pick the right primitive BEFORE exploring tools: (a) "build me a website" → WORKSPACE → create_workspace_v2 + block tools. (b) "add a chatbot to my website / landing page" → AGENT (web chat) → build_website_chatbot (v1.28+ skill bundle: configure_llm + create_agent + publish-test in 1 call; auto-detects ANTHROPIC_API_KEY from env). Drop to primitives (configure_llm_provider + create_agent + publish_agent) only for custom flows. CRITICAL ANTI-PATTERN: chat widgets are NOT blocks. Don't search list_blocks for chat — chat agents are a separate primitive (v1.26+). (c) "auto-reply to inbound SMS/email" → CONVERSATION → send_conversation_turn (one-shot Soul-aware reply, not a website widget). (d) "add hero/services/faq/cta section" → BLOCK → get_block_skill + persist_block. (e) "send campaign email/sms" → MESSAGING. (f) CRM ops → list_contacts/create_deal/etc. CHATBOT CANONICAL FLOW (v1.28+ — 1 call instead of 5): build_website_chatbot({workspace_id, name, faq, pricing_facts, greeting}) → wraps configure_llm + create_agent + publish-test in one call, auto-detects ANTHROPIC_API_KEY from env. Then: operator sandbox-tests at /agents/[id]/test → publish_agent({status:"live"}) auto-runs 8-scenario eval gate (≥87.5% pass required). Observability tools after publish: list_agents, tail_agent_conversations, get_agent_conversation, get_agent_metrics, run_agent_evals, replay_conversation. Dashboard surfaces /agents, /agents/[id]/test, /agents/[id]/settings, /agents/[id]/evals, /agents/[id]/conversations let operators iterate without leaving the browser. WORKSPACE FLOW (legacy reference): create_workspace_v2 → IN PARALLEL for all 7 recommended_blocks (hero, services, about, faq, cta, booking, intake): get_block_skill + persist_block → complete_workspace_v2 → finalize_workspace({workspace_id, email}). Run blocks in PARALLEL (Promise.all) — sequential takes 60+ seconds. v1.10+ CUSTOMIZE: regenerate_block, upload_workspace_image (image_url preferred over base64). v1.11+ STRUCTURAL: get_landing_structure, move_section, delete_section. v1.12+ COMPOSITES: add_composite_section / update_composite_section — manifest ANY block from 12 low-level primitives. Skipping finalize_workspace leaves the operator with no admin login. Every URL is real. NEVER create local files. v1.34+ UI POLISH (smart defaults + optional levers): every published page ships with motion baked in (RevealOnScroll on sections, Stagger on grid blocks, HoverLift on CTAs) — no operator action required. For operators who want to tune intensity: apply_motion_preset({preset: "minimal"|"subtle"|"balanced"|"editorial"}). For operators bringing their own design system: apply_design_md({design_md_content}) accepts the Google Labs DESIGN.md format; import_claude_design_handoff({bundle}) accepts Claude Design's handoff bundle. All three tools are OPTIONAL — surface them only when the operator's vibe asks for it ("make it feel premium", "I have a brand kit", "I just exported from Claude Design"). Never push these on a user who just wants a working site. v1.36+ PROMPT-FATIGUE GUIDANCE: workspace creation typically fires 8–18 MCP tool calls (workspace + blocks + agent + booking + intake + landing). Claude Code prompts on each by default and create_landing_page's Puck schema is verbose JSON the operator should NOT read. DO TWO THINGS to avoid spamming: (1) RECOMMEND THE OPERATOR pick option 2 ('Yes, and do not ask again for seldonframe – <tool>') the FIRST time create_landing_page / persist_block / build_website_chatbot fire — Claude Code caches per-tool autoapprove for the project, the rest of workspace creation runs without interrupting them. Mention this proactively before the flow starts. (2) BUNDLE TOOL CALLS in parallel via Promise.all — never call get_block_skill + persist_block sequentially for 7 blocks; fire them all at once. v1.36+ LANDING-PAGE COMPOSITION FOR LOCAL-SERVICE BUSINESSES: for trades/service businesses (plumbing, HVAC, locksmith, electrician, roofing, towing, dental, salon, mobile mechanic), default to a 10-section composition: navbar → hero (branded gradient empty-state when no photo) → emergencyStrip (sticky brand-colored banner with phone) → trust strip via the Grid block → servicesGrid (per-service cards with price + duration + Book CTA — replaces the SaaS-style pricing block) → benefits (3 differentiators) → process (3-step what-happens-after-booking) → serviceArea (chip cloud of cities/neighborhoods) → testimonials (5+ quotes — placeholder OK pre-launch) → faq → cta → footer. The Pricing block belongs to TIER-based businesses (SaaS, coaches, gyms) NOT per-service businesses — substitute servicesGrid. v1.36.0 added the servicesGrid, emergencyStrip, serviceArea block types — use them when persisting landing pages for trades verticals. The hero block ships a branded-gradient empty state when heroImage is missing; if the operator does not provide a photo, suggest a stock-photo prompt ('your tech truck on a job, sunset shot in Phoenix') so they can drop one in via /landing → Edit later. v1.37.0 GOOGLE MAPS PASTE → WORKSPACE: when the operator pastes a Google Maps business listing (Top Plumbing Experts, ABC Plumbing of San Antonio, etc.), use create_workspace_from_google_paste — NOT create_full_workspace. Same backend pipeline + same finalize_workspace follow-up; the paste-tool's docstring documents the extraction rules (name → bold title, phone → phone-icon row, address → location-pin line, services → categories chip row + 'Services' section deduped, rating + count → '4.7 ★ (950)' element, weekly_hours → 'Monday: 9 AM-5 PM, Tuesday: closed' parsed into canonical {monday:{enabled:true,start:'09:00',end:'17:00'},...} shape). The weekly_hours field flows DIRECTLY into the booking template's metadata.availability, so the operator's actual hours appear on the public /book page on first GET — no separate configure_booking call. 'Open 24 hours' → start:'00:00', end:'23:59'. 'Closed' day → enabled:false. Wrong key shape (e.g. 'mon' instead of 'monday') is silently dropped by the backend and falls back to Mon-Fri 9-5 defaults. NO Google Places API key required — the paste IS the source of truth. v1.36.4 paired fix: the booking page's normalizeAvailability now accepts both 3-letter and full-name day keys, so any legacy rows already in the DB also hydrate correctly. v1.38.3 ADDITIONAL QUALITY BLOCKS: (a) projectGallery — 6-photo masonry of stock photos auto-fetched per service via Unsplash from queries Claude generates inside enhance-blocks; closes the "feels populated" gap that's the single biggest visible difference between a generic SF workspace and a real-business landing page. (b) stickyMobileCTA — fixed bottom-of-screen Call/Book bar, MOBILE ONLY (md:hidden), industry-standard for trades sites with 2-3x mobile booking lift; auto-included whenever a phone is set. (c) testimonial synthesis from Maps paste — when the operator pastes a Google Maps listing that contains review excerpts, Claude Code extracts them VERBATIM into a 'testimonials' field on create_workspace_from_google_paste; backend renders them as-is. NEVER fabricated — if the paste has no review text, the testimonials block is OMITTED from the page entirely (better empty than fake). The testimonials field in the MCP tool input is OPTIONAL — Claude must omit it when the paste doesn't contain real review text. v1.38.0 ATOMIC HORMOZI-QUALITY OUTPUT: every workspace produced by create_full_workspace OR create_workspace_from_google_paste now ships with Hormozi value-equation hero copy + per-business Unsplash photos + scroll-triggered motion baked in — atomically, no follow-up tool calls, no operator action. Pre-1.38.0 the atomic create produced canned 'Welcome to X' copy from personality content templates; only workspaces where Claude Code did the BLOCK-AS-SKILL flow afterward (get_block_skill + persist_block per block) got the rich output. Tirionforge HVAC happened to look great because of that follow-up; atlantic-plumbing didn't get it. v1.38.0 closes the gap by adding Step 12.7 inside the orchestrator: ONE Claude Opus 4.7 call generates hero + servicesGrid + about + benefits + process + faq + cta from src/blocks/*/SKILL.md (the exact same fat skill files Claude Code reads via get_block_skill — single source of truth, no duplication). Output writes to landingPages.sections (LandingPageSection[]), contentHtml/Css get NULLED, route /l/[orgSlug]/[slug] falls through to <PageRenderer> which auto-wraps below-fold sections in <RevealOnScroll>. Hormozi copy + per-business Unsplash + motion all light up at once. EMAIL-FIRST GUARDRAIL UNCHANGED: create_full_workspace + create_workspace_from_google_paste both still RETURN BEFORE THE EMAIL DANCE — operator can't see URLs until they reply with email, finalize_workspace mints the magic admin link + sends the welcome email via Resend. BYOK pass-through: getAIClient checks organizations.integrations.anthropic.apiKey first, falls back to platform ANTHROPIC_API_KEY env. Cost ~$0.10 per workspace, irrelevant since the operator typically supplies their own key. SOFT-FAIL: any error in Step 12.7 leaves the workspace valid (canned-copy Path A intact); never blocks creation. EXPECTED IMPACT: every fresh workspace looks $10k-tier first-shot, no "create workspace then enhance blocks" two-step dance. v1.40.3 IMAGE PIPELINE FOOLPROOFING: removed the deprecated source.unsplash.com keyless fallback from BOTH resolveHeroImageUrlForQuery and resolveGalleryImageUrlsForQueries. Pre-1.40.3 when the official Unsplash API errored or returned no results we fell back to https://source.unsplash.com/{w}x{h}/?{query} — that endpoint is deprecated and now frequently returns broken responses, which were stored verbatim in landingPages.sections and rendered as broken-image icons before the v1.40.2 onError handler eventually flipped state. The HERO Aesthetic Co. test exposed this: hero showed a broken-image icon despite the gallery rendering 4 medspa photos correctly (proving onError wasn't reliably catching deprecated-CDN failures). Fix: never store a known-broken URL. Hero query miss → empty string → branded-gradient empty-state from frame zero (no broken icon ever). Gallery query miss → SKIP that slot → grid auto-reflows to a clean 4-tile composition instead of 6 tiles with 2 broken. Trade-off: workspaces without UNSPLASH_ACCESS_KEY now ship gradient hero + empty gallery (better than broken images). Operators get full control by setting the env var or replacing images post-launch via update_landing_section. ws1-webhook-pricing-fixes branch ships this; redeploy required.`;