@slatesvideo/shared 0.6.1 → 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.
Files changed (32) hide show
  1. package/dist/clients/blender.d.ts +50 -0
  2. package/dist/clients/blender.js +195 -0
  3. package/dist/index.d.ts +3 -1
  4. package/dist/index.js +26 -0
  5. package/dist/operations/index.d.ts +25 -1
  6. package/dist/operations/index.js +454 -24
  7. package/dist/prompts/agent-doctrine.d.ts +36 -0
  8. package/dist/prompts/agent-doctrine.js +194 -0
  9. package/dist/prompts/banned-tokens.d.ts +28 -0
  10. package/dist/prompts/banned-tokens.js +152 -0
  11. package/dist/prompts/model-capabilities.d.ts +13 -1
  12. package/dist/prompts/model-capabilities.js +97 -2
  13. package/dist/prompts/model-facts.d.ts +20 -0
  14. package/dist/prompts/model-facts.js +87 -23
  15. package/dist/prompts/prompting-tips.d.ts +1 -1
  16. package/dist/prompts/prompting-tips.js +65 -0
  17. package/dist/prompts/reference-composer.d.ts +57 -0
  18. package/dist/prompts/reference-composer.js +70 -1
  19. package/dist/skills/content.js +8 -2
  20. package/exports/slates-prompt-builder/generated/SKILL.md +3 -3
  21. package/exports/slates-prompt-builder/generated/reference-seedance.md +2 -1
  22. package/exports/slates-prompt-builder/generated/slates-prompt-builder-manifest.json +8 -8
  23. package/exports/slates-prompt-builder/generated/slates-prompt-builder.skill +0 -0
  24. package/package.json +1 -1
  25. package/skills/slates-blocking-to-prompt.md +250 -0
  26. package/skills/slates-camera-language.md +196 -0
  27. package/skills/slates-dialogue-blocking.md +134 -0
  28. package/skills/slates-previs-blocking.md +153 -0
  29. package/skills/slates-prompting-ltx-2-5.md +180 -0
  30. package/skills/slates-prompting-nano-banana-2.md +10 -0
  31. package/skills/slates-prompting-seedance.md +10 -1
  32. package/skills/slates-restyle-from-blocking.md +121 -0
@@ -0,0 +1,50 @@
1
+ export declare const RENDER_TIMEOUT_MS: number;
2
+ /**
3
+ * Where the add-on comes from, and how to switch it on. ONE string.
4
+ *
5
+ * 🚨 IT WAS TWO, AND THAT IS ALWAYS A DRIFT. The op layer had its own
6
+ * differently-worded copy naming the same URL, so a moved download page or a
7
+ * renamed panel button would have had to be found in two files by someone who
8
+ * remembered both existed. Same fact, same sentence, one home.
9
+ *
10
+ * `slates.video/blender` is a real page as of 2026-08-28 (`slates-web`
11
+ * `src/app/blender/page.tsx`), and it is the ONLY thing that tells a stuck user
12
+ * where the add-on comes from. It is a hard dependency of this string, not a
13
+ * nice-to-have: keep the path or 301 it.
14
+ */
15
+ export declare const BLENDER_SETUP_HINT: string;
16
+ export declare class BlenderNotRunningError extends Error {
17
+ constructor();
18
+ }
19
+ /** Raised when Blender executed the code and Python threw. Carries the traceback. */
20
+ export declare class BlenderExecError extends Error {
21
+ readonly stdout: string;
22
+ readonly stderr: string;
23
+ constructor(message: string, stdout?: string, stderr?: string);
24
+ }
25
+ export declare class BlenderBridgeClient {
26
+ /**
27
+ * Execute Python in Blender and return whatever the code assigned to
28
+ * `result`. `strict_json` is false so a stray Blender object comes back as
29
+ * its repr instead of failing the whole call — the agent can then correct
30
+ * itself, which it cannot do if the error is about serialization.
31
+ */
32
+ execute(code: string, opts?: {
33
+ timeoutMs?: number;
34
+ }): Promise<{
35
+ result: unknown;
36
+ stdout: string;
37
+ stderr: string;
38
+ }>;
39
+ /**
40
+ * Execute code with the add-on package bound to `_slates`, so ops can call
41
+ * the shipped helpers (`_slates.previs`, `_slates.scene`, `_slates.docs`)
42
+ * instead of re-implementing them as inline Python string blobs.
43
+ */
44
+ call(body: string, opts?: {
45
+ timeoutMs?: number;
46
+ }): Promise<unknown>;
47
+ /** True when a Blender with the add-on is reachable. Never throws. */
48
+ isReachable(): Promise<boolean>;
49
+ }
50
+ //# sourceMappingURL=blender.d.ts.map
@@ -0,0 +1,195 @@
1
+ import net from 'node:net';
2
+ // Thin client for the Slates Blender add-on's localhost execution bridge.
3
+ //
4
+ // Wire protocol (matches the add-on's `bridge/server.py`, which is Blender
5
+ // Lab's `blender_mcp` protocol):
6
+ //
7
+ // -> {"type":"execute","code":"...","strict_json":false}\0
8
+ // <- {"status":"ok","result":{...},"stdout":"...","stderr":"..."}\0
9
+ // <- {"status":"error","message":"<traceback>"}\0
10
+ //
11
+ // Requests and responses are NUL-delimited JSON over a plain TCP socket. The
12
+ // add-on executes on Blender's main thread via a timer, so a request is
13
+ // serviced within a tick rather than immediately — the socket stays open until
14
+ // the reply lands, and long jobs (renders) hold it open for as long as they
15
+ // take. That is why the timeout here is generous and configurable per call
16
+ // rather than a single global value.
17
+ const HOST = '127.0.0.1';
18
+ // The add-on binds the first free port in this range, so a second Blender (or
19
+ // a stale process holding 9876) shifts it forward instead of failing. We probe
20
+ // the same range in the same order.
21
+ const BASE_PORT = 9876;
22
+ const PORT_FALLBACKS = 3;
23
+ const DEFAULT_TIMEOUT_MS = 60_000;
24
+ // Renders are the one call that legitimately runs for minutes.
25
+ export const RENDER_TIMEOUT_MS = 15 * 60_000;
26
+ const CONNECT_TIMEOUT_MS = 1_500;
27
+ /**
28
+ * Where the add-on comes from, and how to switch it on. ONE string.
29
+ *
30
+ * 🚨 IT WAS TWO, AND THAT IS ALWAYS A DRIFT. The op layer had its own
31
+ * differently-worded copy naming the same URL, so a moved download page or a
32
+ * renamed panel button would have had to be found in two files by someone who
33
+ * remembered both existed. Same fact, same sentence, one home.
34
+ *
35
+ * `slates.video/blender` is a real page as of 2026-08-28 (`slates-web`
36
+ * `src/app/blender/page.tsx`), and it is the ONLY thing that tells a stuck user
37
+ * where the add-on comes from. It is a hard dependency of this string, not a
38
+ * nice-to-have: keep the path or 301 it.
39
+ */
40
+ export const BLENDER_SETUP_HINT = 'Install the Slates add-on from https://slates.video/blender, then in the 3D ' +
41
+ 'viewport sidebar (press N) open the Slates tab and click Start Bridge.';
42
+ const NOT_RUNNING_MESSAGE = 'No Blender with the Slates add-on is listening on ' +
43
+ `${HOST}:${BASE_PORT}-${BASE_PORT + PORT_FALLBACKS}. ` +
44
+ BLENDER_SETUP_HINT;
45
+ export class BlenderNotRunningError extends Error {
46
+ constructor() {
47
+ super(NOT_RUNNING_MESSAGE);
48
+ this.name = 'BlenderNotRunningError';
49
+ }
50
+ }
51
+ /** Raised when Blender executed the code and Python threw. Carries the traceback. */
52
+ export class BlenderExecError extends Error {
53
+ stdout;
54
+ stderr;
55
+ constructor(message, stdout = '', stderr = '') {
56
+ super(message);
57
+ this.name = 'BlenderExecError';
58
+ this.stdout = stdout;
59
+ this.stderr = stderr;
60
+ }
61
+ }
62
+ /**
63
+ * Resolve the installed add-on package regardless of what Blender named it.
64
+ *
65
+ * Extensions are imported as `bl_ext.<repo>.slates_blender`, and the repo
66
+ * segment depends on where the user installed from — so the name cannot be
67
+ * hardcoded. Scanning `sys.modules` for the root package is the only stable
68
+ * handle. Submodules (`...slates_blender.bridge`) do not match the suffix, so
69
+ * the generator yields exactly the root.
70
+ */
71
+ const PRELUDE = `
72
+ import sys as _sys
73
+ from importlib import import_module as _import_module
74
+ _slates = next(
75
+ (m for n, m in list(_sys.modules.items())
76
+ if n.endswith('slates_blender') and m is not None),
77
+ None,
78
+ )
79
+ if _slates is None:
80
+ raise RuntimeError(
81
+ 'The Slates Blender add-on is not loaded in this Blender. '
82
+ 'Enable it in Edit > Preferences > Add-ons.'
83
+ )
84
+
85
+ def _mod(_name):
86
+ """Import a submodule of the add-on by short name.
87
+
88
+ The root package only imports \`bridge\` at load time — everything else is
89
+ lazy so enabling the add-on stays cheap — so \`_slates.previs\` is not
90
+ reliably an attribute. Go through importlib rather than assuming it is.
91
+ """
92
+ return _import_module(_slates.__name__ + '.' + _name)
93
+ `.trim();
94
+ function connect(port) {
95
+ return new Promise((resolve, reject) => {
96
+ const socket = new net.Socket();
97
+ const onError = (err) => {
98
+ socket.destroy();
99
+ reject(err);
100
+ };
101
+ socket.setTimeout(CONNECT_TIMEOUT_MS, () => onError(new Error('connect timeout')));
102
+ socket.once('error', onError);
103
+ socket.connect(port, HOST, () => {
104
+ socket.setTimeout(0);
105
+ socket.removeListener('error', onError);
106
+ resolve(socket);
107
+ });
108
+ });
109
+ }
110
+ async function connectAny() {
111
+ for (let port = BASE_PORT; port <= BASE_PORT + PORT_FALLBACKS; port++) {
112
+ try {
113
+ return await connect(port);
114
+ }
115
+ catch {
116
+ // Try the next port in the range.
117
+ }
118
+ }
119
+ throw new BlenderNotRunningError();
120
+ }
121
+ function request(socket, payload, timeoutMs) {
122
+ return new Promise((resolve, reject) => {
123
+ let buffer = '';
124
+ let settled = false;
125
+ const finish = (fn) => {
126
+ if (settled)
127
+ return;
128
+ settled = true;
129
+ clearTimeout(timer);
130
+ socket.destroy();
131
+ fn();
132
+ };
133
+ const timer = setTimeout(() => finish(() => reject(new Error(`Blender did not respond within ${Math.round(timeoutMs / 1000)}s. ` +
134
+ 'It may be busy with a modal operator — check the Blender window.'))), timeoutMs);
135
+ socket.setEncoding('utf8');
136
+ socket.on('data', (chunk) => {
137
+ buffer += chunk;
138
+ const end = buffer.indexOf('\0');
139
+ if (end === -1)
140
+ return;
141
+ const raw = buffer.slice(0, end);
142
+ finish(() => {
143
+ try {
144
+ resolve(JSON.parse(raw));
145
+ }
146
+ catch (err) {
147
+ reject(new Error(`Malformed reply from Blender: ${err.message}`));
148
+ }
149
+ });
150
+ });
151
+ socket.on('error', (err) => finish(() => reject(err)));
152
+ socket.on('close', () => finish(() => reject(new Error('Blender closed the connection before replying.'))));
153
+ socket.write(payload + '\0');
154
+ });
155
+ }
156
+ export class BlenderBridgeClient {
157
+ /**
158
+ * Execute Python in Blender and return whatever the code assigned to
159
+ * `result`. `strict_json` is false so a stray Blender object comes back as
160
+ * its repr instead of failing the whole call — the agent can then correct
161
+ * itself, which it cannot do if the error is about serialization.
162
+ */
163
+ async execute(code, opts) {
164
+ const socket = await connectAny();
165
+ const payload = JSON.stringify({ type: 'execute', code, strict_json: false });
166
+ const res = await request(socket, payload, opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS);
167
+ const stdout = res.stdout ?? '';
168
+ const stderr = res.stderr ?? '';
169
+ if (res.status !== 'ok') {
170
+ throw new BlenderExecError(res.message ?? 'Blender reported an error', stdout, stderr);
171
+ }
172
+ return { result: res.result, stdout, stderr };
173
+ }
174
+ /**
175
+ * Execute code with the add-on package bound to `_slates`, so ops can call
176
+ * the shipped helpers (`_slates.previs`, `_slates.scene`, `_slates.docs`)
177
+ * instead of re-implementing them as inline Python string blobs.
178
+ */
179
+ async call(body, opts) {
180
+ const { result } = await this.execute(`${PRELUDE}\n${body}`, opts);
181
+ return result;
182
+ }
183
+ /** True when a Blender with the add-on is reachable. Never throws. */
184
+ async isReachable() {
185
+ try {
186
+ const socket = await connectAny();
187
+ socket.destroy();
188
+ return true;
189
+ }
190
+ catch {
191
+ return false;
192
+ }
193
+ }
194
+ }
195
+ //# sourceMappingURL=blender.js.map
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. */
@@ -277,6 +277,7 @@ export declare const generateVideo: Operation<{
277
277
  videoReferenceAssetIds?: string[];
278
278
  videoReferenceSecondsEach?: number[];
279
279
  audioReferenceAssetIds?: string[];
280
+ audioReferenceSpokenText?: string[];
280
281
  sound?: boolean;
281
282
  audioLanguage?: 'EN' | 'ZH' | 'JA' | 'KO' | 'ES';
282
283
  generateMusic?: boolean;
@@ -538,6 +539,29 @@ export declare const deleteFrame: Operation<{
538
539
  export declare const getPromptingGuide: Operation<{
539
540
  topic: string;
540
541
  }>;
542
+ export declare const blenderStatus: Operation<Record<string, never>>;
543
+ export declare const blenderExecute: Operation<{
544
+ code: string;
545
+ timeoutSeconds?: number;
546
+ }>;
547
+ export declare const blenderScene: Operation<Record<string, never>>;
548
+ export declare const blenderDocs: Operation<{
549
+ identifier: string;
550
+ }>;
551
+ export declare const blenderSearchDocs: Operation<{
552
+ query: string;
553
+ scope?: 'api' | 'manual';
554
+ maxResults?: number;
555
+ }>;
556
+ export declare const blenderRenderBlocking: Operation<{
557
+ projectId?: string;
558
+ resolutionX?: number;
559
+ resolutionY?: number;
560
+ fps?: number;
561
+ frameStart?: number;
562
+ frameEnd?: number;
563
+ basename?: string;
564
+ }>;
541
565
  export declare const ALL_OPERATIONS: ReadonlyArray<Operation<unknown>>;
542
566
  export {};
543
567
  //# sourceMappingURL=index.d.ts.map