@slatesvideo/shared 0.6.11 → 0.7.1
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/auth.js +2 -2
- package/dist/clients/cloud.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/manual/content.d.ts +1 -1
- package/dist/manual/content.js +1 -1
- package/dist/operations/index.d.ts +817 -16
- package/dist/operations/index.js +1413 -360
- package/dist/operations/surface.d.ts +4 -1
- package/dist/operations/surface.js +41 -10
- package/dist/prompts/ad-presets.d.ts +77 -0
- package/dist/prompts/ad-presets.js +43 -0
- package/dist/prompts/agent-doctrine.js +27 -5
- package/dist/prompts/banned-tokens.d.ts +4 -29
- package/dist/prompts/banned-tokens.js +29 -204
- package/dist/prompts/craft-cards.js +2 -2
- package/dist/prompts/generation-policy.d.ts +41 -0
- package/dist/prompts/generation-policy.js +53 -0
- package/dist/prompts/guide-retrieval.d.ts +9 -0
- package/dist/prompts/guide-retrieval.js +53 -0
- package/dist/prompts/index.d.ts +1 -0
- package/dist/prompts/index.js +1 -0
- package/dist/prompts/model-capabilities.d.ts +18 -1
- package/dist/prompts/model-capabilities.js +72 -19
- package/dist/prompts/model-facts.d.ts +34 -2
- package/dist/prompts/model-facts.js +66 -5
- package/dist/prompts/partials.generated.js +8 -2
- package/dist/prompts/prompting-tips.d.ts +1 -1
- package/dist/prompts/prompting-tips.js +61 -16
- package/dist/prompts/reference-composer.d.ts +2 -0
- package/dist/prompts/reference-composer.js +51 -50
- package/dist/prompts/script-document.d.ts +165 -0
- package/dist/prompts/script-document.js +11 -0
- package/dist/prompts/shot-grammar.d.ts +4 -4
- package/dist/prompts/shot-grammar.js +3 -3
- package/dist/prompts/shot-spec.d.ts +13 -0
- package/dist/prompts/shot-spec.js +23 -5
- package/dist/skills/content.js +27 -24
- package/exports/slates-chatgpt-images/generated/SKILL.md +107 -0
- package/exports/slates-chatgpt-images/generated/slates-chatgpt-images.skill +0 -0
- package/exports/slates-prompt-builder/generated/SKILL.md +1 -1
- package/exports/slates-prompt-builder/generated/reference-character.md +9 -1
- package/exports/slates-prompt-builder/generated/reference-kling.md +3 -3
- package/exports/slates-prompt-builder/generated/reference-nano-banana.md +22 -10
- package/exports/slates-prompt-builder/generated/reference-seedance.md +4 -4
- package/exports/slates-prompt-builder/generated/slates-prompt-builder-manifest.json +17 -17
- package/exports/slates-prompt-builder/generated/slates-prompt-builder.skill +0 -0
- package/package.json +9 -3
- package/skills/_partials/cinematic-card.md +8 -0
- package/skills/_partials/cinematic-routes-short.md +2 -0
- package/skills/_partials/cinematic-tips-short.md +2 -0
- package/skills/_partials/decision-log.md +1 -13
- package/skills/_partials/image-defaults.md +11 -0
- package/skills/_partials/lens-video-split.md +1 -0
- package/skills/_partials/reference-rules-core.md +1 -1
- package/skills/_partials/sheet-tool-defaults.md +6 -0
- package/skills/slates-character-identity.md +9 -1
- package/skills/slates-chatgpt-images.md +107 -0
- package/skills/slates-cinematic-look.md +237 -0
- package/skills/slates-cost-discipline.md +18 -12
- package/skills/slates-direct-response-ad.md +13 -53
- package/skills/slates-edit-and-iterate.md +1 -1
- package/skills/slates-model-selection.md +20 -14
- package/skills/slates-one-prompt-film.md +19 -77
- package/skills/slates-project-organization.md +7 -3
- package/skills/slates-prompting-flux-2-max.md +15 -4
- package/skills/slates-prompting-gpt-image-2-5.md +41 -28
- package/skills/slates-prompting-inworld-tts.md +174 -174
- package/skills/slates-prompting-kling-v3.md +3 -3
- package/skills/slates-prompting-lip-sync.md +1 -1
- package/skills/slates-prompting-minimax-h3.md +30 -17
- package/skills/slates-prompting-motion-transfer.md +1 -1
- package/skills/slates-prompting-nano-banana-2.md +24 -11
- package/skills/slates-prompting-seedance-2-5.md +7 -6
- package/skills/slates-prompting-seedance.md +5 -5
- package/skills/slates-prompting-seedream-5-lite.md +14 -3
- package/skills/slates-prompting-veo-3.md +1 -1
- package/skills/slates-script-craft.md +45 -0
- package/skills/slates-shot-variety.md +11 -40
- package/skills/slates-storyboard-from-script.md +14 -66
- package/skills/slates-style-prompting.md +4 -4
- package/skills/slates-ugc-influencer-ad.md +32 -309
- package/skills/slates-vision-feedback-loop.md +2 -1
|
@@ -1,219 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
// THE PROBLEM: "before prompting any model, load the matching guide" is a
|
|
5
|
-
// sentence in a system prompt with nothing checking it. Measured 2026-08-30 in
|
|
6
|
-
// a real Studio Agent session: slates_get_prompting_guide was called ZERO
|
|
7
|
-
// times, and the prompt that shipped tripped the skill's own never-use list
|
|
8
|
-
// twice (`photorealistic`, `cinematic`). A rule with no check is a suggestion,
|
|
9
|
-
// and an LLM is the least reliable enforcer you could pick.
|
|
10
|
-
//
|
|
11
|
-
// THE FIX, in two halves, neither of which the model can skip:
|
|
12
|
-
// (a) the never-use list is INLINED into the generate ops' descriptions.
|
|
13
|
-
// Op descriptions are always in context on BOTH surfaces — there is no
|
|
14
|
-
// call to omit and no discretion to exercise.
|
|
15
|
-
// (b) the submitted prompt is MATCHED against the list and a warning comes
|
|
16
|
-
// back in the op result. Non-blocking: the generation proceeds
|
|
17
|
-
// (PRODUCT_PHILOSOPHY.md → make state visible, never block).
|
|
18
|
-
//
|
|
19
|
-
// 🚨 THE LIST IS NEVER HAND-TYPED HERE. It is extracted from the skill files
|
|
20
|
-
// themselves, between `<!-- @banned:start -->` / `<!-- @banned:end -->`
|
|
21
|
-
// markers. That is this workspace's LLM-docs doctrine — never hand-type a fact
|
|
22
|
-
// an LLM will read — and it is the only way the op description and the skill
|
|
23
|
-
// cannot drift apart. Change the skill; the description follows on the next
|
|
24
|
-
// build. scripts/agent-surface-lockstep-check.mjs fails the build if a token
|
|
25
|
-
// in a description no longer appears in its source skill.
|
|
26
|
-
//
|
|
27
|
-
// Deterministic by construction (document order, no sorting, no dedupe
|
|
28
|
-
// reshuffle) because these strings land in the desktop's prompt-cached tool
|
|
29
|
-
// prefix, whose byte-stability IS the cache mechanism.
|
|
30
|
-
// ============================================================
|
|
31
|
-
import { SKILLS } from '../skills/content.js';
|
|
32
|
-
/**
|
|
33
|
-
* Where each scope's CROSS-MODEL list lives.
|
|
34
|
-
*
|
|
35
|
-
* One skill per scope on purpose: these are the two lists that are GENERIC to
|
|
36
|
-
* their modality (Stable-Diffusion-era tag soup for images, quality
|
|
37
|
-
* incantations for video), not model-specific quirks. A per-model list cannot
|
|
38
|
-
* ride the op DESCRIPTION — a description is one static string for every call,
|
|
39
|
-
* so it cannot change with the `model` argument. That is what
|
|
40
|
-
* `bannedTokensForSkill` below is for: the per-model list rides the estimate
|
|
41
|
-
* RESULT, where the model has just been named.
|
|
42
|
-
*/
|
|
43
|
-
const BANNED_TOKEN_SOURCES = [
|
|
44
|
-
{ skill: 'slates-prompting-nano-banana-2', scope: 'image' },
|
|
45
|
-
{ skill: 'slates-prompting-seedance', scope: 'video' },
|
|
46
|
-
];
|
|
47
|
-
/**
|
|
48
|
-
* 🚨 THE ENFORCEMENT THAT WORKED COVERED TWO SKILLS OF FIFTEEN.
|
|
49
|
-
*
|
|
50
|
-
* `describeBannedTokens('image')` was Nano Banana's list and `('video')` was
|
|
51
|
-
* Seedance's, so a Veo, Kling, LTX, MiniMax, FLUX, Seedream, GPT-Image or audio
|
|
52
|
-
* generation was matched against another model's never-use list and its own was
|
|
53
|
-
* enforced by nothing — while four skills (content-policy, lip-sync,
|
|
54
|
-
* minimax-h3, motion-transfer) carried never-use prose with no markers at all,
|
|
55
|
-
* which is a rule an LLM has to notice.
|
|
56
|
-
*
|
|
57
|
-
* Every skill that carries an `@banned` block now contributes to a per-skill
|
|
58
|
-
* list, delivered on the estimate result beside the craft card. The two above
|
|
59
|
-
* stay ALSO on the op descriptions, because a modality-wide list is true of
|
|
60
|
-
* every call that op can make.
|
|
61
|
-
*/
|
|
62
|
-
function extractPerSkill() {
|
|
63
|
-
const out = new Map();
|
|
64
|
-
for (const [skill, content] of Object.entries(SKILLS)) {
|
|
65
|
-
// Cheap pre-test: only pay the regex for files that carry the marker.
|
|
66
|
-
if (!content.includes('@banned:start'))
|
|
67
|
-
continue;
|
|
68
|
-
const scope = inferScope(skill);
|
|
69
|
-
out.set(skill, extractFromSkill(skill).map((token) => ({ token, skill, scope })));
|
|
70
|
-
}
|
|
71
|
-
return out;
|
|
72
|
-
}
|
|
73
|
-
/** Image-lane skills prompt for pixels; everything else is a time-based lane.
|
|
74
|
-
* Only used to tag a token for the warning text — the per-skill list is
|
|
75
|
-
* matched by SKILL, never by scope, so a wrong guess here cannot mis-enforce. */
|
|
76
|
-
function inferScope(skill) {
|
|
77
|
-
return /nano-banana|gpt-image|flux|seedream/.test(skill) ? 'image' : 'video';
|
|
78
|
-
}
|
|
79
|
-
const FENCE_RE = /<!--\s*@banned:start\s*-->([\s\S]*?)<!--\s*@banned:end\s*-->/g;
|
|
80
|
-
const HTML_COMMENT_RE = /<!--[\s\S]*?-->/g;
|
|
81
|
-
const BACKTICKED_RE = /`([^`\n]+)`/g;
|
|
82
|
-
/**
|
|
83
|
-
* Pull every backticked phrase out of a skill's fenced block(s).
|
|
84
|
-
*
|
|
85
|
-
* HTML comments are stripped FIRST: the fence carries a "MACHINE-READ" note to
|
|
86
|
-
* whoever edits the skill next, and that note itself contains backticks.
|
|
1
|
+
/** Model-specific prompt advice, extracted from each skill's @banned blocks.
|
|
2
|
+
* Warnings never rewrite or block a prompt. No model's list is universal.
|
|
87
3
|
*/
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
out.push(token);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
if (fences === 0) {
|
|
109
|
-
throw new Error(`[banned-tokens] ${skill}.md has no <!-- @banned:start --> / <!-- @banned:end --> block. ` +
|
|
110
|
-
`The op description and the prompt warnings are GENERATED from it — restore the markers, ` +
|
|
111
|
-
`or drop the skill from BANNED_TOKEN_SOURCES.`);
|
|
112
|
-
}
|
|
113
|
-
if (out.length === 0) {
|
|
114
|
-
throw new Error(`[banned-tokens] ${skill}.md has a @banned block with no backticked tokens in it. ` +
|
|
115
|
-
`An empty list would silently disable the check instead of failing it.`);
|
|
116
|
-
}
|
|
117
|
-
return out;
|
|
118
|
-
}
|
|
119
|
-
/** Every banned token, in skill-document order. Integrity asserted at load. */
|
|
120
|
-
export const BANNED_PROMPT_TOKENS = BANNED_TOKEN_SOURCES.flatMap(({ skill, scope }) => extractFromSkill(skill).map((token) => ({ token, skill, scope })));
|
|
121
|
-
const byScope = new Map();
|
|
122
|
-
for (const entry of BANNED_PROMPT_TOKENS) {
|
|
123
|
-
const list = byScope.get(entry.scope) ?? [];
|
|
124
|
-
list.push(entry);
|
|
125
|
-
byScope.set(entry.scope, list);
|
|
126
|
-
}
|
|
127
|
-
export function bannedTokensFor(scope) {
|
|
128
|
-
return byScope.get(scope) ?? [];
|
|
129
|
-
}
|
|
130
|
-
/** Regex cache — one word-boundary matcher per token, built once. */
|
|
4
|
+
import { SKILLS } from '../skills/content.js';
|
|
5
|
+
const bySkill = new Map();
|
|
6
|
+
for (const [skill, content] of Object.entries(SKILLS)) {
|
|
7
|
+
const fences = [...content.matchAll(/<!--\s*@banned:start\s*-->([\s\S]*?)<!--\s*@banned:end\s*-->/g)];
|
|
8
|
+
if (!fences.length)
|
|
9
|
+
continue;
|
|
10
|
+
const tokens = [...new Set(fences.flatMap((f) => [...f[1].replace(/<!--[\s\S]*?-->/g, '').matchAll(/`([^`\n]+)`/g)].map((m) => m[1].trim())))];
|
|
11
|
+
if (!tokens.length)
|
|
12
|
+
throw new Error(`${skill}: @banned block contains no tokens`);
|
|
13
|
+
const scope = /nano-banana|gpt-image|flux|seedream/.test(skill) ? 'image' : 'video';
|
|
14
|
+
bySkill.set(skill, tokens.map((token) => ({ token, skill, scope })));
|
|
15
|
+
}
|
|
16
|
+
/** Compatibility exports: there is no cross-model blacklist. */
|
|
17
|
+
export const BANNED_PROMPT_TOKENS = Object.freeze([]);
|
|
18
|
+
export function bannedTokensFor(_scope) { return BANNED_PROMPT_TOKENS; }
|
|
19
|
+
export function describeBannedTokens(_scope) { return ''; }
|
|
20
|
+
export function bannedTokensForSkill(skill) { return bySkill.get(skill) ?? []; }
|
|
131
21
|
const matchers = new Map();
|
|
132
22
|
function matcherFor(token) {
|
|
133
|
-
let
|
|
134
|
-
if (!
|
|
23
|
+
let matcher = matchers.get(token);
|
|
24
|
+
if (!matcher) {
|
|
135
25
|
const escaped = token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
// word character today; if one ever starts with punctuation, \b would
|
|
139
|
-
// anchor wrong — the load-time check below is what would catch it.
|
|
140
|
-
re = new RegExp(`\\b${escaped}\\b`, 'i');
|
|
141
|
-
matchers.set(token, re);
|
|
142
|
-
}
|
|
143
|
-
return re;
|
|
144
|
-
}
|
|
145
|
-
for (const { token, skill } of BANNED_PROMPT_TOKENS) {
|
|
146
|
-
if (!/^\w/.test(token) || !/\w$/.test(token)) {
|
|
147
|
-
throw new Error(`[banned-tokens] ${skill}: token ${JSON.stringify(token)} does not start and end with a word ` +
|
|
148
|
-
`character, so the \\b word-boundary match would never fire. Rewrite the entry or teach ` +
|
|
149
|
-
`matcherFor() the new shape.`);
|
|
26
|
+
matcher = new RegExp(`(?<!\\w)${escaped}(?!\\w)`, 'i');
|
|
27
|
+
matchers.set(token, matcher);
|
|
150
28
|
}
|
|
29
|
+
return matcher;
|
|
151
30
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
/** The never-use list a single skill declares. Empty for a skill with no block. */
|
|
155
|
-
export function bannedTokensForSkill(skill) {
|
|
156
|
-
return bySkill.get(skill) ?? [];
|
|
31
|
+
export function findBannedTokens(prompt, _scope, skill) {
|
|
32
|
+
return skill ? bannedTokensForSkill(skill).filter((b) => matcherFor(b.token).test(prompt)) : [];
|
|
157
33
|
}
|
|
158
|
-
/**
|
|
159
|
-
* A single model's never-use list, for the estimate RESULT.
|
|
160
|
-
*
|
|
161
|
-
* Deliberately NOT the cross-model list — that one already rides the op
|
|
162
|
-
* description on every call. This is the half that could not: the quirk that is
|
|
163
|
-
* true of Veo and false of Kling.
|
|
164
|
-
*/
|
|
165
34
|
export function describeBannedTokensForSkill(skill) {
|
|
166
35
|
const list = bannedTokensForSkill(skill);
|
|
167
|
-
|
|
168
|
-
return '';
|
|
169
|
-
return (`NEVER put these in a ${skill.replace('slates-prompting-', '')} prompt: ` +
|
|
170
|
-
list.map((b) => `"${b.token}"`).join(', ') +
|
|
171
|
-
`. Describe specifically instead (${skill}).`);
|
|
36
|
+
return list.length ? `Avoid these phrases for ${skill}: ${list.map((b) => `"${b.token}"`).join(', ')}. Describe the intended result specifically.` : '';
|
|
172
37
|
}
|
|
173
|
-
/** The tokens a submitted prompt actually contains. `skill` adds that model's
|
|
174
|
-
* own list to the modality-wide one — the two overlap for nano-banana-2 and
|
|
175
|
-
* seedance, so hits are deduplicated by token. */
|
|
176
|
-
export function findBannedTokens(prompt, scope, skill) {
|
|
177
|
-
const candidates = [...bannedTokensFor(scope), ...(skill ? bannedTokensForSkill(skill) : [])];
|
|
178
|
-
const seen = new Set();
|
|
179
|
-
const hits = [];
|
|
180
|
-
for (const b of candidates) {
|
|
181
|
-
if (seen.has(b.token))
|
|
182
|
-
continue;
|
|
183
|
-
seen.add(b.token);
|
|
184
|
-
if (matcherFor(b.token).test(prompt))
|
|
185
|
-
hits.push(b);
|
|
186
|
-
}
|
|
187
|
-
return hits;
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* The list as it appears INSIDE an op description — always in context, on both
|
|
191
|
-
* surfaces, with no call required to see it. Generated, never hand-typed.
|
|
192
|
-
*/
|
|
193
|
-
export function describeBannedTokens(scope) {
|
|
194
|
-
const list = bannedTokensFor(scope);
|
|
195
|
-
if (list.length === 0)
|
|
196
|
-
return '';
|
|
197
|
-
const skills = [...new Set(list.map((b) => b.skill))].join(' / ');
|
|
198
|
-
return (`NEVER put these in a prompt (they measurably degrade output — full rationale in ${skills}): ` +
|
|
199
|
-
list.map((b) => `"${b.token}"`).join(', ') +
|
|
200
|
-
`. Describe specifically instead.`);
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Non-blocking warning for a submitted prompt. Empty string when clean.
|
|
204
|
-
*
|
|
205
|
-
* Returned in the op RESULT — the one place the agent cannot avoid reading —
|
|
206
|
-
* rather than raised as an error. The generation proceeds either way: the
|
|
207
|
-
* sandbox doctrine says make state visible, never block.
|
|
208
|
-
*/
|
|
209
38
|
export function bannedTokenWarning(prompt, scope, skill) {
|
|
210
39
|
const hits = findBannedTokens(prompt, scope, skill);
|
|
211
|
-
if (hits.length
|
|
40
|
+
if (!hits.length)
|
|
212
41
|
return '';
|
|
213
|
-
|
|
214
|
-
return (`⚠️ PROMPT WARNING: your prompt contains ${hits.map((b) => `"${b.token}"`).join(', ')} — ` +
|
|
215
|
-
`on the never-use list in ${skills}. Not blocked, and this generation ran as submitted. ` +
|
|
216
|
-
`Load that guide with slates_get_prompting_guide and rewrite with specific description ` +
|
|
217
|
-
`(named lens, light direction, stock, composition) before the next generation.`);
|
|
42
|
+
return `⚠️ PROMPT WARNING: ${hits.map((b) => `"${b.token}"`).join(', ')} appear in ${skill}'s avoid list. This warning does not change or block your prompt. Describe the intended result specifically; query that guide for details.`;
|
|
218
43
|
}
|
|
219
44
|
//# sourceMappingURL=banned-tokens.js.map
|
|
@@ -57,7 +57,7 @@ function extractCard(skill, content) {
|
|
|
57
57
|
if (body.length > CRAFT_CARD_CEILING) {
|
|
58
58
|
throw new Error(`[craft-cards] ${skill}.md's @card block is ${body.length} chars, over the ` +
|
|
59
59
|
`${CRAFT_CARD_CEILING} ceiling. A card rides EVERY estimate result for that model — ` +
|
|
60
|
-
`move the overflow into the body of the skill,
|
|
60
|
+
`move the overflow into the body of the skill, retrieved with slates_get_prompting_guide depth: full.`);
|
|
61
61
|
}
|
|
62
62
|
return body;
|
|
63
63
|
}
|
|
@@ -77,6 +77,6 @@ export function describeCraftCard(skill) {
|
|
|
77
77
|
const card = craftCard(skill);
|
|
78
78
|
if (!card)
|
|
79
79
|
return '';
|
|
80
|
-
return `${card}\n\nFull guide (examples, failure modes, sources): slates_get_prompting_guide("${skill}").`;
|
|
80
|
+
return `${card}\n\nFull guide (examples, failure modes, sources): slates_get_prompting_guide({topic: "${skill}", depth: "full"}); use query for one section.`;
|
|
81
81
|
}
|
|
82
82
|
//# sourceMappingURL=craft-cards.js.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Product fan-out policy: one provider request and debit per output.
|
|
2
|
+
* This is NOT a model capability or provider batch limit. Desktop mirror is
|
|
3
|
+
* generated by slate/scripts/sync-generation-policy.mjs; never edit it there. */
|
|
4
|
+
export declare const IMAGE_QUANTITIES: readonly [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
5
|
+
export declare const MAX_IMAGE_VARIATIONS: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
|
|
6
|
+
export type ImageQuantity = typeof IMAGE_QUANTITIES[number];
|
|
7
|
+
export declare const clampImageQuantity: (count?: number) => ImageQuantity;
|
|
8
|
+
/** Default saved-recipe framing, shared by composer restore, quote and dispatch. */
|
|
9
|
+
export declare const DEFAULT_SHOT_ASPECT_RATIO: "16:9";
|
|
10
|
+
/**
|
|
11
|
+
* The built-in tools, and the seat each one renders on when its caller names no
|
|
12
|
+
* model. THE ONE HOME, for both repos: the desktop reads it through its generated
|
|
13
|
+
* mirror (`toolModel` in slate/src/shared/pricing.ts), this package reads it for
|
|
14
|
+
* the op descriptions and the generated skill partial.
|
|
15
|
+
*
|
|
16
|
+
* `'default-image'` means "follow the app's default image model"
|
|
17
|
+
* (`defaultModelFor('image')` in model-facts.ts), so promoting a better image
|
|
18
|
+
* model moves these tools with it and nothing else is edited. A model id pins a
|
|
19
|
+
* tool instead, for a reason recorded beside it. This file stays a
|
|
20
|
+
* dependency-free leaf (it is copied verbatim into the desktop), which is why
|
|
21
|
+
* the seat is a token each consumer resolves rather than a call made here.
|
|
22
|
+
*
|
|
23
|
+
* Before 2026-09-21 the sheet tools' model was typed at every use on the desktop
|
|
24
|
+
* and twice more here (an op description and a skill), so three screens quoted
|
|
25
|
+
* a sheet from one typed id while the handler billed from another.
|
|
26
|
+
*/
|
|
27
|
+
export declare const BUILT_IN_TOOLS: readonly ["character-sheet", "environment-plate", "grid-extract", "image-edit"];
|
|
28
|
+
export type BuiltInTool = typeof BUILT_IN_TOOLS[number];
|
|
29
|
+
export declare const DEFAULT_IMAGE_SEAT: "default-image";
|
|
30
|
+
export declare const TOOL_SEAT: Record<BuiltInTool, typeof DEFAULT_IMAGE_SEAT | string>;
|
|
31
|
+
/** Both sheet tools frame one wide image. On GPT Image the aspect is part of the price. */
|
|
32
|
+
export declare const SHEET_TOOL_ASPECT_RATIO: "16:9";
|
|
33
|
+
/**
|
|
34
|
+
* The GPT Image tier a sheet tool pins. It read 'medium' until 2026-09-09: GPT
|
|
35
|
+
* Image 2.5 renamed the ladder, so the tier GPT Image 2 called `medium` is now
|
|
36
|
+
* `high`, and a stored 'medium' would have kept compiling while naming a
|
|
37
|
+
* materially cheaper tier. Sheets are the identity lane (every downstream shot
|
|
38
|
+
* inherits this frame's likeness), so this is deliberately not the draft tier.
|
|
39
|
+
*/
|
|
40
|
+
export declare const SHEET_TOOL_GPT_QUALITY: "high";
|
|
41
|
+
//# sourceMappingURL=generation-policy.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** Product fan-out policy: one provider request and debit per output.
|
|
2
|
+
* This is NOT a model capability or provider batch limit. Desktop mirror is
|
|
3
|
+
* generated by slate/scripts/sync-generation-policy.mjs; never edit it there. */
|
|
4
|
+
export const IMAGE_QUANTITIES = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
5
|
+
export const MAX_IMAGE_VARIATIONS = IMAGE_QUANTITIES[IMAGE_QUANTITIES.length - 1];
|
|
6
|
+
export const clampImageQuantity = (count = 1) => Math.max(1, Math.min(MAX_IMAGE_VARIATIONS, Number.isFinite(count) ? Math.floor(count) : 1));
|
|
7
|
+
/** Default saved-recipe framing, shared by composer restore, quote and dispatch. */
|
|
8
|
+
export const DEFAULT_SHOT_ASPECT_RATIO = '16:9';
|
|
9
|
+
/**
|
|
10
|
+
* The built-in tools, and the seat each one renders on when its caller names no
|
|
11
|
+
* model. THE ONE HOME, for both repos: the desktop reads it through its generated
|
|
12
|
+
* mirror (`toolModel` in slate/src/shared/pricing.ts), this package reads it for
|
|
13
|
+
* the op descriptions and the generated skill partial.
|
|
14
|
+
*
|
|
15
|
+
* `'default-image'` means "follow the app's default image model"
|
|
16
|
+
* (`defaultModelFor('image')` in model-facts.ts), so promoting a better image
|
|
17
|
+
* model moves these tools with it and nothing else is edited. A model id pins a
|
|
18
|
+
* tool instead, for a reason recorded beside it. This file stays a
|
|
19
|
+
* dependency-free leaf (it is copied verbatim into the desktop), which is why
|
|
20
|
+
* the seat is a token each consumer resolves rather than a call made here.
|
|
21
|
+
*
|
|
22
|
+
* Before 2026-09-21 the sheet tools' model was typed at every use on the desktop
|
|
23
|
+
* and twice more here (an op description and a skill), so three screens quoted
|
|
24
|
+
* a sheet from one typed id while the handler billed from another.
|
|
25
|
+
*/
|
|
26
|
+
export const BUILT_IN_TOOLS = ['character-sheet', 'environment-plate', 'grid-extract', 'image-edit'];
|
|
27
|
+
export const DEFAULT_IMAGE_SEAT = 'default-image';
|
|
28
|
+
export const TOOL_SEAT = {
|
|
29
|
+
'character-sheet': DEFAULT_IMAGE_SEAT,
|
|
30
|
+
'environment-plate': DEFAULT_IMAGE_SEAT,
|
|
31
|
+
// Pinned: the image viewer offers grid extraction a fixed short list of seats,
|
|
32
|
+
// and the cell-extraction prompt has a variant per seat family. Moving it is a
|
|
33
|
+
// picker and a prompt change first.
|
|
34
|
+
'grid-extract': 'nano-banana-2',
|
|
35
|
+
// Follows the default image model (2026-09-29). The viewer's Edit box lists
|
|
36
|
+
// every image model from the registry, and the edit handler sends references
|
|
37
|
+
// to each one up to its own cap, so nothing ties this tool to one family. Until
|
|
38
|
+
// then it was pinned to Nano Banana 2 while the app's default was GPT Image 2.5
|
|
39
|
+
// Sunburst, so Edit and the prompt bar opened on different models for the
|
|
40
|
+
// same picture.
|
|
41
|
+
'image-edit': DEFAULT_IMAGE_SEAT,
|
|
42
|
+
};
|
|
43
|
+
/** Both sheet tools frame one wide image. On GPT Image the aspect is part of the price. */
|
|
44
|
+
export const SHEET_TOOL_ASPECT_RATIO = '16:9';
|
|
45
|
+
/**
|
|
46
|
+
* The GPT Image tier a sheet tool pins. It read 'medium' until 2026-09-09: GPT
|
|
47
|
+
* Image 2.5 renamed the ladder, so the tier GPT Image 2 called `medium` is now
|
|
48
|
+
* `high`, and a stored 'medium' would have kept compiling while naming a
|
|
49
|
+
* materially cheaper tier. Sheets are the identity lane (every downstream shot
|
|
50
|
+
* inherits this frame's likeness), so this is deliberately not the draft tier.
|
|
51
|
+
*/
|
|
52
|
+
export const SHEET_TOOL_GPT_QUALITY = 'high';
|
|
53
|
+
//# sourceMappingURL=generation-policy.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type GuideDepth = 'card' | 'index' | 'section' | 'full';
|
|
2
|
+
/** Parse headings outside code fences; maintainer comments never reach agents. */
|
|
3
|
+
export declare function guideSections(content: string): Array<{
|
|
4
|
+
title: string;
|
|
5
|
+
body: string;
|
|
6
|
+
}>;
|
|
7
|
+
/** Bounded retrieval. A missing card never silently expands to the full guide. */
|
|
8
|
+
export declare function retrieveGuide(skill: string, content: string, depth: GuideDepth, query?: string): string;
|
|
9
|
+
//# sourceMappingURL=guide-retrieval.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { craftCard } from './craft-cards.js';
|
|
2
|
+
/** Parse headings outside code fences; maintainer comments never reach agents. */
|
|
3
|
+
export function guideSections(content) {
|
|
4
|
+
const clean = content.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, '').replace(/<!--[\s\S]*?-->/g, '').trim();
|
|
5
|
+
const sections = [];
|
|
6
|
+
let current = { title: 'Overview', body: '' };
|
|
7
|
+
let fenced = false;
|
|
8
|
+
for (const line of clean.split(/\r?\n/)) {
|
|
9
|
+
if (/^\s*```/.test(line))
|
|
10
|
+
fenced = !fenced;
|
|
11
|
+
if (!fenced && /^#{1,3} /.test(line)) {
|
|
12
|
+
if (current.body.trim())
|
|
13
|
+
sections.push(current);
|
|
14
|
+
current = { title: line.replace(/^#+ /, ''), body: line };
|
|
15
|
+
}
|
|
16
|
+
else
|
|
17
|
+
current.body += '\n' + line;
|
|
18
|
+
}
|
|
19
|
+
if (current.body.trim())
|
|
20
|
+
sections.push(current);
|
|
21
|
+
return sections;
|
|
22
|
+
}
|
|
23
|
+
/** Bounded retrieval. A missing card never silently expands to the full guide. */
|
|
24
|
+
export function retrieveGuide(skill, content, depth, query) {
|
|
25
|
+
const sections = guideSections(content);
|
|
26
|
+
const index = sections.map((s) => `- ${s.title}`).join('\n');
|
|
27
|
+
if (depth === 'full')
|
|
28
|
+
return sections.map((s) => s.body.trim()).join('\n\n');
|
|
29
|
+
if (depth === 'index')
|
|
30
|
+
return index;
|
|
31
|
+
if (query?.trim()) {
|
|
32
|
+
const q = query.trim().toLowerCase();
|
|
33
|
+
// Exact technique IDs return one complete row with its source heading.
|
|
34
|
+
for (const s of sections) {
|
|
35
|
+
const row = s.body.split('\n').find((line) => line.toLowerCase().startsWith(`| \`${q}\` |`));
|
|
36
|
+
if (row)
|
|
37
|
+
return `${s.title}\n\n| Technique | Evidence | What it does | Reach for · skip | Say |\n|---|---|---|---|---|\n${row}`;
|
|
38
|
+
}
|
|
39
|
+
const words = q.split(/[^\p{L}\p{N}-]+/u).filter(Boolean);
|
|
40
|
+
const ranked = sections.map((s) => ({ s, score: words.reduce((n, w) => n + (s.title.toLowerCase().includes(w) ? 4 : s.body.toLowerCase().includes(w) ? 1 : 0), 0) }))
|
|
41
|
+
.filter((x) => x.score > 0).sort((a, b) => b.score - a.score);
|
|
42
|
+
if (!ranked.length)
|
|
43
|
+
return `No section matches "${query}". Available sections:\n${index}`;
|
|
44
|
+
const body = ranked[0].s.body.trim();
|
|
45
|
+
if (body.length <= 6000)
|
|
46
|
+
return body;
|
|
47
|
+
// Never cut a table row or a worked prompt mid-sentence.
|
|
48
|
+
return `Section "${ranked[0].s.title}" is too large for a selective response. Use a technique ID or depth "full".\n${index}`;
|
|
49
|
+
}
|
|
50
|
+
const card = craftCard(skill);
|
|
51
|
+
return `${card ?? sections[0]?.body.trim().slice(0, 1600) ?? 'No overview available.'}\n\nSections (request with query, or depth "full"):\n${index}`;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=guide-retrieval.js.map
|
package/dist/prompts/index.d.ts
CHANGED
package/dist/prompts/index.js
CHANGED
|
@@ -28,4 +28,5 @@ export * from './prompting-tips.js';
|
|
|
28
28
|
// gallery. Also its own leaf subpath (`@slatesvideo/shared/asset-label`) for the
|
|
29
29
|
// renderer, which cannot import the root barrel.
|
|
30
30
|
export * from './asset-label.js';
|
|
31
|
+
export * from './generation-policy.js';
|
|
31
32
|
//# sourceMappingURL=index.js.map
|
|
@@ -47,6 +47,14 @@ export interface VideoResolutionCapability {
|
|
|
47
47
|
fixed?: VideoResolution;
|
|
48
48
|
/** Default when this model is chosen (falls back to `options[0]`). */
|
|
49
49
|
default?: VideoResolution;
|
|
50
|
+
/**
|
|
51
|
+
* Tiers the provider makes by UPSCALING a smaller native render, keyed to that
|
|
52
|
+
* render. They stay selectable; every surface that offers one says so and says
|
|
53
|
+
* we do not recommend it (Eric, 2026-09-30: "more expensive and they actually
|
|
54
|
+
* look worse"). A refinement pass the provider documents as its own stage, such
|
|
55
|
+
* as H3 Max's 1080p, is not an upscale and is not listed.
|
|
56
|
+
*/
|
|
57
|
+
upscaledFrom?: Partial<Record<VideoResolution, VideoResolution>>;
|
|
50
58
|
}
|
|
51
59
|
/** Everything a model will ACCEPT. Capability only — never a price. */
|
|
52
60
|
/**
|
|
@@ -86,6 +94,7 @@ export interface VoiceCloneCapability {
|
|
|
86
94
|
}
|
|
87
95
|
export declare const GPT_QUALITY_TIERS: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
88
96
|
export type GptQuality = (typeof GPT_QUALITY_TIERS)[number];
|
|
97
|
+
export declare const DEFAULT_GPT_QUALITY: GptQuality;
|
|
89
98
|
export declare const GPT_BACKGROUNDS: readonly ["auto", "transparent", "opaque"];
|
|
90
99
|
export type GptBackground = (typeof GPT_BACKGROUNDS)[number];
|
|
91
100
|
export type ImageResolution = '1k' | '2k' | '3k' | '4k';
|
|
@@ -128,6 +137,8 @@ export declare function falImageSize(model: string, aspectRatio?: string, resolu
|
|
|
128
137
|
};
|
|
129
138
|
export interface ModelCapability {
|
|
130
139
|
imageResolutions?: ImageResolution[];
|
|
140
|
+
/** Product default shared by estimates and generation. */
|
|
141
|
+
defaultImageResolution?: ImageResolution;
|
|
131
142
|
/**
|
|
132
143
|
* Images ONE request may ask the provider for in a single batch.
|
|
133
144
|
*
|
|
@@ -204,6 +215,8 @@ export declare function voiceCloneFor(model: string): VoiceCloneCapability | und
|
|
|
204
215
|
/** Aspect ratios a model accepts, honouring the provider override. */
|
|
205
216
|
export declare function aspectRatiosFor(model: string, provider?: string): AspectRatio[];
|
|
206
217
|
/** Video resolutions a model accepts. A FIXED model reports exactly its one value. */
|
|
218
|
+
/** The native render an upscaled tier is made from, or undefined for a native tier. */
|
|
219
|
+
export declare function upscaledFrom(model: string, resolution: string | undefined): VideoResolution | undefined;
|
|
207
220
|
export declare function videoResolutionsFor(model: string): VideoResolution[];
|
|
208
221
|
/** The resolution a model would actually run at. Fixed wins; else keep a legal
|
|
209
222
|
* current value; else the model's own default. Mirrors `clampVideoResolution`. */
|
|
@@ -250,7 +263,9 @@ export declare function describeDurations(models: readonly string[]): string;
|
|
|
250
263
|
export declare function describeReferenceImageCaps(models: readonly string[]): string;
|
|
251
264
|
/** H3 Max reference accounting, fal's worked tables read 2026-09-09.
|
|
252
265
|
* https://fal.ai/models/minimax/h3-max/reference-to-video
|
|
253
|
-
* 1080p
|
|
266
|
+
* 1080p: fal's page, read 2026-09-29: "768p and 1080p output use the same
|
|
267
|
+
* reference-video token counts." So 1080p takes the 768p figure; it was absent
|
|
268
|
+
* until fal published that sentence, and must never be inferred from output rates.
|
|
254
269
|
*/
|
|
255
270
|
export declare const MINIMAX_MAX_REFERENCE: {
|
|
256
271
|
readonly freeTokens: 4096;
|
|
@@ -265,4 +280,6 @@ export declare function minimaxMaxReferenceTokens(input: {
|
|
|
265
280
|
audioSeconds: number;
|
|
266
281
|
resolution: string;
|
|
267
282
|
}): number;
|
|
283
|
+
/** Quotes and generation use the same model default. */
|
|
284
|
+
export declare function defaultImageResolutionFor(model: string): ImageResolution;
|
|
268
285
|
//# sourceMappingURL=model-capabilities.d.ts.map
|