@slatesvideo/shared 0.6.2 → 0.6.3
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 +3 -1
- package/dist/index.js +26 -0
- package/dist/operations/index.d.ts +1 -1
- package/dist/operations/index.js +211 -24
- package/dist/prompts/agent-doctrine.d.ts +36 -0
- package/dist/prompts/agent-doctrine.js +194 -0
- package/dist/prompts/banned-tokens.d.ts +28 -0
- package/dist/prompts/banned-tokens.js +152 -0
- package/dist/prompts/model-capabilities.d.ts +13 -1
- package/dist/prompts/model-capabilities.js +97 -2
- package/dist/prompts/model-facts.d.ts +20 -0
- package/dist/prompts/model-facts.js +87 -23
- package/dist/prompts/prompting-tips.d.ts +1 -1
- package/dist/prompts/prompting-tips.js +65 -0
- package/dist/skills/content.js +3 -2
- package/exports/slates-prompt-builder/generated/SKILL.md +3 -3
- package/exports/slates-prompt-builder/generated/reference-seedance.md +2 -1
- package/exports/slates-prompt-builder/generated/slates-prompt-builder-manifest.json +8 -8
- package/exports/slates-prompt-builder/generated/slates-prompt-builder.skill +0 -0
- package/package.json +1 -1
- package/skills/slates-prompting-ltx-2-5.md +180 -0
- package/skills/slates-prompting-nano-banana-2.md +10 -0
- package/skills/slates-prompting-seedance.md +10 -1
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,9 @@ export { SlatesDesktopClient, type DesktopHealth } from './clients/desktop.js';
|
|
|
4
4
|
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
|
-
export { MODEL_FACTS, getModelFact, multimodalRefSummary, multimodalRefModels, SEEDANCE_TASK_INTENT_WORDS, seedanceTaskIntentWords, type ModelFact, } from './prompts/model-facts.js';
|
|
7
|
+
export { MODEL_FACTS, getModelFact, multimodalRefSummary, multimodalRefModels, describeRouting, SEEDANCE_TASK_INTENT_WORDS, seedanceTaskIntentWords, type ModelFact, } from './prompts/model-facts.js';
|
|
8
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';
|
|
9
|
+
export { buildAgentDoctrine, type AgentSurface } from './prompts/agent-doctrine.js';
|
|
10
|
+
export { BANNED_PROMPT_TOKENS, describeBannedTokens, findBannedTokens, bannedTokenWarning, type BannedToken, type BannedTokenScope, } from './prompts/banned-tokens.js';
|
|
9
11
|
export { PROMPTING_TIPS, getPromptingTips, type PromptingTipsEntry, type PromptingTipCard, type PromptingTipsKey } from './prompts/prompting-tips.js';
|
|
10
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,9 @@ export { ALL_OPERATIONS, VIDEO_MODELS, AUDIO_MODELS, defaultContext } from './op
|
|
|
8
8
|
// prompt derives its MODEL ROUTING doctrine from (kind: image | video | audio,
|
|
9
9
|
// default/premium/niche notes). Edit model-facts.ts, never prose copies.
|
|
10
10
|
export { MODEL_FACTS, getModelFact, multimodalRefSummary, multimodalRefModels,
|
|
11
|
+
// THE routing renderer — the system prompt, the MCP instructions and the
|
|
12
|
+
// generate ops all call this one function, so routing prose exists once.
|
|
13
|
+
describeRouting,
|
|
11
14
|
// Mirrored in slate/src/shared/pricing.ts — see the constant's own header for
|
|
12
15
|
// why the mirror exists and why it must never drive a prompt rewrite.
|
|
13
16
|
SEEDANCE_TASK_INTENT_WORDS, seedanceTaskIntentWords, } from './prompts/model-facts.js';
|
|
@@ -17,6 +20,29 @@ SEEDANCE_TASK_INTENT_WORDS, seedanceTaskIntentWords, } from './prompts/model-fac
|
|
|
17
20
|
// the op surface validates against them and GENERATES its `.describe()` prose
|
|
18
21
|
// from them. Never hand-type a capability fact an LLM will read.
|
|
19
22
|
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';
|
|
23
|
+
// 🚨 AGENT GUIDANCE SSOT — the working method, the hard rules and the guide
|
|
24
|
+
// index, as ONE string both surfaces consume: the desktop Studio Agent's whole
|
|
25
|
+
// system prompt (slate/src/main/studio-agent/context.ts) and the MCP server's
|
|
26
|
+
// `instructions`. Neither may author doctrine prose of its own; the guidance
|
|
27
|
+
// layer drifted for exactly as long as it had no single home.
|
|
28
|
+
//
|
|
29
|
+
// Exported from the ROOT barrel only, never from ./prompts — that subpath is
|
|
30
|
+
// bundled by the desktop RENDERER and must stay small and Node-free, and this
|
|
31
|
+
// module pulls in the whole embedded SKILLS record.
|
|
32
|
+
// Only what a CONSUMER calls. `buildSkillIndex`, `WORKING_METHOD` and
|
|
33
|
+
// `HARD_RULES` are used inside agent-doctrine.ts and by nothing else, so they
|
|
34
|
+
// stay off the public surface — an export nothing calls is an export nothing
|
|
35
|
+
// keeps honest, which is why `buildModelRouting` was deleted rather than left
|
|
36
|
+
// here "in case".
|
|
37
|
+
export { buildAgentDoctrine } from './prompts/agent-doctrine.js';
|
|
38
|
+
// Banned prompt tokens — EXTRACTED from the skills' own never-use lists and
|
|
39
|
+
// inlined into the generate ops' descriptions. Enforcement of "load the guide"
|
|
40
|
+
// that the model cannot skip, because a description is always in context.
|
|
41
|
+
export {
|
|
42
|
+
// BANNED_PROMPT_TOKENS + describeBannedTokens: the lockstep checker and the
|
|
43
|
+
// ops. findBannedTokens: the eval harness scorer. bannedTokenWarning: the ops.
|
|
44
|
+
// `bannedTokensFor` is internal to the module and stays there.
|
|
45
|
+
BANNED_PROMPT_TOKENS, describeBannedTokens, findBannedTokens, bannedTokenWarning, } from './prompts/banned-tokens.js';
|
|
20
46
|
// Per-model prompting tips — the SSOT for the desktop "See prompting tips"
|
|
21
47
|
// modals. The desktop renders these; it never hand-writes tips content.
|
|
22
48
|
export { PROMPTING_TIPS, getPromptingTips } from './prompts/prompting-tips.js';
|
|
@@ -29,7 +29,7 @@ export declare const getCreditBalance: Operation<Record<string, never>>;
|
|
|
29
29
|
export declare const listAvailableModels: Operation<{
|
|
30
30
|
filter?: string;
|
|
31
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", "minimax-h3", "minimax-h3-max"];
|
|
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", "minimax-h3", "minimax-h3-max", "ltx-2-5", "ltx-2-5-pro"];
|
|
33
33
|
type VideoModel = (typeof VIDEO_MODELS)[number];
|
|
34
34
|
/** The exact `model` ids `slates_edit_video` accepts. Edit rows are deliberately
|
|
35
35
|
* NOT in VIDEO_MODELS — they take a source clip, not frames. */
|
package/dist/operations/index.js
CHANGED
|
@@ -16,7 +16,12 @@ import { BlenderBridgeClient, BLENDER_SETUP_HINT, RENDER_TIMEOUT_MS } from '../c
|
|
|
16
16
|
import { SKILLS } from '../skills/content.js';
|
|
17
17
|
// Reference-capacity prose is DERIVED, never hand-typed — root CLAUDE.md:
|
|
18
18
|
// "never hand-type a fact an LLM will read". These helpers read MODEL_FACTS.
|
|
19
|
-
import { multimodalRefSummary, multimodalRefModels, seedanceTaskIntentWords
|
|
19
|
+
import { multimodalRefSummary, multimodalRefModels, seedanceTaskIntentWords,
|
|
20
|
+
// 🚨 THE routing renderer. Routing prose is GENERATED here, never typed:
|
|
21
|
+
// slates-mcp/CLAUDE.md forbids restating it in an op description, and this
|
|
22
|
+
// file did it anyway for 1,282 characters that repeated MODEL_FACTS phrase
|
|
23
|
+
// for phrase. Edit model-facts.ts; both surfaces follow.
|
|
24
|
+
describeRouting, } from '../prompts/model-facts.js';
|
|
20
25
|
// 🚨 MODEL CAPABILITY SSOT. Aspect ratios, video resolutions and durations are
|
|
21
26
|
// VALIDATED against and DESCRIBED from `MODEL_CAPABILITIES` — this file must
|
|
22
27
|
// never re-state one of those constraints as a literal enum or a sentence
|
|
@@ -27,6 +32,13 @@ import { multimodalRefSummary, multimodalRefModels, seedanceTaskIntentWords } fr
|
|
|
27
32
|
// 1080p. A customer burned a round trip on `4:5` + Seedance: accepted here,
|
|
28
33
|
// queued, credits reserved, rejected by the provider asynchronously.
|
|
29
34
|
import { AGENT_ROUTE_PROVIDER, aspectRatioUnion, videoResolutionUnion, durationBounds, aspectRatiosFor, getModelCapability, defaultVideoResolutionFor, checkAspectRatio, checkVideoResolution, checkDuration, describeAspectRatios, describeVideoResolutions, describeDurations, describeReferenceImageCaps, } from '../prompts/model-capabilities.js';
|
|
35
|
+
// 🚨 "LOAD THE GUIDE" MADE STRUCTURAL. The never-use token lists are EXTRACTED
|
|
36
|
+
// from the skill files (between `@banned` markers) and inlined into the two
|
|
37
|
+
// generate ops' descriptions, which are always in context on both surfaces —
|
|
38
|
+
// no call to skip, no discretion. `bannedTokenWarning` then reports what the
|
|
39
|
+
// submitted prompt actually contained, in the result, without blocking it.
|
|
40
|
+
// Never hand-type one of these tokens here; edit the skill.
|
|
41
|
+
import { describeBannedTokens, bannedTokenWarning } from '../prompts/banned-tokens.js';
|
|
30
42
|
export function defaultContext() {
|
|
31
43
|
return {
|
|
32
44
|
cloud: () => new SlatesCloudClient(),
|
|
@@ -80,6 +92,30 @@ function creditsFromDollars(dollars) {
|
|
|
80
92
|
// Shared describe-text for the background flag on every generate_* op.
|
|
81
93
|
const BACKGROUND_DESCRIBE = 'Submit and return immediately with generationId(s) instead of blocking until the file is saved. ' +
|
|
82
94
|
'Poll with slates_get_generation_status. Recommended for video (1-5 min renders).';
|
|
95
|
+
// ── Vision QC pointers (the "quality-check with vision" rule, made structural) ──
|
|
96
|
+
//
|
|
97
|
+
// QUALITY-CHECK is a POST-condition, so it cannot be gated the way a
|
|
98
|
+
// pre-condition can. What it CAN have is a result the agent cannot avoid
|
|
99
|
+
// reading, naming the exact op. Deliberately NOT an auto-fetch: that would
|
|
100
|
+
// spend vision tokens on every generation whether review was wanted or not,
|
|
101
|
+
// and the sandbox doctrine says the tool is available, not mandatory.
|
|
102
|
+
//
|
|
103
|
+
// ⚠️ These three differ because what the agent already HAS differs, and telling
|
|
104
|
+
// it to re-fetch something already in front of it burns a turn for nothing:
|
|
105
|
+
// a blocking image generation returns the pixels inline, a video generation
|
|
106
|
+
// returns none, and a background submission has no asset yet.
|
|
107
|
+
const IMAGE_INLINE_REVIEW = 'The image is attached to this result — look at it against the brief before you describe it. ' +
|
|
108
|
+
'Any claim about how it LOOKS must come from pixels you actually received (slates-vision-feedback-loop).';
|
|
109
|
+
const VIDEO_REVIEW_POINTER = 'You have NOT seen this clip: call slates_get_asset_video_frames on the asset id above before ' +
|
|
110
|
+
'describing how it looks. A quality claim you cannot point to a tool result for is a REAL NUMBERS ONLY violation.';
|
|
111
|
+
const BACKGROUND_REVIEW_POINTER = 'When it completes, look at it before you describe it — slates_get_asset_image for images, ' +
|
|
112
|
+
'slates_get_asset_video_frames for video.';
|
|
113
|
+
// The image saved, but reading it back off disk failed (best-effort fetch). The
|
|
114
|
+
// agent has an asset and NO pixels, which is the one state where a quality
|
|
115
|
+
// claim would be pure invention — so this branch has to say so rather than
|
|
116
|
+
// fall through to no pointer at all.
|
|
117
|
+
const IMAGE_FETCH_POINTER = 'The pixels could not be attached to this result: call slates_get_asset_image on the asset id ' +
|
|
118
|
+
'above before describing how it looks.';
|
|
83
119
|
// Early-return shape when a generation route accepted the job in background
|
|
84
120
|
// mode ({ background: true } in the response). No inline-image fetch — the
|
|
85
121
|
// asset doesn't exist yet; the poller delivers it on completion.
|
|
@@ -88,7 +124,8 @@ function backgroundSubmitted(kind, ids, extra, note) {
|
|
|
88
124
|
return {
|
|
89
125
|
text: `Submitted ${kind} in the background — generationId(s): ${idText}. ` +
|
|
90
126
|
`Call slates_get_generation_status with waitSeconds: 45 (it long-polls and returns on completion — ` +
|
|
91
|
-
`never a rapid loop; video renders commonly take 1-5 minutes). Generations survive app restarts
|
|
127
|
+
`never a rapid loop; video renders commonly take 1-5 minutes). Generations survive app restarts. ` +
|
|
128
|
+
BACKGROUND_REVIEW_POINTER +
|
|
92
129
|
(note ? ` ${note}` : ''),
|
|
93
130
|
data: { generationIds: ids, status: 'processing', ...extra },
|
|
94
131
|
};
|
|
@@ -191,6 +228,21 @@ export const VIDEO_MODELS = [
|
|
|
191
228
|
// the Max row at base rates and offers it 2K/4K it cannot render.
|
|
192
229
|
'minimax-h3',
|
|
193
230
|
'minimax-h3-max',
|
|
231
|
+
// LTX-2.5, two seats in one family (2026-08-29). Base is the VOLUME seat —
|
|
232
|
+
// cheapest native 1080p second we sell, free native audio at every tier, the
|
|
233
|
+
// only row reaching 1440p, and the longest clips in the catalogue (20s).
|
|
234
|
+
// Pro is the fidelity seat and is NOT a superset: shorter ladder (no
|
|
235
|
+
// 1440p/4K), shorter clips (6/8/10 only) and ~1/3 dearer.
|
|
236
|
+
//
|
|
237
|
+
// NEVER PREFIX-MATCH: 'ltx-2-5-pro' starts with 'ltx-2-5'. A prefix test
|
|
238
|
+
// bills Pro at base rates AND offers it 1440p/4K and 12-20s durations it
|
|
239
|
+
// cannot render — the same trap as the MiniMax pair, one row worse.
|
|
240
|
+
//
|
|
241
|
+
// Durations are DISCRETE AND EVEN (6,8,10,12,14,16,18,20); the union bounds
|
|
242
|
+
// below stay 3-30 because other rows are wider, so `assertVideoCapabilities`
|
|
243
|
+
// is what refuses an odd second. It reads `values`, not just min/max.
|
|
244
|
+
'ltx-2-5',
|
|
245
|
+
'ltx-2-5-pro',
|
|
194
246
|
];
|
|
195
247
|
// ── Capability-derived param vocabulary + guard ─────────────────
|
|
196
248
|
//
|
|
@@ -231,6 +283,17 @@ const VIDEO_RESOLUTION_VOCAB = VIDEO_RESOLUTIONS;
|
|
|
231
283
|
const MINIMAX_MODELS = new Set(['minimax-h3', 'minimax-h3-max']);
|
|
232
284
|
/** Reference images fal does not charge for. */
|
|
233
285
|
const MINIMAX_FREE_REF_IMAGES = 5;
|
|
286
|
+
/**
|
|
287
|
+
* The LTX-2.5 pair. A SET, not a prefix test — `ltx-2-5-pro` starts with
|
|
288
|
+
* `ltx-2-5`, and the two rows differ on ladder, duration list AND price.
|
|
289
|
+
*
|
|
290
|
+
* Their cost key is the plainest shape in the file — `{model}-{res}-{N}s` with
|
|
291
|
+
* no suffix ever, because LTX has no paid option: native audio is included at
|
|
292
|
+
* every tier (so no `-audio` variant like Kling) and there is no reference
|
|
293
|
+
* endpoint at all (so no `-ref{K}` variant like H3). Mirrors `ltxCreditKey()`
|
|
294
|
+
* in slate/src/shared/pricing.ts.
|
|
295
|
+
*/
|
|
296
|
+
const LTX_MODELS = new Set(['ltx-2-5', 'ltx-2-5-pro']);
|
|
234
297
|
/** K for the `-ref{K}` suffix: images past the free five, capped by the model's
|
|
235
298
|
* own declared ceiling. 0 for h3-max (no reference transport) and for anything
|
|
236
299
|
* that is not a MiniMax row. Mirrors refImageSurchargeCount() in
|
|
@@ -1077,7 +1140,17 @@ function imageCostKey(model, resolution, quality = 'medium') {
|
|
|
1077
1140
|
}
|
|
1078
1141
|
export const generateImage = {
|
|
1079
1142
|
id: 'slates_generate_image',
|
|
1080
|
-
description: 'Generate an image via Slates credits
|
|
1143
|
+
description: 'Generate an image via Slates credits.\n' +
|
|
1144
|
+
// GENERATED from MODEL_FACTS — the hand-typed model list that stood here
|
|
1145
|
+
// was a third copy of the routing doctrine, and it had already gone stale
|
|
1146
|
+
// (it still described nano-banana-2-lite by a capability the param owns).
|
|
1147
|
+
`${describeRouting('image')}\n` +
|
|
1148
|
+
'Full table: the slates-model-selection skill. ' +
|
|
1149
|
+
'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. ' +
|
|
1150
|
+
// GENERATED from the skill file's own never-use list -- the one piece of
|
|
1151
|
+
// prompting doctrine that is ALWAYS in context, because the agent has
|
|
1152
|
+
// demonstrably skipped the call that would have taught it.
|
|
1153
|
+
describeBannedTokens('image'),
|
|
1081
1154
|
input: z.object({
|
|
1082
1155
|
prompt: z.string().min(1).max(4000),
|
|
1083
1156
|
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.'),
|
|
@@ -1096,6 +1169,10 @@ export const generateImage = {
|
|
|
1096
1169
|
// Mirrors the cost confirm gate — defaults silently wasted credits
|
|
1097
1170
|
// (1:1 when user wanted 16:9, 4k when 1k would have done). The LLM
|
|
1098
1171
|
// is forced to ask the user or read the skill instead of guessing.
|
|
1172
|
+
// Non-blocking prompt hygiene. Computed once, reported on every exit path
|
|
1173
|
+
// that echoes a prompt -- the clarification and confirm gates are PRE-spend,
|
|
1174
|
+
// which is where a rewrite is still free.
|
|
1175
|
+
const promptWarning = bannedTokenWarning(input.prompt, 'image');
|
|
1099
1176
|
if (!input.aspectRatio || !input.resolution) {
|
|
1100
1177
|
const missing = [];
|
|
1101
1178
|
if (!input.aspectRatio)
|
|
@@ -1105,7 +1182,9 @@ export const generateImage = {
|
|
|
1105
1182
|
return ok({
|
|
1106
1183
|
requires_clarification: true,
|
|
1107
1184
|
missing,
|
|
1108
|
-
|
|
1185
|
+
...(promptWarning ? { prompt_warning: promptWarning } : {}),
|
|
1186
|
+
message: (promptWarning ? `${promptWarning}\n\n` : '') +
|
|
1187
|
+
`Missing required field(s): ${missing.join(', ')}. ` +
|
|
1109
1188
|
`Read the slates-prompting-nano-banana-2 + slates-cost-discipline skills, ` +
|
|
1110
1189
|
// Generated from MODEL_CAPABILITIES — never retype a ratio list.
|
|
1111
1190
|
`or ask the user. Aspect ratio by model: ${describeAspectRatios(IMAGE_MODELS)}. ` +
|
|
@@ -1189,7 +1268,9 @@ export const generateImage = {
|
|
|
1189
1268
|
model: costKey,
|
|
1190
1269
|
estimated_cents: totalCents,
|
|
1191
1270
|
estimated_credits: totalCents,
|
|
1192
|
-
|
|
1271
|
+
...(promptWarning ? { prompt_warning: promptWarning } : {}),
|
|
1272
|
+
message: (promptWarning ? `${promptWarning}\n\n` : '') +
|
|
1273
|
+
`Cost exceeds ${CONFIRM_CREDITS} credits. Re-call with confirm=true to proceed, or pick a smaller resolution / count.`,
|
|
1193
1274
|
});
|
|
1194
1275
|
}
|
|
1195
1276
|
const previews = await previewAssets(ctx, referenceAssetIds.map((id) => ({ id, type: 'image', role: 'reference' })));
|
|
@@ -1201,7 +1282,8 @@ export const generateImage = {
|
|
|
1201
1282
|
`Review them against your prompt — every reference's role must be labeled in the prompt text. ` +
|
|
1202
1283
|
`If the references suggest a different composition / style than the current prompt captures, REVISE the prompt before confirming. ` +
|
|
1203
1284
|
`When you talk to the user about this gen, refer to each reference by its code (e.g. "${previews[0]?.ref ?? 'IMG-A?'}") — they'll see the matching badge in the Slates gallery.` +
|
|
1204
|
-
`\n\nWhen ready, re-call slates_generate_image with confirm=true and the (possibly revised) prompt
|
|
1285
|
+
`\n\nWhen ready, re-call slates_generate_image with confirm=true and the (possibly revised) prompt.` +
|
|
1286
|
+
(promptWarning ? `\n\n${promptWarning}` : ''),
|
|
1205
1287
|
images: previews.flatMap((p) => p.images),
|
|
1206
1288
|
data: {
|
|
1207
1289
|
requires_confirm: true,
|
|
@@ -1276,15 +1358,21 @@ export const generateImage = {
|
|
|
1276
1358
|
}
|
|
1277
1359
|
const requestedCount = input.count ?? 1;
|
|
1278
1360
|
return {
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
`
|
|
1287
|
-
|
|
1361
|
+
// The pixels are ALREADY here when the disk read worked, so the
|
|
1362
|
+
// review pointer says "look at what you have", not "call another op".
|
|
1363
|
+
// Telling the agent to re-fetch an image already in its context would
|
|
1364
|
+
// buy a wasted turn and teach the wrong habit.
|
|
1365
|
+
text: `${images.length > 0 ? IMAGE_INLINE_REVIEW : IMAGE_FETCH_POINTER} ` +
|
|
1366
|
+
(promptWarning ? `${promptWarning} ` : '') +
|
|
1367
|
+
(partialFailure
|
|
1368
|
+
? `Partial result: ${assetList.length} of ${requestedCount} image(s) saved into project ${input.projectId} ` +
|
|
1369
|
+
`(error on the rest: ${result.error ?? 'unknown error'}). ` +
|
|
1370
|
+
`The saved assets are in data.assets — re-generate only the missing count, don't redo the whole batch. ` +
|
|
1371
|
+
`Prompt: "${input.prompt.slice(0, 60)}${input.prompt.length > 60 ? '...' : ''}"`
|
|
1372
|
+
: `Generated ${assetList.length} image(s) into project ${input.projectId} ` +
|
|
1373
|
+
`for ${fmtCredits(totalCents)}. ` +
|
|
1374
|
+
`Prompt: "${input.prompt.slice(0, 60)}${input.prompt.length > 60 ? '...' : ''}"` +
|
|
1375
|
+
(refEcho ? ` ${refEcho}` : '')),
|
|
1288
1376
|
images,
|
|
1289
1377
|
data: {
|
|
1290
1378
|
model: imageModel,
|
|
@@ -1352,7 +1440,9 @@ export const generateImage = {
|
|
|
1352
1440
|
images.push({ data: buf.toString('base64'), mimeType: mt });
|
|
1353
1441
|
}
|
|
1354
1442
|
return {
|
|
1355
|
-
text:
|
|
1443
|
+
text: `${IMAGE_INLINE_REVIEW} ` +
|
|
1444
|
+
(promptWarning ? `${promptWarning} ` : '') +
|
|
1445
|
+
`Generated ${urls.length} image(s) for ${fmtCredits(totalCents)}. ` +
|
|
1356
1446
|
`Prompt: "${input.prompt.slice(0, 60)}${input.prompt.length > 60 ? '...' : ''}"`,
|
|
1357
1447
|
images,
|
|
1358
1448
|
data: {
|
|
@@ -1569,6 +1659,16 @@ export function videoCostKey(input) {
|
|
|
1569
1659
|
const k = minimaxRefSurchargeCount(input.model, input.referenceImages);
|
|
1570
1660
|
return `${input.model}-${res}-${input.duration}s${k > 0 ? `-ref${k}` : ''}`;
|
|
1571
1661
|
}
|
|
1662
|
+
// LTX-2.5, both seats. EXACT-ID SET, NEVER A PREFIX — `ltx-2-5-pro` starts
|
|
1663
|
+
// with `ltx-2-5`, and a prefix match would quote base rates for the dearer
|
|
1664
|
+
// row. No suffix dimension exists: audio is free and there are no references.
|
|
1665
|
+
// The resolution default is read PER ROW (both are 1080p today, but the two
|
|
1666
|
+
// ladders differ, so a shared literal would be a latent bug the day one
|
|
1667
|
+
// moves). Mirrors ltxCreditKey() in slate/src/shared/pricing.ts.
|
|
1668
|
+
if (LTX_MODELS.has(input.model)) {
|
|
1669
|
+
const res = input.videoResolution ?? defaultVideoResolutionFor(input.model);
|
|
1670
|
+
return `${input.model}-${res}-${input.duration}s`;
|
|
1671
|
+
}
|
|
1572
1672
|
if (input.model.startsWith('seedance')) {
|
|
1573
1673
|
// Mirrors seedanceCreditKey() in slate/src/shared/pricing.ts (version × face
|
|
1574
1674
|
// × vref × res × duration). AI-face route bills the `-face-` key (~45% over
|
|
@@ -1867,7 +1967,10 @@ function promptingSkillFor(model) {
|
|
|
1867
1967
|
}
|
|
1868
1968
|
export const generateVideo = {
|
|
1869
1969
|
id: 'slates_generate_video',
|
|
1870
|
-
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 / slates-prompting-minimax-h3) — 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").'
|
|
1970
|
+
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 / slates-prompting-minimax-h3) — 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"). ' +
|
|
1971
|
+
// GENERATED from the skill's own slop-token list. Always in context on both
|
|
1972
|
+
// surfaces, so it survives an agent that skips slates_get_prompting_guide.
|
|
1973
|
+
describeBannedTokens('video'),
|
|
1871
1974
|
input: z.object({
|
|
1872
1975
|
prompt: z.string().min(1).max(4000),
|
|
1873
1976
|
// ROUTING doctrine only. Every capability number was stripped on 2026-08-16
|
|
@@ -1876,7 +1979,14 @@ export const generateVideo = {
|
|
|
1876
1979
|
// "seedance-2.5 480p/720p" were both stated here AND there, and the two
|
|
1877
1980
|
// copies disagreed — and the second of those went stale on 2026-08-24 when
|
|
1878
1981
|
// 2.5 gained 1080p, which is exactly the failure mode generating it fixes.
|
|
1879
|
-
model: z.string().describe(`One of: ${VIDEO_MODELS.join(' | ')}. Pass the BASE id — duration and videoResolution are separate
|
|
1982
|
+
model: z.string().describe(`One of: ${VIDEO_MODELS.join(' | ')}. Pass the BASE id — duration and videoResolution are separate ` +
|
|
1983
|
+
`params (registry cost keys like "kling-v3-standard-8s" auto-resolve). All are VIDEO-only.\n` +
|
|
1984
|
+
// GENERATED from MODEL_FACTS. The paragraph that stood here restated it by
|
|
1985
|
+
// hand and had already drifted a phrase at a time.
|
|
1986
|
+
`${describeRouting('video', 'generate')}\n` +
|
|
1987
|
+
`Full table: the slates-model-selection skill. Each model's legal aspect ratios, durations and ` +
|
|
1988
|
+
`resolutions are in those params' own descriptions — read them there, not from memory. ` +
|
|
1989
|
+
`For per-call cost, call slates_estimate_generation_cost.`),
|
|
1880
1990
|
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.'),
|
|
1881
1991
|
// 🚨 THESE THREE DESCRIPTIONS ARE GENERATED FROM `MODEL_CAPABILITIES`.
|
|
1882
1992
|
// Never hand-write a ratio, resolution or duration into them again — every
|
|
@@ -1940,11 +2050,16 @@ export const generateVideo = {
|
|
|
1940
2050
|
// no asset to reference later, and a failed gen leaves the user with
|
|
1941
2051
|
// nothing. The MCP-only headless path that exists for image gen is
|
|
1942
2052
|
// not reasonable for video given the cost.
|
|
2053
|
+
// Non-blocking prompt hygiene, computed once. Reported on the gates that
|
|
2054
|
+
// fire BEFORE any spend, where a rewrite is still free.
|
|
2055
|
+
const promptWarning = bannedTokenWarning(input.prompt, 'video');
|
|
1943
2056
|
if (!input.projectId) {
|
|
1944
2057
|
return ok({
|
|
1945
2058
|
requires_clarification: true,
|
|
1946
2059
|
missing: ['projectId'],
|
|
1947
|
-
|
|
2060
|
+
...(promptWarning ? { prompt_warning: promptWarning } : {}),
|
|
2061
|
+
message: (promptWarning ? `${promptWarning}\n\n` : '') +
|
|
2062
|
+
'projectId is required for video generation. Use slates_list_projects to find one or slates_create_project to make a new one. Video gens cost tens to a few hundred credits per call — they need to land in a project so the user sees the progress card and the result.',
|
|
1948
2063
|
});
|
|
1949
2064
|
}
|
|
1950
2065
|
if (!input.aspectRatio || !input.duration) {
|
|
@@ -1956,7 +2071,9 @@ export const generateVideo = {
|
|
|
1956
2071
|
return ok({
|
|
1957
2072
|
requires_clarification: true,
|
|
1958
2073
|
missing,
|
|
1959
|
-
|
|
2074
|
+
...(promptWarning ? { prompt_warning: promptWarning } : {}),
|
|
2075
|
+
message: (promptWarning ? `${promptWarning}\n\n` : '') +
|
|
2076
|
+
`Missing required field(s): ${missing.join(', ')}. ` +
|
|
1960
2077
|
`Read the slates-cost-discipline + ${promptingSkillFor(input.model)} skills, ` +
|
|
1961
2078
|
// Generated from MODEL_CAPABILITIES. The prose that stood here claimed
|
|
1962
2079
|
// "Veo locks to 16:9", "Kling/Seedance support 1:1 16:9 9:16 4:3 3:4
|
|
@@ -2002,6 +2119,34 @@ export const generateVideo = {
|
|
|
2002
2119
|
});
|
|
2003
2120
|
}
|
|
2004
2121
|
}
|
|
2122
|
+
// LTX-2.5's SHAPE constraint: FRAMES, NEVER REFERENCES. fal publishes
|
|
2123
|
+
// text-to-video and image-to-video for `lightricks/ltx-2.5` and nothing
|
|
2124
|
+
// else — there is no reference-to-video endpoint on either seat, so there
|
|
2125
|
+
// is no transport for an ingredient, character, environment, style,
|
|
2126
|
+
// reference-video or reference-audio input.
|
|
2127
|
+
//
|
|
2128
|
+
// This has to be an EXPLICIT refusal rather than a silent no-op: accepting
|
|
2129
|
+
// reference ids we cannot send is the exact "no error, no warning, no
|
|
2130
|
+
// images in the request" failure `seedance-2.5-edit` shipped with. Start
|
|
2131
|
+
// and end FRAMES are unaffected — image-to-video carries `image_url` plus
|
|
2132
|
+
// an optional `end_image_url`, which is what `features.lastFrame` records.
|
|
2133
|
+
if (LTX_MODELS.has(input.model)) {
|
|
2134
|
+
const refImages = (input.ingredientAssetIds?.length ?? 0) +
|
|
2135
|
+
(input.characterAssetIds?.length ?? 0) +
|
|
2136
|
+
(input.environmentAssetIds?.length ?? 0) +
|
|
2137
|
+
(input.styleAssetIds?.length ?? 0);
|
|
2138
|
+
const refMedia = (input.videoReferenceAssetIds?.length ?? 0) +
|
|
2139
|
+
(input.audioReferenceAssetIds?.length ?? 0) +
|
|
2140
|
+
(input.videoReferenceAssetId ? 1 : 0) +
|
|
2141
|
+
(input.audioReferenceAssetId ? 1 : 0);
|
|
2142
|
+
if (refImages > 0 || refMedia > 0) {
|
|
2143
|
+
return ok({
|
|
2144
|
+
requires_clarification: true,
|
|
2145
|
+
missing: [],
|
|
2146
|
+
message: `${input.model} takes a prompt and up to two frames (start and/or end) — it has no reference endpoint at all, so reference images, video and audio cannot be sent. Drop them, or switch to minimax-h3, which reads ${getModelCapability('minimax-h3')?.maxIngredientImages ?? 9} images plus reference video and audio.`,
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2005
2150
|
// MiniMax H3's SHAPE constraints. Counts and caps are read from the
|
|
2006
2151
|
// capability SSOT; only the endpoint SHAPE is stated here, because it is
|
|
2007
2152
|
// not a number the registry models: fal publishes text-to-video,
|
|
@@ -2268,6 +2413,7 @@ export const generateVideo = {
|
|
|
2268
2413
|
text: `Pre-flight for ${input.duration}s ${input.model} (${costKey}): ` +
|
|
2269
2414
|
`${fmtCredits(totalCents)}.` +
|
|
2270
2415
|
refSummary +
|
|
2416
|
+
(promptWarning ? `\n\n${promptWarning}` : '') +
|
|
2271
2417
|
`\n\nWhen ready, re-call slates_generate_video with confirm=true and the (possibly revised) prompt.`,
|
|
2272
2418
|
images: refImages,
|
|
2273
2419
|
data: {
|
|
@@ -2346,7 +2492,12 @@ export const generateVideo = {
|
|
|
2346
2492
|
}, refEcho);
|
|
2347
2493
|
}
|
|
2348
2494
|
return {
|
|
2349
|
-
text:
|
|
2495
|
+
text:
|
|
2496
|
+
// No frames come back with a video generation, so unlike the image op
|
|
2497
|
+
// this one has to name the op that fetches them.
|
|
2498
|
+
`${VIDEO_REVIEW_POINTER} ` +
|
|
2499
|
+
(promptWarning ? `${promptWarning} ` : '') +
|
|
2500
|
+
`Generated ${input.duration}s ${input.model} video into project ${input.projectId} ` +
|
|
2350
2501
|
`for ${fmtCredits(totalCents)}. ` +
|
|
2351
2502
|
`Prompt: "${input.prompt.slice(0, 60)}${input.prompt.length > 60 ? '...' : ''}"` +
|
|
2352
2503
|
(refEcho ? ` ${refEcho}` : ''),
|
|
@@ -2375,7 +2526,10 @@ export const generateAudio = {
|
|
|
2375
2526
|
projectId: z.string().uuid().describe('Slates project the audio asset lands in. Required — the renderer refreshes live.'),
|
|
2376
2527
|
model: z
|
|
2377
2528
|
.enum(AUDIO_MODELS)
|
|
2378
|
-
.describe(
|
|
2529
|
+
.describe(
|
|
2530
|
+
// GENERATED from MODEL_FACTS — the audio lane's routing lived only in the
|
|
2531
|
+
// system prompt and in a one-line paraphrase here.
|
|
2532
|
+
`Audio surface.\n${describeRouting('audio')}\nFull table: the slates-model-selection skill.`),
|
|
2379
2533
|
prompt: z
|
|
2380
2534
|
.string()
|
|
2381
2535
|
.min(1)
|
|
@@ -2743,7 +2897,13 @@ export const editVideo = {
|
|
|
2743
2897
|
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.'),
|
|
2744
2898
|
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."'),
|
|
2745
2899
|
// Clip windows and resolutions GENERATED from MODEL_CAPABILITIES.
|
|
2746
|
-
model: zEnum(EDIT_VIDEO_MODELS).optional().describe(
|
|
2900
|
+
model: zEnum(EDIT_VIDEO_MODELS).optional().describe(
|
|
2901
|
+
// Routing GENERATED from MODEL_FACTS; the paragraph that stood here was
|
|
2902
|
+
// hand-written and carried per-second prices, which drift and which the
|
|
2903
|
+
// agent's own REAL NUMBERS ONLY rule forbids it repeating.
|
|
2904
|
+
`Default kling-v3.0-omni-edit. Neither omni-flash-edit nor seedance-2.5-edit takes character/style refs on this op.\n` +
|
|
2905
|
+
`${describeRouting('video', 'edit')}\n` +
|
|
2906
|
+
`Source-clip length per engine: ${describeDurations(EDIT_VIDEO_MODELS)}. Output resolution: ${describeVideoResolutions(EDIT_VIDEO_MODELS)}`),
|
|
2747
2907
|
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.'),
|
|
2748
2908
|
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.'),
|
|
2749
2909
|
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).'),
|
|
@@ -3578,6 +3738,12 @@ function resolveGuideTopic(topic) {
|
|
|
3578
3738
|
t.startsWith('h3-')) {
|
|
3579
3739
|
return 'slates-prompting-minimax-h3';
|
|
3580
3740
|
}
|
|
3741
|
+
// LTX-2.5 — both seats share one skill. A PREFIX is right HERE (unlike every
|
|
3742
|
+
// rate, key and endpoint lookup, which must be exact) precisely because both
|
|
3743
|
+
// rows resolve to the same guide: `ltx-2-5-pro` matching the `ltx` prefix is
|
|
3744
|
+
// the intended outcome, not a collision.
|
|
3745
|
+
if (t.startsWith('ltx') || t === 'lightricks')
|
|
3746
|
+
return 'slates-prompting-ltx-2-5';
|
|
3581
3747
|
if (t.startsWith('kling-mc'))
|
|
3582
3748
|
return 'slates-prompting-motion-transfer';
|
|
3583
3749
|
if (t === 'edit-video' || t === 'video-edit' || t === 'edit video' || t === 'video edit')
|
|
@@ -3630,6 +3796,27 @@ function resolveGuideTopic(topic) {
|
|
|
3630
3796
|
}
|
|
3631
3797
|
return null;
|
|
3632
3798
|
}
|
|
3799
|
+
/**
|
|
3800
|
+
* The guide index, GENERATED from SKILLS.
|
|
3801
|
+
*
|
|
3802
|
+
* The list here was hand-typed and had drifted to 25 of 32 names — the prompting
|
|
3803
|
+
* guides for GPT Image 2, MiniMax H3, LTX-2.5, Seedance 2.5 and Omni Flash were
|
|
3804
|
+
* all missing, so an agent reading this description could not learn they exist.
|
|
3805
|
+
* A hand-typed index of a generated corpus is a stale index; it is only a matter
|
|
3806
|
+
* of when.
|
|
3807
|
+
*
|
|
3808
|
+
* Per-model guides are listed as BARE NAMES: the name is the description, and
|
|
3809
|
+
* `resolveGuideTopic()` resolves a model id to the right one anyway.
|
|
3810
|
+
*/
|
|
3811
|
+
function describeGuideTopics() {
|
|
3812
|
+
const names = Object.keys(SKILLS).sort();
|
|
3813
|
+
const perModel = names.filter((n) => n.startsWith('slates-prompting-'));
|
|
3814
|
+
const rest = names.filter((n) => !n.startsWith('slates-prompting-'));
|
|
3815
|
+
return (`Workflow and cross-cutting guides: ${rest.join(', ')}. ` +
|
|
3816
|
+
`Per-model prompting guides (or just pass the model id, which resolves to one of these): ` +
|
|
3817
|
+
`${perModel.join(', ')}. ` +
|
|
3818
|
+
`Style names (photoreal, anime, painterly, 3d-render) resolve to slates-style-prompting.`);
|
|
3819
|
+
}
|
|
3633
3820
|
export const getPromptingGuide = {
|
|
3634
3821
|
id: 'slates_get_prompting_guide',
|
|
3635
3822
|
description: "Return the full markdown of a bundled Slates prompting/workflow guide. MCP-only clients (Claude Desktop, Smithery) don't get the CLI-installed skill files — call this instead. Accepts a guide name or a model id (e.g. 'veo-3.1-fast', 'kling-v3.0-pro', 'seedance-2', 'nano-banana-2') which maps to the right guide. ALWAYS read 'slates-cost-discipline' plus the relevant model guide before your first generation in a session.",
|
|
@@ -3637,7 +3824,7 @@ export const getPromptingGuide = {
|
|
|
3637
3824
|
topic: z
|
|
3638
3825
|
.string()
|
|
3639
3826
|
.min(1)
|
|
3640
|
-
.describe(
|
|
3827
|
+
.describe(`Guide name, model id, or style name. ${describeGuideTopics()}`),
|
|
3641
3828
|
}),
|
|
3642
3829
|
async run(input) {
|
|
3643
3830
|
const resolved = resolveGuideTopic(input.topic);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which agent surface is being briefed.
|
|
3
|
+
*
|
|
4
|
+
* `desktop` — the in-app Studio Agent. Its loop enforces plan approval in
|
|
5
|
+
* code, auto-polls generation status, and displays orchestration cost itself.
|
|
6
|
+
* `mcp` — Claude Code / Claude Desktop / Cursor / Codex over stdio. No
|
|
7
|
+
* `present_plan` tool, no auto-poll, no app chrome; the consent gate is the
|
|
8
|
+
* op-level `requires_confirm` threshold plus the host client's own per-call
|
|
9
|
+
* tool approval. The asymmetry is DELIBERATE — see slates-mcp/CLAUDE.md.
|
|
10
|
+
*/
|
|
11
|
+
export type AgentSurface = 'desktop' | 'mcp';
|
|
12
|
+
interface SkillIndexEntry {
|
|
13
|
+
name: string;
|
|
14
|
+
description: string;
|
|
15
|
+
}
|
|
16
|
+
/** Parse `name:`/`description:` out of each embedded skill's frontmatter. */
|
|
17
|
+
export declare function buildSkillIndex(): SkillIndexEntry[];
|
|
18
|
+
export declare const WORKING_METHOD: ReadonlyArray<Record<AgentSurface, string>>;
|
|
19
|
+
export declare const HARD_RULES: ReadonlyArray<Record<AgentSurface, string>>;
|
|
20
|
+
/**
|
|
21
|
+
* THE doctrine string for a surface. The desktop's whole system prompt and the
|
|
22
|
+
* MCP server's `instructions` are both exactly this — neither consumer adds
|
|
23
|
+
* doctrine prose of its own.
|
|
24
|
+
*
|
|
25
|
+
* SENTINEL: the literal below must appear in this workspace in exactly ONE
|
|
26
|
+
* file — this one. scripts/agent-surface-lockstep-check.mjs assembles the same
|
|
27
|
+
* string from its parts rather than writing it out, precisely so that grepping
|
|
28
|
+
* for it stays a meaningful question. It is how "no consumer grew a private
|
|
29
|
+
* copy of the doctrine" is proved rather than assumed.
|
|
30
|
+
* SLATES-AGENT-DOCTRINE-SSOT
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildAgentDoctrine({ surface }: {
|
|
33
|
+
surface: AgentSurface;
|
|
34
|
+
}): string;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=agent-doctrine.d.ts.map
|