wyren-mcp 1.0.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.
- package/README.md +35 -0
- package/package.json +25 -0
- package/setup.mjs +53 -0
- package/skills/wyren/SKILL.md +204 -0
- package/skills/wyren/rules/best-practices.md +359 -0
- package/skills/wyren/rules/billing.md +51 -0
- package/skills/wyren/rules/execution.md +121 -0
- package/skills/wyren/rules/models.md +108 -0
- package/skills/wyren/rules/nodes.md +112 -0
- package/skills/wyren/rules/products.md +68 -0
- package/skills/wyren/rules/recommendations.md +297 -0
- package/skills/wyren/rules/workflow-patterns.md +330 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Nodes and Connections
|
|
2
|
+
|
|
3
|
+
## Node categories
|
|
4
|
+
|
|
5
|
+
### Input nodes (provide data to the pipeline)
|
|
6
|
+
|
|
7
|
+
| Type | Label | Output socket | Purpose |
|
|
8
|
+
| ------------ | ----------- | ------------- | --------------------------------------------------- |
|
|
9
|
+
| `textInput` | Text Input | text | User-provided text (prompts, descriptions, scripts) |
|
|
10
|
+
| `imageInput` | Image Input | image | User-provided image (reference photos, logos) |
|
|
11
|
+
| `videoInput` | Video Input | video | User-provided video (source footage) |
|
|
12
|
+
|
|
13
|
+
### AI nodes (generate content — cost credits)
|
|
14
|
+
|
|
15
|
+
| Type | Label | Input sockets | Output socket | Execution |
|
|
16
|
+
| ----------------- | ---------------- | -------------------------- | ------------- | ----------------------------------------- |
|
|
17
|
+
| `textAI` | Text AI | text | text | Sync — instant result |
|
|
18
|
+
| `storyAI` | Story AI | text | text | Sync — instant result |
|
|
19
|
+
| `imageAI` | Image AI | text, image (optional ref) | image | Async — background job, polls until done |
|
|
20
|
+
| `videoAI` | Video AI | text, image | video | Async — background job (can take minutes) |
|
|
21
|
+
| `voiceAI` | Voice AI | text | audio | Sync — instant result |
|
|
22
|
+
| `websiteResearch` | Website Research | text | text | Sync |
|
|
23
|
+
|
|
24
|
+
### Data nodes (configure and filter)
|
|
25
|
+
|
|
26
|
+
| Type | Label | Purpose |
|
|
27
|
+
| ---------------- | --------------- | --------------------------------------------------------- |
|
|
28
|
+
| `globalStyle` | Global Style | Applies a visual style template to all connected AI nodes |
|
|
29
|
+
| `gate` | Gate | Human-in-the-loop candidate selector — accumulates upstream runs, user picks one, downstream sees the pick |
|
|
30
|
+
| `trendSelector` | Trend Selector | Picks trending topics from the trend database |
|
|
31
|
+
| `tiktokResearch` | TikTok Research | Analyzes TikTok trends and content |
|
|
32
|
+
|
|
33
|
+
### Edit nodes (transform media)
|
|
34
|
+
|
|
35
|
+
| Type | Label | Input | Output | Purpose |
|
|
36
|
+
| --------------- | -------------- | ----------------------- | ------ | ------------------------------ |
|
|
37
|
+
| `videoTrim` | Video Trim | video | video | Trim start/end of a video |
|
|
38
|
+
| `videoMerge` | Video Merge | video (multiple) | video | Combine multiple videos |
|
|
39
|
+
| `videoCaptions` | Video Captions | video, audio (optional) | video | Add captions/subtitles overlay |
|
|
40
|
+
|
|
41
|
+
### Compose nodes (combine media)
|
|
42
|
+
|
|
43
|
+
| Type | Label | Purpose |
|
|
44
|
+
| ----------- | --------- | --------------------------------------------- |
|
|
45
|
+
| `slideshow` | Slideshow | Combine images + audio into a video slideshow |
|
|
46
|
+
|
|
47
|
+
### Flow nodes (control execution)
|
|
48
|
+
|
|
49
|
+
| Type | Label | Purpose |
|
|
50
|
+
| --------------- | -------------- | ------------------------------------------------------ |
|
|
51
|
+
| `iterator` | Iterator | Loop over an array — executes body nodes for each item |
|
|
52
|
+
| `closeIterator` | Close Iterator | Collects loop results back into an array |
|
|
53
|
+
|
|
54
|
+
## Socket types
|
|
55
|
+
|
|
56
|
+
There are 5 socket types. Connections are only valid between compatible sockets:
|
|
57
|
+
|
|
58
|
+
| Socket | Color | Compatible with |
|
|
59
|
+
| ------- | ------ | ------------------------- |
|
|
60
|
+
| `text` | Blue | text, any |
|
|
61
|
+
| `image` | Green | image, any |
|
|
62
|
+
| `video` | Purple | video, any |
|
|
63
|
+
| `audio` | Orange | audio, any |
|
|
64
|
+
| `any` | Gray | text, image, video, audio |
|
|
65
|
+
|
|
66
|
+
**Rule**: You can only connect an output to an input if their socket types are compatible.
|
|
67
|
+
|
|
68
|
+
## Connection rules
|
|
69
|
+
|
|
70
|
+
- Each input handle accepts **one** connection by default (some nodes override this via `connectionValidator`)
|
|
71
|
+
- Output handles can connect to **multiple** inputs
|
|
72
|
+
- No cycles allowed — the graph must be a DAG
|
|
73
|
+
- Use `get_node_type_info` to check a node's exact handles and connection limits
|
|
74
|
+
|
|
75
|
+
## Configuring nodes
|
|
76
|
+
|
|
77
|
+
Each node has configurable fields set via `build_graph` with `dataUpdates` (partial merge). The server validates all values and returns clear error messages for out-of-range parameters — with the field's full schema in `details.fieldErrors`. Unambiguous scalar type mismatches (e.g., `"off"` for a boolean) are auto-coerced.
|
|
78
|
+
|
|
79
|
+
Common fields:
|
|
80
|
+
|
|
81
|
+
- **AI nodes**: `model` (model ID), `promptTemplate` (prompt template slug or "custom"), `customPrompt` (when promptTemplate is "custom"), `style` (style template slug)
|
|
82
|
+
- **Text Input**: `text` (the content), `label` (display name)
|
|
83
|
+
- **Image Input**: `url` (image URL — auto-normalized to internal format), `label` (display name)
|
|
84
|
+
- **Video Input**: `url` (video URL), `label` (display name)
|
|
85
|
+
- **Voice AI**: `model`, `presetVoiceId` (voice ID from `list_voices`), `stability`, `similarityBoost`
|
|
86
|
+
- **Video AI**: `model`, `mode` (standard/pro), `duration`, `aspectRatio`
|
|
87
|
+
- **Website Research**: `url`, `crawlDepth` (1/2/3), `provider` (`firecrawl` = 5 credits, screenshots + JS rendering, `standard` = 3 credits, fetch+cheerio, SSR sites only, no screenshots). Default `firecrawl`. If the user wants to spend fewer credits or the site is static HTML, set `provider: "standard"`.
|
|
88
|
+
- **Gate**: `productLabel` (user-facing prompt, e.g. "Pick your hero image"), `productName` (semantic slug for `<ProductGate name="..." />` — required for custom product pages), `maxCandidates` (`"accumulate"` keeps all runs, `1` replaces), `maxRetries` / `maxRetriesUnlimited` (retry cap for product-form runs), `showOnNode` (render candidate gallery inline on canvas). Gate has ONE `any` input + ONE `any` output, max 1 incoming connection; it infers socket type from whatever's plugged in and passes the selected value straight through.
|
|
89
|
+
|
|
90
|
+
Use `get_node_type_info` for the exact field schema of any node if you need to check constraints before setting values.
|
|
91
|
+
|
|
92
|
+
## Handle IDs
|
|
93
|
+
|
|
94
|
+
Both input and output handles use the raw ID from node definitions — no suffixes, no transformations. Use `get_node_type_info({ nodeType: "..." })` to see exact IDs for any node.
|
|
95
|
+
|
|
96
|
+
**Naming convention**: Multi-input composition nodes use **plural** handle names (`videos`, `images`). Single-input nodes use **singular** names (`video`, `text`, `image`). When unsure, call `get_node_type_info` — handle IDs must be exact.
|
|
97
|
+
|
|
98
|
+
Common handles:
|
|
99
|
+
|
|
100
|
+
- `textInput`: output `text`
|
|
101
|
+
- `textAI`: output `text`, input `text`
|
|
102
|
+
- `storyAI`: outputs `scene_1`–`scene_5`, input `text`
|
|
103
|
+
- `imageAI`: output `image`, inputs `text`, `image`
|
|
104
|
+
- `videoAI`: output `video`, inputs `text`, `startFrame` (not all models accept startFrame — use `get_model_capabilities` to check)
|
|
105
|
+
- `voiceAI`: output `audio`, input `text`
|
|
106
|
+
- `websiteResearch`: outputs `brandDocument`, `colorPalette`, `screenshots`
|
|
107
|
+
- `videoCaptions`: output `video`, inputs `video`, `audio`
|
|
108
|
+
- `videoMerge`: outputs `video`, `duration`; input `videos` (plural — accepts up to 10 connections)
|
|
109
|
+
- `videoTrim`: output `video`, input `video`
|
|
110
|
+
- `audioOverlay`: output `video`, `duration`; inputs `video`, `audio` — merges audio onto video (replace or mix mode)
|
|
111
|
+
- `slideshow`: output `video`, inputs `images` (plural), `audio`
|
|
112
|
+
- `iterator`/`closeIterator`: output `items`/`collected`
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Products (API Publishing)
|
|
2
|
+
|
|
3
|
+
Workflows can be published as Products — REST API endpoints that external applications can call.
|
|
4
|
+
|
|
5
|
+
## Publishing flow
|
|
6
|
+
|
|
7
|
+
1. **Build and test the workflow** — make sure it runs correctly
|
|
8
|
+
2. **Mark API inputs/outputs** with `set_product_inputs`:
|
|
9
|
+
- `product_inputs`: input nodes that callers provide values for
|
|
10
|
+
- `product_outputs`: terminal nodes whose outputs are returned to callers
|
|
11
|
+
3. **Publish** with `publish_product`:
|
|
12
|
+
- Generates a URL slug and API endpoint
|
|
13
|
+
- Creates an API key if the user doesn't have one
|
|
14
|
+
- Returns: `slug`, `endpoint_url`, `schema_url`, `estimate_url`
|
|
15
|
+
4. **Share the endpoint** — callers use the API key + endpoint URL
|
|
16
|
+
|
|
17
|
+
## Configuring publish options
|
|
18
|
+
|
|
19
|
+
`publish_product` and `republish_product` accept:
|
|
20
|
+
|
|
21
|
+
- `exposed_inputs` — map of node IDs to fields that API callers can set
|
|
22
|
+
- `allowed_models` — which models callers can override (with allowed list)
|
|
23
|
+
- `allowed_configs` — which config fields callers can override
|
|
24
|
+
- `gate_config` — per-gate behavior in API runs (`auto_approve`, `skip`, or `fail`)
|
|
25
|
+
|
|
26
|
+
### Gate nodes in published workflows
|
|
27
|
+
|
|
28
|
+
Gate nodes are **human-in-the-loop candidate selectors** in the studio (see [workflow-patterns.md](workflow-patterns.md) → "Human-in-the-loop gate before expensive steps"). In a published Product they behave two different ways depending on how the product is consumed:
|
|
29
|
+
|
|
30
|
+
**1. Custom product pages (web form consumers).** Gates render as candidate galleries via `<ProductGate />`. Two fields on the gate node drive the rendering:
|
|
31
|
+
|
|
32
|
+
- `productLabel` — user-facing heading shown above the gallery (e.g. "Pick your hero frame")
|
|
33
|
+
- `productName` — semantic slug used to address the gate in a custom page: `<ProductGate name="hero-frame" />`
|
|
34
|
+
|
|
35
|
+
Always set both when publishing. Without `productName`, the default page still renders the gate but custom pages can't target it by name. Without `productLabel`, the UI falls back to a generic "Gate" heading.
|
|
36
|
+
|
|
37
|
+
**2. API consumers (`run_workflow` via REST).** There is no human to click — the server resolves each gate via the `gate_config` map:
|
|
38
|
+
|
|
39
|
+
- `auto_approve` (default) — pass the **most recent** upstream output straight through. This is what you want for almost every API-published workflow.
|
|
40
|
+
- `skip` — same pass-through behavior, softer semantic (the gate is ignored without error).
|
|
41
|
+
- `fail` — hard error; the API run aborts at this node. Use only when human approval is genuinely mandatory and the workflow should not run via API.
|
|
42
|
+
|
|
43
|
+
When publishing a workflow that contains gates, always explicitly set `gate_config` on every gate node — don't rely on the default map being populated by the client. Leaving a gate out of `gate_config` still falls through to `auto_approve`, but being explicit keeps behavior obvious when you come back to the workflow later.
|
|
44
|
+
|
|
45
|
+
## API keys
|
|
46
|
+
|
|
47
|
+
- `create_api_key` — creates a new key (raw key shown only once)
|
|
48
|
+
- `list_api_keys` — shows prefix and status (not full key)
|
|
49
|
+
- `revoke_api_key` — permanently disables a key
|
|
50
|
+
|
|
51
|
+
First publish auto-generates a key if the user has none.
|
|
52
|
+
|
|
53
|
+
## Monitoring runs
|
|
54
|
+
|
|
55
|
+
- `get_run_status({ run_id })` — status, outputs, credits consumed, timing
|
|
56
|
+
- `list_runs({ slug })` — recent runs for a published product
|
|
57
|
+
|
|
58
|
+
## Updating a published product
|
|
59
|
+
|
|
60
|
+
Use `republish_product` with the same slug — creates a new version while keeping the endpoint URL stable.
|
|
61
|
+
|
|
62
|
+
## Unpublishing
|
|
63
|
+
|
|
64
|
+
`unpublish_product({ slug })` — deactivates the endpoint. In-flight runs complete normally.
|
|
65
|
+
|
|
66
|
+
## Schema inspection
|
|
67
|
+
|
|
68
|
+
`get_product_schema({ slug })` — returns the full API contract: expected inputs, available model overrides, output structure, and endpoint URLs. Useful for building integrations.
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
# Budget-Aware Recommendations
|
|
2
|
+
|
|
3
|
+
Load this file when the user asks an ideation question scoped to a budget — e.g., "what can I build for $2?", "what's possible with 500 credits?", "ideas for 1000 credits", "show me options under $5", "what marketing video can I build with $1", "what should I make with my budget?".
|
|
4
|
+
|
|
5
|
+
Your job: return a **single ranked list of 4–6 creative concepts**. Every concept names the pipeline that realizes it — a saved user workflow, a published product, an Essential, a template, or a custom build. No buckets, no section headers. The user sees one list of ideas, each with a clear "uses X" realization line.
|
|
6
|
+
|
|
7
|
+
## Step 1 — Call `discover_options` FIRST (non-skippable)
|
|
8
|
+
|
|
9
|
+
Every budget question starts with exactly one call:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
discover_options({
|
|
13
|
+
budget: <dollars or credits from user>,
|
|
14
|
+
include?: <required node types from intent>,
|
|
15
|
+
exclude?: <disallowed node types from intent>,
|
|
16
|
+
})
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Red flag — stop and restart:** If you are about to list options, propose builds, or describe "what you could make" without having called `discover_options` in this turn, STOP. `discover_options` is the only source of truth for the user's workflows, products, essentials, and templates. Do not substitute it with memory, `get_pricing`, or `list_workflows`.
|
|
20
|
+
|
|
21
|
+
**Intent → filter mapping:**
|
|
22
|
+
|
|
23
|
+
| User says | Set |
|
|
24
|
+
| ---------------------------------- | ------------------------------ |
|
|
25
|
+
| "product video", "marketing video" | `include: ["videoAI"]` |
|
|
26
|
+
| "voiceover", "narration" | `include: ["voiceAI"]` |
|
|
27
|
+
| "slideshow" | `include: ["slideshow"]` |
|
|
28
|
+
| "captions", "subtitled" | `include: ["videoCaptions"]` |
|
|
29
|
+
| "brand analysis", "from my site" | `include: ["websiteResearch"]` |
|
|
30
|
+
| "no captions" | `exclude: ["videoCaptions"]` |
|
|
31
|
+
| "image only", "no video" | `exclude: ["videoAI"]` |
|
|
32
|
+
| "no voice" | `exclude: ["voiceAI"]` |
|
|
33
|
+
|
|
34
|
+
If the intent is generic ("what can I build for $X"), omit `include`/`exclude` and let `discover_options` rank across everything.
|
|
35
|
+
|
|
36
|
+
## Step 1b — Call `get_trend_inspiration` silently (creative intent only)
|
|
37
|
+
|
|
38
|
+
When the user's intent is **creative** — they're asking for a video ad, marketing concept, hook idea, or brand/product pitch (anything that will result in a concept pitch in Step 3) — you MUST also call `get_trend_inspiration` in this turn, in parallel with `discover_options`. Skip this step only for purely inventory-style questions ("list my workflows", "what's my credit balance").
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
get_trend_inspiration({
|
|
42
|
+
keywords: <3-6 niche/audience/product terms derived from the user>,
|
|
43
|
+
platform: "tiktok", // default; omit for cross-platform
|
|
44
|
+
limit: 3,
|
|
45
|
+
random: true,
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Keyword derivation:** pull from the user's business/product/industry/audience ("handmade candles for gift shops" → `["candles", "home decor", "gift", "asmr", "aesthetic"]`). Mix concrete product words with style/vibe descriptors. Do NOT include the dollar amount or generic words like "video" / "ad".
|
|
50
|
+
|
|
51
|
+
**What you get back:**
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
{
|
|
55
|
+
trends: [
|
|
56
|
+
{
|
|
57
|
+
caption, hashtags, play_count, engagement_rate,
|
|
58
|
+
video_style, video_content, hook_style, hook_content
|
|
59
|
+
},
|
|
60
|
+
...
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Silent grounding rule (HARD):** The `trends` array is inspiration data for YOU, not the user. Absorb the `hook_style`, `video_style`, `hook_content`, `video_content` descriptions and let them shape the hooks, pacing, and structure of the concepts you generate in Step 3. Then discard them.
|
|
66
|
+
|
|
67
|
+
You MUST NOT:
|
|
68
|
+
- Mention trends, "trending", "top-performing", TikTok, engagement rates, play counts, or the tool in any form to the user
|
|
69
|
+
- Cite a specific trend, copy its caption, or paraphrase it closely
|
|
70
|
+
- Say "based on what's working right now" or any variation
|
|
71
|
+
- Surface hashtags, URLs, or any field from the response
|
|
72
|
+
- Reference the fact that you looked at data
|
|
73
|
+
|
|
74
|
+
The user sees only the creative concept — the trend grounding is invisible. Think of it as your creative director whispering references while you write; the final pitch reads as your own idea.
|
|
75
|
+
|
|
76
|
+
**Empty result (`trends: []`):** proceed silently with pure creative reasoning from the vocabulary in Step 3. Do not tell the user the lookup was empty.
|
|
77
|
+
|
|
78
|
+
**Response shape:**
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
{
|
|
82
|
+
budget, credit_balance,
|
|
83
|
+
options: [
|
|
84
|
+
{
|
|
85
|
+
source_type: 'workflow' | 'product' | 'essential' | 'template',
|
|
86
|
+
source_id, name, description, capabilities: string[],
|
|
87
|
+
estimated_cost, cost_type: 'exact' | 'estimated',
|
|
88
|
+
affordable: boolean,
|
|
89
|
+
action: { tool: 'run_workflow' | 'create_from_template', args: {...} }
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
cheapest_option: <option | null>, // only when options is empty
|
|
93
|
+
note: string | null, // explanation when nothing fits
|
|
94
|
+
inventory: {
|
|
95
|
+
workflows_total, workflows_affordable,
|
|
96
|
+
products_total, products_affordable,
|
|
97
|
+
essentials_total, essentials_affordable,
|
|
98
|
+
templates_total, templates_affordable,
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use `inventory` to decorate empty-bucket headers with the real reason (see Step 2).
|
|
104
|
+
|
|
105
|
+
## Step 2 — Unify everything into a single concept list
|
|
106
|
+
|
|
107
|
+
There are no separate buckets. Every recommendation the user sees is a **creative concept**, and every concept names which pipeline realizes it. The realization is either a library item (a user workflow, a published product, an Essential, or a template) or a custom build the agent synthesizes.
|
|
108
|
+
|
|
109
|
+
For each `option` returned by `discover_options`, pitch a concept that *matches the option's fixed topology and capabilities*. You are not free to invent any concept for a library item — the concept must be something that pipeline can actually produce. For a `textInput → textAI → videoAI + voiceAI → audioOverlay` essential, the concept must be a narrated single-clip video, not a multi-scene montage.
|
|
110
|
+
|
|
111
|
+
Then synthesize 1–2 **custom** concepts (see Step 3) that cover creative territory no library item can reach.
|
|
112
|
+
|
|
113
|
+
Rank the final list so the strongest fit to the user's intent comes first, regardless of source. Cap at 4–6 concepts total. Do not pad.
|
|
114
|
+
|
|
115
|
+
**Handling empty inventory:** if `inventory.workflows_total === 0` or no library items match the intent, just say so briefly in the header line ("Your library doesn't have anything matching this yet — here are custom builds.") and proceed with custom concepts. Do not render empty bucket headers.
|
|
116
|
+
|
|
117
|
+
## Step 3 — Concept-first presentation (library items AND custom builds)
|
|
118
|
+
|
|
119
|
+
Every concept in your response — whether it's realized by a saved workflow, an Essential, a published product, a template, or a custom build — follows the same concept-first rules below. The difference is only in the **Pipeline** line, which names the realization:
|
|
120
|
+
|
|
121
|
+
- `uses your "<workflow name>" workflow` → for `source_type === 'workflow'`
|
|
122
|
+
- `uses the "<product name>" product` → for `source_type === 'product'` (non-essential)
|
|
123
|
+
- `uses the "<essential name>" essential` → for `source_type === 'essential'`
|
|
124
|
+
- `uses the "<template name>" template` → for `source_type === 'template'`
|
|
125
|
+
- `custom pipeline: <node chain>` → for agent-synthesized builds
|
|
126
|
+
|
|
127
|
+
Always include at least one custom build in the final list, even when `discover_options` returns plenty of library results. Custom builds are the creative safety valve — they show the user what's possible when no existing pipeline fits.
|
|
128
|
+
|
|
129
|
+
### Hard rule: no format-only names
|
|
130
|
+
|
|
131
|
+
❌ **Banned** concept names (these describe a container, not an idea):
|
|
132
|
+
- "Slideshow ad", "Hero shot video", "Cinematic product ad"
|
|
133
|
+
- "TikTok / Reel ad", "30-second commercial", "Explainer video"
|
|
134
|
+
- "Multi-shot ad", "Product demo", "Brand film"
|
|
135
|
+
|
|
136
|
+
✅ **Required**: the concept name must describe the *idea, hook, or story beat* — what the viewer actually sees and why it stops the scroll. Examples:
|
|
137
|
+
- "Frost-crack reveal" (frozen block shatters to expose the product)
|
|
138
|
+
- "Liquid morph" (one product pours and re-forms as another)
|
|
139
|
+
- "Hands-only ritual" (unboxing and use shot entirely from POV, no talking)
|
|
140
|
+
- "Before/after in one take" (camera pans across a split-world transformation)
|
|
141
|
+
- "Mirror swap" (reflection reveals a different version of the wearer)
|
|
142
|
+
- "Stop-motion flatlay assembly" (ingredients march into place on a marble counter)
|
|
143
|
+
|
|
144
|
+
### Required pitch structure
|
|
145
|
+
|
|
146
|
+
Each fresh concept MUST be pitched with exactly these three lines, in order:
|
|
147
|
+
|
|
148
|
+
1. **Concept (2 sentences max)** — sentence 1: the hook + beat + payoff collapsed into one vivid description of what the viewer sees. Sentence 2: why it trends right now (link to a current pattern from the vocabulary below). No bullet sub-points, no four-beat breakdown — just two sentences total.
|
|
149
|
+
2. **Pipeline** — the node chain (e.g. `textInput → textAI → imageAI → videoAI`) with the model tier picked in parentheses.
|
|
150
|
+
3. **Est. cost (upper bound)** — a concrete ceiling in credits, derived from `get_pricing` calls (see sizing below). Present as "up to N credits" — never a range, never a lowball.
|
|
151
|
+
|
|
152
|
+
Only *after* the creative concept is locked in do you describe the pipeline and cost. Cost is a consequence of the concept, never the headline.
|
|
153
|
+
|
|
154
|
+
### Cost discipline — no ballparks
|
|
155
|
+
|
|
156
|
+
- **Library items** (workflows / products / essentials / templates): use the `estimated_cost` from `discover_options` directly. The backend returns upper-bound estimates — do not second-guess them. Present as "up to N credits".
|
|
157
|
+
- **Custom builds**: you MUST call `get_pricing` in **chain mode** with every AI node in the concept's pipeline before quoting a cost:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
get_pricing({
|
|
161
|
+
chain: [
|
|
162
|
+
{ type: "textAI" },
|
|
163
|
+
{ type: "imageAI", config: { aspectRatio: "9:16" } },
|
|
164
|
+
{ type: "videoAI", model: "kling-v2-6", config: { mode: "standard", duration: 5, sound: false } }
|
|
165
|
+
]
|
|
166
|
+
})
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The response returns `perNode`, a server-computed `totalUpperBound`, a `totalFloorEstimate`, and a `hasWorstCaseFallback` flag. **Use `totalUpperBound` verbatim** — never hand-sum the `perNode` entries. That is how small costs (textAI ≈ 1 credit) get dropped and quotes come out low. Do not eyeball from billing.md tier tables.
|
|
170
|
+
|
|
171
|
+
**If `hasWorstCaseFallback: true`**, the ceiling is inflated (typically 3–5× reality) because one or more nodes had no model/config specified, so the server used the most expensive row in that operation's rate card. Two options: (a) re-call `get_pricing` with explicit `model` + `config` on every chain entry to get a tight ceiling, or (b) present the quote honestly as `"up to ${totalUpperBound} credits (real cost likely closer to ${totalFloorEstimate})"`. Never hide the fallback — the user deserves to know the quote is a worst case.
|
|
172
|
+
|
|
173
|
+
### Concept realizability check
|
|
174
|
+
|
|
175
|
+
Before finalizing a pitch, walk every visual element in the Concept sentence and verify it maps to a pipeline stage. Common mismatches:
|
|
176
|
+
|
|
177
|
+
- Concept says "logo fades in" / "brand text overlay" / "title card" / "on-screen caption of the product name" — but the pipeline has no `logoOverlay`, no text overlay stage, and only `videoCaptions` (which captions the voiceover, not a static title). **Drop the line from the pitch** or add the missing stage. Don't promise what the graph can't render.
|
|
178
|
+
- Concept says "music swells" or "synchronized to beat" — but the pipeline has no `audioOverlay` / music track. Drop or add.
|
|
179
|
+
- Concept says "product spins 360°" — but `videoAI` can't guarantee a specific motion arc from a start frame. Soften to "gently rotates" or equivalent.
|
|
180
|
+
|
|
181
|
+
### Concept vocabulary (draw from these patterns)
|
|
182
|
+
|
|
183
|
+
These are the trending motifs the Fresh concepts bucket should sample from. Mix and match to fit the user's brand or product:
|
|
184
|
+
|
|
185
|
+
- **Reveals**: frost-crack, shrink-wrap tear, dust-cloud clear, water-ripple, paper-origami unfold
|
|
186
|
+
- **Transforms**: liquid pour morph, stop-motion assembly, time-lapse bloom, split-world before/after, ingredient tornado
|
|
187
|
+
- **POV & hands**: unboxing-only-hands, ASMR close-up, desk-top tableau, first-person ritual, mirror reflection
|
|
188
|
+
- **Text-first hooks**: text appears before product ("the only X that does Y"), kinetic typography countdown, question-answer reveal
|
|
189
|
+
- **Story beats**: "this or that" binary choice, "wait for it" delayed payoff, "things I wish I knew", "POV: you just discovered…"
|
|
190
|
+
- **Motion tricks**: match-cut transitions, tracking-shot continuous take, scale-shift (tiny→massive), camera whip-pan between scenes
|
|
191
|
+
- **3-beat ad structure**: hook (1 s text pop) → payoff (product in action) → CTA (logo + text)
|
|
192
|
+
- **On-screen text-first**: bold claim flashes before the product appears
|
|
193
|
+
- **Scroll-stop sting**: percussive audio hit synced to the first cut
|
|
194
|
+
- **Logo reveal**: brand mark slides / flashes in at the end of the payoff or CTA
|
|
195
|
+
|
|
196
|
+
### Sizing a custom build to the budget
|
|
197
|
+
|
|
198
|
+
1. Pick a concept from the vocabulary matching the user's intent
|
|
199
|
+
2. Pick a pattern from [workflow-patterns.md](workflow-patterns.md) that can realize it
|
|
200
|
+
3. Call `get_pricing` for every AI node in the pattern and sum them. Round up. This is the upper bound you quote.
|
|
201
|
+
4. **Only propose concepts you can actually fit in the budget** — if the upper bound exceeds it, swap to a cheaper realization (shorter duration, cheaper model tier, fewer nodes)
|
|
202
|
+
5. Every concept — library or custom — presents as four lines: **Concept** (2 sentences) → **Pipeline** (realization line) → **Est. cost** (up to N credits) → action line.
|
|
203
|
+
|
|
204
|
+
## Presentation format
|
|
205
|
+
|
|
206
|
+
Lead with a single budget-context line, then a ranked list of 4–6 concepts. No section headers. No buckets. Every concept looks the same regardless of whether it's realized by a saved workflow, a product, an essential, a template, or a custom build.
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
Credit balance: <credit_balance> | Your budget: <budget>
|
|
210
|
+
|
|
211
|
+
1. **<Concept name>**
|
|
212
|
+
- Concept: <sentence 1: hook+beat+payoff in one vivid line.> <sentence 2: why it trends right now.>
|
|
213
|
+
- Pipeline: uses your "<workflow name>" workflow
|
|
214
|
+
- Est. cost: up to <N> credits
|
|
215
|
+
- → say "run it" and I'll execute "<workflow name>"
|
|
216
|
+
|
|
217
|
+
2. **<Concept name>**
|
|
218
|
+
- Concept: <sentence 1.> <sentence 2.>
|
|
219
|
+
- Pipeline: uses the "Narrated Video" essential
|
|
220
|
+
- Est. cost: up to <N> credits
|
|
221
|
+
- → say "use it" and I'll copy the "Narrated Video" essential into your workspace, then run it
|
|
222
|
+
|
|
223
|
+
3. **<Concept name>**
|
|
224
|
+
- Concept: <sentence 1.> <sentence 2.>
|
|
225
|
+
- Pipeline: custom — `textInput → textAI → imageAI → videoAI` (Kling budget tier)
|
|
226
|
+
- Est. cost: up to <N> credits
|
|
227
|
+
- → say "build it" and I'll assemble the pipeline
|
|
228
|
+
|
|
229
|
+
4. **<Concept name>**
|
|
230
|
+
- Concept: <sentence 1.> <sentence 2.>
|
|
231
|
+
- Pipeline: uses the "<template name>" template
|
|
232
|
+
- Est. cost: up to <N> credits
|
|
233
|
+
- → say "use it" and I'll create a workflow from "<template name>"
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Internal tool calls stay internal.** The user should never see raw UUIDs, tool names, or function-call syntax in the action line. Keep the friendly prompt ("say 'run it'…") on screen and, when the user confirms, call the correct MCP tool with the IDs from the `discover_options` response under the hood. The agent is responsible for remembering which concept maps to which `source_id` when the user says "run it" / "build it" / "use it".
|
|
237
|
+
|
|
238
|
+
**Essentials need a two-step run.** Essentials (`source_type === 'essential'`) are global seeded pipelines — they live in the `products` table, have no owner, and `run_workflow` / `get_workflow` / `duplicate_workflow` cannot touch them directly. When the user confirms an essential, the agent MUST:
|
|
239
|
+
|
|
240
|
+
1. Call `use_essential({ essentialId: <source_id> })` to copy the essential into the user's own `workflows` table. This returns a new `workflow_id`.
|
|
241
|
+
2. Then call `run_workflow({ workflowId: <new id>, userConfirmed: true, userInputs: {...} })` with that new ID.
|
|
242
|
+
|
|
243
|
+
This two-step happens silently under the hood — the user just sees "copying 'Narrated Video' into your workspace… running it now." Never show the intermediate tool calls in prose. Workflows, templates, and products (non-essential) keep their normal single-step flows: `run_workflow` / `create_from_template` / `run_workflow`.
|
|
244
|
+
|
|
245
|
+
**Action-line mapping by source_type:**
|
|
246
|
+
|
|
247
|
+
| `source_type` | Friendly action line | Under-the-hood calls |
|
|
248
|
+
| --- | --- | --- |
|
|
249
|
+
| `workflow` | "say 'run it' and I'll execute '<name>'" | `run_workflow` |
|
|
250
|
+
| `essential` | "say 'use it' and I'll copy '<name>' into your workspace, then run it" | `use_essential` → `run_workflow` |
|
|
251
|
+
| `product` | "say 'run it' and I'll execute '<name>'" | `run_workflow` (on `source_workflow_id`) |
|
|
252
|
+
| `template` | "say 'use it' and I'll create a workflow from '<name>'" | `create_from_template` |
|
|
253
|
+
| custom | "say 'build it' and I'll assemble the pipeline" | `build_graph` |
|
|
254
|
+
|
|
255
|
+
**Rules:**
|
|
256
|
+
|
|
257
|
+
- Always show `credit_balance` and the user's budget on the first line.
|
|
258
|
+
- Rank by best fit to the user's intent, not by source type.
|
|
259
|
+
- Every concept — library or custom — uses the same 4-line format: **Concept** (2 sentences) → **Pipeline** (realization) → **Est. cost** (up to N credits) → action line.
|
|
260
|
+
- The **Pipeline** line MUST identify the realization by name: `uses your "<name>" workflow` / `uses the "<name>" product` / `uses the "<name>" essential` / `uses the "<name>" template` / `custom — <node chain>`. Never show `workflow_id` / `template_id` / UUIDs to the user.
|
|
261
|
+
- The **action line** MUST be a plain-English prompt ("say 'run it' and I'll execute…") — never raw tool-call syntax, never a UUID, never a JSON args object. Remember the `source_id` internally for when the user confirms.
|
|
262
|
+
- Library costs come from `discover_options` `estimated_cost` directly; custom costs come from `get_pricing` per node, summed and rounded up. Always present as "up to N credits", never a range.
|
|
263
|
+
- Mark any concept where the underlying option has `affordable: false` with a trailing "⚠ exceeds your credit balance".
|
|
264
|
+
- Always include at least one custom concept in the list, even when library results are plentiful.
|
|
265
|
+
- If the user's library has no items matching the intent, open with one sentence ("Your library doesn't have anything matching this yet — here are custom builds.") and proceed.
|
|
266
|
+
- Never call `run_workflow` or `build_graph` without explicit user confirmation — this playbook is recommendations only.
|
|
267
|
+
|
|
268
|
+
## When nothing in the library fits
|
|
269
|
+
|
|
270
|
+
If `discover_options` returns `options: []`, use `cheapest_option`, `note`, and `inventory`:
|
|
271
|
+
|
|
272
|
+
1. Tell the user the `note` verbatim ("No options available within X credits. The cheapest option is Y credits.")
|
|
273
|
+
2. Still return a ranked concept list — just skip all library concepts and lead straight into custom builds
|
|
274
|
+
3. If `cheapest_option` exists, include it as one concept in the list (marked with "⚠ exceeds your credit balance" if `affordable: false`) so the user can see what's closest
|
|
275
|
+
4. Propose 1–2 custom concepts — the cheapest possible realizations of trending patterns that match the user's intent (image-only, no video, shortest duration). If even those don't fit the budget, say so explicitly and suggest topping up credits.
|
|
276
|
+
|
|
277
|
+
## Do not
|
|
278
|
+
|
|
279
|
+
- Do not pitch a branded custom build that omits multi-scene pacing, voiceover, and captions unless (a) the user has explicitly rejected those components in this conversation, or (b) `get_pricing` shows the rich default physically cannot fit the budget. A flat `websiteResearch → textAI → imageAI → videoAI` is a fallback, not a default. See workflow-patterns.md → "TikTok 3-scene branded ad" for the default topology.
|
|
280
|
+
- Do not propose a branded ad concept without first running the Pre-flight research phase (SKILL.md) and surfacing candidate brand images to the user. If the user declines to pick one and the site had nothing usable, proceed but note in the concept line that the output will look generic.
|
|
281
|
+
- Do not skip `discover_options` — ever. It's the only source of truth.
|
|
282
|
+
- Do not skip `get_trend_inspiration` when the user is asking for a creative concept / video ad / hook idea. Call it in parallel with `discover_options`.
|
|
283
|
+
- Do not mention, cite, paraphrase, or surface `get_trend_inspiration` results in the user-facing response. It is silent grounding only.
|
|
284
|
+
- Do not use bucket headers or separate library items from custom builds. Everything is a single concept list.
|
|
285
|
+
- Do not name any concept by its format ("Slideshow ad", "Hero shot", "Cinematic ad") — it must be a creative idea.
|
|
286
|
+
- Do not propose a concept you cannot tie to a specific trending pattern from the vocabulary.
|
|
287
|
+
- Do not lead a concept with cost or pipeline — lead with the 2-sentence Concept line.
|
|
288
|
+
- Do not expand the concept into a 4-beat breakdown. Two sentences, total.
|
|
289
|
+
- Do not invent a concept for a library item that its fixed pipeline can't actually produce.
|
|
290
|
+
- Do not quote a custom-build cost without calling `get_pricing({ chain: [...] })` first. Use `totalUpperBound` verbatim — no hand-summing, no ballparks.
|
|
291
|
+
- Do not present a cost as a range ("15–20 credits") — always a single upper bound ("up to 20 credits").
|
|
292
|
+
- Do not show UUIDs, raw tool names, or JSON tool-call syntax to the user. Action lines are plain English.
|
|
293
|
+
- Do not execute anything (`run_workflow`, `build_graph`) without explicit user confirmation.
|
|
294
|
+
- Do not invent workflows or products that aren't in the `discover_options` response.
|
|
295
|
+
- Do not present more than 6 concepts total — overwhelm kills ideation.
|
|
296
|
+
- Do not propose a concept that ignores the user's stated brand, industry, or URL. Every concept must be specific to their business — if you could copy-paste the pitch to any other company, it isn't specific enough. Name the brand in the concept name.
|
|
297
|
+
- **Never invent concrete facts to make a concept feel specific.** Do not write fictional listing prices, city names, square footage, product SKUs, menu items, employee names, or any verifiable detail the user didn't provide. If you need a specific to make the concept land, ASK the user for it or pull it from `websiteResearch` output — don't make it up.
|