@slatesvideo/shared 0.5.9 → 0.5.10
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/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/operations/index.d.ts +19 -10
- package/dist/operations/index.js +236 -76
- package/dist/prompts/index.d.ts +1 -0
- package/dist/prompts/index.js +4 -0
- package/dist/prompts/model-capabilities.d.ts +117 -0
- package/dist/prompts/model-capabilities.js +536 -0
- package/dist/prompts/model-facts.d.ts +11 -3
- package/dist/prompts/model-facts.js +63 -47
- package/dist/skills/content.js +3 -3
- package/exports/slates-prompt-builder/generated/slates-prompt-builder-manifest.json +1 -1
- package/package.json +5 -1
- package/skills/slates-model-selection.md +6 -5
- package/skills/slates-one-prompt-film.md +2 -2
- package/skills/slates-prompting-veo-3.md +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -5,5 +5,6 @@ export { SKILLS } from './skills/content.js';
|
|
|
5
5
|
export * as operations from './operations/index.js';
|
|
6
6
|
export { ALL_OPERATIONS, VIDEO_MODELS, AUDIO_MODELS, defaultContext, type Operation, type OperationContext, type OperationResult } from './operations/index.js';
|
|
7
7
|
export { MODEL_FACTS, getModelFact, multimodalRefSummary, multimodalRefModels, SEEDANCE_TASK_INTENT_WORDS, seedanceTaskIntentWords, type ModelFact, } from './prompts/model-facts.js';
|
|
8
|
+
export { MODEL_CAPABILITIES, ALL_ASPECT_RATIOS, AGENT_ROUTE_PROVIDER, getModelCapability, aspectRatiosFor, videoResolutionsFor, defaultVideoResolutionFor, durationsFor, durationValuesFor, aspectRatioUnion, videoResolutionUnion, durationBounds, checkAspectRatio, checkVideoResolution, checkDuration, describeAspectRatios, describeVideoResolutions, describeDurations, describeReferenceImageCaps, type AspectRatio, type VideoResolution, type ModelCapability, type DurationCapability, type VideoResolutionCapability, } from './prompts/model-capabilities.js';
|
|
8
9
|
export { PROMPTING_TIPS, getPromptingTips, type PromptingTipsEntry, type PromptingTipCard, type PromptingTipsKey } from './prompts/prompting-tips.js';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,12 @@ export { MODEL_FACTS, getModelFact, multimodalRefSummary, multimodalRefModels,
|
|
|
11
11
|
// Mirrored in slate/src/shared/pricing.ts — see the constant's own header for
|
|
12
12
|
// why the mirror exists and why it must never drive a prompt rewrite.
|
|
13
13
|
SEEDANCE_TASK_INTENT_WORDS, seedanceTaskIntentWords, } from './prompts/model-facts.js';
|
|
14
|
+
// Model CAPABILITY SSOT — what each model will ACCEPT (aspect ratios incl.
|
|
15
|
+
// per-provider overrides, video resolutions, duration windows, reference caps).
|
|
16
|
+
// `slate/src/shared/pricing.ts` spreads these into every MODEL_REGISTRY entry;
|
|
17
|
+
// the op surface validates against them and GENERATES its `.describe()` prose
|
|
18
|
+
// from them. Never hand-type a capability fact an LLM will read.
|
|
19
|
+
export { MODEL_CAPABILITIES, ALL_ASPECT_RATIOS, AGENT_ROUTE_PROVIDER, getModelCapability, aspectRatiosFor, videoResolutionsFor, defaultVideoResolutionFor, durationsFor, durationValuesFor, aspectRatioUnion, videoResolutionUnion, durationBounds, checkAspectRatio, checkVideoResolution, checkDuration, describeAspectRatios, describeVideoResolutions, describeDurations, describeReferenceImageCaps, } from './prompts/model-capabilities.js';
|
|
14
20
|
// Per-model prompting tips — the SSOT for the desktop "See prompting tips"
|
|
15
21
|
// modals. The desktop renders these; it never hand-writes tips content.
|
|
16
22
|
export { PROMPTING_TIPS, getPromptingTips } from './prompts/prompting-tips.js';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { SlatesCloudClient } from '../clients/cloud.js';
|
|
3
3
|
import { SlatesDesktopClient } from '../clients/desktop.js';
|
|
4
|
+
import { type AspectRatio, type VideoResolution } from '../prompts/model-capabilities.js';
|
|
4
5
|
export interface OperationContext {
|
|
5
6
|
cloud: () => SlatesCloudClient;
|
|
6
7
|
desktop: () => SlatesDesktopClient;
|
|
@@ -28,6 +29,12 @@ export declare const getCreditBalance: Operation<Record<string, never>>;
|
|
|
28
29
|
export declare const listAvailableModels: Operation<{
|
|
29
30
|
filter?: string;
|
|
30
31
|
}>;
|
|
32
|
+
export declare const VIDEO_MODELS: readonly ["kling-v3.0-std", "kling-v3.0-pro", "kling-v3.0-omni", "veo-3.1-fast", "veo-3.1-standard", "seedance-2", "seedance-2.5", "omni-flash"];
|
|
33
|
+
type VideoModel = (typeof VIDEO_MODELS)[number];
|
|
34
|
+
/** The exact `model` ids `slates_edit_video` accepts. Edit rows are deliberately
|
|
35
|
+
* NOT in VIDEO_MODELS — they take a source clip, not frames. */
|
|
36
|
+
export declare const EDIT_VIDEO_MODELS: readonly ["kling-v3.0-omni-edit", "kling-v3.0-omni-pro-edit", "omni-flash-edit", "seedance-2.5-edit"];
|
|
37
|
+
type EditVideoModel = (typeof EDIT_VIDEO_MODELS)[number];
|
|
31
38
|
export declare const estimateGenerationCost: Operation<{
|
|
32
39
|
model: string;
|
|
33
40
|
quantity?: number;
|
|
@@ -156,12 +163,14 @@ export declare const addFrame: Operation<{
|
|
|
156
163
|
position?: number;
|
|
157
164
|
}>;
|
|
158
165
|
export type ImageModelId = 'nano-banana-2' | 'nano-banana-2-lite' | 'nano-banana-pro' | 'gpt-image-2' | 'flux-2-max' | 'seedream-5-lite';
|
|
166
|
+
/** The exact `model` ids `slates_generate_image` accepts. */
|
|
167
|
+
export declare const IMAGE_MODELS: readonly ["nano-banana-2", "nano-banana-2-lite", "nano-banana-pro", "gpt-image-2", "flux-2-max", "seedream-5-lite"];
|
|
159
168
|
export declare const generateImage: Operation<{
|
|
160
169
|
prompt: string;
|
|
161
170
|
model?: ImageModelId;
|
|
162
171
|
projectId?: string;
|
|
163
172
|
resolution?: '1k' | '2k' | '3k' | '4k';
|
|
164
|
-
aspectRatio?:
|
|
173
|
+
aspectRatio?: AspectRatio;
|
|
165
174
|
quality?: 'medium' | 'high';
|
|
166
175
|
count?: number;
|
|
167
176
|
referenceImageUrls?: string[];
|
|
@@ -181,8 +190,6 @@ export declare const editImage: Operation<{
|
|
|
181
190
|
confirm?: boolean;
|
|
182
191
|
background?: boolean;
|
|
183
192
|
}>;
|
|
184
|
-
export declare const VIDEO_MODELS: readonly ["kling-v3.0-std", "kling-v3.0-pro", "kling-v3.0-omni", "veo-3.1-fast", "veo-3.1-standard", "seedance-2", "seedance-2.5", "omni-flash"];
|
|
185
|
-
type VideoModel = (typeof VIDEO_MODELS)[number];
|
|
186
193
|
export declare function videoCostKey(input: {
|
|
187
194
|
model: VideoModel;
|
|
188
195
|
duration: number;
|
|
@@ -197,9 +204,11 @@ export declare function videoCostKey(input: {
|
|
|
197
204
|
}): string;
|
|
198
205
|
export declare function klingEditCostKey(model: 'kling-v3.0-omni-edit' | 'kling-v3.0-omni-pro-edit', duration: number): string;
|
|
199
206
|
export declare function omniFlashEditCostKey(duration: number): string;
|
|
200
|
-
/** Seedance 2.5 source-clip bounds for the edit task type
|
|
201
|
-
|
|
202
|
-
|
|
207
|
+
/** Seedance 2.5 source-clip bounds for the edit task type — READ from the
|
|
208
|
+
* capability SSOT, not typed. Kept as named exports because published builds
|
|
209
|
+
* import them. */
|
|
210
|
+
export declare const SEEDANCE_25_EDIT_MIN_SECONDS: number;
|
|
211
|
+
export declare const SEEDANCE_25_EDIT_MAX_SECONDS: number;
|
|
203
212
|
export declare function seedanceEditCostKey(input: {
|
|
204
213
|
duration: number;
|
|
205
214
|
videoResolution?: '480p' | '720p';
|
|
@@ -241,9 +250,9 @@ export declare const generateVideo: Operation<{
|
|
|
241
250
|
prompt: string;
|
|
242
251
|
model: string;
|
|
243
252
|
projectId?: string;
|
|
244
|
-
aspectRatio?:
|
|
253
|
+
aspectRatio?: AspectRatio;
|
|
245
254
|
duration?: number;
|
|
246
|
-
videoResolution?:
|
|
255
|
+
videoResolution?: VideoResolution;
|
|
247
256
|
firstFrameAssetId?: string;
|
|
248
257
|
lastFrameAssetId?: string;
|
|
249
258
|
ingredientAssetIds?: string[];
|
|
@@ -313,11 +322,11 @@ export declare const editVideo: Operation<{
|
|
|
313
322
|
projectId: string;
|
|
314
323
|
sourceVideoAssetId: string;
|
|
315
324
|
prompt: string;
|
|
316
|
-
model?:
|
|
325
|
+
model?: EditVideoModel;
|
|
317
326
|
characterAssetIds?: string[];
|
|
318
327
|
styleAssetIds?: string[];
|
|
319
328
|
keepAudio?: boolean;
|
|
320
|
-
videoResolution?:
|
|
329
|
+
videoResolution?: VideoResolution;
|
|
321
330
|
seedanceFace?: boolean;
|
|
322
331
|
background?: boolean;
|
|
323
332
|
confirm?: boolean;
|
package/dist/operations/index.js
CHANGED
|
@@ -16,6 +16,16 @@ import { SKILLS } from '../skills/content.js';
|
|
|
16
16
|
// Reference-capacity prose is DERIVED, never hand-typed — root CLAUDE.md:
|
|
17
17
|
// "never hand-type a fact an LLM will read". These helpers read MODEL_FACTS.
|
|
18
18
|
import { multimodalRefSummary, multimodalRefModels, seedanceTaskIntentWords } from '../prompts/model-facts.js';
|
|
19
|
+
// 🚨 MODEL CAPABILITY SSOT. Aspect ratios, video resolutions and durations are
|
|
20
|
+
// VALIDATED against and DESCRIBED from `MODEL_CAPABILITIES` — this file must
|
|
21
|
+
// never re-state one of those constraints as a literal enum or a sentence
|
|
22
|
+
// again. It did until 2026-08-16, and every axis had drifted: an invented
|
|
23
|
+
// `9:21`, "Kling/Seedance support all" (Seedance takes 6 of 11 and has no
|
|
24
|
+
// `4:5`), "Veo locks to 16:9" (two on fal), "Kling: 5-15" (min is 3), a
|
|
25
|
+
// `videoResolution` line that never mentioned Kling, and 4s quoted for Veo at
|
|
26
|
+
// 1080p. A customer burned a round trip on `4:5` + Seedance: accepted here,
|
|
27
|
+
// queued, credits reserved, rejected by the provider asynchronously.
|
|
28
|
+
import { AGENT_ROUTE_PROVIDER, aspectRatioUnion, videoResolutionUnion, durationBounds, aspectRatiosFor, getModelCapability, defaultVideoResolutionFor, checkAspectRatio, checkVideoResolution, checkDuration, describeAspectRatios, describeVideoResolutions, describeDurations, describeReferenceImageCaps, } from '../prompts/model-capabilities.js';
|
|
19
29
|
export function defaultContext() {
|
|
20
30
|
return {
|
|
21
31
|
cloud: () => new SlatesCloudClient(),
|
|
@@ -23,6 +33,18 @@ export function defaultContext() {
|
|
|
23
33
|
};
|
|
24
34
|
}
|
|
25
35
|
// ── Helpers ─────────────────────────────────────────────────────
|
|
36
|
+
/**
|
|
37
|
+
* `z.enum` over a GENERATED list. Zod's signature wants a non-empty tuple
|
|
38
|
+
* literal, which is exactly what you can't write when the values come from the
|
|
39
|
+
* capability SSOT — and writing them out is how the enums drifted in the first
|
|
40
|
+
* place. Callers must pass a non-empty array; every call site derives from
|
|
41
|
+
* `MODEL_CAPABILITIES`, which never yields an empty union for a real model set.
|
|
42
|
+
*/
|
|
43
|
+
function zEnum(values) {
|
|
44
|
+
if (values.length === 0)
|
|
45
|
+
throw new Error('zEnum: empty value list');
|
|
46
|
+
return z.enum(values);
|
|
47
|
+
}
|
|
26
48
|
function ok(data, text) {
|
|
27
49
|
return {
|
|
28
50
|
// Compact JSON — pretty-printing (indent 2) cost ~10-15% extra tokens
|
|
@@ -133,14 +155,82 @@ export const listAvailableModels = {
|
|
|
133
155
|
};
|
|
134
156
|
},
|
|
135
157
|
};
|
|
158
|
+
// ── Video model vocabulary ──────────────────────────────────────
|
|
159
|
+
//
|
|
160
|
+
// Declared HERE, above every op, because both `slates_estimate_generation_cost`
|
|
161
|
+
// and `slates_generate_video` build their Zod schemas from it at module load —
|
|
162
|
+
// and a schema is evaluated in source order, so a list declared below the first
|
|
163
|
+
// op that reads it is a temporal-dead-zone crash, not a lint nit.
|
|
164
|
+
// Exported: the exact `model` ids slates_generate_video accepts — consumed
|
|
165
|
+
// by the desktop Studio Agent system prompt (SSOT; never restate these ids
|
|
166
|
+
// in prose that can drift).
|
|
167
|
+
export const VIDEO_MODELS = [
|
|
168
|
+
'kling-v3.0-std',
|
|
169
|
+
'kling-v3.0-pro',
|
|
170
|
+
'kling-v3.0-omni',
|
|
171
|
+
'veo-3.1-fast',
|
|
172
|
+
'veo-3.1-standard',
|
|
173
|
+
'seedance-2',
|
|
174
|
+
// Seedance 2.5 is a SECOND SEAT, not a replacement: 30s takes, 30 image
|
|
175
|
+
// references, audio-only references — and 480p/720p ONLY. 2.0 keeps the
|
|
176
|
+
// ladder to native 4K and stays the default. Its EDIT row is not here; edit
|
|
177
|
+
// models live on slates_edit_video, same as the Kling and Omni Flash ones.
|
|
178
|
+
'seedance-2.5',
|
|
179
|
+
'omni-flash',
|
|
180
|
+
];
|
|
181
|
+
// ── Capability-derived param vocabulary + guard ─────────────────
|
|
182
|
+
//
|
|
183
|
+
// 🚨 EVERYTHING BELOW IS GENERATED FROM `MODEL_CAPABILITIES`. Not one aspect
|
|
184
|
+
// ratio, resolution or duration is typed into this file any more, and none may
|
|
185
|
+
// be again. The enums are the UNION of what the video models accept (so the
|
|
186
|
+
// JSON schema still advertises the legal universe to the LLM) and
|
|
187
|
+
// `assertVideoCapabilities` does the PER-MODEL narrowing.
|
|
188
|
+
/** Union of the ratios every video model accepts on the agent's route. */
|
|
189
|
+
const VIDEO_ASPECT_RATIOS = aspectRatioUnion(VIDEO_MODELS, AGENT_ROUTE_PROVIDER);
|
|
190
|
+
/** Union of the resolutions every video model renders at. */
|
|
191
|
+
const VIDEO_RESOLUTIONS = videoResolutionUnion(VIDEO_MODELS);
|
|
192
|
+
/** Widest legal duration window across every video model. */
|
|
193
|
+
const VIDEO_DURATION_BOUNDS = durationBounds(VIDEO_MODELS);
|
|
194
|
+
/** Omni Flash's combined reference-image cap, read from the SSOT (7). */
|
|
195
|
+
const omniFlashRefCap = getModelCapability('omni-flash')?.maxIngredientImages ?? 0;
|
|
196
|
+
/** The exact `model` ids `slates_edit_video` accepts. Edit rows are deliberately
|
|
197
|
+
* NOT in VIDEO_MODELS — they take a source clip, not frames. */
|
|
198
|
+
export const EDIT_VIDEO_MODELS = [
|
|
199
|
+
'kling-v3.0-omni-edit',
|
|
200
|
+
'kling-v3.0-omni-pro-edit',
|
|
201
|
+
'omni-flash-edit',
|
|
202
|
+
'seedance-2.5-edit',
|
|
203
|
+
];
|
|
204
|
+
/**
|
|
205
|
+
* Source-clip length an edit engine accepts, read from the SSOT.
|
|
206
|
+
*
|
|
207
|
+
* On an edit row the output length FOLLOWS the source clip, so the model's
|
|
208
|
+
* declared duration window is exactly the legal clip window — Kling 3-15s,
|
|
209
|
+
* Omni Flash 3-10s, Seedance 2.5 4-30s. These three pairs used to be typed
|
|
210
|
+
* inline as `isSeedanceEdit ? 4 : 3` / `isSeedanceEdit ? 30 : isOmniFlashEdit ?
|
|
211
|
+
* 10 : 15`, i.e. a fourth copy of registry data keyed on model-id comparisons.
|
|
212
|
+
*/
|
|
213
|
+
function editClipBounds(model) {
|
|
214
|
+
const d = getModelCapability(model)?.duration;
|
|
215
|
+
if (!d)
|
|
216
|
+
throw new Error(`editClipBounds: no duration capability for "${model}"`);
|
|
217
|
+
return { min: d.min, max: d.max };
|
|
218
|
+
}
|
|
219
|
+
/** Resolutions any edit engine exposes as a PARAM. Only Seedance's edit price
|
|
220
|
+
* moves with resolution; the Kling and Omni Flash rows are fixed to the source. */
|
|
221
|
+
const EDIT_VIDEO_RESOLUTIONS = videoResolutionUnion(['seedance-2.5-edit']);
|
|
136
222
|
export const estimateGenerationCost = {
|
|
137
223
|
id: 'slates_estimate_generation_cost',
|
|
138
224
|
description: 'Pre-flight cost estimate. Call before any generate_* op so the user sees "this will cost N credits" up front. Takes the SAME base model ids as the generate ops (video: "seedance-2" + duration + videoResolution; image: "nano-banana-2" + resolution) — exact registry cost keys also work. Pairs with the confirm gate.',
|
|
139
225
|
input: z.object({
|
|
140
226
|
model: z.string().describe('Base model id as passed to the generate op (e.g. "seedance-2", "kling-v3.0-std", "nano-banana-2") or an exact registry cost key ("nano-banana-2-2k", "seedance-2-1080p-8s")'),
|
|
141
227
|
quantity: z.number().int().min(1).max(10).optional().describe('Number of generations (default 1)'),
|
|
142
|
-
|
|
143
|
-
|
|
228
|
+
// Video windows and resolutions GENERATED from MODEL_CAPABILITIES. The
|
|
229
|
+
// hand-typed "Video 3-15" here was wrong the day seedance-2.5 (4-30s)
|
|
230
|
+
// shipped, and "Seedance defaults to 1080p" was wrong for 2.5, which has no
|
|
231
|
+
// 1080p at all.
|
|
232
|
+
duration: z.number().int().min(1).max(360).optional().describe(`Seconds; cost scales linearly. Required with a video or audio base id. Video per model: ${describeDurations(VIDEO_MODELS)}. Audio: seed-audio 3-120 (⚠️ the requested duration IS the bill), eleven-sfx 1-22.`),
|
|
233
|
+
videoResolution: zEnum(VIDEO_RESOLUTIONS).optional().describe(`Video only. Omitted, each model quotes at its own default. Per model: ${describeVideoResolutions(VIDEO_MODELS)}`),
|
|
144
234
|
resolution: z.enum(['1k', '2k', '3k', '4k']).optional().describe('Image only (default 2k; 3k = gpt-image-2 1440p class).'),
|
|
145
235
|
quality: z.enum(['medium', 'high']).optional().describe('gpt-image-2 only — quality tier (default medium).'),
|
|
146
236
|
sound: z.boolean().optional().describe('Veo only — audio flag changes the cost key.'),
|
|
@@ -218,6 +308,16 @@ export const estimateGenerationCost = {
|
|
|
218
308
|
message: `"${resolved.model}" cost scales with duration — pass duration (seconds) to estimate.`,
|
|
219
309
|
});
|
|
220
310
|
}
|
|
311
|
+
// Refuse an out-of-range combination rather than quoting a key that does
|
|
312
|
+
// not exist — the generate op gates the same way, from the same data, so
|
|
313
|
+
// a quote can never promise something generation then refuses.
|
|
314
|
+
const capErr = assertVideoCapabilities({
|
|
315
|
+
model: resolved.model,
|
|
316
|
+
videoResolution: input.videoResolution ?? resolved.videoResolution,
|
|
317
|
+
duration,
|
|
318
|
+
});
|
|
319
|
+
if (capErr)
|
|
320
|
+
return ok(capErr);
|
|
221
321
|
key = videoCostKey({
|
|
222
322
|
model: resolved.model,
|
|
223
323
|
duration,
|
|
@@ -225,10 +325,9 @@ export const estimateGenerationCost = {
|
|
|
225
325
|
resolved.videoResolution ??
|
|
226
326
|
// Seedance quotes are resolution-scaled, so a missing resolution has
|
|
227
327
|
// to fall back to the model's OWN default — 2.5 has no 1080p at all,
|
|
228
|
-
// and a blanket '1080p' here quoted a key that does not exist.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
: undefined),
|
|
328
|
+
// and a blanket '1080p' here quoted a key that does not exist. Read
|
|
329
|
+
// from the SSOT; this used to be a hand-typed model-id ternary.
|
|
330
|
+
defaultVideoResolutionFor(resolved.model),
|
|
232
331
|
sound: input.sound ?? resolved.sound,
|
|
233
332
|
seedanceFace: input.seedanceFace ?? resolved.seedanceFace,
|
|
234
333
|
seedanceRealFace: input.seedanceRealFace,
|
|
@@ -872,6 +971,30 @@ async function previewAssets(ctx, refs) {
|
|
|
872
971
|
}
|
|
873
972
|
return out;
|
|
874
973
|
}
|
|
974
|
+
/** The exact `model` ids `slates_generate_image` accepts. */
|
|
975
|
+
export const IMAGE_MODELS = [
|
|
976
|
+
'nano-banana-2',
|
|
977
|
+
'nano-banana-2-lite',
|
|
978
|
+
'nano-banana-pro',
|
|
979
|
+
'gpt-image-2',
|
|
980
|
+
'flux-2-max',
|
|
981
|
+
'seedream-5-lite',
|
|
982
|
+
];
|
|
983
|
+
/**
|
|
984
|
+
* The aspect ratios an image generation can carry — the UNION over what these
|
|
985
|
+
* models declare in `MODEL_CAPABILITIES`, generated so the enum cannot hold a
|
|
986
|
+
* value no model accepts. It used to be a hand-typed eleven-value list whose
|
|
987
|
+
* `9:21` exists in ZERO models; that phantom is gone by construction.
|
|
988
|
+
*
|
|
989
|
+
* ⚠️ STILL A UNION, NOT A PER-MODEL CHECK. `gpt-image-2` takes five of these
|
|
990
|
+
* ten and the other five would be accepted here. The image param surface has
|
|
991
|
+
* not been audited (aspect ratios, resolution classes, per-model reference
|
|
992
|
+
* caps) — that audit is the named follow-up in
|
|
993
|
+
* `slate/docs/plan-docs/2026-08-16-MODEL-CAPABILITY-SSOT.md` §5. The machinery
|
|
994
|
+
* to close it already exists: call `checkAspectRatio(model, ratio)` the way
|
|
995
|
+
* `assertVideoCapabilities` does below.
|
|
996
|
+
*/
|
|
997
|
+
const IMAGE_ASPECT_RATIOS = aspectRatioUnion(IMAGE_MODELS);
|
|
875
998
|
// Registry cost-key for an image model+resolution. Mirrors imageCreditKey()
|
|
876
999
|
// in slate/src/shared/pricing.ts — MUST byte-match it (the same hard rule as
|
|
877
1000
|
// videoCostKey): NB2/NB Pro price per resolution, FLUX.2 Max prices per
|
|
@@ -895,11 +1018,11 @@ export const generateImage = {
|
|
|
895
1018
|
description: 'Generate an image via Slates credits. Models: nano-banana-2 (default), nano-banana-2-lite (fast/cheap drafts, 1K only), nano-banana-pro (hero-frame/typography premium), gpt-image-2 (sharp text / character sheets / grids; quality medium|high), flux-2-max (photoreal, less censored), seedream-5-lite (cheapest flat, less censored). Which model for which job: read the slates-model-selection skill. Pass projectId to save into a Slates project (recommended — asset appears live in the desktop UI). All models except nano-banana-2 REQUIRE projectId (no headless path). REQUIRED before calling: read the slates-cost-discipline skill (and the model\'s slates-prompting-* skill). You MUST pass aspectRatio and resolution explicitly (the server returns requires_clarification when missing — defaults waste credits). Cost > $0.50 returns requires_confirm — pass confirm=true after explicit user OK. MCP/CLI generation always charges credits. No skill files installed? Call slates_get_prompting_guide with the model\'s topic (and \'slates-cost-discipline\') before first use.',
|
|
896
1019
|
input: z.object({
|
|
897
1020
|
prompt: z.string().min(1).max(4000),
|
|
898
|
-
model:
|
|
1021
|
+
model: zEnum(IMAGE_MODELS).optional().describe('Image model. Default nano-banana-2. Routing doctrine: slates-model-selection skill. All except nano-banana-2 require projectId.'),
|
|
899
1022
|
projectId: z.string().uuid().optional().describe('Save into this Slates project. Renderer refreshes live. Required for every model except nano-banana-2.'),
|
|
900
1023
|
resolution: z.enum(['1k', '2k', '3k', '4k']).optional().describe('Pick deliberately: 1k drafts, 2k hero shots, 4k print/final. nano-banana-2-lite is 1k-only. gpt-image-2 classes: 1k=1024², 2k=1080p, 3k=1440p, 4k=2160p (3k is gpt-image-2 only). Never default this.'),
|
|
901
1024
|
quality: z.enum(['medium', 'high']).optional().describe('gpt-image-2 only. medium (default) = sharp text, fast, the value seat; high = max text precision + reasoning at ~4× the price. Ignored by other models.'),
|
|
902
|
-
aspectRatio:
|
|
1025
|
+
aspectRatio: zEnum(IMAGE_ASPECT_RATIOS).optional().describe(`Pick deliberately from the use case. Cinematic → 16:9. TikTok/Reels/Story → 9:16. IG square → 1:1. Ultra-wide → 21:9. Ask the user when ambiguous. Per model: ${describeAspectRatios(IMAGE_MODELS)}`),
|
|
903
1026
|
count: z.number().int().min(1).max(4).optional(),
|
|
904
1027
|
referenceImageUrls: z.array(z.string().url()).max(14).optional().describe('Headless (no-projectId) nano-banana-2 only: up to 14 ref URLs. For projectId runs, upload refs with slates_upload_reference_image first. Always label each image\'s role in the prompt text.'),
|
|
905
1028
|
referenceAssetIds: z.array(z.string()).max(14).optional().describe("Project assets to use as reference/ingredient images — asset UUIDs or badge codes (\"IMG-A8\"); codes resolve against the project at call time. Requires projectId. For nano-banana-2 up to 14 refs; FLUX/Seedream route to their edit endpoints with lower per-model caps. Label each reference's role in the prompt text."),
|
|
@@ -922,7 +1045,8 @@ export const generateImage = {
|
|
|
922
1045
|
missing,
|
|
923
1046
|
message: `Missing required field(s): ${missing.join(', ')}. ` +
|
|
924
1047
|
`Read the slates-prompting-nano-banana-2 + slates-cost-discipline skills, ` +
|
|
925
|
-
|
|
1048
|
+
// Generated from MODEL_CAPABILITIES — never retype a ratio list.
|
|
1049
|
+
`or ask the user. Aspect ratio by model: ${describeAspectRatios(IMAGE_MODELS)}. ` +
|
|
926
1050
|
`Resolution options: 1k 2k 4k (same price band — pick by need, not cost).`,
|
|
927
1051
|
});
|
|
928
1052
|
}
|
|
@@ -1310,23 +1434,52 @@ export const editImage = {
|
|
|
1310
1434
|
},
|
|
1311
1435
|
};
|
|
1312
1436
|
// ── Generate video ──────────────────────────────────────────────
|
|
1313
|
-
//
|
|
1314
|
-
//
|
|
1315
|
-
//
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1437
|
+
//
|
|
1438
|
+
// VIDEO_MODELS and the capability-derived param vocabulary are declared near the
|
|
1439
|
+
// top of this file: `slates_estimate_generation_cost`'s schema is evaluated at
|
|
1440
|
+
// module load and reads them, so they have to initialise before it.
|
|
1441
|
+
/**
|
|
1442
|
+
* Reject an illegal aspectRatio / videoResolution / duration BEFORE submit,
|
|
1443
|
+
* naming the legal set. Returns a `requires_clarification` payload or null.
|
|
1444
|
+
*
|
|
1445
|
+
* ⚠️ WHY THIS IS A RUN()-TIME GUARD AND NOT A SCHEMA `superRefine`. The op
|
|
1446
|
+
* accepts registry COST-KEY spellings as `model` ("seedance-2-1080p-8s") and
|
|
1447
|
+
* `resolveVideoModel()` unpacks them into base id + duration + resolution at
|
|
1448
|
+
* the top of `run()`. A schema-level refinement runs BEFORE that, so it would
|
|
1449
|
+
* see an unnormalised model id and a `duration` that is still undefined — it
|
|
1450
|
+
* would reject legal calls and wave illegal ones through. The check has to sit
|
|
1451
|
+
* after normalisation, which is here. It also lets the failure arrive as the
|
|
1452
|
+
* same teaching `requires_clarification` shape as every other gate in this op,
|
|
1453
|
+
* rather than a raw Zod error the agent has to guess its way out of.
|
|
1454
|
+
*
|
|
1455
|
+
* `promptMode` matters: Veo's reference-to-video endpoint is 8s only, declared
|
|
1456
|
+
* as `duration.modeOverrides.ingredients`. Free reference images with no
|
|
1457
|
+
* first/last frame IS ingredients mode — the same condition
|
|
1458
|
+
* `buildFalVeoRequest` uses to pick the ref2v endpoint.
|
|
1459
|
+
*/
|
|
1460
|
+
function assertVideoCapabilities(input) {
|
|
1461
|
+
const freeRefs = (input.ingredientAssetIds?.length ?? 0) +
|
|
1462
|
+
(input.characterAssetIds?.length ?? 0) +
|
|
1463
|
+
(input.environmentAssetIds?.length ?? 0) +
|
|
1464
|
+
(input.styleAssetIds?.length ?? 0);
|
|
1465
|
+
const promptMode = freeRefs > 0 && !input.firstFrameAssetId && !input.lastFrameAssetId ? 'ingredients' : undefined;
|
|
1466
|
+
const ratioErr = checkAspectRatio(input.model, input.aspectRatio, AGENT_ROUTE_PROVIDER);
|
|
1467
|
+
if (ratioErr)
|
|
1468
|
+
return { requires_clarification: true, missing: ['aspectRatio'], message: ratioErr };
|
|
1469
|
+
const resErr = checkVideoResolution(input.model, input.videoResolution);
|
|
1470
|
+
if (resErr)
|
|
1471
|
+
return { requires_clarification: true, missing: ['videoResolution'], message: resErr };
|
|
1472
|
+
// Duration LAST: its legal window depends on the resolution, so a bad
|
|
1473
|
+
// resolution must be reported first or the duration message names a window
|
|
1474
|
+
// derived from a value the model never had.
|
|
1475
|
+
const durErr = checkDuration(input.model, input.duration, {
|
|
1476
|
+
videoResolution: input.videoResolution,
|
|
1477
|
+
promptMode,
|
|
1478
|
+
});
|
|
1479
|
+
if (durErr)
|
|
1480
|
+
return { requires_clarification: true, missing: ['duration'], message: durErr };
|
|
1481
|
+
return null;
|
|
1482
|
+
}
|
|
1330
1483
|
// Model → registry cost-key. Each provider's keys ship with their own
|
|
1331
1484
|
// shape (verified against /api/agent/models):
|
|
1332
1485
|
// Kling: kling-v3-{standard|pro|omni}-{N}s — note the user-facing
|
|
@@ -1419,9 +1572,11 @@ export function omniFlashEditCostKey(duration) {
|
|
|
1419
1572
|
* 2.0: refs 2–15s + output 4–15s. 2.5: refs to 30s + output to 30s. */
|
|
1420
1573
|
const SEEDANCE_20_VREF_MAX_TOTAL = 30;
|
|
1421
1574
|
const SEEDANCE_25_VREF_MAX_TOTAL = 60;
|
|
1422
|
-
/** Seedance 2.5 source-clip bounds for the edit task type
|
|
1423
|
-
|
|
1424
|
-
|
|
1575
|
+
/** Seedance 2.5 source-clip bounds for the edit task type — READ from the
|
|
1576
|
+
* capability SSOT, not typed. Kept as named exports because published builds
|
|
1577
|
+
* import them. */
|
|
1578
|
+
export const SEEDANCE_25_EDIT_MIN_SECONDS = editClipBounds('seedance-2.5-edit').min;
|
|
1579
|
+
export const SEEDANCE_25_EDIT_MAX_SECONDS = editClipBounds('seedance-2.5-edit').max;
|
|
1425
1580
|
// Seedance 2.5 video-edit cost key — mirrors seedanceCreditKey() in
|
|
1426
1581
|
// slate/src/shared/pricing.ts for the edit row (must byte-match; checked by the
|
|
1427
1582
|
// slates-api pricing-consistency script). Unlike the Kling and Omni Flash edit
|
|
@@ -1589,14 +1744,27 @@ export const generateVideo = {
|
|
|
1589
1744
|
description: 'Generate video via Slates credits. REQUIRED before calling: read slates-model-selection (the routing doctrine), slates-cost-discipline, and the matching per-model prompting skill (slates-prompting-seedance / slates-prompting-seedance-2-5 / slates-prompting-kling-v3 / slates-prompting-veo-3) — video models prompt very differently; load them via slates_get_prompting_guide if no skill files are installed. Read slates-content-policy when the scene involves conflict, creatures, crowds, destruction, weapons, or young characters. projectId, aspectRatio, and duration are required (requires_clarification otherwise). Cost > $0.50 returns requires_confirm — pass confirm=true after explicit user OK. Image-to-video via firstFrameAssetId; first+last frames = Veo/Seedance only; ingredients via ingredientAssetIds (Kling Omni / Seedance). Asset params take UUIDs or badge codes ("IMG-A8").',
|
|
1590
1745
|
input: z.object({
|
|
1591
1746
|
prompt: z.string().min(1).max(4000),
|
|
1592
|
-
|
|
1747
|
+
// ROUTING doctrine only. Every capability number was stripped on 2026-08-16
|
|
1748
|
+
// and now lives in the aspectRatio / duration / videoResolution descriptions
|
|
1749
|
+
// below, generated from MODEL_CAPABILITIES. "Veo = 16:9 only" and
|
|
1750
|
+
// "seedance-2.5 480p/720p" were both stated here AND there, and the two
|
|
1751
|
+
// copies disagreed.
|
|
1752
|
+
model: z.string().describe(`One of: ${VIDEO_MODELS.join(' | ')}. Pass the BASE id — duration and videoResolution are separate params (registry cost keys like "kling-v3-standard-8s" auto-resolve). Route per the slates-model-selection skill: Kling std = general-purpose DEFAULT, Seedance 2 = premium physics/effects/hero tier, seedance-2.5 = a SECOND SEAT beside it (longer takes, far more references, audio-only refs — but no 1080p or 4K, so stay on seedance-2 whenever resolution matters), Veo = native-synced-audio niche only, never the default, omni-flash = cheap tier with audio included (t2v, single-start-frame i2v, or reference images; no last frame, no video/audio refs). All are VIDEO-only. Each model's legal aspect ratios, durations and resolutions are in those params' own descriptions — read them there, not from memory. For per-call cost, call slates_estimate_generation_cost.`),
|
|
1593
1753
|
projectId: z.string().uuid().optional().describe('Save into this Slates project. Strongly recommended — the desktop UI shows a progress card live and the asset appears when complete.'),
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1754
|
+
// 🚨 THESE THREE DESCRIPTIONS ARE GENERATED FROM `MODEL_CAPABILITIES`.
|
|
1755
|
+
// Never hand-write a ratio, resolution or duration into them again — every
|
|
1756
|
+
// one of the hand-written claims that stood here had drifted, and an
|
|
1757
|
+
// out-of-set value is accepted by the client and rejected by the provider
|
|
1758
|
+
// ASYNCHRONOUSLY, after the job queues and credits reserve.
|
|
1759
|
+
aspectRatio: zEnum(VIDEO_ASPECT_RATIOS).optional().describe(`NOT every model takes every value — an out-of-set ratio is REFUSED before submit, not silently ignored. Per model: ${describeAspectRatios(VIDEO_MODELS, AGENT_ROUTE_PROVIDER)}`),
|
|
1760
|
+
duration: z.number().int().min(VIDEO_DURATION_BOUNDS.min).max(VIDEO_DURATION_BOUNDS.max).optional().describe(`Seconds. Cost scales linearly — a 30s seedance-2.5 take is several hundred credits, so be explicit rather than defaulting. Per model: ${describeDurations(VIDEO_MODELS)}`),
|
|
1761
|
+
videoResolution: zEnum(VIDEO_RESOLUTIONS).optional().describe(`An unsupported resolution is REJECTED, never downgraded. Per model: ${describeVideoResolutions(VIDEO_MODELS)}. 4K video is Pro-only (the server returns PRO_REQUIRED for a base-tier account).`),
|
|
1597
1762
|
firstFrameAssetId: z.string().optional().describe('Starting frame for image-to-video: asset UUID or badge code ("IMG-A8") — codes resolve against the project at call time, so a code the user just spoke is always safe to pass.'),
|
|
1598
1763
|
lastFrameAssetId: z.string().optional().describe('Ending frame (UUID or badge code). Veo and Seedance only. Pairs with firstFrameAssetId for guided transitions.'),
|
|
1599
|
-
ingredientAssetIds: z.array(z.string()).max(30).optional().describe(
|
|
1764
|
+
ingredientAssetIds: z.array(z.string()).max(30).optional().describe(
|
|
1765
|
+
// Caps DERIVED from MODEL_CAPABILITIES — the hand-typed list omitted
|
|
1766
|
+
// kling-v3.0-omni-pro entirely and read as if 7 were an Omni Flash-only rule.
|
|
1767
|
+
`Visual reference / ingredient assets (UUIDs or badge codes). Cap per model (combined across ingredient/character/environment/style params): ${describeReferenceImageCaps(VIDEO_MODELS)}. More is not better: 2-4 strong references beat both extremes, and past 4 reference PEOPLE output stability drops on Seedance regardless of the cap.`),
|
|
1600
1768
|
characterAssetIds: z.array(z.string()).optional().describe('Character sheet assets (UUIDs or badge codes) — keeps a character consistent across the shot.'),
|
|
1601
1769
|
environmentAssetIds: z.array(z.string()).optional().describe('Environment reference assets (UUIDs or badge codes) — keeps a location/setting consistent across the shot.'),
|
|
1602
1770
|
styleAssetIds: z.array(z.string()).optional().describe('Style reference assets (UUIDs or badge codes) — locks the visual style of the shot.'),
|
|
@@ -1662,22 +1830,28 @@ export const generateVideo = {
|
|
|
1662
1830
|
missing,
|
|
1663
1831
|
message: `Missing required field(s): ${missing.join(', ')}. ` +
|
|
1664
1832
|
`Read the slates-cost-discipline + ${promptingSkillFor(input.model)} skills, ` +
|
|
1665
|
-
|
|
1666
|
-
|
|
1833
|
+
// Generated from MODEL_CAPABILITIES. The prose that stood here claimed
|
|
1834
|
+
// "Veo locks to 16:9", "Kling/Seedance support 1:1 16:9 9:16 4:3 3:4
|
|
1835
|
+
// 21:9" and "Kling 5-15s" — three wrong claims in one sentence.
|
|
1836
|
+
`or ask the user. ` +
|
|
1837
|
+
`Aspect ratio for ${input.model}: ${aspectRatiosFor(input.model, AGENT_ROUTE_PROVIDER).join(', ')}. ` +
|
|
1838
|
+
`Duration: ${describeDurations([input.model])}. ` +
|
|
1839
|
+
`Cost scales linearly with duration.`,
|
|
1667
1840
|
});
|
|
1668
1841
|
}
|
|
1669
|
-
//
|
|
1670
|
-
//
|
|
1671
|
-
//
|
|
1672
|
-
//
|
|
1842
|
+
// 🚨 CAPABILITY GATE — the one that stops a client-side accept of a
|
|
1843
|
+
// server-side reject. Registry-driven and model-keyed: an aspect ratio,
|
|
1844
|
+
// resolution or duration the chosen model does not take is refused HERE,
|
|
1845
|
+
// before the job queues and credits reserve. Runs after normalisation so it
|
|
1846
|
+
// sees the resolved model and any params a cost key back-filled.
|
|
1847
|
+
const capErr = assertVideoCapabilities(input);
|
|
1848
|
+
if (capErr)
|
|
1849
|
+
return ok(capErr);
|
|
1850
|
+
// Omni Flash's SHAPE constraints — t2v, single-start-frame i2v, or ref2v
|
|
1851
|
+
// with reference IMAGES. No last frame, no video/audio refs. (Its duration
|
|
1852
|
+
// and resolution are covered by the capability gate above; the 3-10s check
|
|
1853
|
+
// that used to live here was a hand-typed duplicate of registry data.)
|
|
1673
1854
|
if (input.model === 'omni-flash') {
|
|
1674
|
-
if (input.duration < 3 || input.duration > 10) {
|
|
1675
|
-
return ok({
|
|
1676
|
-
requires_clarification: true,
|
|
1677
|
-
missing: ['duration'],
|
|
1678
|
-
message: `Omni Flash supports 3-10 seconds (you passed ${input.duration}s). Pick a duration in that range.`,
|
|
1679
|
-
});
|
|
1680
|
-
}
|
|
1681
1855
|
if (input.lastFrameAssetId ||
|
|
1682
1856
|
input.videoReferenceAssetId || input.audioReferenceAssetId ||
|
|
1683
1857
|
(input.videoReferenceAssetIds?.length ?? 0) > 0 ||
|
|
@@ -1685,40 +1859,26 @@ export const generateVideo = {
|
|
|
1685
1859
|
return ok({
|
|
1686
1860
|
requires_clarification: true,
|
|
1687
1861
|
missing: [],
|
|
1688
|
-
message:
|
|
1862
|
+
message: `Omni Flash takes a prompt, an optional start frame, and up to ${omniFlashRefCap} reference IMAGES — last frames and video/audio references are not supported. Drop those, or switch to seedance-2 (video/audio refs, last frame) or veo (last frame).`,
|
|
1689
1863
|
});
|
|
1690
1864
|
}
|
|
1691
1865
|
const refCount = (input.ingredientAssetIds?.length ?? 0) +
|
|
1692
1866
|
(input.characterAssetIds?.length ?? 0) +
|
|
1693
1867
|
(input.environmentAssetIds?.length ?? 0) +
|
|
1694
1868
|
(input.styleAssetIds?.length ?? 0);
|
|
1695
|
-
if (refCount >
|
|
1869
|
+
if (refCount > omniFlashRefCap) {
|
|
1696
1870
|
return ok({
|
|
1697
1871
|
requires_clarification: true,
|
|
1698
1872
|
missing: [],
|
|
1699
|
-
message: `Omni Flash takes at most
|
|
1700
|
-
});
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
// Veo exists only at discrete durations 4/6/8s, and 4K only at 8s.
|
|
1704
|
-
// Validate up front so the agent gets an actionable message instead of
|
|
1705
|
-
// a generic "Model variant not in registry" throw from the cost lookup.
|
|
1706
|
-
if (input.model.startsWith('veo')) {
|
|
1707
|
-
if (![4, 6, 8].includes(input.duration)) {
|
|
1708
|
-
return ok({
|
|
1709
|
-
requires_clarification: true,
|
|
1710
|
-
missing: ['duration'],
|
|
1711
|
-
message: `Veo 3.1 supports only 4s, 6s, or 8s (you passed ${input.duration}s). Pick one of those.`,
|
|
1712
|
-
});
|
|
1713
|
-
}
|
|
1714
|
-
if (input.videoResolution === '4k' && input.duration !== 8) {
|
|
1715
|
-
return ok({
|
|
1716
|
-
requires_clarification: true,
|
|
1717
|
-
missing: ['duration'],
|
|
1718
|
-
message: `Veo 3.1 4K renders only at 8s (you passed ${input.duration}s at 4k). Use duration=8 for 4K, or drop to 720p/1080p for 4s/6s.`,
|
|
1873
|
+
message: `Omni Flash takes at most ${omniFlashRefCap} reference images combined (you passed ${refCount}). Trim the list.`,
|
|
1719
1874
|
});
|
|
1720
1875
|
}
|
|
1721
1876
|
}
|
|
1877
|
+
// ⛔ The hand-written Veo duration block that stood here is GONE. It read
|
|
1878
|
+
// `![4,6,8].includes(duration)` plus "4K only at 8s" — and the registry
|
|
1879
|
+
// forces 8s at 1080p TOO, so it happily quoted a 4s 1080p Veo the provider
|
|
1880
|
+
// rejects. `assertVideoCapabilities` above covers both, plus the
|
|
1881
|
+
// reference-to-video 8s rule it never knew about, from the SSOT.
|
|
1722
1882
|
// Resolve every asset reference (UUID or badge code) against the
|
|
1723
1883
|
// project AT CALL TIME. Codes the user just spoke ("a8 is the one")
|
|
1724
1884
|
// resolve to whatever exists NOW — including assets created in the UI
|
|
@@ -2355,11 +2515,12 @@ export const editVideo = {
|
|
|
2355
2515
|
projectId: z.string().uuid().describe('Project the source clip lives in.'),
|
|
2356
2516
|
sourceVideoAssetId: z.string().describe('The VIDEO asset to edit — UUID or badge code ("VID-V3", bare "V3"); codes resolve against the project at call time. Kling: 3–15s clips; omni-flash-edit: 3–10s.'),
|
|
2357
2517
|
prompt: z.string().min(1).max(2500).describe('The change, not the whole scene — e.g. "replace the man with @marcus", "make it a rainy night", "turn the street into a neon Tokyo alley". Mention subjects with @name; the transport compiles them to Kling\'s @ElementN notation (Kling models). For omni-flash-edit keep it simple and add "Keep everything else the same."'),
|
|
2358
|
-
|
|
2518
|
+
// Clip windows and resolutions GENERATED from MODEL_CAPABILITIES.
|
|
2519
|
+
model: zEnum(EDIT_VIDEO_MODELS).optional().describe(`Default kling-v3.0-omni-edit. Pro (~25¢/s vs ~19¢/s) only for hero shots where fidelity matters. omni-flash-edit (~19¢/s) for prompt-only edits — it takes NO character/style refs. seedance-2.5-edit is the ONLY engine that accepts a clip longer than 15s; it also takes NO character/style refs on this op. Source-clip length per engine: ${describeDurations(EDIT_VIDEO_MODELS)}. Output resolution: ${describeVideoResolutions(EDIT_VIDEO_MODELS)}`),
|
|
2359
2520
|
characterAssetIds: z.array(z.string()).max(4).optional().describe('Subject/element image assets to swap IN (UUIDs or badge codes). Each becomes a Kling element (@ElementN). KLING MODELS ONLY — rejected on omni-flash-edit.'),
|
|
2360
2521
|
styleAssetIds: z.array(z.string()).max(4).optional().describe('Style/appearance reference images (@ImageN). Max 4 combined with characterAssetIds. KLING MODELS ONLY — rejected on omni-flash-edit.'),
|
|
2361
2522
|
keepAudio: z.boolean().optional().describe('Preserve the original audio track (default true; Kling models only — omni-flash-edit and seedance-2.5-edit output carry their own audio).'),
|
|
2362
|
-
videoResolution:
|
|
2523
|
+
videoResolution: zEnum(EDIT_VIDEO_RESOLUTIONS).optional().describe(`seedance-2.5-edit ONLY (default ${defaultVideoResolutionFor('seedance-2.5-edit')}). Ignored by the Kling and Omni Flash engines, whose output follows the source clip.`),
|
|
2363
2524
|
seedanceFace: z.boolean().optional().describe('seedance-2.5-edit ONLY: set true when a CHARACTER FACE is visible in the clip. Faceless edits run on BytePlus; faces are blocked there and must route to the relaxed provider, which costs ~35% more. A face edit submitted without this flag is rejected by the provider, not silently downgraded.'),
|
|
2364
2525
|
background: z.boolean().optional().describe(BACKGROUND_DESCRIBE),
|
|
2365
2526
|
confirm: z.boolean().optional().describe('Set true to bypass the cost confirm gate after the user OKs the spend.'),
|
|
@@ -2373,10 +2534,9 @@ export const editVideo = {
|
|
|
2373
2534
|
const model = input.model ?? 'kling-v3.0-omni-edit';
|
|
2374
2535
|
const isOmniFlashEdit = model === 'omni-flash-edit';
|
|
2375
2536
|
const isSeedanceEdit = model === 'seedance-2.5-edit';
|
|
2376
|
-
//
|
|
2377
|
-
//
|
|
2378
|
-
const minClipSeconds
|
|
2379
|
-
const maxClipSeconds = isSeedanceEdit ? SEEDANCE_25_EDIT_MAX_SECONDS : isOmniFlashEdit ? 10 : 15;
|
|
2537
|
+
// Source-clip window, read from the capability SSOT per engine — never a
|
|
2538
|
+
// model-id ternary. (Kling 3–15s, Omni Flash 3–10s, Seedance 2.5 4–30s.)
|
|
2539
|
+
const { min: minClipSeconds, max: maxClipSeconds } = editClipBounds(model);
|
|
2380
2540
|
if ((isOmniFlashEdit || isSeedanceEdit) && ((input.characterAssetIds?.length ?? 0) > 0 || (input.styleAssetIds?.length ?? 0) > 0)) {
|
|
2381
2541
|
throw new Error(`${model} takes the prompt and the source clip only on this op — no character/style reference images. Drop the refs, or switch to kling-v3.0-omni-edit which supports elements.`);
|
|
2382
2542
|
}
|
|
@@ -2419,7 +2579,7 @@ export const editVideo = {
|
|
|
2419
2579
|
const costKey = isSeedanceEdit
|
|
2420
2580
|
? seedanceEditCostKey({
|
|
2421
2581
|
duration: billedSeconds,
|
|
2422
|
-
videoResolution: input.videoResolution ?? '
|
|
2582
|
+
videoResolution: (input.videoResolution ?? defaultVideoResolutionFor('seedance-2.5-edit')),
|
|
2423
2583
|
seedanceFace: input.seedanceFace === true,
|
|
2424
2584
|
})
|
|
2425
2585
|
: isOmniFlashEdit
|
package/dist/prompts/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './reference-rules.js';
|
|
|
2
2
|
export * from './reference-composer.js';
|
|
3
3
|
export * from './content-policy.js';
|
|
4
4
|
export * from './style-library.js';
|
|
5
|
+
export * from './model-capabilities.js';
|
|
5
6
|
export * from './model-facts.js';
|
|
6
7
|
export * from './character-sheet.js';
|
|
7
8
|
export * from './environment-sheet.js';
|
package/dist/prompts/index.js
CHANGED
|
@@ -13,6 +13,10 @@ export * from './reference-rules.js';
|
|
|
13
13
|
export * from './reference-composer.js';
|
|
14
14
|
export * from './content-policy.js';
|
|
15
15
|
export * from './style-library.js';
|
|
16
|
+
// Model CAPABILITY SSOT (aspect ratios, video resolutions, durations, reference
|
|
17
|
+
// caps). The desktop's MODEL_REGISTRY spreads these into every entry and the op
|
|
18
|
+
// surface validates + generates its descriptions from them — never hand-typed.
|
|
19
|
+
export * from './model-capabilities.js';
|
|
16
20
|
export * from './model-facts.js';
|
|
17
21
|
export * from './character-sheet.js';
|
|
18
22
|
export * from './environment-sheet.js';
|