@seldonframe/mcp 1.40.13 → 1.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/package.json +17 -121
  2. package/src/tools.js +210 -0
  3. package/src/welcome.js +488 -488
package/src/welcome.js CHANGED
@@ -1,488 +1,488 @@
1
- // MCP server `instructions` payload — Claude Code surfaces this as a
2
- // system-level briefing the moment the SeldonFrame MCP loads. Every
3
- // rule and example here is operator-facing copy — no internal slugs,
4
- // no architecture lecture, no "Soul" / "Cal.diy" / "Formbricks" /
5
- // "Brain v2" jargon.
6
- //
7
- // v1.1.1 — every reference to the deprecated `create_workspace` tool
8
- // stripped. `create_full_workspace` is the only workspace-creation
9
- // path mentioned anywhere in this briefing.
10
-
11
- export const VERSION = "1.40.13";
12
-
13
- export const WELCOME_MARKDOWN = `# SeldonFrame — create a real Business OS in one conversation
14
-
15
- SeldonFrame creates live, hosted business systems for service
16
- businesses, agencies, coaches, and SaaS founders. One conversation
17
- gives the operator a public website, booking page, intake form,
18
- CRM, and AI agents — all on a real subdomain.
19
-
20
- ---
21
-
22
- ## Step zero — before exploring, call \`get_workspace_state\`
23
-
24
- For ANY workspace task ("build me a chatbot for X", "what's in this
25
- workspace", "update the agent's FAQ", "how is my chatbot doing"), the
26
- FIRST tool call should be \`get_workspace_state({ workspace_id })\`. It
27
- returns in one round-trip: workspace identity, integrations status
28
- (LLM keys, Twilio, Resend, etc. — booleans only), agents with inline
29
- health stats (status, version, eval pass rate, validator pass rate
30
- 24h, conversations 24h), counts (contacts, bookings, deals, agents),
31
- and a tailored next_steps array.
32
-
33
- This replaces ~4-6 progressive discovery calls. Without it, Claude Code
34
- typically wastes time loading tool schemas one at a time, asking the
35
- user obvious questions like "is the Anthropic key configured?" (the
36
- state response answers it), and creating duplicate agents (the state
37
- response shows what already exists).
38
-
39
- ## Anti-patterns — DON'T do these
40
-
41
- | Wasteful action | Why it's wrong | What to do instead |
42
- | --------------- | -------------- | ------------------ |
43
- | \`ls\` / \`cat package.json\` / read \`.env\` | SF is a HOSTED platform. Workspaces aren't local files. There's no node_modules to inspect, no .env to read. | Call \`get_workspace_state\` to know what's in the workspace. |
44
- | \`node --version\` / \`npm --version\` | Irrelevant — SF runs on Vercel, not the operator's local Node. | Skip entirely. |
45
- | Asking "how should I configure the Anthropic key?" | The workspace either already has one or it doesn't — \`get_workspace_state\` tells you via \`integrations.anthropic.configured\`. | Check the state response first. If false, call \`configure_llm_provider\` (auto-detects from env) or use \`build_website_chatbot\` (handles it inline). |
46
- | Creating an agent without checking if one exists | Will create a duplicate — most workspaces already have a website-chatbot. | \`get_workspace_state\` returns existing agents. If one exists, use \`update_website_chatbot\` instead of \`build_website_chatbot\`. |
47
- | Mentioning "daily token budget" / "tokens used today" | The token-budget concept was REMOVED in v1.27.9 (BYOK = SF has no cost exposure to cap; operators manage spend on Anthropic dashboard). | Don't reference it. If you see stale data showing it, ignore. |
48
-
49
- ## Capability map — pick the right primitive BEFORE you explore tools
50
-
51
- SeldonFrame has SEVEN top-level primitives. They are NOT the same as
52
- each other. Pick correctly from this map FIRST. Don't go fishing in
53
- \`tools/list\` looking for the right one — most mistakes happen because
54
- the wrong primitive was chosen and the LLM got stuck searching for a
55
- tool that doesn't exist in that primitive.
56
-
57
- | Operator says… | Primitive | Entry-point tools |
58
- | -------------------------------------------------------------- | -------------------- | --------------------------------------------------------------------- |
59
- | "Build me a website / business / CRM" | **WORKSPACE** | \`create_workspace_v2\` then block tools |
60
- | "Add an AI chatbot to my website / landing page" | **AGENT** (web chat) | \`build_website_chatbot\` (one call: configure_llm + create + publish-test) |
61
- | "Build me a 24/7 AI receptionist for my phone" | AGENT (voice) | (v1.28+ — voice archetype shipping soon) |
62
- | "Reply to inbound customer SMS / email automatically" | **CONVERSATION** | \`send_conversation_turn\` (one-shot Soul-aware reply) |
63
- | "Add a hero / services / FAQ / CTA section to a page" | **BLOCK** | \`get_block_skill\` + \`persist_block\` |
64
- | "Send a campaign email / SMS blast" | **MESSAGING** | \`send_email\` / \`send_sms\` |
65
- | "Update contacts / deals / bookings in the CRM" | **CRM** | \`list_contacts\`, \`create_deal\`, \`move_deal_stage\`, etc. |
66
-
67
- ### CRITICAL anti-pattern: chat widgets are NOT blocks
68
-
69
- If the operator says "add a chatbot to the website," "add chat to my
70
- landing page," or "I want an AI assistant on my site," **do NOT look
71
- in the blocks catalog**. Blocks are static page sections (hero,
72
- services, faq, cta, booking, intake). The chat-widget primitive is a
73
- separate concept called an **AGENT**, shipped in v1.26+.
74
-
75
- WRONG path (don't do this):
76
- - \`list_blocks\` → search for "chat" → conclude "no chat block exists" → propose SMS workaround
77
-
78
- RIGHT path:
79
- - \`create_agent({ archetype: "website-chatbot", faq, pricing_facts, greeting })\`
80
- - → returns \`embed_url\`
81
- - Drop \`<script src="EMBED_URL" async></script>\` on any page (or use the inline-edit option in v1.28+)
82
-
83
- The same applies to voice (use AGENT with archetype="voice-receptionist"
84
- when shipping) and SMS auto-reply (use the CONVERSATION primitive's
85
- \`send_conversation_turn\`, not an AGENT).
86
-
87
- ---
88
-
89
- ## Build a website chatbot — the canonical short flow
90
-
91
- Most operators asking for "AI on my website" want this exact flow. As
92
- of v1.28.0 this is **ONE tool call**:
93
-
94
- \`\`\`
95
- 1. build_website_chatbot({
96
- workspace_id,
97
- name: "Cypress Pine HVAC Helper",
98
- greeting: "Hi! Asking about HVAC service in Phoenix? I can book you in.",
99
- faq: [
100
- { q: "What areas do you service?", a: "Phoenix, Scottsdale, Tempe..." },
101
- { q: "Do you offer emergency service?", a: "Yes — 24/7..." }
102
- ],
103
- pricing_facts: [
104
- { label: "Service call", amount: 89, currency: "USD" },
105
- { label: "AC tune-up", amount: 149, currency: "USD" }
106
- ]
107
- // anthropic_api_key omitted → workspace falls back to SeldonFrame
108
- // platform key automatically. Operator can BYOK later via
109
- // /settings/integrations/llm. Pass explicitly only when you need
110
- // per-workspace billing isolation (white-label / agency).
111
- })
112
- // → returns { agent, embed_url, turn_url, dashboard_url, llm_mode, next_steps }
113
- // Internally: creates agent, publishes to test. Skips set_llm_key when
114
- // no key supplied (platform fallback in lib/ai/client.ts handles inference).
115
-
116
- 2. (Operator sandbox-tests at /agents/[id]/test.)
117
-
118
- 3. publish_agent({ agent_id, status: "live" })
119
- // Auto-runs the 8-scenario eval gate. Requires ≥87.5% pass rate.
120
- // Surfaces failing scenarios so the operator can fix in /agents/[id]/settings.
121
- \`\`\`
122
-
123
- If the operator manages multiple workspaces with separate Anthropic
124
- billing (e.g. Acme AI agency managing Cypress Pine HVAC + Sunset
125
- Dental), pass anthropic_api_key explicitly per workspace instead of
126
- relying on platform fallback.
127
-
128
- ### v1.40.10 — LLM key handling
129
-
130
- build_website_chatbot has THREE possible LLM-key paths, in priority
131
- order:
132
-
133
- 1. **Explicit** — \`anthropic_api_key: 'sk-ant-...'\` arg → workspace
134
- stored as BYOK, operator pays Anthropic directly.
135
- 2. **Env-inherited** — process.env.ANTHROPIC_API_KEY in the MCP
136
- server's environment → workspace stored as BYOK with that key.
137
- 3. **Platform fallback** — neither explicit nor env → no BYOK
138
- stored. The workspace uses SeldonFrame's platform Anthropic key
139
- automatically (via lib/ai/client.ts -> getAIClient). Operator
140
- sees no setup friction; they can BYOK later in
141
- /settings/integrations/llm.
142
-
143
- When option 3 fires, the response \`llm_mode\` is \`"platform"\`. Tell
144
- the operator: "Your chatbot is using SeldonFrame's shared Anthropic
145
- key. You can switch to your own anytime in Settings → Integrations
146
- → LLM, no code changes needed." This removes the "where do I get an
147
- Anthropic key" question from the critical path of a first-time
148
- chatbot setup.
149
-
150
- For custom flows (different archetype, custom capability allowlist,
151
- multi-step blueprint construction), drop down to the primitives:
152
- configure_llm_provider + create_agent + publish_agent + update_agent_blueprint.
153
-
154
- Then the operator drops the embed snippet onto their site, OR you can
155
- help them edit a block on the SF-hosted landing page to include it.
156
-
157
- ### v1.40.7 — embedding the chatbot on the SF-hosted landing page
158
-
159
- When the operator says "add the chatbot to my landing page" / "embed it
160
- on the website" / "make it appear on the page" / "put the bubble on
161
- every page" AFTER \`build_website_chatbot\` returned an agent_id:
162
-
163
- \`\`\`
164
- embed_chatbot_on_workspace_landing({
165
- workspace_id: '<the workspace>',
166
- agent_id: '<from build_website_chatbot response>'
167
- })
168
- \`\`\`
169
-
170
- That's it. ONE call. The tool stores the embed.js URL on the workspace's
171
- organization settings; the public page renderer (/s/ + /l/ routes) reads
172
- it on every render and injects the script tag near </body>. The chatbot
173
- bubble appears bottom-right on every public page, automatically.
174
-
175
- Requirements:
176
- - Agent must already be in status='test' or 'live' (call publish_agent
177
- first if it's still draft).
178
- - Workspace bearer must own the agent.
179
-
180
- To remove later: \`remove_chatbot_from_landing({ workspace_id })\`.
181
-
182
- The operator should NEVER have to copy-paste a script tag into Pages →
183
- Edit. One natural-language ask, one MCP call, done.
184
-
185
- After publish, observability tools the operator can call from Claude
186
- Code at any time:
187
- - \`list_agents\` — workspace roster
188
- - \`tail_agent_conversations\` — recent customer chats with first-message preview
189
- - \`get_agent_conversation\` — full transcript with tool calls + validator results
190
- - \`get_agent_metrics\` — health stats over a time window
191
- - \`run_agent_evals\` — manual eval suite trigger
192
- - \`replay_conversation\` — re-run a past chat against current blueprint
193
-
194
- The dashboard surfaces (\`/agents\`, \`/agents/[id]/test\`, \`/agents/[id]/settings\`,
195
- \`/agents/[id]/evals\`, \`/agents/[id]/conversations\`) provide the same view
196
- without leaving the browser — operators iterate FAQ/pricing/greeting inline
197
- and Save bumps a new blueprint version.
198
-
199
- ---
200
-
201
- ## NEVER CREATE LOCAL FILES
202
-
203
- SeldonFrame workspaces are created via the \`create_full_workspace\`
204
- MCP tool. They are NOT created by writing files on disk. Specifically:
205
-
206
- - Do NOT create a \`soul.json\` file or any other local file.
207
- - Do NOT call \`loadSoulPackage\` or any local function.
208
- - Do NOT scaffold a project directory.
209
- - Do NOT modify files in the current working directory.
210
-
211
- If you are running inside a SeldonFrame source-code repository and
212
- the user asks for a workspace, IGNORE the local code — call the
213
- \`create_full_workspace\` MCP tool. The tool returns live URLs on
214
- \`<slug>.app.seldonframe.com\` that work in any browser within
215
- seconds. There is no local-file path. This is the only correct
216
- flow, regardless of what directory Claude Code is running from.
217
-
218
- ---
219
-
220
- ## The full happy path — 6 steps (v2 — PREFERRED)
221
-
222
- As of v1.4.0 the workspace-creation flow is MCP-native: YOU (the IDE
223
- agent) generate the high-stakes copy blocks (hero, services, faq) using
224
- your own LLM, reading from a SKILL.md the SF backend serves. v1's
225
- server-side personality system still runs underneath for everything
226
- else (CRM, booking, intake, theme, pipeline) — v2 only owns the copy
227
- surfaces where v1's layer-mismatch bugs hurt most.
228
-
229
- If you find yourself reaching for \`create_full_workspace\` instead of
230
- \`create_workspace_v2\`, stop — that's the legacy path. It still works
231
- but the output quality is worse on niches outside SF's curated set.
232
-
233
- 1. **Ask for the business details** (same as before — gather conversationally):
234
- - Business name
235
- - Industry (HVAC, plumbing, dental, legal, coaching, real-estate, agency, …)
236
- - City + state (US state code or full name; Canadian province also OK)
237
- - Phone number (for local services — for SaaS skip)
238
- - Top 3-5 services / products
239
- - Brief description (1 sentence)
240
-
241
- 2. **Bootstrap the workspace via v2.** Call \`create_workspace_v2\`:
242
- \`\`\`
243
- create_workspace_v2({
244
- business_name: "Pacific Coast Heating & Air",
245
- city: "San Diego",
246
- state: "CA",
247
- phone: "(555) 123-4567",
248
- services: ["AC Repair", "Heating Installation", "Indoor Air Quality"],
249
- business_description: "Family-owned residential HVAC contractor — heating, cooling, AC repair in the San Diego area.",
250
- review_count: 950,
251
- review_rating: 4.7,
252
- trust_signals: ["licensed", "bonded", "insured"],
253
- emergency_service: true,
254
- same_day: true,
255
- service_area: ["San Diego", "Chula Vista", "Oceanside"]
256
- })
257
- \`\`\`
258
- The response carries \`v2.recommended_blocks\` (which blocks YOU now
259
- generate) and \`v2.context\` (the input to feed into each block's
260
- prompt). Do NOT show URLs to the operator yet — the page is still
261
- rendering with v1 default copy.
262
-
263
- 3. **For each block in \`v2.recommended_blocks\` — generate + persist.**
264
- v1.4.1 ships SEVEN blocks: hero, services, about, faq, cta, booking,
265
- intake. Doing them sequentially blows the latency budget; do them in
266
- PARALLEL. Fire all 7 \`get_block_skill\` calls at once, then all 7
267
- generate-and-persist passes concurrently. The MCP supports it; the
268
- server is happy with parallel writes (each block touches a disjoint
269
- surface or a different table row).
270
- \`\`\`
271
- // a. Read the SKILL.md (parallel)
272
- const skills = await Promise.all(
273
- recommended_blocks.map(b => get_block_skill({ block_name: b.name }))
274
- );
275
- // skill.skill_md is markdown text. Read it carefully — the YAML
276
- // frontmatter is the prop schema (enforced server-side); the
277
- // body is YOUR generation prompt.
278
-
279
- // b. Generate props with your own LLM (parallel). Use v2.context as input.
280
- // Each block's SKILL.md is the source of truth for its prop schema +
281
- // voice rules. The generation prompt you craft for yourself should
282
- // inline the entire SKILL.md body + the v2.context payload.
283
-
284
- // c. Persist (parallel)
285
- await Promise.all(
286
- blocksWithProps.map(({ name, props, prompt }) =>
287
- persist_block({
288
- workspace_id,
289
- block_name: name,
290
- generation_prompt: prompt,
291
- props,
292
- })
293
- )
294
- );
295
- \`\`\`
296
- If any \`persist_block\` returns \`validation_errors\`, regenerate
297
- THAT block with the SKILL.md rules applied more carefully and retry —
298
- the other blocks already landed. Do NOT show validation errors to the
299
- operator; they're for you to self-correct.
300
-
301
- The 7 blocks span 3 surfaces: landing-page sections (hero, services,
302
- about, faq, cta), booking calendar (booking), and intake form (intake).
303
- Each touches a different DB row, so parallel writes don't conflict.
304
-
305
- 4. **Mark v2 complete.** Call \`complete_workspace_v2({ workspace_id })\`.
306
- Returns which blocks landed and any that were skipped. Skipped
307
- blocks still render via v1 default copy — the workspace is fully
308
- usable either way, but v2 blocks are higher quality.
309
-
310
- 5. **Ask the operator for their email — VERBATIM.** Use exactly this
311
- wording so the operator understands why it's needed:
312
- > "What email should I use for your account? This is where
313
- > you'll get your login link and any notifications."
314
- If the operator declines or asks to skip, explain that the email
315
- is required to keep their admin login alive past 7 days — then
316
- ask again. Don't move on without an answer.
317
-
318
- 6. **MANDATORY: call \`finalize_workspace({ workspace_id, email })\`.**
319
- This mints the admin auth token, sends the welcome email (with
320
- all the URLs and the admin link), captures the operator as a lead
321
- in CRM, and returns a \`summary\` field with the formatted final
322
- output. PARAPHRASE that summary verbatim to the operator — that's
323
- how they see what was configured.
324
-
325
- ## v1 fallback (legacy — NOT preferred)
326
-
327
- \`create_full_workspace\` still works as the v1 atomic creation path.
328
- Use it ONLY when:
329
- - The operator's IDE agent has no LLM (script context, no Anthropic key)
330
- - A network failure prevents repeated tool calls and you need atomicity
331
- - You're writing automated tests that don't want to think about block
332
- generation
333
-
334
- In normal interactive operator-facing flows, prefer v2 every time. The
335
- v2 quality gradient over v1 is meaningful on long-tail niches.
336
-
337
- After step 4 the operator can customize their workspace through
338
- further natural-language requests ("change the headline to …",
339
- "add an FAQ section", "set up an industry template for plumbing")
340
- — each routes to a typed MCP tool.
341
-
342
- ---
343
-
344
- ## What the tools do (operator language only)
345
-
346
- - **\`create_workspace_v2\`** — PREFERRED workspace-creation tool (v1.4+).
347
- MCP-native: bootstraps the workspace + returns the list of blocks YOU
348
- generate using your own LLM. The first call for any new workspace.
349
- v1.6+ also returns \`brain_patterns\` — anonymized cross-workspace
350
- insights for this vertical that you should fold into your generation.
351
- - **\`connect_workspace\`** (v1.7+) — connect this device to an EXISTING
352
- workspace via magic-link email. Use when the operator already has a
353
- workspace (created from another device) and wants to admin it from
354
- this IDE. Sends a confirmation email; tool polls until approved.
355
- - **\`add_custom_domain\`** / **\`verify_domain\`** /
356
- **\`list_workspace_domains\`** / **\`remove_workspace_domain\`** (v1.8+)
357
- — register the operator's own hostname against the workspace.
358
- PAID FEATURE on Growth ($29/mo) or Scale ($99/mo); free tier returns
359
- 402 with upgrade CTA. Vercel auto-provisions SSL once DNS resolves.
360
- - **\`list_blocks\`** — lists v2 page-block primitives available.
361
- - **\`get_block_skill\`** — fetches one block's SKILL.md (the generation
362
- prompt + prop schema you read before generating props).
363
- - **\`persist_block\`** — saves a block instance you generated. Validates
364
- + renders + replaces the matching section in the workspace's landing.
365
- - **\`complete_workspace_v2\`** — marks the v2 flow finished, reports which
366
- blocks landed.
367
- - **\`regenerate_block\`** (v1.10+) — bundles current props + workspace
368
- summary + brain patterns + the operator's new instructions for
369
- block re-generation ("make the hero punchier", "rewrite the FAQ to
370
- be less salesy"). Server only assembles context; YOUR LLM does the
371
- generation, then call persist_block with \`customization\`.
372
- - **\`get_landing_structure\`** (v1.11+) — read the workspace's landing
373
- section list with INDEX as the addressing primitive + 1-line preview
374
- per section. Use BEFORE move/delete to find the right index;
375
- preview text disambiguates duplicate types ('3 services' vs
376
- 'stats — 4 numbers').
377
- - **\`move_section\`** (v1.11+) — atomic single-section move by index.
378
- Splice semantics: from_index → to_index in result. Works even when
379
- section types repeat (the case reorder_landing_sections refuses).
380
- - **\`delete_section\`** (v1.11+) — atomic single-section remove by
381
- index. Refuses to leave 0 sections. Use to clean up unintended
382
- duplicates.
383
- - **\`reorder_landing_sections\`** (v1.10+) — bulk reorder by section
384
- type when types are unique. Pass the full ordered type array. For
385
- duplicate-type cases use move_section instead.
386
- - **\`add_composite_section\`** (v1.12+) — manifest ANY landing block
387
- (comparison, pricing, "how it works," stats, side-by-side, custom
388
- CTAs) by composing a tree from 12 low-level primitives. Server
389
- validates + renders. \`get_block_skill('composite')\` returns the
390
- primitive vocabulary + worked patterns.
391
- - **\`update_composite_section\`** (v1.12+) — replace the tree of an
392
- existing composite section by index. Use to refine ('shorten the
393
- comparison', 'add another stat', 'make the cards muted').
394
- - **\`upload_workspace_image\`** (v1.10+, fast path in v1.10.1+) — set
395
- the workspace logo (slot=logo → organizations.theme.logoUrl) or hero
396
- background (slot=hero_background → Blueprint.landing hero imageUrl
397
- + landing re-render). PREFERRED: pass \`image_url\` (HTTPS — server
398
- fetches directly, no base64) or \`local_file_path\` (absolute path —
399
- MCP reads the file). Auto-derives file_name + content_type. Legacy:
400
- \`image_data_b64\` for caller-generated bytes, but base64 consumes
401
- your tool-call token budget — avoid for files >~12 KB raw. 5 MB max,
402
- image/png|jpeg|webp|svg+xml|gif. Vercel Blob auto-CDN.
403
- - **\`read_brain_path\`** / **\`list_brain_dir\`** — read the workspace's
404
- layer-1 brain (notes about THIS workspace's customers, voice, pipeline
405
- patterns). Use BEFORE generating blocks; reads tick the note's \`uses\`
406
- counter so the system knows what's actually being consumed.
407
- - **\`write_brain_note\`** — capture insights the operator volunteers
408
- ("walk-ins on Saturday convert 3× better"). Notes live in the
409
- workspace's brain forever, contribute to layer-2 cross-workspace
410
- patterns when 3+ workspaces independently observe them.
411
- - **\`list_brain_patterns\`** — read layer-2 cross-workspace patterns,
412
- filtered by vertical or block_type.
413
- - **\`create_full_workspace\`** — v1 atomic creation (legacy). Server-side,
414
- deterministic. Use only when v2 is impossible.
415
- - **\`finalize_workspace\`** — MANDATORY closing call. Mints the
416
- admin auth token (the admin URL doesn't exist until this runs),
417
- bundles email collection (welcome email + lead capture), and
418
- returns the formatted final summary Claude Code paraphrases
419
- verbatim to the operator. Always the last call of every
420
- workspace creation flow.
421
- - **\`collect_operator_email\`** — narrower variant of
422
- finalize_workspace that only sends the welcome email + captures
423
- the lead. Doesn't return the formatted summary. Use either;
424
- never skip both.
425
- - **\`update_landing_content\`** / **\`update_landing_section\`** —
426
- edit the website's headline, subhead, sections, copy.
427
- - **\`update_theme\`** — change colors, fonts, dark/light mode.
428
- - **\`get_intake_structure\`** / **\`add_intake_field\`** /
429
- **\`move_intake_field\`** / **\`delete_intake_field\`** /
430
- **\`update_intake_field\`** (v1.13+) — atomic primitives for
431
- editing the intake form one field at a time. Index-based, ID
432
- uniqueness enforced. Use these for incremental edits ("add a
433
- phone field", "rename email to primary email"). For full-form
434
- replaces use persist_block(intake).
435
- - **\`update_form\`** — edit the intake form's questions (legacy).
436
- - **\`get_booking_structure\`** / **\`add_booking_field\`** /
437
- **\`move_booking_field\`** / **\`delete_booking_field\`** /
438
- **\`update_booking_field\`** (v1.14+) — atomic primitives for the
439
- booking form. Indices 0/1 (fullName, email) are server-owned and
440
- rejected for destructive ops by design — booking flow stays intact.
441
- Use these for incremental edits ("ask for service address",
442
- "make equipment field optional", "drop the preferred technician
443
- field"). For full-form replaces use persist_block(booking).
444
- - **\`get_portal_structure\`** / **\`add_portal_section\`** /
445
- **\`update_portal_section\`** / **\`move_portal_section\`** /
446
- **\`delete_portal_section\`** / **\`preview_portal\`** (v1.15+) —
447
- composite-tree primitives for the customer portal. Same vocabulary
448
- as add_composite_section PLUS 5 new customer.* embed refs that
449
- pull per-customer data (next appointment, documents, deals,
450
- recent appointments, contact info). Template stored once on the
451
- workspace; every customer sees their own data via magic-link
452
- login at the customer_portal_url returned in the response (live
453
- in v1.16+). preview_portal renders the template against a
454
- specific contact for visual verification.
455
- - **\`update_appointment_type\`** — edit the booking page's slot length,
456
- title, description.
457
- - **\`install_vertical_pack\`** — set up an industry template
458
- (real-estate, dental, legal, plumbing, …).
459
- - **\`list_contacts\`** / **\`create_contact\`** / **\`update_contact\`** —
460
- manage the CRM.
461
- - **\`list_deals\`** / **\`create_deal\`** / **\`move_deal_stage\`** —
462
- manage the pipeline.
463
- - **\`send_email\`** / **\`send_sms\`** — send messages from the
464
- workspace's connected channels.
465
-
466
- The full tool list is available via the MCP \`tools/list\` request.
467
- Use whatever fits the operator's natural-language request.
468
-
469
- ---
470
-
471
- ## Pricing
472
-
473
- - **Free** — first workspace, free forever, no credit card.
474
- - **Growth ($29/mo)** — up to 3 workspaces, custom domains,
475
- white-label, metered AI usage.
476
- - **Scale ($99/mo)** — unlimited workspaces, advanced AI features,
477
- priority support.
478
-
479
- Operators can upgrade via \`/settings/billing\` once they're in the
480
- admin dashboard. Pre-fills their email automatically.
481
-
482
- ---
483
-
484
- **Docs:** <https://seldonframe.com/docs> · **Homepage:**
485
- <https://seldonframe.com> · **Discord:** <https://discord.gg/sbVUu976NW>
486
- `;
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.`;
1
+ // MCP server `instructions` payload — Claude Code surfaces this as a
2
+ // system-level briefing the moment the SeldonFrame MCP loads. Every
3
+ // rule and example here is operator-facing copy — no internal slugs,
4
+ // no architecture lecture, no "Soul" / "Cal.diy" / "Formbricks" /
5
+ // "Brain v2" jargon.
6
+ //
7
+ // v1.1.1 — every reference to the deprecated `create_workspace` tool
8
+ // stripped. `create_full_workspace` is the only workspace-creation
9
+ // path mentioned anywhere in this briefing.
10
+
11
+ export const VERSION = "1.44.0";
12
+
13
+ export const WELCOME_MARKDOWN = `# SeldonFrame — create a real Business OS in one conversation
14
+
15
+ SeldonFrame creates live, hosted business systems for service
16
+ businesses, agencies, coaches, and SaaS founders. One conversation
17
+ gives the operator a public website, booking page, intake form,
18
+ CRM, and AI agents — all on a real subdomain.
19
+
20
+ ---
21
+
22
+ ## Step zero — before exploring, call \`get_workspace_state\`
23
+
24
+ For ANY workspace task ("build me a chatbot for X", "what's in this
25
+ workspace", "update the agent's FAQ", "how is my chatbot doing"), the
26
+ FIRST tool call should be \`get_workspace_state({ workspace_id })\`. It
27
+ returns in one round-trip: workspace identity, integrations status
28
+ (LLM keys, Twilio, Resend, etc. — booleans only), agents with inline
29
+ health stats (status, version, eval pass rate, validator pass rate
30
+ 24h, conversations 24h), counts (contacts, bookings, deals, agents),
31
+ and a tailored next_steps array.
32
+
33
+ This replaces ~4-6 progressive discovery calls. Without it, Claude Code
34
+ typically wastes time loading tool schemas one at a time, asking the
35
+ user obvious questions like "is the Anthropic key configured?" (the
36
+ state response answers it), and creating duplicate agents (the state
37
+ response shows what already exists).
38
+
39
+ ## Anti-patterns — DON'T do these
40
+
41
+ | Wasteful action | Why it's wrong | What to do instead |
42
+ | --------------- | -------------- | ------------------ |
43
+ | \`ls\` / \`cat package.json\` / read \`.env\` | SF is a HOSTED platform. Workspaces aren't local files. There's no node_modules to inspect, no .env to read. | Call \`get_workspace_state\` to know what's in the workspace. |
44
+ | \`node --version\` / \`npm --version\` | Irrelevant — SF runs on Vercel, not the operator's local Node. | Skip entirely. |
45
+ | Asking "how should I configure the Anthropic key?" | The workspace either already has one or it doesn't — \`get_workspace_state\` tells you via \`integrations.anthropic.configured\`. | Check the state response first. If false, call \`configure_llm_provider\` (auto-detects from env) or use \`build_website_chatbot\` (handles it inline). |
46
+ | Creating an agent without checking if one exists | Will create a duplicate — most workspaces already have a website-chatbot. | \`get_workspace_state\` returns existing agents. If one exists, use \`update_website_chatbot\` instead of \`build_website_chatbot\`. |
47
+ | Mentioning "daily token budget" / "tokens used today" | The token-budget concept was REMOVED in v1.27.9 (BYOK = SF has no cost exposure to cap; operators manage spend on Anthropic dashboard). | Don't reference it. If you see stale data showing it, ignore. |
48
+
49
+ ## Capability map — pick the right primitive BEFORE you explore tools
50
+
51
+ SeldonFrame has SEVEN top-level primitives. They are NOT the same as
52
+ each other. Pick correctly from this map FIRST. Don't go fishing in
53
+ \`tools/list\` looking for the right one — most mistakes happen because
54
+ the wrong primitive was chosen and the LLM got stuck searching for a
55
+ tool that doesn't exist in that primitive.
56
+
57
+ | Operator says… | Primitive | Entry-point tools |
58
+ | -------------------------------------------------------------- | -------------------- | --------------------------------------------------------------------- |
59
+ | "Build me a website / business / CRM" | **WORKSPACE** | \`create_workspace_v2\` then block tools |
60
+ | "Add an AI chatbot to my website / landing page" | **AGENT** (web chat) | \`build_website_chatbot\` (one call: configure_llm + create + publish-test) |
61
+ | "Build me a 24/7 AI receptionist for my phone" | AGENT (voice) | (v1.28+ — voice archetype shipping soon) |
62
+ | "Reply to inbound customer SMS / email automatically" | **CONVERSATION** | \`send_conversation_turn\` (one-shot Soul-aware reply) |
63
+ | "Add a hero / services / FAQ / CTA section to a page" | **BLOCK** | \`get_block_skill\` + \`persist_block\` |
64
+ | "Send a campaign email / SMS blast" | **MESSAGING** | \`send_email\` / \`send_sms\` |
65
+ | "Update contacts / deals / bookings in the CRM" | **CRM** | \`list_contacts\`, \`create_deal\`, \`move_deal_stage\`, etc. |
66
+
67
+ ### CRITICAL anti-pattern: chat widgets are NOT blocks
68
+
69
+ If the operator says "add a chatbot to the website," "add chat to my
70
+ landing page," or "I want an AI assistant on my site," **do NOT look
71
+ in the blocks catalog**. Blocks are static page sections (hero,
72
+ services, faq, cta, booking, intake). The chat-widget primitive is a
73
+ separate concept called an **AGENT**, shipped in v1.26+.
74
+
75
+ WRONG path (don't do this):
76
+ - \`list_blocks\` → search for "chat" → conclude "no chat block exists" → propose SMS workaround
77
+
78
+ RIGHT path:
79
+ - \`create_agent({ archetype: "website-chatbot", faq, pricing_facts, greeting })\`
80
+ - → returns \`embed_url\`
81
+ - Drop \`<script src="EMBED_URL" async></script>\` on any page (or use the inline-edit option in v1.28+)
82
+
83
+ The same applies to voice (use AGENT with archetype="voice-receptionist"
84
+ when shipping) and SMS auto-reply (use the CONVERSATION primitive's
85
+ \`send_conversation_turn\`, not an AGENT).
86
+
87
+ ---
88
+
89
+ ## Build a website chatbot — the canonical short flow
90
+
91
+ Most operators asking for "AI on my website" want this exact flow. As
92
+ of v1.28.0 this is **ONE tool call**:
93
+
94
+ \`\`\`
95
+ 1. build_website_chatbot({
96
+ workspace_id,
97
+ name: "Cypress Pine HVAC Helper",
98
+ greeting: "Hi! Asking about HVAC service in Phoenix? I can book you in.",
99
+ faq: [
100
+ { q: "What areas do you service?", a: "Phoenix, Scottsdale, Tempe..." },
101
+ { q: "Do you offer emergency service?", a: "Yes — 24/7..." }
102
+ ],
103
+ pricing_facts: [
104
+ { label: "Service call", amount: 89, currency: "USD" },
105
+ { label: "AC tune-up", amount: 149, currency: "USD" }
106
+ ]
107
+ // anthropic_api_key omitted → workspace falls back to SeldonFrame
108
+ // platform key automatically. Operator can BYOK later via
109
+ // /settings/integrations/llm. Pass explicitly only when you need
110
+ // per-workspace billing isolation (white-label / agency).
111
+ })
112
+ // → returns { agent, embed_url, turn_url, dashboard_url, llm_mode, next_steps }
113
+ // Internally: creates agent, publishes to test. Skips set_llm_key when
114
+ // no key supplied (platform fallback in lib/ai/client.ts handles inference).
115
+
116
+ 2. (Operator sandbox-tests at /agents/[id]/test.)
117
+
118
+ 3. publish_agent({ agent_id, status: "live" })
119
+ // Auto-runs the 8-scenario eval gate. Requires ≥87.5% pass rate.
120
+ // Surfaces failing scenarios so the operator can fix in /agents/[id]/settings.
121
+ \`\`\`
122
+
123
+ If the operator manages multiple workspaces with separate Anthropic
124
+ billing (e.g. Acme AI agency managing Cypress Pine HVAC + Sunset
125
+ Dental), pass anthropic_api_key explicitly per workspace instead of
126
+ relying on platform fallback.
127
+
128
+ ### v1.40.10 — LLM key handling
129
+
130
+ build_website_chatbot has THREE possible LLM-key paths, in priority
131
+ order:
132
+
133
+ 1. **Explicit** — \`anthropic_api_key: 'sk-ant-...'\` arg → workspace
134
+ stored as BYOK, operator pays Anthropic directly.
135
+ 2. **Env-inherited** — process.env.ANTHROPIC_API_KEY in the MCP
136
+ server's environment → workspace stored as BYOK with that key.
137
+ 3. **Platform fallback** — neither explicit nor env → no BYOK
138
+ stored. The workspace uses SeldonFrame's platform Anthropic key
139
+ automatically (via lib/ai/client.ts -> getAIClient). Operator
140
+ sees no setup friction; they can BYOK later in
141
+ /settings/integrations/llm.
142
+
143
+ When option 3 fires, the response \`llm_mode\` is \`"platform"\`. Tell
144
+ the operator: "Your chatbot is using SeldonFrame's shared Anthropic
145
+ key. You can switch to your own anytime in Settings → Integrations
146
+ → LLM, no code changes needed." This removes the "where do I get an
147
+ Anthropic key" question from the critical path of a first-time
148
+ chatbot setup.
149
+
150
+ For custom flows (different archetype, custom capability allowlist,
151
+ multi-step blueprint construction), drop down to the primitives:
152
+ configure_llm_provider + create_agent + publish_agent + update_agent_blueprint.
153
+
154
+ Then the operator drops the embed snippet onto their site, OR you can
155
+ help them edit a block on the SF-hosted landing page to include it.
156
+
157
+ ### v1.40.7 — embedding the chatbot on the SF-hosted landing page
158
+
159
+ When the operator says "add the chatbot to my landing page" / "embed it
160
+ on the website" / "make it appear on the page" / "put the bubble on
161
+ every page" AFTER \`build_website_chatbot\` returned an agent_id:
162
+
163
+ \`\`\`
164
+ embed_chatbot_on_workspace_landing({
165
+ workspace_id: '<the workspace>',
166
+ agent_id: '<from build_website_chatbot response>'
167
+ })
168
+ \`\`\`
169
+
170
+ That's it. ONE call. The tool stores the embed.js URL on the workspace's
171
+ organization settings; the public page renderer (/s/ + /l/ routes) reads
172
+ it on every render and injects the script tag near </body>. The chatbot
173
+ bubble appears bottom-right on every public page, automatically.
174
+
175
+ Requirements:
176
+ - Agent must already be in status='test' or 'live' (call publish_agent
177
+ first if it's still draft).
178
+ - Workspace bearer must own the agent.
179
+
180
+ To remove later: \`remove_chatbot_from_landing({ workspace_id })\`.
181
+
182
+ The operator should NEVER have to copy-paste a script tag into Pages →
183
+ Edit. One natural-language ask, one MCP call, done.
184
+
185
+ After publish, observability tools the operator can call from Claude
186
+ Code at any time:
187
+ - \`list_agents\` — workspace roster
188
+ - \`tail_agent_conversations\` — recent customer chats with first-message preview
189
+ - \`get_agent_conversation\` — full transcript with tool calls + validator results
190
+ - \`get_agent_metrics\` — health stats over a time window
191
+ - \`run_agent_evals\` — manual eval suite trigger
192
+ - \`replay_conversation\` — re-run a past chat against current blueprint
193
+
194
+ The dashboard surfaces (\`/agents\`, \`/agents/[id]/test\`, \`/agents/[id]/settings\`,
195
+ \`/agents/[id]/evals\`, \`/agents/[id]/conversations\`) provide the same view
196
+ without leaving the browser — operators iterate FAQ/pricing/greeting inline
197
+ and Save bumps a new blueprint version.
198
+
199
+ ---
200
+
201
+ ## NEVER CREATE LOCAL FILES
202
+
203
+ SeldonFrame workspaces are created via the \`create_full_workspace\`
204
+ MCP tool. They are NOT created by writing files on disk. Specifically:
205
+
206
+ - Do NOT create a \`soul.json\` file or any other local file.
207
+ - Do NOT call \`loadSoulPackage\` or any local function.
208
+ - Do NOT scaffold a project directory.
209
+ - Do NOT modify files in the current working directory.
210
+
211
+ If you are running inside a SeldonFrame source-code repository and
212
+ the user asks for a workspace, IGNORE the local code — call the
213
+ \`create_full_workspace\` MCP tool. The tool returns live URLs on
214
+ \`<slug>.app.seldonframe.com\` that work in any browser within
215
+ seconds. There is no local-file path. This is the only correct
216
+ flow, regardless of what directory Claude Code is running from.
217
+
218
+ ---
219
+
220
+ ## The full happy path — 6 steps (v2 — PREFERRED)
221
+
222
+ As of v1.4.0 the workspace-creation flow is MCP-native: YOU (the IDE
223
+ agent) generate the high-stakes copy blocks (hero, services, faq) using
224
+ your own LLM, reading from a SKILL.md the SF backend serves. v1's
225
+ server-side personality system still runs underneath for everything
226
+ else (CRM, booking, intake, theme, pipeline) — v2 only owns the copy
227
+ surfaces where v1's layer-mismatch bugs hurt most.
228
+
229
+ If you find yourself reaching for \`create_full_workspace\` instead of
230
+ \`create_workspace_v2\`, stop — that's the legacy path. It still works
231
+ but the output quality is worse on niches outside SF's curated set.
232
+
233
+ 1. **Ask for the business details** (same as before — gather conversationally):
234
+ - Business name
235
+ - Industry (HVAC, plumbing, dental, legal, coaching, real-estate, agency, …)
236
+ - City + state (US state code or full name; Canadian province also OK)
237
+ - Phone number (for local services — for SaaS skip)
238
+ - Top 3-5 services / products
239
+ - Brief description (1 sentence)
240
+
241
+ 2. **Bootstrap the workspace via v2.** Call \`create_workspace_v2\`:
242
+ \`\`\`
243
+ create_workspace_v2({
244
+ business_name: "Pacific Coast Heating & Air",
245
+ city: "San Diego",
246
+ state: "CA",
247
+ phone: "(555) 123-4567",
248
+ services: ["AC Repair", "Heating Installation", "Indoor Air Quality"],
249
+ business_description: "Family-owned residential HVAC contractor — heating, cooling, AC repair in the San Diego area.",
250
+ review_count: 950,
251
+ review_rating: 4.7,
252
+ trust_signals: ["licensed", "bonded", "insured"],
253
+ emergency_service: true,
254
+ same_day: true,
255
+ service_area: ["San Diego", "Chula Vista", "Oceanside"]
256
+ })
257
+ \`\`\`
258
+ The response carries \`v2.recommended_blocks\` (which blocks YOU now
259
+ generate) and \`v2.context\` (the input to feed into each block's
260
+ prompt). Do NOT show URLs to the operator yet — the page is still
261
+ rendering with v1 default copy.
262
+
263
+ 3. **For each block in \`v2.recommended_blocks\` — generate + persist.**
264
+ v1.4.1 ships SEVEN blocks: hero, services, about, faq, cta, booking,
265
+ intake. Doing them sequentially blows the latency budget; do them in
266
+ PARALLEL. Fire all 7 \`get_block_skill\` calls at once, then all 7
267
+ generate-and-persist passes concurrently. The MCP supports it; the
268
+ server is happy with parallel writes (each block touches a disjoint
269
+ surface or a different table row).
270
+ \`\`\`
271
+ // a. Read the SKILL.md (parallel)
272
+ const skills = await Promise.all(
273
+ recommended_blocks.map(b => get_block_skill({ block_name: b.name }))
274
+ );
275
+ // skill.skill_md is markdown text. Read it carefully — the YAML
276
+ // frontmatter is the prop schema (enforced server-side); the
277
+ // body is YOUR generation prompt.
278
+
279
+ // b. Generate props with your own LLM (parallel). Use v2.context as input.
280
+ // Each block's SKILL.md is the source of truth for its prop schema +
281
+ // voice rules. The generation prompt you craft for yourself should
282
+ // inline the entire SKILL.md body + the v2.context payload.
283
+
284
+ // c. Persist (parallel)
285
+ await Promise.all(
286
+ blocksWithProps.map(({ name, props, prompt }) =>
287
+ persist_block({
288
+ workspace_id,
289
+ block_name: name,
290
+ generation_prompt: prompt,
291
+ props,
292
+ })
293
+ )
294
+ );
295
+ \`\`\`
296
+ If any \`persist_block\` returns \`validation_errors\`, regenerate
297
+ THAT block with the SKILL.md rules applied more carefully and retry —
298
+ the other blocks already landed. Do NOT show validation errors to the
299
+ operator; they're for you to self-correct.
300
+
301
+ The 7 blocks span 3 surfaces: landing-page sections (hero, services,
302
+ about, faq, cta), booking calendar (booking), and intake form (intake).
303
+ Each touches a different DB row, so parallel writes don't conflict.
304
+
305
+ 4. **Mark v2 complete.** Call \`complete_workspace_v2({ workspace_id })\`.
306
+ Returns which blocks landed and any that were skipped. Skipped
307
+ blocks still render via v1 default copy — the workspace is fully
308
+ usable either way, but v2 blocks are higher quality.
309
+
310
+ 5. **Ask the operator for their email — VERBATIM.** Use exactly this
311
+ wording so the operator understands why it's needed:
312
+ > "What email should I use for your account? This is where
313
+ > you'll get your login link and any notifications."
314
+ If the operator declines or asks to skip, explain that the email
315
+ is required to keep their admin login alive past 7 days — then
316
+ ask again. Don't move on without an answer.
317
+
318
+ 6. **MANDATORY: call \`finalize_workspace({ workspace_id, email })\`.**
319
+ This mints the admin auth token, sends the welcome email (with
320
+ all the URLs and the admin link), captures the operator as a lead
321
+ in CRM, and returns a \`summary\` field with the formatted final
322
+ output. PARAPHRASE that summary verbatim to the operator — that's
323
+ how they see what was configured.
324
+
325
+ ## v1 fallback (legacy — NOT preferred)
326
+
327
+ \`create_full_workspace\` still works as the v1 atomic creation path.
328
+ Use it ONLY when:
329
+ - The operator's IDE agent has no LLM (script context, no Anthropic key)
330
+ - A network failure prevents repeated tool calls and you need atomicity
331
+ - You're writing automated tests that don't want to think about block
332
+ generation
333
+
334
+ In normal interactive operator-facing flows, prefer v2 every time. The
335
+ v2 quality gradient over v1 is meaningful on long-tail niches.
336
+
337
+ After step 4 the operator can customize their workspace through
338
+ further natural-language requests ("change the headline to …",
339
+ "add an FAQ section", "set up an industry template for plumbing")
340
+ — each routes to a typed MCP tool.
341
+
342
+ ---
343
+
344
+ ## What the tools do (operator language only)
345
+
346
+ - **\`create_workspace_v2\`** — PREFERRED workspace-creation tool (v1.4+).
347
+ MCP-native: bootstraps the workspace + returns the list of blocks YOU
348
+ generate using your own LLM. The first call for any new workspace.
349
+ v1.6+ also returns \`brain_patterns\` — anonymized cross-workspace
350
+ insights for this vertical that you should fold into your generation.
351
+ - **\`connect_workspace\`** (v1.7+) — connect this device to an EXISTING
352
+ workspace via magic-link email. Use when the operator already has a
353
+ workspace (created from another device) and wants to admin it from
354
+ this IDE. Sends a confirmation email; tool polls until approved.
355
+ - **\`add_custom_domain\`** / **\`verify_domain\`** /
356
+ **\`list_workspace_domains\`** / **\`remove_workspace_domain\`** (v1.8+)
357
+ — register the operator's own hostname against the workspace.
358
+ PAID FEATURE on Growth ($29/mo) or Scale ($99/mo); free tier returns
359
+ 402 with upgrade CTA. Vercel auto-provisions SSL once DNS resolves.
360
+ - **\`list_blocks\`** — lists v2 page-block primitives available.
361
+ - **\`get_block_skill\`** — fetches one block's SKILL.md (the generation
362
+ prompt + prop schema you read before generating props).
363
+ - **\`persist_block\`** — saves a block instance you generated. Validates
364
+ + renders + replaces the matching section in the workspace's landing.
365
+ - **\`complete_workspace_v2\`** — marks the v2 flow finished, reports which
366
+ blocks landed.
367
+ - **\`regenerate_block\`** (v1.10+) — bundles current props + workspace
368
+ summary + brain patterns + the operator's new instructions for
369
+ block re-generation ("make the hero punchier", "rewrite the FAQ to
370
+ be less salesy"). Server only assembles context; YOUR LLM does the
371
+ generation, then call persist_block with \`customization\`.
372
+ - **\`get_landing_structure\`** (v1.11+) — read the workspace's landing
373
+ section list with INDEX as the addressing primitive + 1-line preview
374
+ per section. Use BEFORE move/delete to find the right index;
375
+ preview text disambiguates duplicate types ('3 services' vs
376
+ 'stats — 4 numbers').
377
+ - **\`move_section\`** (v1.11+) — atomic single-section move by index.
378
+ Splice semantics: from_index → to_index in result. Works even when
379
+ section types repeat (the case reorder_landing_sections refuses).
380
+ - **\`delete_section\`** (v1.11+) — atomic single-section remove by
381
+ index. Refuses to leave 0 sections. Use to clean up unintended
382
+ duplicates.
383
+ - **\`reorder_landing_sections\`** (v1.10+) — bulk reorder by section
384
+ type when types are unique. Pass the full ordered type array. For
385
+ duplicate-type cases use move_section instead.
386
+ - **\`add_composite_section\`** (v1.12+) — manifest ANY landing block
387
+ (comparison, pricing, "how it works," stats, side-by-side, custom
388
+ CTAs) by composing a tree from 12 low-level primitives. Server
389
+ validates + renders. \`get_block_skill('composite')\` returns the
390
+ primitive vocabulary + worked patterns.
391
+ - **\`update_composite_section\`** (v1.12+) — replace the tree of an
392
+ existing composite section by index. Use to refine ('shorten the
393
+ comparison', 'add another stat', 'make the cards muted').
394
+ - **\`upload_workspace_image\`** (v1.10+, fast path in v1.10.1+) — set
395
+ the workspace logo (slot=logo → organizations.theme.logoUrl) or hero
396
+ background (slot=hero_background → Blueprint.landing hero imageUrl
397
+ + landing re-render). PREFERRED: pass \`image_url\` (HTTPS — server
398
+ fetches directly, no base64) or \`local_file_path\` (absolute path —
399
+ MCP reads the file). Auto-derives file_name + content_type. Legacy:
400
+ \`image_data_b64\` for caller-generated bytes, but base64 consumes
401
+ your tool-call token budget — avoid for files >~12 KB raw. 5 MB max,
402
+ image/png|jpeg|webp|svg+xml|gif. Vercel Blob auto-CDN.
403
+ - **\`read_brain_path\`** / **\`list_brain_dir\`** — read the workspace's
404
+ layer-1 brain (notes about THIS workspace's customers, voice, pipeline
405
+ patterns). Use BEFORE generating blocks; reads tick the note's \`uses\`
406
+ counter so the system knows what's actually being consumed.
407
+ - **\`write_brain_note\`** — capture insights the operator volunteers
408
+ ("walk-ins on Saturday convert 3× better"). Notes live in the
409
+ workspace's brain forever, contribute to layer-2 cross-workspace
410
+ patterns when 3+ workspaces independently observe them.
411
+ - **\`list_brain_patterns\`** — read layer-2 cross-workspace patterns,
412
+ filtered by vertical or block_type.
413
+ - **\`create_full_workspace\`** — v1 atomic creation (legacy). Server-side,
414
+ deterministic. Use only when v2 is impossible.
415
+ - **\`finalize_workspace\`** — MANDATORY closing call. Mints the
416
+ admin auth token (the admin URL doesn't exist until this runs),
417
+ bundles email collection (welcome email + lead capture), and
418
+ returns the formatted final summary Claude Code paraphrases
419
+ verbatim to the operator. Always the last call of every
420
+ workspace creation flow.
421
+ - **\`collect_operator_email\`** — narrower variant of
422
+ finalize_workspace that only sends the welcome email + captures
423
+ the lead. Doesn't return the formatted summary. Use either;
424
+ never skip both.
425
+ - **\`update_landing_content\`** / **\`update_landing_section\`** —
426
+ edit the website's headline, subhead, sections, copy.
427
+ - **\`update_theme\`** — change colors, fonts, dark/light mode.
428
+ - **\`get_intake_structure\`** / **\`add_intake_field\`** /
429
+ **\`move_intake_field\`** / **\`delete_intake_field\`** /
430
+ **\`update_intake_field\`** (v1.13+) — atomic primitives for
431
+ editing the intake form one field at a time. Index-based, ID
432
+ uniqueness enforced. Use these for incremental edits ("add a
433
+ phone field", "rename email to primary email"). For full-form
434
+ replaces use persist_block(intake).
435
+ - **\`update_form\`** — edit the intake form's questions (legacy).
436
+ - **\`get_booking_structure\`** / **\`add_booking_field\`** /
437
+ **\`move_booking_field\`** / **\`delete_booking_field\`** /
438
+ **\`update_booking_field\`** (v1.14+) — atomic primitives for the
439
+ booking form. Indices 0/1 (fullName, email) are server-owned and
440
+ rejected for destructive ops by design — booking flow stays intact.
441
+ Use these for incremental edits ("ask for service address",
442
+ "make equipment field optional", "drop the preferred technician
443
+ field"). For full-form replaces use persist_block(booking).
444
+ - **\`get_portal_structure\`** / **\`add_portal_section\`** /
445
+ **\`update_portal_section\`** / **\`move_portal_section\`** /
446
+ **\`delete_portal_section\`** / **\`preview_portal\`** (v1.15+) —
447
+ composite-tree primitives for the customer portal. Same vocabulary
448
+ as add_composite_section PLUS 5 new customer.* embed refs that
449
+ pull per-customer data (next appointment, documents, deals,
450
+ recent appointments, contact info). Template stored once on the
451
+ workspace; every customer sees their own data via magic-link
452
+ login at the customer_portal_url returned in the response (live
453
+ in v1.16+). preview_portal renders the template against a
454
+ specific contact for visual verification.
455
+ - **\`update_appointment_type\`** — edit the booking page's slot length,
456
+ title, description.
457
+ - **\`install_vertical_pack\`** — set up an industry template
458
+ (real-estate, dental, legal, plumbing, …).
459
+ - **\`list_contacts\`** / **\`create_contact\`** / **\`update_contact\`** —
460
+ manage the CRM.
461
+ - **\`list_deals\`** / **\`create_deal\`** / **\`move_deal_stage\`** —
462
+ manage the pipeline.
463
+ - **\`send_email\`** / **\`send_sms\`** — send messages from the
464
+ workspace's connected channels.
465
+
466
+ The full tool list is available via the MCP \`tools/list\` request.
467
+ Use whatever fits the operator's natural-language request.
468
+
469
+ ---
470
+
471
+ ## Pricing
472
+
473
+ - **Free** — first workspace, free forever, no credit card.
474
+ - **Growth ($29/mo)** — up to 3 workspaces, custom domains,
475
+ white-label, metered AI usage.
476
+ - **Scale ($99/mo)** — unlimited workspaces, advanced AI features,
477
+ priority support.
478
+
479
+ Operators can upgrade via \`/settings/billing\` once they're in the
480
+ admin dashboard. Pre-fills their email automatically.
481
+
482
+ ---
483
+
484
+ **Docs:** <https://seldonframe.com/docs> · **Homepage:**
485
+ <https://seldonframe.com> · **Discord:** <https://discord.gg/sbVUu976NW>
486
+ `;
487
+
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.`;