@kolbo/mcp 1.81.2 → 1.81.5

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 (34) hide show
  1. package/README.md +5 -2
  2. package/package.json +1 -1
  3. package/skill/GENERATED.md +1 -1
  4. package/skill/SKILL.md +68 -89
  5. package/skill/VERSION +1 -1
  6. package/skill/references/models/creative-director.md +2 -8
  7. package/skill/references/models/music.md +1 -1
  8. package/skill/references/models/prompt-copilot.md +1 -1
  9. package/skill/references/models/seedance.md +5 -21
  10. package/skill/references/models/seedance25.md +1 -1
  11. package/skill/references/models/veo.md +1 -1
  12. package/skill/references/workflows/color-dna.md +12 -0
  13. package/skill/references/workflows/cost-and-validation.md +6 -26
  14. package/skill/references/workflows/dtc-ads.md +1 -1
  15. package/skill/references/workflows/filmmaking.md +1 -25
  16. package/skill/references/workflows/marketing-studio.md +14 -14
  17. package/skill/references/workflows/media-library.md +11 -1
  18. package/skill/references/workflows/product-photoshoot.md +1 -1
  19. package/skill/references/workflows/production-log.md +2 -14
  20. package/skill/references/workflows/production-planning.md +6 -5
  21. package/skill/references/workflows/review-collections.md +98 -0
  22. package/skill/references/workflows/troubleshooting.md +2 -8
  23. package/skill/references/workflows/visual-dna.md +5 -34
  24. package/src/apps/bridge.js +4 -0
  25. package/src/apps/html.js +2 -1
  26. package/src/apps/index.js +1 -0
  27. package/src/apps/theme.js +2 -1
  28. package/src/apps/widgets/catalog.js +1 -1
  29. package/src/apps/widgets/generation.js +67 -17
  30. package/src/apps/widgets/list.js +6 -6
  31. package/src/apps/widgets/mediaGrid.js +3 -6
  32. package/src/tools/_shared.js +55 -9
  33. package/src/tools/generate.js +21 -9
  34. package/src/tools/projects.js +41 -9
@@ -52,30 +52,6 @@ Compile only what the current generation needs. A video model cannot resolve “
52
52
 
53
53
  Kolbo Visual DNA is semantic project truth, not merely reference imagery. Read and preserve the saved DNA type and analyzed context: character DNAs own identity/state/performance/voice; environment and scene DNAs own location/geography/light; product DNAs own prop/product identity, scale, material, and state; style DNAs own the visual register. Keep exact tags and never reinterpret one DNA type as another.
54
54
 
55
- ## Choose control density
56
-
57
- Never equate sophistication with maximum length.
58
-
59
- - **Strict** — lock exact blocking, count, timing, dialogue, hand/prop state, axis, scale, or failure-prone physics. Use for continuity-heavy dialogue, expensive hero shots, repeated failures, and exact music synchronization.
60
- - **Anchored** — dictate non-negotiable story/continuity/physics anchors and allow camera or performance variation inside them. Use for complex spectacle where controlled discovery is valuable.
61
- - **Exploratory** — protect identity, world, safety, and essential beats while inviting coverage variations. Use for montage, inserts, music-video coverage, and ideation.
62
-
63
- If the user supplied an exact prompt, preserve its chosen density unless the failure diagnosis proves density itself is the problem.
64
-
65
- ## Select craft packs
66
-
67
- Load only what the shot needs:
68
-
69
- - Story causality and scene reversals: [scene-engine.md](references/filmmaking/scene-engine.md)
70
- - Asset building, versions, and stress tests: [asset-preproduction.md](references/filmmaking/asset-preproduction.md)
71
- - Character performance, listening, and voice identity: [acting-direction.md](references/filmmaking/acting-direction.md)
72
- - Geography, axes, eyelines, diagrams, and state continuity: [blocking-continuity.md](references/filmmaking/blocking-continuity.md)
73
- - Shot size, optics, operator behavior, and visual grammar: [cinematography.md](references/filmmaking/cinematography.md)
74
- - Action feasibility, mass, materials, transformations, and impossible shots: [physics-action.md](references/filmmaking/physics-action.md)
75
- - Dialogue, ambience, native audio, source-song performance, and post music: [audio-dialogue-music.md](references/filmmaking/audio-dialogue-music.md)
76
-
77
- Do not paste every craft pack into every prompt. Translate the selected pack into the shortest observable instructions that preserve the intended result.
78
-
79
55
  ## Compile a shot
80
56
 
81
57
  Read [prompt-contracts.md](references/filmmaking/prompt-contracts.md) for exact structures.
@@ -92,7 +68,7 @@ Before writing, establish:
92
68
  8. Audio ownership, exact words/lyrics, and whether music is native, source-driven, or reserved for post.
93
69
  9. Model capability limits and target generation mode.
94
70
 
95
- Prompt-length limits apply to the entire compiled generation prompt as one string, including whitespace, headers, timecodes, dialogue, audio, and locks. Count after compilation. For the current Seedance 2.5 adapter snapshot, the hard ceiling is 30,000 characters; never borrow that number for another model.
71
+ Prompt-length limits apply to the entire compiled generation prompt as one string, including whitespace, headers, timecodes, dialogue, audio, and locks. Count after compilation; read the cap from `max_prompt_length` via `list_models` (see `models/seedance25.md`).
96
72
 
97
73
  **Seedance 2 / Seedance 2.5 / `generate_elements` — Locked Intro is the only compile shape.** Read `references/models/seedance.md` (and `seedance25.md` for 2.5 caps). Do not emit the SCENE CONTEXT / OPTICS / ACTION department pack below as the generation prompt. Every Visual DNA in play must be `@ExactName` in CAST and in each shot — never "the left man" or a possessive.
98
74
 
@@ -44,15 +44,17 @@ If the user mentions a product / brand but no mode word, default to `ugc`. If th
44
44
 
45
45
  The mode determines which Kolbo MCP tool to call, what defaults to set, and what's forbidden.
46
46
 
47
- | Mode | Primary tool | aspect_ratio | duration | sound_enabled | Captions / watermarks |
48
- |---|---|---|---|:-:|:-:|
49
- | `ugc`, `ugc_how_to`, `ugc_unboxing`, `ugc_virtual_try_on`, `product_review` | `generate_video_from_image` (frame-first) OR `generate_elements` (Visual DNA → video) | **`9:16`** | model's `default_duration` (5–8s) | OFF | **Never add** |
50
- | `product_showcase` | `generate_creative_director` with `workflow_type: "video"` (for multi-shot) OR `generate_video` (single) | `16:9` or `1:1` | 5–10s | ON if model supports `sound_generation_type: "native"` | Allowed if user asks |
51
- | `tv_spot` | `generate_creative_director` with `workflow_type: "video"` (3–6 shots for a beat structure) | `16:9` | 15–30s total | ON (full audio + dialogue) | Allowed if part of the spot |
52
- | `virtual_try_on` | `generate_elements` with character Visual DNA + product as `reference_images` | `9:16` or `4:5` | 5–8s | OFF | Never add |
53
- | `wild_card` | User's chosen model with broader prompt latitude (no mode-specific defaults) | User's pick | User's pick | User's pick | User's pick |
47
+ | Mode | Primary tool |
48
+ |---|---|
49
+ | `ugc`, `ugc_how_to`, `ugc_unboxing`, `ugc_virtual_try_on`, `product_review` | `generate_video_from_image` (frame-first) OR `generate_elements` (Visual DNA → video) |
50
+ | `product_showcase` | `generate_creative_director` with `workflow_type: "video"` (for multi-shot) OR `generate_video` (single) |
51
+ | `tv_spot` | `generate_creative_director` with `workflow_type: "video"` (3–6 shots for a beat structure) |
52
+ | `virtual_try_on` | `generate_elements` with character Visual DNA + product as `reference_images` |
53
+ | `wild_card` | User's chosen model with broader prompt latitude (no mode-specific defaults) |
54
+
55
+ Aspect / duration / sound / captions defaults for the `ugc*` family live in "UGC Family Defaults" below.
54
56
 
55
- **Pick the actual model** with `list_models({ type: "..." })` and validate caps before firing — see SKILL.md "Resolution / Aspect / Duration — validate against caps".
57
+ **Pick the actual model** with `list_models({ type: "..." })` and validate caps before firing — see `references/workflows/cost-and-validation.md`.
56
58
 
57
59
  ## The Look Itself — read `workflows/ugc-smartphone.md`
58
60
 
@@ -149,8 +151,7 @@ Scale to 2–6 slots. Keep `hook → demo → payoff` as the minimum arc; add `t
149
151
  ### 2. Rendering rules (hard invariants — apply to EVERY slot)
150
152
 
151
153
  - One aspect ratio across all slots (UGC = `9:16`). Never mix.
152
- - **No on-image text**, captions, subtitles, watermarks, or lower-thirds (users add captions in post).
153
- - **Identity lock**: same presenter, same wardrobe, same lighting environment across all slots — open the prompt with `same character throughout all shots`.
154
+ - **Identity lock**: same presenter, same wardrobe, same lighting environment across all slots — bind identity by tagging `@<dna-name>` in every slot description (identity binds via the DNA; the phrase "same character throughout all shots" is FORBIDDEN — see `models/seedance.md`).
154
155
  - Hands and product must read cleanly — no deformed hands, no floating / clipping product, product logo legible when held.
155
156
  - Phone-shot aesthetic (handheld sway, window/screen key) unless the mode is polished (`tv_spot`, `product_showcase`).
156
157
 
@@ -183,7 +184,7 @@ Track these so each slot's call is reproducible and the arc stays coherent:
183
184
  Brief: *"15s UGC review of a skincare serum, tech-savvy woman creator."*
184
185
 
185
186
  1. Ensure/create presenter Visual DNA (tech-savvy woman) → `visual_dna_id`.
186
- 2. Board: `generate_image` a 3-panel `16:9` sheet — (a) chest-up hook holding the serum, (b) hands applying it, (c) thumbs-up reaction — `same character throughout all shots`, locked to the DNA. → `board_media_id`.
187
+ 2. Board: `generate_image` a 3-panel `16:9` sheet — (a) chest-up hook holding the serum, (b) hands applying it, (c) thumbs-up reaction — `@<dna-name>` tagged in every panel description, locked to the DNA. → `board_media_id`.
187
188
  3. Slots (each `9:16`, ~5s, sound OFF, animate from the matching board panel + product `@image2`):
188
189
  - Slot 1 (hook): "Before this serum my routine was five products…" holding it to camera.
189
190
  - Slot 2 (demo): hands applying, product in active use.
@@ -194,9 +195,8 @@ Brief: *"15s UGC review of a skincare serum, tech-savvy woman creator."*
194
195
 
195
196
  1. **Always pick a mode explicitly.** Don't auto-pick from one ambiguous word. If the user said "make me an ad" with no other signal, offer labeled options: `[UGC / TV Spot / Product Showcase / Surprise me]`.
196
197
  2. **Always confirm aspect ratio + duration + sound** before firing — these materially change output and cost. One question, labeled options.
197
- 3. **Default UGC settings are hard rules** captions OFF, music OFF, watermarks OFF even when the user doesn't mention them. Only flip when they ask.
198
- 4. **No auto-retry on failure.** If the generation fails (content policy, model OOM), surface the reason and let the user adjust prompt or product.
199
- 5. **Show results without dumping URLs** — see SKILL.md "Generated URLs in chat".
198
+ 3. **Retries:** one retry only when `failure.retryable === true` or the generation completed with empty URLs (SKILL.md "⚠️ Generation lifecycle"); otherwise surface the reason and let the user adjust prompt or product.
199
+ 4. **Show results without dumping URLs** see SKILL.md "Generated URLs in chat".
200
200
 
201
201
  ## Prompt Template Seed for UGC
202
202
 
@@ -70,7 +70,7 @@ fails to open (exit 26).
70
70
 
71
71
  | User says | Call |
72
72
  |---|---|
73
- | "Upload this file" / "host this" / "give me a public URL for this" | `upload_media` — but see "Local files" below if it's a path on the user's disk |
73
+ | "Upload this file" / "host this" / "give me a public URL for this" | `upload_media` — but see "Local files" above if it's a path on the user's disk |
74
74
  | "Show my media" / "list my images/videos" / "what do I have?" | `list_media` (pass `type` / `category` / `project_id` / `folder_id` / `search`) |
75
75
  | "Show my favorites" / "list starred items" | `list_media` with `category=favorites` |
76
76
  | "List everything in project X" | `list_media` with `project_id=X` |
@@ -110,3 +110,13 @@ fails to open (exit 26).
110
110
  8. **Bulk caps:** 1000 ids for `bulk_delete_media` / `bulk_restore_media` / `bulk_permanently_delete_media` / `bulk_move_media`; 500 ids for `add_media_to_folder` / `remove_media_from_folder`. Split larger jobs into successive calls.
111
111
  9. **Folder share resolution:** `share_media_folder` takes emails; users not found come back in `not_found`. Report those to the user — don't assume the share succeeded silently. Members can list/add/remove items but cannot delete the folder or reshare it.
112
112
  10. **`get_media` accepts a generation_id as a fallback** for the `media_id` arg, so you can chase down items the user references by their original generation rather than by library id.
113
+
114
+ ## SYNCI licensed music — operational detail
115
+
116
+ The `*_music_library` tools (`search_music_library` / `browse_music_library` / `get_music_library_facets` / `get_music_track_audio` / `get_music_track_lyrics` / `get_music_track_related` / `analyze_script_for_music` / `acquire_clean_music_track` / `import_music_track_to_library`) front **SYNCI**, a commercially licensed catalog — not free stock.
117
+
118
+ - Discovery and previews are free but **watermarked** — there is no unwatermarked URL until you pay.
119
+ - `acquire_clean_music_track` **CHARGES CREDITS** for the clean master. Confirm with the user first, and pass a stable `requestId` so a retry doesn't buy the track twice.
120
+ - `import_music_track_to_library` charges the same way AND also copies the clean track into the media library.
121
+ - `analyze_script_for_music` turns a script into search terms for `search_music_library`.
122
+ - Use this family when the user needs music cleared for commercial use. When free stock will do, use `search_stock_media` with `mediaType: "music"` instead.
@@ -61,7 +61,7 @@ The mode determines which Kolbo MCP tool to call and what defaults to use.
61
61
 
62
62
  **For multi-output modes** (`social_carousel`, `ad_creative_pack`), always use `generate_creative_director` — never fire ≥2 `generate_image` calls in a loop. See `models/creative-director.md`.
63
63
 
64
- **Always validate** `aspect_ratio` and `resolution` against the chosen model's `supported_aspect_ratios` / `supported_resolutions` via `list_models` — see SKILL.md "Resolution / Aspect / Duration — validate against caps".
64
+ **Always validate** `aspect_ratio` and `resolution` against the chosen model's `supported_aspect_ratios` / `supported_resolutions` via `list_models` — see `references/workflows/cost-and-validation.md`.
65
65
 
66
66
  ## Pre-Generation Interview (CRITICAL)
67
67
 
@@ -38,20 +38,8 @@ The log records **what the user approved** — not everything you produced. Gett
38
38
 
39
39
  **Never write an artifact into the log as approved without the user's approval.**
40
40
 
41
- If they did not volunteer a yes, end the turn with a **GATE** the next message can
42
- parse (same contract as `production-planning.md`):
43
-
44
- ```
45
- GATE — <bucket name>
46
- Presented: <what is in play>
47
- Lock + next: "lock <bucket>" / "yes" / "next" / "now <next bucket>"
48
- Stay: "redo @name" / "another take of …"
49
- ```
50
-
51
- Confirmation the agent may treat as a lock: `yes`, `ok`, `lock`, `approved`,
52
- `that's the one`, `use take 2`, `next`, `go`, `continue`, or they name the next
53
- planned bucket while treating this set as done. Silence / "maybe" / a new
54
- question is **not** a lock — repeat the GATE once, do not invent a yes.
41
+ If they didn't volunteer a yes, end the turn with the GATE block from
42
+ `production-planning.md` §3.
55
43
 
56
44
  **If the user genuinely doesn't care** — "whatever you think", "you pick", "don't care", or they hand you the whole job — then **you decide**. Choose, say in one line which you picked and why, and log it as usual with `(agent-selected)`. Do not stall a production waiting for an approval the user has already delegated to you.
57
45
 
@@ -143,11 +143,12 @@ the next planned bucket in **its** session.
143
143
  Only now compile shots. Defaults:
144
144
 
145
145
  - **`generate_elements` with Seedance 2.5** (`seedance-2-5`) for the film itself —
146
- up to 30s and 30 shots in ONE generation, up to 20 Visual DNAs, dialogue and SFX
147
- baked in. `generate_video` also accepts `visual_dna_ids` now; Elements remains
148
- the primary reference-driven route.
149
- - **Seedance 2.0** (`seedance-2`, cheaper, 4–15s, 9 DNAs) when the piece is short
150
- and the cast is small. `seedance-2-fast` / `seedance-2-mini` for cheap blocking.
146
+ up to 30s and 30 shots in ONE generation, dialogue and SFX baked in. DNA cap:
147
+ read `max_visual_dna` from `list_models`. `generate_video` also accepts
148
+ `visual_dna_ids` now; Elements remains the primary reference-driven route.
149
+ - **Seedance 2.0** (`seedance-2`, cheaper, 4–15s, smaller DNA cap per
150
+ `list_models`) when the piece is short and the cast is small.
151
+ `seedance-2-fast` / `seedance-2-mini` for cheap blocking.
151
152
  - Every DNA in `visual_dna_ids` must also appear as `@ExactName` in the prompt.
152
153
  - Dialogue in quotes inside its shot beat — English only, never TTS or lipsync.
153
154
  See `models/seedance25.md`.
@@ -0,0 +1,98 @@
1
+ # Kolbo Review — client review & approval collections
2
+
3
+ Load this file when the user wants **feedback on finished media**: send a cut to a client,
4
+ collect timestamped comments, run an approve / request-changes loop, ship a v2 against the
5
+ same feedback thread, or share work with someone who has no Kolbo account. This is a
6
+ Frame.io-style layer over the media library — assets, versions, comments, statuses, and
7
+ guest share links — all project-scoped like everything else in Kolbo.
8
+
9
+ Not for: publishing a page (`publish_html_artifact`), sharing a whole media folder
10
+ (`share_media_folder`), or internal doc collaboration (`share_doc`).
11
+
12
+ ## Tool inventory
13
+
14
+ | Tool | What it does |
15
+ |---|---|
16
+ | `create_review_asset` | New review asset with v1 media attached (`name`, `media_id`, optional `collection_id`, `version_note`). |
17
+ | `list_review_assets` / `get_review_asset` | Browse a project's review assets (filter by `collection_id` / `status`); fetch one with all versions + URLs. |
18
+ | `update_review_asset` | Rename, move to a collection (`collection_id: null` = uncollected), or switch `current_version_index`. |
19
+ | `add_review_version` | Append a new version to an existing asset from a `media_id`. |
20
+ | `set_review_status` | Workflow status: `in_progress` / `needs_review` / `approved` / `changes_requested`. |
21
+ | `delete_review_asset` | Soft-delete an asset AND its underlying review media. |
22
+ | `create_review_collection` / `list_review_collections` / `update_review_collection` / `delete_review_collection` | Folder layer. Deleting a collection is soft — its assets become uncollected, not deleted. |
23
+ | `create_review_comment` / `list_review_comments` / `reply_review_comment` / `edit_review_comment` / `delete_review_comment` | Text comments, optional video timecodes (`time_start` / `time_end`, seconds). One level of reply threading. |
24
+ | `resolve_review_comment` / `unresolve_review_comment` | Close / reopen a comment thread. |
25
+ | `create_review_share_link` / `list_review_share_links` / `revoke_review_share_link` | Guest links (no Kolbo account) for one asset or a whole collection. |
26
+ | `get_review_storage_usage` | `usedBytes` vs the 5GB review cap for the API-key owner. |
27
+
28
+ ## The core flow
29
+
30
+ ```
31
+ upload_media (or reuse a generation's media_id from list_media)
32
+ → create_review_collection (only if grouping multiple assets)
33
+ → create_review_asset ← media becomes v1
34
+ → set_review_status "needs_review"
35
+ → create_review_share_link ← hand the client the share_url
36
+ → list_review_comments ← read what came back
37
+ → fix → add_review_version ← v2 on the SAME asset
38
+ → resolve_review_comment on each addressed note
39
+ → set_review_status "approved" (usually the client does this via the link)
40
+ ```
41
+
42
+ Media must already exist in the library — every attach point takes a `media_id` from
43
+ `upload_media` / `create_upload_ticket` / `media_upload_widget` / `list_media`, never a raw
44
+ URL or local path.
45
+
46
+ ## Version semantics
47
+
48
+ - Versions **append**; labels are auto-set `v1`, `v2`, … — you can't choose or reorder them.
49
+ - `add_review_version` automatically makes the new version current. Use
50
+ `update_review_asset({ current_version_index })` only to point BACK at an older cut.
51
+ - **Comments attach to a version's media**, not the asset. `list_review_comments` defaults
52
+ to the current version — after adding v2, pass `version_media_id` to re-read v1 feedback.
53
+ Comments do not carry forward; the v2 thread starts clean.
54
+ - Never delete+recreate an asset to "update" it — that orphans the comment history and
55
+ every share link already sent to the client. New cut = `add_review_version`. Rename /
56
+ re-file = `update_review_asset`. Delete is for abandoning the review entirely.
57
+
58
+ ## Share links — permissions and defaults
59
+
60
+ `create_review_share_link` targets an `asset` or a `collection` (collection links cover
61
+ every asset inside, including ones added later). Guest defaults if you pass nothing:
62
+
63
+ | Permission | Default |
64
+ |---|---|
65
+ | `canComment`, `canViewOtherComments`, `canSwitchVersions` | **true** |
66
+ | `canDownload`, `canResolveOwn`, `canSetStatus` | **false** |
67
+ | `require_email` | **true** — guests identify by email before viewing |
68
+ | `role_label` | `"Client"` (max 40 chars) |
69
+
70
+ - Want the client to approve directly? Pass `permissions: { canSetStatus: true }` —
71
+ otherwise they can only comment and you relay the verdict via `set_review_status`.
72
+ - Lockdown options: `password` (a NEW guest password — never an account credential),
73
+ `allowed_emails`, `expires_at` (ISO8601).
74
+ - Links are revoked by id (`revoke_review_share_link`), not edited — to change permissions,
75
+ create a new link and revoke the old one. This is the one place recreate IS the mechanism.
76
+ - When someone other than the owner sets a status, the owner gets a notification — don't
77
+ also announce it manually.
78
+
79
+ ## Storage — the 5GB cap
80
+
81
+ Review media is **copied into dedicated review storage** and counts against a flat 5GB cap
82
+ per account — separate from library storage. The cap is enforced on `create_review_asset`
83
+ AND `add_review_version`; hitting it returns `REVIEW_STORAGE_LIMIT` (413). That error is a
84
+ real limit, not a transient failure — don't retry. Check `get_review_storage_usage`
85
+ (`usedBytes` / `capBytes`) before bulk-adding large videos, and free space by deleting
86
+ finished review assets (the library originals are untouched).
87
+
88
+ ## Practical notes
89
+
90
+ - All of this is instant CRUD — no credits, no polling, no `get_generation_status`.
91
+ - Project contract applies: pass the same `project_id` you resolved via `list_projects` on
92
+ `create_review_asset` / `create_review_collection` / the list calls.
93
+ - `status` filter on `list_review_assets` takes exactly the four enum values — "pending" /
94
+ "done" are not statuses.
95
+ - Comment ids are `note_id` in the reply/edit/delete/resolve tools; version notes cap at
96
+ 1000 chars.
97
+ - Guests can only resolve their own comments, and only if you granted `canResolveOwn` — the
98
+ resolve loop on client feedback is normally yours to run after fixing.
@@ -26,9 +26,9 @@ If Kolbo tools timeout or aren't listed, the MCP server may not be wired. Tell t
26
26
  ```
27
27
  This re-wires the MCP configuration automatically. Then restart the session.
28
28
 
29
- ## Black / empty chat card while "Generating"
29
+ ## Generation status, waiting, black cards
30
30
 
31
- **Not a bug and not a failure.** The chat generation card's preview stays dark until the job has media. Library shows a K/logo placeholder tile for the same in-flight job. Do **not** re-fire `generate_*`, do **not** `list_media` to "find" it. Wait, or call `get_generation_status` once with `wait=true`. When complete, the result appears in Library (This session) that is the user-facing source of truth.
31
+ Status semantics, `wait=true` batching, black-card-is-normal and the credit guard live in SKILL.md "⚠️ Generation lifecycle — source of truth, waiting, failures".
32
32
 
33
33
  ## "Rate limited" (429 errors)
34
34
 
@@ -47,12 +47,6 @@ Call `list_color_palettes` and look for `is_active: true`. Then either
47
47
  grade by writing colours into the prompt — the palette is applied after, and the
48
48
  prompt loses.
49
49
 
50
- ## Checking generation status without spinning
51
-
52
- `get_generation_status` supports `wait=true` (blocks server-side until the generation reaches a final state, up to ~3 min) and `generation_ids` (many ids in one call → returns `all_done`, `still_processing`, and per-generation results). **Never call it repeatedly in a loop** — one `wait=true` call replaces the loop. If some generations are still running after the wait window, call it ONCE more with `wait=true` and only the `still_processing` ids.
53
-
54
- **Credit guard:** after a generate tool returns `submitted` / `_timed_out`, do not keep thinking or editing files while the card spins — that burns coding credits. End the turn, or make **one** `wait=true` status call if you need the URLs next.
55
-
56
50
  ## Failure envelope from `get_generation_status`
57
51
 
58
52
  When a generation fails, `get_generation_status` returns a structured `failure` field alongside `error`:
@@ -8,7 +8,7 @@ Visual DNA profiles capture the visual "identity" of a character, style, product
8
8
 
9
9
  ## Workflow
10
10
 
11
- 1. **Sheet first, then DNA.** For any production asset (character / location / prop), resolve the sheet **preset** (`list_presets` with `search`) and `generate_image` with that `preset_id` — custom instructions live on the preset. Then `create_visual_dna` with the sheet as `character_sheet_url` (max 4 extra images — if the user gives more, pick the 4 most representative **that share the same identity and vibe**; never pass 5+). Optionally video and audio. See **Purity** above before you generate those stills.
11
+ 1. **Sheet first, then DNA.** For any production asset (character / location / prop), resolve the sheet **preset** (`list_presets` with `search`) and `generate_image` with that `preset_id` — custom instructions live on the preset. Then `create_visual_dna` with the sheet as `character_sheet_url` (max 4 extra images — if the user gives more, pick the 4 most representative **that share the same identity and vibe**; never pass 5+). Optionally video and audio. See **Purity** below before you generate those stills.
12
12
  2. **Types**: `character` (default), `style`, `product`, `scene`, `environment`.
13
13
  3. **Use** the profile by passing its `id` in `visual_dna_ids` in: `generate_image`, `generate_creative_director`, `generate_elements`, `generate_video_from_image`, `generate_video_from_video`, `generate_first_last_frame`.
14
14
  4. **List/inspect** profiles with `list_visual_dnas` / `get_visual_dna`.
@@ -22,7 +22,7 @@ Visual DNA profiles capture the visual "identity" of a character, style, product
22
22
  Kolbo no longer sends only the first still or the character sheet. For every attached DNA:
23
23
 
24
24
  1. **User-uploaded refs take image slots first.**
25
- 2. **Remaining slots:** one main still per DNA, then leftover stills from each DNA **round-robin** until the model's image-slot cap (`elementsMaxImages` / equivalent) is full.
25
+ 2. **Remaining slots:** one main still per DNA, then leftover stills from each DNA **round-robin** until the model's image-slot cap (`elements_max_images` / equivalent) is full.
26
26
  3. **If every still fits the cap, every still is sent** as its own reference. A 4-image character DNA on a 9-slot model is four slots, not one.
27
27
  4. **If a DNA only gets one leftover slot**, has **no distinct character sheet**, and still has unused stills, those leftovers are composited into a **white grid / collage** (up to 9 cells) so the model still sees them. A real character sheet is never overwritten by a collage.
28
28
  5. **Native Kling Elements** stays one element per DNA (sheet / frontal). Other providers use the slot pack above.
@@ -104,16 +104,7 @@ Fields to read for the image source (use the first one present on the item): `th
104
104
 
105
105
  ## ⚠️ @name Syntax — ALWAYS use it when passing visual_dna_ids (MANDATORY)
106
106
 
107
- Whenever a generation call passes `visual_dna_ids` (even just one), the prompt MUST refer to each Visual DNA by `@<exact-name>` — the literal `name` field as it was set in `create_visual_dna` and as it appears in `list_visual_dnas`. This is how the engine binds the DNA to a role in the scene. Without `@name`, the engine guesses, drops the DNA, or blends multiple DNAs together.
108
-
109
- **Use the actual stored name, programmatically.** When you call `list_visual_dnas` (or `create_visual_dna`), read the `name` field off the response and use that exact string after the `@`. Do NOT:
110
-
111
- - Translate the name into another language ("אסתר" / "esther" / "אסתי" — pick whichever string is in `name` and use ONLY that one).
112
- - Invent a friendlier alias ("the model", "המודל", "her", "Zohar's", "the left man", "the man on the LEFT").
113
- - Write a "Visual DNA anchors:" prose block that describes position/wardrobe but never writes `@ExactName`.
114
- - Write the character's name in plain text without the `@` prefix.
115
- - Drop the `@name` when only one DNA is passed — the engine still needs the binding so it knows the DNA is the *subject* and not a passive style.
116
- - **Drop or "clean" tags while rewriting a prompt** (help-widget parity). Compiling SCENE CONTEXT / Locked Intro / a "better" English prompt is not permission to delete `@gal_suit` or rewrite `@yonatan` as `Yonatan`. Copy every existing `@` / `#` token into the new prompt, then add craft around them.
107
+ SKILL.md's `@Name` hard rule applies; here is why it binds that way:
117
108
 
118
109
  **Wrong** (DNA `name` is `esther_model`, user wrote prompt in Hebrew):
119
110
  ```
@@ -228,31 +219,11 @@ You can combine all three reference types in a single call — they're additive,
228
219
 
229
220
  ## Visual DNA Limits
230
221
 
231
- Read `max_visual_dna` from `list_models` for the exact cap, AND `supports_visual_dna` for the on/off boolean. A model can support DNA without an explicit cap, or have a non-null cap but silently ignore DNA on certain paths (e.g. `generate_video`). Typical ranges: image models (non-Kling) up to **8**, Kling image models **3**, Elements video models **3–5**, everything else up to **3**.
222
+ Read `max_visual_dna` (and `elements_max_images` for image-slot packing) from `list_models` for the chosen model, AND `supports_visual_dna` for the on/off boolean. A model can support DNA without an explicit cap, or have a non-null cap but silently ignore DNA on certain paths (e.g. `generate_video`). Typical ranges: image models (non-Kling) up to **8**, Kling image models **3**, Elements video models **3–5**, everything else up to **3**.
232
223
 
233
224
  ## ⚠️ Visual DNA Creation — Always Generate Reference Images First (MANDATORY)
234
225
 
235
- **Before calling `create_visual_dna` for a character**, always generate 2 reference images first and include them alongside any user-provided images. These give the Visual DNA engine multi-angle coverage and dramatically improve consistency.
236
-
237
- **Step 1 — Generate both images in parallel (one `generate_image` call each, fire simultaneously):**
238
-
239
- 1. **4-angle character sheet** — prompt: `"[character description], character reference sheet showing front view, back view, left side view, right side view, four panels arranged in a 2x2 grid, neutral solid background, full body, photorealistic"`, aspect ratio `16:9` (or `3:2` — always landscape, see the aspect-ratio rule below)
240
- 2. **Close-up portrait** — prompt: `"[character description], close-up portrait, face and shoulders, neutral solid background, soft studio lighting, photorealistic"`, aspect ratio `1:1`
241
-
242
- **Step 2 — Call `create_visual_dna`** with:
243
- - `images`: the 4-angle sheet URL first, then the close-up URL — **plus** the user's reference photo(s) only if they provided one (i.e. a real person or existing character they want to match). If they gave no reference image, the 2 generated images alone are sufficient.
244
- - `type`: `"character"`
245
- - `name`: single-token lowercase descriptive name (see naming rule above)
246
-
247
- **Why:** A single reference photo only shows one angle. The close-up gives the engine facial detail; the 4-angle sheet gives it body geometry and pose range. Together they produce far more consistent generations. Both stills (and any user photos you add) must be the **same person, same vibe** — they will all be packed into the next generation.
248
-
249
- **Skip this only if** the user explicitly says "just use my image as-is" or provides 3+ reference images already covering multiple angles.
250
-
251
- ### Environments, products, style — same precision
252
-
253
- - **Environment / location:** generate empty (or crowd-only) plates. Prompt out heroes and readable faces. A location DNA that contains `@maya` in the frame will put Maya in every later shot of that place.
254
- - **Product:** isolated angles, consistent lighting, readable label. No extra hero unless the product is worn and the body is generic.
255
- - **Style:** one look, applied cleanly. Do not mix neon-cyber and dusty-western stills on the same style DNA.
226
+ **Before calling `create_visual_dna` for a character**, generate the reference stills first — a multi-angle sheet plus a close-up gives the engine far better coverage than a single photo. Route the stills through the **preset contract** (`list_presets` search → `preset_id` on `generate_image`), never a raw hand-written sheet prompt — see "Character sheet — default for production assets" below for the full flow, preset search terms, and aspect-ratio rules. Include the user's reference photo(s) alongside only if they provided one. **Skip this only if** the user explicitly says "just use my image as-is" or provides 3+ reference images already covering multiple angles.
256
227
 
257
228
  ## When to Use
258
229
 
@@ -22,6 +22,7 @@
22
22
  * .onThemeChange(fn) — fn(hostContext) on host-context-changed
23
23
  * .callTool(name, args) — Promise<CallToolResult>
24
24
  * .sendMessage(text) — append a user chat message (returns Promise)
25
+ * .insertText(text) — paste into the host composer, do not send
25
26
  * .openLink(url) — open external URL
26
27
  * .copyText(text) — copy via the host clipboard (ui/copy-text)
27
28
  * .notifySize() — report content size to host
@@ -183,6 +184,9 @@ const BRIDGE_JS = `
183
184
  sendMessage: function (text) {
184
185
  return request('ui/message', { role: 'user', content: [{ type: 'text', text: text }] });
185
186
  },
187
+ insertText: function (text) {
188
+ return request('ui/insert-text', { text: text });
189
+ },
186
190
  openLink: function (url) { return request('ui/open-link', { url: url }); },
187
191
  copyText: function (text) { return request('ui/copy-text', { text: text }); },
188
192
  // Hand a piece of this widget's media to the host's composer. Dragging it
package/src/apps/html.js CHANGED
@@ -136,7 +136,8 @@ function modelChipHTML(name, iconUrl) {
136
136
  return '<span class="k-chip brand">' + inner + esc(name) + '</span>';
137
137
  }
138
138
  function monogram(name) {
139
- return '<span class="k-mono-icon">' + esc(String(name).trim().charAt(0).toUpperCase()) + '</span>';
139
+ var ch = String(name || '').replace(/^[@#]+/, '').trim().charAt(0) || '?';
140
+ return '<span class="k-mono-icon">' + esc(ch.toUpperCase()) + '</span>';
140
141
  }
141
142
  // In-widget preview overlay — every card (generation chips, media grid, list
142
143
  // rows) uses this so a DNA / reference / library thumb opens a popup in Kolbo
package/src/apps/index.js CHANGED
@@ -630,6 +630,7 @@ const TOOL_WIDGETS = {
630
630
  list_docs: UI.list,
631
631
  list_media_folders: UI.list,
632
632
  list_visual_dna_folders: UI.list,
633
+ list_project_assets: UI.list,
633
634
  };
634
635
 
635
636
  function attachToolWidgetMeta(server) {
package/src/apps/theme.js CHANGED
@@ -340,7 +340,8 @@ html.k-fullscreen .k-actions { flex: none; padding-top: 8px; }
340
340
  background: var(--brand-soft); }
341
341
  .k-audio-meta { flex: 1; min-width: 0; }
342
342
  .k-audio-title { font-size: 12.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
343
- .k-audio-sub { font-size: 11px; color: var(--text-faint); }
343
+ .k-audio-sub { font-size: 11px; color: var(--text-faint);
344
+ display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
344
345
  .k-generated-audio {
345
346
  display: grid; grid-template-columns: 40px minmax(0, 1fr) auto;
346
347
  align-items: center; gap: 7px 10px;
@@ -67,7 +67,7 @@ function boot(sc) {
67
67
  Array.prototype.forEach.call(document.querySelectorAll('[data-g]'), function (row) {
68
68
  row.onclick = function () {
69
69
  var m = state.groups[+row.getAttribute('data-g')].models[+row.getAttribute('data-m')];
70
- window.kolbo.sendMessage(m.use_hint || ('Generate something with the "' + m.name + '" model — ask me what I want to make.'));
70
+ window.kolbo.insertText(m.identifier || m.name || '');
71
71
  };
72
72
  });
73
73
  window.kolbo.notifySize();
@@ -75,6 +75,34 @@ var TOOL_TITLES = {
75
75
  generate_creative_director: 'Creative Director', edit_image: 'Image Edit', edit_video: 'Video Edit',
76
76
  get_generation_status: 'Generations'
77
77
  };
78
+ var OPEN_ROUTES = {
79
+ generate_image: { path: '/image-tools', tool: 'text-to-image' },
80
+ generate_image_edit: { path: '/image-tools', tool: 'image-editing' },
81
+ edit_image: { path: '/image-tools', tool: 'image-editing' },
82
+ generate_video: { path: '/video-tools', tool: 'text-to-video' },
83
+ generate_video_from_image: { path: '/video-tools', tool: 'image-to-video' },
84
+ generate_elements: { path: '/video-tools', tool: 'image-to-video', mode: 'elements' },
85
+ generate_first_last_frame: { path: '/video-tools', tool: 'image-to-video', mode: 'first-last' },
86
+ generate_video_from_video: { path: '/video-tools', tool: 'video-to-video' },
87
+ generate_lipsync: { path: '/video-tools', tool: 'lipsync' },
88
+ generate_music: { path: '/audio-tools', tool: 'music-generator' },
89
+ generate_speech: { path: '/audio-tools', tool: 'text-to-speech' },
90
+ generate_sound: { path: '/audio-tools', tool: 'text-to-sound' },
91
+ transcribe_audio: { path: '/audio-tools', tool: 'speech-to-text' },
92
+ generate_creative_director: { path: '/creative-director' }
93
+ };
94
+ function kolboUrl(sc) {
95
+ if (sc && typeof sc.open_url === 'string' && sc.open_url) return sc.open_url;
96
+ var sid = sc && (sc.session_id || sc.sessionId);
97
+ var route = OPEN_ROUTES[(sc && sc.tool) || originTool];
98
+ if (!route || !sid) return 'https://app.kolbo.ai';
99
+ var url = 'https://app.kolbo.ai' + route.path + '?session=' + encodeURIComponent(sid);
100
+ if (route.tool) url += '&tool=' + route.tool;
101
+ if (route.mode) url += '&mode=' + route.mode;
102
+ var pid = sc && (sc.project_id || sc.projectId);
103
+ if (pid) url += '&project=' + encodeURIComponent(pid);
104
+ return url;
105
+ }
78
106
 
79
107
  // Long text is clamped by CSS (.k-prompt 2 lines / .k-caption 1 line). Expand
80
108
  // lives on a separate button so the text itself stays selectable.
@@ -488,20 +516,20 @@ function stopNow(sc, spec) {
488
516
  var refund = 0;
489
517
  sts.forEach(function (s) { if (s.credits_refunded) refund += s.credits_refunded; });
490
518
  return {
491
- cancelled: sts.some(function (s) { return s.cancelled !== false; }),
519
+ cancelled: sts.some(function (s) { return s.cancelled === true; }),
492
520
  credits_refunded: refund || undefined
493
521
  };
494
522
  })
495
523
  : window.kolbo.callTool(spec.tool, spec.args).then(function (r) { return structured(r) || {}; });
496
- call.then(function (st) {
497
- if (st.cancelled === false) {
498
- // Already terminal let the normal poll path report the real outcome
499
- // instead of claiming a cancel that did not happen.
500
- cancelRequested = false;
501
- renderStopButton(sc);
502
- return schedulePoll(sc);
503
- }
504
- renderCancelled(st.credits_refunded);
524
+ call.then(function (st) {
525
+ if (!st || st.cancelled !== true) {
526
+ // Empty host ack used to land here as {} and the card painted
527
+ // cancelled while the job kept running. Only an explicit cancel counts.
528
+ cancelRequested = false;
529
+ renderStopButton(sc);
530
+ return schedulePoll(sc);
531
+ }
532
+ renderCancelled(st.credits_refunded);
505
533
  }).catch(function () {
506
534
  cancelRequested = false;
507
535
  renderStopButton(sc);
@@ -608,7 +636,12 @@ function poll(sc) {
608
636
  var done = Object.assign({}, sc, r, {
609
637
  phase: 'completed',
610
638
  urls: r.urls || st.urls || [],
611
- credits_used: st.credits_used != null ? st.credits_used : sc.credits_used
639
+ credits_used: st.credits_used != null ? st.credits_used : sc.credits_used,
640
+ // Status structuredContent used to set open_url:undefined and wipe the
641
+ // session deep-link the generating card already had.
642
+ open_url: (sc && sc.open_url) || r.open_url,
643
+ session_id: (sc && (sc.session_id || sc.sessionId)) || r.session_id || st.session_id,
644
+ project_id: (sc && (sc.project_id || sc.projectId)) || r.project_id || st.project_id
612
645
  });
613
646
  state = done;
614
647
  el('credits').textContent = done.credits_used != null ? fmtCredits(done.credits_used) : '';
@@ -652,6 +685,11 @@ function handleBatchStatus(sc, st) {
652
685
  if (!st.all_done) return schedulePoll(sc);
653
686
 
654
687
  var scenes = [], failedCount = 0, credits = 0, haveCredits = false, allUrls = [];
688
+ // get_generation_status resolves model_name/model_icon into EACH result
689
+ // (addDisplayNames in generate.js) — sc is only the submit-time guess
690
+ // (usually "Smart Select"). Every id in one batch ran the same model, so the
691
+ // first resolved one is enough to replace the guess on the finished card.
692
+ var resolved = {};
655
693
  gens.forEach(function (g, i) {
656
694
  var r = g.result || g;
657
695
  var urls = (r && r.urls) || [];
@@ -659,6 +697,11 @@ function handleBatchStatus(sc, st) {
659
697
  allUrls = allUrls.concat(urls);
660
698
  var c = g.credits_used != null ? g.credits_used : (r.credits_used != null ? r.credits_used : null);
661
699
  if (c != null) { credits += c; haveCredits = true; }
700
+ if (!resolved.model_name && r.model_name) {
701
+ resolved.model = r.model;
702
+ resolved.model_name = r.model_name;
703
+ resolved.model_icon = r.model_icon;
704
+ }
662
705
  scenes.push({
663
706
  scene_number: i + 1,
664
707
  title: (sc.prompts && sc.prompts[i]) || '',
@@ -668,7 +711,7 @@ function handleBatchStatus(sc, st) {
668
711
  });
669
712
  if (!scenes.length) return renderError('All ' + gens.length + ' generations failed');
670
713
 
671
- var done = Object.assign({}, sc, {
714
+ var done = Object.assign({}, sc, resolved, {
672
715
  phase: 'completed', kind: 'scenes', batch: true, scenes: scenes, urls: [],
673
716
  credits_used: haveCredits ? credits : sc.credits_used
674
717
  });
@@ -902,10 +945,14 @@ function renderBatchGrid(sc) {
902
945
  dlBtnHTML(it.url) + '</div>';
903
946
  }).join('') + '</div>';
904
947
  wireDlButtons(el('stage'));
948
+ // In-widget popup, not the host round-trip focusMedia() uses — a batch grid
949
+ // tile has no visible full-size image otherwise, so a host that never resolves
950
+ // requestDisplayMode() (or drops window.open after the async round trip eats
951
+ // the click's user-activation window) leaves the click doing nothing at all.
905
952
  Array.prototype.forEach.call(el('stage').querySelectorAll('[data-focus]'), function (cell) {
906
953
  var it = items[+cell.getAttribute('data-focus')];
907
954
  if (it.type !== 'image') return; // <video controls> owns its own clicks
908
- cell.onclick = function () { focusMedia(it.url); };
955
+ cell.onclick = function () { openPeek(it.url, 'image', it.label); };
909
956
  });
910
957
  renderActions(sc);
911
958
  window.kolbo.notifySize();
@@ -949,7 +996,7 @@ function renderStatusGrid(sc) {
949
996
  Array.prototype.forEach.call(el('stage').querySelectorAll('[data-focus]'), function (cell) {
950
997
  var it = items[+cell.getAttribute('data-focus')];
951
998
  if (it.kind !== 'image') return; // <video controls> owns its own clicks
952
- cell.onclick = function () { focusMedia(it.url); };
999
+ cell.onclick = function () { openPeek(it.url, 'image', it.title); };
953
1000
  });
954
1001
  renderActions(sc);
955
1002
  window.kolbo.notifySize();
@@ -1030,7 +1077,7 @@ function renderTrackingIssue(msg) {
1030
1077
  (state && state.generation_id ? '\\nGeneration ID: ' + state.generation_id : ''));
1031
1078
  };
1032
1079
  el('tracking-open').onclick = function () {
1033
- window.kolbo.openLink((state && state.open_url) || 'https://app.kolbo.ai');
1080
+ window.kolbo.openLink(kolboUrl(state));
1034
1081
  };
1035
1082
  window.kolbo.notifySize();
1036
1083
  }
@@ -1100,7 +1147,7 @@ function renderActions(sc) {
1100
1147
  el('actions').innerHTML = a.join('');
1101
1148
 
1102
1149
  bind('btn-download', function () { window.kolbo.openLink(downloadUrl(currentUrl())); });
1103
- bind('btn-open', function () { window.kolbo.openLink(state.open_url || 'https://app.kolbo.ai'); });
1150
+ bind('btn-open', function () { window.kolbo.openLink(kolboUrl(state)); });
1104
1151
  bind('btn-recreate', function () {
1105
1152
  window.kolbo.sendMessage('Recreate this with the same settings' +
1106
1153
  (state.model ? '\\nModel: ' + state.model : '') +
@@ -1195,7 +1242,10 @@ function completedFromPlain(sc) {
1195
1242
  aspect_ratio: originArgs.aspect_ratio,
1196
1243
  quality: originArgs.quality
1197
1244
  },
1198
- urls: sc.urls || []
1245
+ urls: sc.urls || [],
1246
+ session_id: sc.session_id || originArgs.session_id,
1247
+ project_id: sc.project_id || originArgs.project_id,
1248
+ open_url: sc.open_url
1199
1249
  });
1200
1250
  }
1201
1251