vidspotai-shared 1.0.123 → 1.0.124

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.
@@ -158,6 +158,39 @@ export declare const ReferenceLinkSchema: z.ZodObject<{
158
158
  transcriptExcerpt: z.ZodOptional<z.ZodString>;
159
159
  }, z.core.$strip>>;
160
160
  }, z.core.$strip>;
161
+ /**
162
+ * One Reference Library item the user @-mentioned in the composer (a built-in
163
+ * seed or one of their own refs), flattened across kinds by the frontend's
164
+ * `collectMentionedRefs` (wt-dev-frontend refs/tokens.ts — keep in sync).
165
+ *
166
+ * Unlike `references` (bare URLs), this carries what the item MEANS so the
167
+ * pipeline can honor it: characters / elements are pinned into the Visual
168
+ * Bible with their image (→ threaded as inputImageUrls on every scene they
169
+ * appear in, see mentionedRefs.ts), and style / effect / color / camera
170
+ * instructions are applied by the planner scene by scene.
171
+ */
172
+ export declare const MentionedRefSchema: z.ZodObject<{
173
+ kind: z.ZodEnum<{
174
+ element: "element";
175
+ motion: "motion";
176
+ color: "color";
177
+ style: "style";
178
+ character: "character";
179
+ effect: "effect";
180
+ camera: "camera";
181
+ voice: "voice";
182
+ model: "model";
183
+ }>;
184
+ slug: z.ZodString;
185
+ name: z.ZodString;
186
+ instruction: z.ZodOptional<z.ZodString>;
187
+ imageUrls: z.ZodOptional<z.ZodArray<z.ZodString>>;
188
+ subkind: z.ZodOptional<z.ZodString>;
189
+ presetKey: z.ZodOptional<z.ZodString>;
190
+ swatches: z.ZodOptional<z.ZodArray<z.ZodString>>;
191
+ voiceId: z.ZodOptional<z.ZodString>;
192
+ modelKey: z.ZodOptional<z.ZodString>;
193
+ }, z.core.$strip>;
161
194
  export declare const VideoBriefSchema: z.ZodObject<{
162
195
  rawPrompt: z.ZodString;
163
196
  goal: z.ZodOptional<z.ZodString>;
@@ -278,6 +311,28 @@ export declare const VideoBriefSchema: z.ZodObject<{
278
311
  transcriptExcerpt: z.ZodOptional<z.ZodString>;
279
312
  }, z.core.$strip>>;
280
313
  }, z.core.$strip>>>;
314
+ mentionedRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
315
+ kind: z.ZodEnum<{
316
+ element: "element";
317
+ motion: "motion";
318
+ color: "color";
319
+ style: "style";
320
+ character: "character";
321
+ effect: "effect";
322
+ camera: "camera";
323
+ voice: "voice";
324
+ model: "model";
325
+ }>;
326
+ slug: z.ZodString;
327
+ name: z.ZodString;
328
+ instruction: z.ZodOptional<z.ZodString>;
329
+ imageUrls: z.ZodOptional<z.ZodArray<z.ZodString>>;
330
+ subkind: z.ZodOptional<z.ZodString>;
331
+ presetKey: z.ZodOptional<z.ZodString>;
332
+ swatches: z.ZodOptional<z.ZodArray<z.ZodString>>;
333
+ voiceId: z.ZodOptional<z.ZodString>;
334
+ modelKey: z.ZodOptional<z.ZodString>;
335
+ }, z.core.$strip>>>;
281
336
  preferredModelKeys: z.ZodOptional<z.ZodArray<z.ZodString>>;
282
337
  tierBudget: z.ZodDefault<z.ZodEnum<{
283
338
  T0: "T0";
@@ -303,6 +358,7 @@ export type VoiceProfile = z.infer<typeof VoiceProfileSchema>;
303
358
  export type VoiceStyle = z.infer<typeof VoiceStyleSchema>;
304
359
  export type MusicProfile = z.infer<typeof MusicProfileSchema>;
305
360
  export type ReferenceLink = z.infer<typeof ReferenceLinkSchema>;
361
+ export type MentionedRef = z.infer<typeof MentionedRefSchema>;
306
362
  export type VideoNiche = z.infer<typeof VideoNicheSchema>;
307
363
  export type Tone = z.infer<typeof ToneSchema>;
308
364
  export type HookStyle = z.infer<typeof HookStyleSchema>;
@@ -1 +1 @@
1
- {"version":3,"file":"brief.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/brief.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;EAc3B,CAAC;AAEH,eAAO,MAAM,UAAU;;;;;;;;;;EAUrB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;EAQ1B,CAAC;AAEH,eAAO,MAAM,YAAY;;;;;EAAkD,CAAC;AAE5E;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB;;;;;;;EAA+C,CAAC;AAC7E,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,CASjE;AAKD,eAAO,MAAM,gBAAgB;;;;;;;;;;;EAW3B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkB7B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;iBAiB7B,CAAC;AAEH,eAAO,MAAM,YAAY;;;iBAKvB,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;iBAa9B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkG3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAC9C,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AACxD,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC"}
1
+ {"version":3,"file":"brief.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/brief.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;EAc3B,CAAC;AAEH,eAAO,MAAM,UAAU;;;;;;;;;;EAUrB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;EAQ1B,CAAC;AAEH,eAAO,MAAM,YAAY;;;;;EAAkD,CAAC;AAE5E;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB;;;;;;;EAA+C,CAAC;AAC7E,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,CASjE;AAKD,eAAO,MAAM,gBAAgB;;;;;;;;;;;EAW3B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkB7B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;iBAiB7B,CAAC;AAEH,eAAO,MAAM,YAAY;;;iBAKvB,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;iBAa9B,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;iBAe7B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwG3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAC9C,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AACxD,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VideoBriefSchema = exports.ReferenceLinkSchema = exports.ClaimsSchema = exports.MusicProfileSchema = exports.VoiceProfileSchema = exports.VoiceStyleSchema = exports.LengthTierSchema = exports.PacingSchema = exports.HookStyleSchema = exports.ToneSchema = exports.VideoNicheSchema = void 0;
3
+ exports.VideoBriefSchema = exports.MentionedRefSchema = exports.ReferenceLinkSchema = exports.ClaimsSchema = exports.MusicProfileSchema = exports.VoiceProfileSchema = exports.VoiceStyleSchema = exports.LengthTierSchema = exports.PacingSchema = exports.HookStyleSchema = exports.ToneSchema = exports.VideoNicheSchema = void 0;
4
4
  exports.deriveLengthTier = deriveLengthTier;
5
5
  const zod_1 = require("zod");
6
6
  const project_schema_1 = require("./project.schema");
@@ -163,6 +163,33 @@ exports.ReferenceLinkSchema = zod_1.z.object({
163
163
  })
164
164
  .optional(),
165
165
  });
166
+ /**
167
+ * One Reference Library item the user @-mentioned in the composer (a built-in
168
+ * seed or one of their own refs), flattened across kinds by the frontend's
169
+ * `collectMentionedRefs` (wt-dev-frontend refs/tokens.ts — keep in sync).
170
+ *
171
+ * Unlike `references` (bare URLs), this carries what the item MEANS so the
172
+ * pipeline can honor it: characters / elements are pinned into the Visual
173
+ * Bible with their image (→ threaded as inputImageUrls on every scene they
174
+ * appear in, see mentionedRefs.ts), and style / effect / color / camera
175
+ * instructions are applied by the planner scene by scene.
176
+ */
177
+ exports.MentionedRefSchema = zod_1.z.object({
178
+ kind: zod_1.z.enum(["character", "style", "color", "element", "effect", "camera", "motion", "voice", "model"]),
179
+ slug: zod_1.z.string().max(120),
180
+ name: zod_1.z.string().max(120),
181
+ /** The look / recipe / direction this ref stands for. */
182
+ instruction: zod_1.z.string().max(2000).optional(),
183
+ /** Absolute reference image URLs (character / style / element). */
184
+ imageUrls: zod_1.z.array(zod_1.z.string().url()).max(4).optional(),
185
+ /** Element sub-kind — "location" pins as an environment, else an object. */
186
+ subkind: zod_1.z.string().max(40).optional(),
187
+ /** Canonical model camera-control preset (camera / motion refs). */
188
+ presetKey: zod_1.z.string().max(60).optional(),
189
+ swatches: zod_1.z.array(zod_1.z.string().max(20)).max(12).optional(),
190
+ voiceId: zod_1.z.string().max(120).optional(),
191
+ modelKey: zod_1.z.string().max(120).optional(),
192
+ });
166
193
  exports.VideoBriefSchema = zod_1.z.object({
167
194
  /** Free-text user request, kept verbatim. */
168
195
  rawPrompt: zod_1.z.string(),
@@ -199,6 +226,11 @@ exports.VideoBriefSchema = zod_1.z.object({
199
226
  cta: zod_1.z.string().optional(),
200
227
  claims: exports.ClaimsSchema.default({ allow: [], block: [] }),
201
228
  references: zod_1.z.array(exports.ReferenceLinkSchema).default([]),
229
+ /**
230
+ * Reference Library items the user @-mentioned (see MentionedRefSchema).
231
+ * Optional (not defaulted) so existing briefs and eval seeds are unchanged.
232
+ */
233
+ mentionedRefs: zod_1.z.array(exports.MentionedRefSchema).max(24).optional(),
202
234
  /**
203
235
  * Soft generation-model preference supplied by the user (via the composer's
204
236
  * "@Models" mention category). These are real catalog keys (e.g.
@@ -143,6 +143,28 @@ export declare const EvalBriefSchema: z.ZodObject<{
143
143
  transcriptExcerpt: z.ZodOptional<z.ZodString>;
144
144
  }, z.core.$strip>>;
145
145
  }, z.core.$strip>>>;
146
+ mentionedRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
147
+ kind: z.ZodEnum<{
148
+ element: "element";
149
+ motion: "motion";
150
+ color: "color";
151
+ style: "style";
152
+ character: "character";
153
+ effect: "effect";
154
+ camera: "camera";
155
+ voice: "voice";
156
+ model: "model";
157
+ }>;
158
+ slug: z.ZodString;
159
+ name: z.ZodString;
160
+ instruction: z.ZodOptional<z.ZodString>;
161
+ imageUrls: z.ZodOptional<z.ZodArray<z.ZodString>>;
162
+ subkind: z.ZodOptional<z.ZodString>;
163
+ presetKey: z.ZodOptional<z.ZodString>;
164
+ swatches: z.ZodOptional<z.ZodArray<z.ZodString>>;
165
+ voiceId: z.ZodOptional<z.ZodString>;
166
+ modelKey: z.ZodOptional<z.ZodString>;
167
+ }, z.core.$strip>>>;
146
168
  preferredModelKeys: z.ZodOptional<z.ZodArray<z.ZodString>>;
147
169
  tierBudget: z.ZodDefault<z.ZodEnum<{
148
170
  T0: "T0";
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/services/agent/eval/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;GAUG;AAEH,4CAA4C;AAC5C,eAAO,MAAM,gBAAgB;;;;;;;iBAQ3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAM1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;iBAY9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,+DAA+D;AAC/D,eAAO,MAAM,iBAAiB;;;;iBAI5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAW9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,eAAe;;;;;;;;;;;;;;iBAQ1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/services/agent/eval/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;GAUG;AAEH,4CAA4C;AAC5C,eAAO,MAAM,gBAAgB;;;;;;;iBAQ3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAM1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;iBAY9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,+DAA+D;AAC/D,eAAO,MAAM,iBAAiB;;;;iBAI5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAW9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,eAAe;;;;;;;;;;;;;;iBAQ1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { VideoBrief } from "../../schemas/brief.schema";
2
+ import { AgentVisualBible } from "../../schemas/videoPlan.schema";
3
+ /**
4
+ * Pin every mentioned character / element into the bible. When the bible
5
+ * builder already made an entity for it (matched by name — the brief text
6
+ * names the ref, since tokens resolve to names), that entity keeps its id and
7
+ * gains the ref's image + description; otherwise a new entity is appended.
8
+ *
9
+ * Creates a bible when there is none (C0 briefs skip the builder, and the
10
+ * builder soft-fails to undefined) — a user who picked a character expects
11
+ * that face even on the cheapest tier, and the bible is how it gets there.
12
+ * Returns the input untouched when nothing is pinnable.
13
+ */
14
+ export declare function pinMentionedRefsIntoBible(bible: AgentVisualBible | undefined, brief: VideoBrief): AgentVisualBible | undefined;
15
+ /**
16
+ * Planner system-prompt section for the user's @-mentioned refs. Empty string
17
+ * when there are none (the common path adds zero tokens).
18
+ */
19
+ export declare function buildMentionedRefsSection(brief: VideoBrief, bible: AgentVisualBible | null): string;
20
+ //# sourceMappingURL=mentionedRefs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mentionedRefs.d.ts","sourceRoot":"","sources":["../../../src/services/agent/mentionedRefs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAe,MAAM,gCAAgC,CAAC;AAgD/E;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,gBAAgB,GAAG,SAAS,EACnC,KAAK,EAAE,UAAU,GAChB,gBAAgB,GAAG,SAAS,CAwC9B;AAID;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,gBAAgB,GAAG,IAAI,GAC7B,MAAM,CAmER"}
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pinMentionedRefsIntoBible = pinMentionedRefsIntoBible;
4
+ exports.buildMentionedRefsSection = buildMentionedRefsSection;
5
+ const ID_PREFIX = {
6
+ characters: "CHAR",
7
+ objects: "OBJ",
8
+ environments: "ENV",
9
+ };
10
+ function entityKindFor(ref) {
11
+ if (ref.kind === "character")
12
+ return "characters";
13
+ if (ref.kind === "element")
14
+ return ref.subkind === "location" ? "environments" : "objects";
15
+ return null;
16
+ }
17
+ function nextId(entities, prefix) {
18
+ const max = entities.reduce((m, e) => {
19
+ const n = Number(e.id.split("_")[1]);
20
+ return Number.isFinite(n) && n > m ? n : m;
21
+ }, 0);
22
+ return `${prefix}_${max + 1}`;
23
+ }
24
+ const norm = (s) => s.trim().toLowerCase();
25
+ /**
26
+ * Pin every mentioned character / element into the bible. When the bible
27
+ * builder already made an entity for it (matched by name — the brief text
28
+ * names the ref, since tokens resolve to names), that entity keeps its id and
29
+ * gains the ref's image + description; otherwise a new entity is appended.
30
+ *
31
+ * Creates a bible when there is none (C0 briefs skip the builder, and the
32
+ * builder soft-fails to undefined) — a user who picked a character expects
33
+ * that face even on the cheapest tier, and the bible is how it gets there.
34
+ * Returns the input untouched when nothing is pinnable.
35
+ */
36
+ function pinMentionedRefsIntoBible(bible, brief) {
37
+ const pinnable = (brief.mentionedRefs ?? []).filter((r) => entityKindFor(r));
38
+ if (pinnable.length === 0)
39
+ return bible;
40
+ const tier = brief.consistencyTier ?? "C1";
41
+ const out = {
42
+ characters: [...(bible?.characters ?? [])],
43
+ objects: [...(bible?.objects ?? [])],
44
+ environments: [...(bible?.environments ?? [])],
45
+ consistencyTier: bible?.consistencyTier ?? (tier === "C0" ? "C1" : tier),
46
+ };
47
+ for (const ref of pinnable) {
48
+ const kind = entityKindFor(ref);
49
+ const list = out[kind];
50
+ const imageUrl = ref.imageUrls?.[0];
51
+ const description = ref.instruction?.trim() || ref.name;
52
+ const idx = list.findIndex((e) => norm(e.name) === norm(ref.name) || norm(e.name).includes(norm(ref.name)));
53
+ const existing = idx >= 0 ? list[idx] : undefined;
54
+ if (existing) {
55
+ list[idx] = {
56
+ ...existing,
57
+ name: ref.name,
58
+ // The ref's description matches its image; the builder's was
59
+ // inferred from the brief text alone.
60
+ description: imageUrl ? description : existing.description,
61
+ ...(imageUrl ? { referenceImageUrl: imageUrl } : {}),
62
+ };
63
+ }
64
+ else {
65
+ list.push({
66
+ id: nextId(list, ID_PREFIX[kind]),
67
+ name: ref.name,
68
+ description,
69
+ ...(imageUrl ? { referenceImageUrl: imageUrl } : {}),
70
+ });
71
+ }
72
+ }
73
+ return out;
74
+ }
75
+ const DIRECTION_KINDS = ["style", "effect", "color", "camera", "motion"];
76
+ /**
77
+ * Planner system-prompt section for the user's @-mentioned refs. Empty string
78
+ * when there are none (the common path adds zero tokens).
79
+ */
80
+ function buildMentionedRefsSection(brief, bible) {
81
+ const refs = brief.mentionedRefs ?? [];
82
+ if (refs.length === 0)
83
+ return "";
84
+ const allEntities = bible
85
+ ? [...bible.characters, ...bible.objects, ...bible.environments]
86
+ : [];
87
+ const pinned = refs
88
+ .filter((r) => entityKindFor(r))
89
+ .map((r) => ({ ref: r, entity: allEntities.find((e) => norm(e.name) === norm(r.name)) }))
90
+ .filter((p) => p.entity);
91
+ const by = (k) => refs.filter((r) => r.kind === k && (r.instruction || r.swatches?.length));
92
+ const lines = [
93
+ "",
94
+ "USER-SELECTED REFERENCES — the user explicitly picked these from their Reference Library by",
95
+ "@-mentioning them in the brief. They are requirements, not suggestions; the brief text names",
96
+ "each one where the user wants it.",
97
+ ];
98
+ if (pinned.length) {
99
+ lines.push(" Pinned subjects (each carries a reference image the generator receives):");
100
+ pinned.forEach(({ ref, entity }) => lines.push(` ${entity.id} — ${ref.name} (${ref.kind === "character" ? "character" : ref.subkind ?? "element"})`));
101
+ lines.push(" Every scene that shows one of these MUST list its id in scene.bibleEntityIds — that is how", " the reference image reaches the generator. A scene that omits the id renders a stranger.", " Scenes showing a pinned subject must use an AI-generated strategy (not stock / user-asset).");
102
+ }
103
+ const styles = by("style");
104
+ if (styles.length) {
105
+ lines.push(" Visual style — apply to EVERY scene prompt, verbatim:");
106
+ styles.forEach((r) => lines.push(` ${r.name}: ${r.instruction}`));
107
+ }
108
+ const colors = by("color");
109
+ if (colors.length) {
110
+ lines.push(" Color palette — grade every scene toward these colors:");
111
+ colors.forEach((r) => lines.push(` ${r.name}: ${r.swatches?.join(", ") ?? r.instruction}`));
112
+ }
113
+ const effects = by("effect");
114
+ if (effects.length) {
115
+ lines.push(" Effects — add to every scene prompt unless the brief ties one to a specific moment:");
116
+ effects.forEach((r) => lines.push(` ${r.name}: ${r.instruction}`));
117
+ }
118
+ const angles = by("camera");
119
+ if (angles.length) {
120
+ lines.push(" Camera angles / framing — use on the scene(s) the brief ties them to; if the brief doesn't say", " where, use it on the opening scene. Put the framing text in scene.prompt:");
121
+ angles.forEach((r) => lines.push(` ${r.name}: ${r.instruction}`));
122
+ }
123
+ const moves = by("motion");
124
+ if (moves.length) {
125
+ lines.push(" Camera moves — same placement rule as angles. Copy the direction into scene.prompt verbatim.", " A still can't carry a camera move, so those scenes must use ai-image-to-video or", " ai-text-to-video (never ai-image-static / stock-image-ken-burns):");
126
+ moves.forEach((r) => lines.push(` ${r.name}: ${r.instruction}`));
127
+ }
128
+ return lines.join("\n");
129
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"plannerMessages.d.ts","sourceRoot":"","sources":["../../../../src/services/agent/planner/plannerMessages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAehF;;;;;GAKG;AAEH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,YAAY,GAAG,SAAS,EACjC,QAAQ,EAAE,cAAc,GAAG,IAAI,EAC/B,WAAW,EAAE,iBAAiB,GAAG,IAAI,EACrC,KAAK,EAAE,gBAAgB,GAAG,IAAI,EAC9B,UAAU,CAAC,EAAE,MAAM,EACnB,eAAe,UAAQ;;;;;;KA0JxB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAiChE"}
1
+ {"version":3,"file":"plannerMessages.d.ts","sourceRoot":"","sources":["../../../../src/services/agent/planner/plannerMessages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAgBhF;;;;;GAKG;AAEH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,YAAY,GAAG,SAAS,EACjC,QAAQ,EAAE,cAAc,GAAG,IAAI,EAC/B,WAAW,EAAE,iBAAiB,GAAG,IAAI,EACrC,KAAK,EAAE,gBAAgB,GAAG,IAAI,EAC9B,UAAU,CAAC,EAAE,MAAM,EACnB,eAAe,UAAQ;;;;;;KA4JxB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAiChE"}
@@ -7,6 +7,7 @@ const modelQualityNotes_1 = require("../modelQualityNotes");
7
7
  const outputLanguage_1 = require("../../translation/outputLanguage");
8
8
  const promptSections_1 = require("./promptSections");
9
9
  const structuralRules_1 = require("./structuralRules");
10
+ const mentionedRefs_1 = require("../mentionedRefs");
10
11
  /**
11
12
  * Planner prompt assembly + the web-research heuristic. Pure functions (no LLM
12
13
  * call, no class state) so the Planner class stays focused on the
@@ -32,6 +33,7 @@ function buildPlannerMessages(brief, persona, brandKit, styleMemory, bible, last
32
33
  const brandKitSection = (0, promptSections_1.buildBrandKitSection)(brandKit);
33
34
  const styleMemorySection = (0, promptSections_1.buildStyleMemorySection)(styleMemory);
34
35
  const bibleSection = (0, promptSections_1.buildBibleSection)(bible);
36
+ const mentionedRefsSection = (0, mentionedRefs_1.buildMentionedRefsSection)(brief, bible);
35
37
  const modelCatalogSection = (0, promptSections_1.buildModelCatalogSection)();
36
38
  const preferredModelsSection = (0, promptSections_1.buildPreferredModelsSection)(brief.preferredModelKeys);
37
39
  const videoStrategySection = (0, promptSections_1.buildVideoStrategySection)(brief.videoStrategy);
@@ -127,6 +129,7 @@ function buildPlannerMessages(brief, persona, brandKit, styleMemory, bible, last
127
129
  personaSection,
128
130
  structuralRulesSection,
129
131
  bibleSection,
132
+ mentionedRefsSection,
130
133
  brandKitSection,
131
134
  styleMemorySection,
132
135
  "",
@@ -1 +1 @@
1
- {"version":3,"file":"promptSections.d.ts","sourceRoot":"","sources":["../../../../src/services/agent/planner/promptSections.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAShF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAGjE;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,GACnD,MAAM,CAkCR;AAED,wBAAgB,wBAAwB,IAAI,MAAM,CAWjD;AAYD;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CACzC,kBAAkB,EAAE,MAAM,EAAE,GAAG,SAAS,GACvC,MAAM,CAmCR;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,GAAG,MAAM,CAgC5E;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,GAAG,MAAM,CAqCxE;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,GAAG,MAAM,CAsBhF"}
1
+ {"version":3,"file":"promptSections.d.ts","sourceRoot":"","sources":["../../../../src/services/agent/planner/promptSections.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAShF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAGjE;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,GACnD,MAAM,CAkCR;AAED,wBAAgB,wBAAwB,IAAI,MAAM,CAWjD;AAYD;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CACzC,kBAAkB,EAAE,MAAM,EAAE,GAAG,SAAS,GACvC,MAAM,CAmCR;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,GAAG,MAAM,CAgC5E;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,GAAG,MAAM,CA6CxE;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,GAAG,MAAM,CAsBhF"}
@@ -181,6 +181,11 @@ function buildBibleSection(bible) {
181
181
  const environments = bible.environments ?? [];
182
182
  if (!characters.length && !objects.length && !environments.length)
183
183
  return "";
184
+ // Entities pinned from the user's @-mentioned refs arrive with an image even
185
+ // below C2 (see mentionedRefs.ts).
186
+ const withImages = [...characters, ...objects, ...environments]
187
+ .filter((e) => e.referenceImageUrl)
188
+ .map((e) => e.id);
184
189
  const formatEntity = (e) => ` ${e.id} — ${e.name}: ${e.description}`;
185
190
  const lines = [
186
191
  "",
@@ -200,7 +205,9 @@ function buildBibleSection(bible) {
200
205
  }
201
206
  lines.push("", "Bible usage contract (hard rule):", " - For every scene, set scene.bibleEntityIds to the list of entity ids that appear in that scene (e.g. [\"CHAR_1\", \"OBJ_1\"]).", " - For every entity in scene.bibleEntityIds, re-state its description verbatim in scene.prompt. Do NOT paraphrase — copy the exact words from the bible. Different scenes referencing the same entity must use IDENTICAL description text.", " - characterAnchorSceneIndex (AG-11) is still useful as a backup signal but the bible is primary. If both are set, both should agree.", bible.consistencyTier === "C2"
202
207
  ? " - C2 note: entities will additionally get pre-rendered reference images (executor threads them as inputImageUrls on image-gen calls). Your descriptions seed those reference images, so be visually specific."
203
- : " - C1 note: descriptions are the ONLY consistency signal — no reference images. Be as visually specific as possible.");
208
+ : withImages.length
209
+ ? ` - C1 note: ${withImages.join(", ")} carry user-supplied reference images (threaded as inputImageUrls on every scene that lists them); for the rest, descriptions are the only consistency signal — be as visually specific as possible.`
210
+ : " - C1 note: descriptions are the ONLY consistency signal — no reference images. Be as visually specific as possible.");
204
211
  return lines.join("\n");
205
212
  }
206
213
  function buildStyleMemorySection(memory) {
@@ -1 +1 @@
1
- {"version":3,"file":"runPlanning.d.ts","sourceRoot":"","sources":["../../../src/services/agent/runPlanning.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAoB,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,SAAS,EACV,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAwBnD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,cAAc,GAAG,IAAI,GAC9B,MAAM,CAUR;AAiED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,OAAO,EAAE,kBAAkB,CAAC;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC;IAClC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,GAAG,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CACtC,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBhF;AAED,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC;IAC7D,OAAO,EAAE,gBAAgB,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB,GAAG,IAAI,CAkCP;AAED,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,IAAI,EAAE,SAAS,CAAC;IAChB,6EAA6E;IAC7E,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpC,wGAAwG;IACxG,UAAU,EAAE,OAAO,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACtC,8CAA8C;IAC9C,OAAO,EAAE,kBAAkB,CAAC;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACtC,qEAAqE;IACrE,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAClC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CACtC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAuD7B"}
1
+ {"version":3,"file":"runPlanning.d.ts","sourceRoot":"","sources":["../../../src/services/agent/runPlanning.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAoB,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,SAAS,EACV,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAyBnD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,cAAc,GAAG,IAAI,GAC9B,MAAM,CAaR;AAiED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,OAAO,EAAE,kBAAkB,CAAC;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC;IAClC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,GAAG,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CACtC,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAuBhF;AAED,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC;IAC7D,OAAO,EAAE,gBAAgB,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB,GAAG,IAAI,CAkCP;AAED,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,IAAI,EAAE,SAAS,CAAC;IAChB,6EAA6E;IAC7E,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpC,wGAAwG;IACxG,UAAU,EAAE,OAAO,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACtC,8CAA8C;IAC9C,OAAO,EAAE,kBAAkB,CAAC;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACtC,qEAAqE;IACrE,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAClC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CACtC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAuD7B"}
@@ -11,6 +11,7 @@ const brief_schema_1 = require("../../schemas/brief.schema");
11
11
  const llmCallerRegistry_1 = require("./llmCallerRegistry");
12
12
  const bibleBuilder_1 = require("./bibleBuilder");
13
13
  const bibleImageVision_1 = require("./bibleImageVision");
14
+ const mentionedRefs_1 = require("./mentionedRefs");
14
15
  const planner_1 = require("./planner");
15
16
  const musicSelect_1 = require("./musicSelect");
16
17
  const beatSnap_1 = require("./beatSnap");
@@ -37,7 +38,10 @@ const isoNow = () => new Date().toISOString();
37
38
  * we never cache nothing as something.
38
39
  */
39
40
  function computeBibleCacheKey(brief, brandKit) {
40
- if ((brief.consistencyTier ?? "C1") === "C0")
41
+ // C0 builds no bible — unless the user @-mentioned a character/element,
42
+ // which gets pinned into one (see pinMentionedRefsIntoBible).
43
+ const pins = (brief.mentionedRefs ?? []).some((r) => r.kind === "character" || r.kind === "element");
44
+ if ((brief.consistencyTier ?? "C1") === "C0" && !pins)
41
45
  return "";
42
46
  return (0, crypto_1.createHash)("sha256")
43
47
  .update(JSON.stringify({
@@ -125,8 +129,11 @@ async function getBibleWithCache(args) {
125
129
  persona: args.persona,
126
130
  brandKit: args.brandKit,
127
131
  });
132
+ // Pin @-mentioned characters/elements (with their reference images) before
133
+ // the vision pass, so their descriptions get re-read from the image too.
134
+ const pinned = (0, mentionedRefs_1.pinMentionedRefsIntoBible)(built, args.brief);
128
135
  args.onStage?.("aligning_bible_with_images");
129
- const bible = await safeBibleImageVision(built);
136
+ const bible = await safeBibleImageVision(pinned);
130
137
  return { bible, fresh: true, key };
131
138
  }
132
139
  /**
@@ -140,6 +140,28 @@ declare const InputSchema: z.ZodObject<{
140
140
  transcriptExcerpt: z.ZodOptional<z.ZodString>;
141
141
  }, z.core.$strip>>;
142
142
  }, z.core.$strip>>>;
143
+ mentionedRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
144
+ kind: z.ZodEnum<{
145
+ element: "element";
146
+ motion: "motion";
147
+ color: "color";
148
+ style: "style";
149
+ character: "character";
150
+ effect: "effect";
151
+ camera: "camera";
152
+ voice: "voice";
153
+ model: "model";
154
+ }>;
155
+ slug: z.ZodString;
156
+ name: z.ZodString;
157
+ instruction: z.ZodOptional<z.ZodString>;
158
+ imageUrls: z.ZodOptional<z.ZodArray<z.ZodString>>;
159
+ subkind: z.ZodOptional<z.ZodString>;
160
+ presetKey: z.ZodOptional<z.ZodString>;
161
+ swatches: z.ZodOptional<z.ZodArray<z.ZodString>>;
162
+ voiceId: z.ZodOptional<z.ZodString>;
163
+ modelKey: z.ZodOptional<z.ZodString>;
164
+ }, z.core.$strip>>>;
143
165
  preferredModelKeys: z.ZodOptional<z.ZodArray<z.ZodString>>;
144
166
  tierBudget: z.ZodDefault<z.ZodEnum<{
145
167
  T0: "T0";
@@ -1 +1 @@
1
- {"version":3,"file":"generateScript.tool.d.ts","sourceRoot":"","sources":["../../../../src/services/agent/tools/generateScript.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAoB,MAAM,+BAA+B,CAAC;AAC7E,OAAO,EAAE,MAAM,EAAgB,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzC,OAAO,EAAgB,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAG/D;;;;;;;;;;;;;;;GAeG;AAEH,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAUf,CAAC;AAeH,QAAA,MAAM,YAAY;;;;;;;;iBAGhB,CAAC;AAEH;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,UAAU,EACjB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAoEtD;AAED,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAC7C,OAAO,WAAW,EAClB,OAAO,YAAY,CAcpB,CAAC"}
1
+ {"version":3,"file":"generateScript.tool.d.ts","sourceRoot":"","sources":["../../../../src/services/agent/tools/generateScript.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAoB,MAAM,+BAA+B,CAAC;AAC7E,OAAO,EAAE,MAAM,EAAgB,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzC,OAAO,EAAgB,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAG/D;;;;;;;;;;;;;;;GAeG;AAEH,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAUf,CAAC;AAeH,QAAA,MAAM,YAAY;;;;;;;;iBAGhB,CAAC;AAEH;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,UAAU,EACjB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAoEtD;AAED,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAC7C,OAAO,WAAW,EAClB,OAAO,YAAY,CAcpB,CAAC"}
@@ -123,6 +123,28 @@ declare const InputSchema: z.ZodObject<{
123
123
  transcriptExcerpt: z.ZodOptional<z.ZodString>;
124
124
  }, z.core.$strip>>;
125
125
  }, z.core.$strip>>>;
126
+ mentionedRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
127
+ kind: z.ZodEnum<{
128
+ element: "element";
129
+ motion: "motion";
130
+ color: "color";
131
+ style: "style";
132
+ character: "character";
133
+ effect: "effect";
134
+ camera: "camera";
135
+ voice: "voice";
136
+ model: "model";
137
+ }>;
138
+ slug: z.ZodString;
139
+ name: z.ZodString;
140
+ instruction: z.ZodOptional<z.ZodString>;
141
+ imageUrls: z.ZodOptional<z.ZodArray<z.ZodString>>;
142
+ subkind: z.ZodOptional<z.ZodString>;
143
+ presetKey: z.ZodOptional<z.ZodString>;
144
+ swatches: z.ZodOptional<z.ZodArray<z.ZodString>>;
145
+ voiceId: z.ZodOptional<z.ZodString>;
146
+ modelKey: z.ZodOptional<z.ZodString>;
147
+ }, z.core.$strip>>>;
126
148
  preferredModelKeys: z.ZodOptional<z.ZodArray<z.ZodString>>;
127
149
  tierBudget: z.ZodDefault<z.ZodEnum<{
128
150
  T0: "T0";
@@ -1 +1 @@
1
- {"version":3,"file":"planVideo.tool.d.ts","sourceRoot":"","sources":["../../../../src/services/agent/tools/planVideo.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAgB,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAQ/D,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAExD;AAED,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAIf,CAAC;AAEH,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAEhB,CAAC;AAEH,eAAO,MAAM,aAAa,EAAE,cAAc,CACxC,OAAO,WAAW,EAClB,OAAO,YAAY,CAuBpB,CAAC"}
1
+ {"version":3,"file":"planVideo.tool.d.ts","sourceRoot":"","sources":["../../../../src/services/agent/tools/planVideo.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAgB,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAQ/D,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAExD;AAED,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAIf,CAAC;AAEH,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAEhB,CAAC;AAEH,eAAO,MAAM,aAAa,EAAE,cAAc,CACxC,OAAO,WAAW,EAClB,OAAO,YAAY,CAuBpB,CAAC"}
@@ -28,9 +28,17 @@ import { ELANGUAGE_CODE, IDetectLang } from "../../globals/types";
28
28
  */
29
29
  export declare const TRANSLATION_FAILED = "TRANSLATION_FAILED";
30
30
  /**
31
- * Translate `text` from → to (default English). Primary CF first (with a couple
32
- * of quick retries), then a cheap LLM. Throws Error(TRANSLATION_FAILED) only if
33
- * BOTH layers fail; `.cause` carries the last error.
31
+ * Split `text` into pieces of at most `max` chars whose concatenation is
32
+ * exactly `text`. Breaks at sentence / line ends where possible; a single
33
+ * over-long sentence is hard-split at the last whitespace before `max`.
34
+ */
35
+ export declare function splitForTranslation(text: string, max?: number): string[];
36
+ /**
37
+ * Translate `text` from → to (default English). Long text is split into
38
+ * chunks translated in parallel (see CHUNK_MAX_CHARS); each chunk goes
39
+ * primary CF first (with a quick retry), then a cheap LLM. Throws
40
+ * Error(TRANSLATION_FAILED) only if BOTH layers fail for some chunk; `.cause`
41
+ * carries the last error.
34
42
  */
35
43
  declare function translate(text: string, from: ELANGUAGE_CODE, to?: ELANGUAGE_CODE, version?: "v1" | "v2"): Promise<string>;
36
44
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"translation.service.d.ts","sourceRoot":"","sources":["../../../src/services/translation/translation.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAmJvD;;;;GAIG;AACH,iBAAe,SAAS,CACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,cAAc,EACpB,EAAE,GAAE,cAAkC,EACtC,OAAO,GAAE,IAAI,GAAG,IAAW,GAC1B,OAAO,CAAC,MAAM,CAAC,CAsCjB;AAqDD;;;;;;;;;;;;;;GAcG;AACH,iBAAe,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA8ChE;AAOD,eAAO,MAAM,kBAAkB;;;CAG9B,CAAC;AAGF,qDAAqD;AACrD,eAAO,MAAM,qBAAqB,kBAAY,CAAC;AAC/C,0DAA0D;AAC1D,eAAO,MAAM,UAAU,uBAAiB,CAAC"}
1
+ {"version":3,"file":"translation.service.d.ts","sourceRoot":"","sources":["../../../src/services/translation/translation.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAiDvD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAkB,GAAG,MAAM,EAAE,CA8BjF;AA4ID;;;;;;GAMG;AACH,iBAAe,SAAS,CACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,cAAc,EACpB,EAAE,GAAE,cAAkC,EACtC,OAAO,GAAE,IAAI,GAAG,IAAW,GAC1B,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAiGD;;;;;;;;;;;;;;GAcG;AACH,iBAAe,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA8ChE;AAOD,eAAO,MAAM,kBAAkB;;;CAG9B,CAAC;AAGF,qDAAqD;AACrD,eAAO,MAAM,qBAAqB,kBAAY,CAAC;AAC/C,0DAA0D;AAC1D,eAAO,MAAM,UAAU,uBAAiB,CAAC"}
@@ -37,6 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.detectLang = exports.getTranslationWrapper = exports.translationService = exports.TRANSLATION_FAILED = void 0;
40
+ exports.splitForTranslation = splitForTranslation;
40
41
  const axios_1 = __importDefault(require("axios"));
41
42
  const enums_1 = require("../../globals/aiModels/enums");
42
43
  const types_1 = require("../../globals/types");
@@ -90,6 +91,76 @@ const PRIMARY_MAX_ATTEMPTS = 2;
90
91
  // handshake) gets a second, fresh try instead of being fatal.
91
92
  const FALLBACK_ATTEMPT_TIMEOUT_MS = 8000;
92
93
  const FALLBACK_MAX_ATTEMPTS = 2;
94
+ // 2026-09-28 ROOT CAUSE of the recurring "BOTH primary and LLM fallback"
95
+ // Slack alerts: fixed timeouts vs. LONG prompts. 14 days of prod logs: every
96
+ // sampled both-layer failure was a 483–2523-word prompt; every fast success
97
+ // was ≤~100 words. Neither upstream was down — a healthy gpt-4o-mini call on
98
+ // a real failed 726-word `id` prompt took 17s (1141 output tokens), so an 8s
99
+ // attempt could never win, and retrying just repeated the doomed call.
100
+ //
101
+ // Fix: split long text on paragraph/sentence boundaries into ≤CHUNK_MAX_CHARS
102
+ // pieces and translate them IN PARALLEL, so wall time tracks one chunk rather
103
+ // than the whole prompt. Per-attempt timeouts also scale with chunk length
104
+ // (base + per-char) so a max-size chunk has real headroom.
105
+ const CHUNK_MAX_CHARS = 1200;
106
+ const CHUNK_CONCURRENCY = 6;
107
+ const PRIMARY_TIMEOUT_PER_CHAR_MS = 2;
108
+ const FALLBACK_TIMEOUT_PER_CHAR_MS = 5;
109
+ function primaryTimeoutFor(text) {
110
+ return PRIMARY_TIMEOUT_MS + text.length * PRIMARY_TIMEOUT_PER_CHAR_MS;
111
+ }
112
+ function fallbackTimeoutFor(text) {
113
+ return FALLBACK_ATTEMPT_TIMEOUT_MS + text.length * FALLBACK_TIMEOUT_PER_CHAR_MS;
114
+ }
115
+ /**
116
+ * Split `text` into pieces of at most `max` chars whose concatenation is
117
+ * exactly `text`. Breaks at sentence / line ends where possible; a single
118
+ * over-long sentence is hard-split at the last whitespace before `max`.
119
+ */
120
+ function splitForTranslation(text, max = CHUNK_MAX_CHARS) {
121
+ if (text.length <= max)
122
+ return [text];
123
+ // Sentence-ish units, each carrying its trailing whitespace/newlines.
124
+ const units = text.match(/[^.!?。!?\n]*(?:[.!?。!?]+["'”’)\]]*|\n|$)\s*/g) ?? [text];
125
+ const pieces = [];
126
+ for (const unit of units) {
127
+ if (!unit)
128
+ continue;
129
+ let rest = unit;
130
+ while (rest.length > max) {
131
+ const cut = rest.lastIndexOf(" ", max);
132
+ const at = cut > max / 2 ? cut + 1 : max;
133
+ pieces.push(rest.slice(0, at));
134
+ rest = rest.slice(at);
135
+ }
136
+ if (rest)
137
+ pieces.push(rest);
138
+ }
139
+ const chunks = [];
140
+ let cur = "";
141
+ for (const p of pieces) {
142
+ if (cur && cur.length + p.length > max) {
143
+ chunks.push(cur);
144
+ cur = "";
145
+ }
146
+ cur += p;
147
+ }
148
+ if (cur)
149
+ chunks.push(cur);
150
+ return chunks;
151
+ }
152
+ /** Runs `fn` over `items` with at most `limit` in flight; preserves order. */
153
+ async function mapWithConcurrency(items, limit, fn) {
154
+ const out = new Array(items.length);
155
+ let next = 0;
156
+ const worker = async () => {
157
+ for (let i = next++; i < items.length; i = next++) {
158
+ out[i] = await fn(items[i]);
159
+ }
160
+ };
161
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
162
+ return out;
163
+ }
93
164
  // gpt-4o-mini: cheap (~$0.15/1M in, $0.60/1M out → fractions of a cent per
94
165
  // prompt), reliable, and independent of the Google video quota. Swap here if a
95
166
  // cheaper effective model becomes available.
@@ -105,7 +176,7 @@ function languageLabel(code) {
105
176
  async function callPrimaryTranslate(text, from, to, version) {
106
177
  const res = await axios_1.default.post(`${PRIMARY_BASE}/translate`, { text, from, to, version }, {
107
178
  headers: { "internal-key": process.env.TRANSLATION_SERVICE_KEY || "" },
108
- timeout: PRIMARY_TIMEOUT_MS,
179
+ timeout: primaryTimeoutFor(text),
109
180
  });
110
181
  const translated = res.data?.translated;
111
182
  if (typeof translated !== "string" || !translated.length) {
@@ -171,7 +242,7 @@ async function callLlmTranslate(text, from, to) {
171
242
  let lastErr;
172
243
  for (let attempt = 1; attempt <= FALLBACK_MAX_ATTEMPTS; attempt++) {
173
244
  try {
174
- return await withTimeout(callLlmTranslateOnce(text, from, to), FALLBACK_ATTEMPT_TIMEOUT_MS, "LLM fallback");
245
+ return await withTimeout(callLlmTranslateOnce(text, from, to), fallbackTimeoutFor(text), "LLM fallback");
175
246
  }
176
247
  catch (err) {
177
248
  lastErr = err;
@@ -180,13 +251,33 @@ async function callLlmTranslate(text, from, to) {
180
251
  throw lastErr;
181
252
  }
182
253
  /**
183
- * Translate `text` from → to (default English). Primary CF first (with a couple
184
- * of quick retries), then a cheap LLM. Throws Error(TRANSLATION_FAILED) only if
185
- * BOTH layers fail; `.cause` carries the last error.
254
+ * Translate `text` from → to (default English). Long text is split into
255
+ * chunks translated in parallel (see CHUNK_MAX_CHARS); each chunk goes
256
+ * primary CF first (with a quick retry), then a cheap LLM. Throws
257
+ * Error(TRANSLATION_FAILED) only if BOTH layers fail for some chunk; `.cause`
258
+ * carries the last error.
186
259
  */
187
260
  async function translate(text, from, to = types_1.ELANGUAGE_CODE.en, version = "v2") {
188
261
  if (!text || !text.trim())
189
262
  return text;
263
+ const chunks = splitForTranslation(text);
264
+ if (chunks.length === 1)
265
+ return translateChunk(text, from, to, version);
266
+ const translated = await mapWithConcurrency(chunks, CHUNK_CONCURRENCY, async (chunk) => {
267
+ // Translate the trimmed core and re-attach the chunk's own boundary
268
+ // whitespace — both layers trim, which would otherwise glue sentences
269
+ // together or drop paragraph breaks at chunk seams.
270
+ const lead = chunk.match(/^\s*/)?.[0] ?? "";
271
+ const trail = chunk.match(/\s*$/)?.[0] ?? "";
272
+ const core = chunk.trim();
273
+ if (!core)
274
+ return chunk;
275
+ return lead + (await translateChunk(core, from, to, version)).trim() + trail;
276
+ });
277
+ return translated.join("");
278
+ }
279
+ /** One chunk through primary → LLM fallback. */
280
+ async function translateChunk(text, from, to, version) {
190
281
  // ── Layer 1: primary Cloud Function ──────────────────────────────────────
191
282
  let primaryErr;
192
283
  for (let attempt = 1; attempt <= PRIMARY_MAX_ATTEMPTS; attempt++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vidspotai-shared",
3
- "version": "1.0.123",
3
+ "version": "1.0.124",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "exports": {