@nodaro/prompts 1.52.0 → 1.53.0
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/{chunk-M6EXNY42.cjs → chunk-EKRO42T3.cjs} +21 -7
- package/dist/chunk-EKRO42T3.cjs.map +1 -0
- package/dist/{chunk-HETYHGGW.js → chunk-XR7RAHZI.js} +21 -7
- package/dist/chunk-XR7RAHZI.js.map +1 -0
- package/dist/entries/assembly.cjs +24 -24
- package/dist/entries/assembly.d.cts +21 -6
- package/dist/entries/assembly.d.ts +21 -6
- package/dist/entries/assembly.js +1 -1
- package/dist/index.cjs +24 -24
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/src/__tests__/frame-reference-slots.test.ts +16 -0
- package/src/frame-reference-slots.ts +4 -4
- package/src/ref-binding.ts +44 -7
- package/src/video-reference-resolver.ts +1 -1
- package/dist/chunk-HETYHGGW.js.map +0 -1
- package/dist/chunk-M6EXNY42.cjs.map +0 -1
|
@@ -22,12 +22,26 @@ var REF_BINDING = {
|
|
|
22
22
|
ordinal: (n) => `@image_${n}`,
|
|
23
23
|
frame: (n, role) => `Use @image_${n} as the ${role} (${role === "opening" ? "first" : "last"}) frame of the video.`
|
|
24
24
|
};
|
|
25
|
+
var roleWords = (role) => `${role} (${role === "opening" ? "first" : "last"})`;
|
|
26
|
+
function plainList(first, last) {
|
|
27
|
+
const names = Array.from({ length: last - first + 1 }, (_, i) => `image ${first + i}`);
|
|
28
|
+
return names.length === 1 ? names[0] : `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
|
|
29
|
+
}
|
|
25
30
|
function imageBindingFor(syntax) {
|
|
26
|
-
if (syntax === "
|
|
27
|
-
|
|
31
|
+
if (syntax === "image N") {
|
|
32
|
+
return {
|
|
33
|
+
ordinal: (n) => `image ${n}`,
|
|
34
|
+
frame: (n, role) => `image ${n} is the ${roleWords(role)} frame of the video.`,
|
|
35
|
+
frames: (first, last) => `image ${first} is the ${roleWords("opening")} frame and image ${last} is the ${roleWords("closing")} frame of the video.`,
|
|
36
|
+
identities: (first, last) => first === last ? `image ${first} is a reference for the subject; it is not a frame.` : `${plainList(first, last)} are references for the subject; they are not frames.`
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const ordinal = syntax === "@image_N" ? REF_BINDING.ordinal : syntax === "@image(n)" ? (n) => `@image(${n})` : (n) => `[Image ${n}]`;
|
|
28
40
|
return {
|
|
29
41
|
ordinal,
|
|
30
|
-
frame: (n, role) => `Use ${ordinal(n)} as the ${
|
|
42
|
+
frame: syntax === "@image_N" ? REF_BINDING.frame : (n, role) => `Use ${ordinal(n)} as the ${roleWords(role)} frame of the video.`,
|
|
43
|
+
frames: (first, last) => `Use ${ordinal(first)} as the ${roleWords("opening")} frame and ${ordinal(last)} as the ${roleWords("closing")} frame of the video.`,
|
|
44
|
+
identities: (first, last) => identityRefsSentence(first, last, ordinal)
|
|
31
45
|
};
|
|
32
46
|
}
|
|
33
47
|
var SEEDANCE_VIDEO_EDIT_PREFIX = "edit {video:1} as follows:\n";
|
|
@@ -796,13 +810,13 @@ function placeFramesAfterReferences(args) {
|
|
|
796
810
|
const promptBindsStart = syntax === "@image_N" && promptBindsFirstFrame(args.prompt);
|
|
797
811
|
let frameSentence = "";
|
|
798
812
|
if (promptBindsStart && firstFrameOrdinal > 0 && lastFrameOrdinal === 0) ; else if (firstFrameOrdinal > 0 && lastFrameOrdinal > 0) {
|
|
799
|
-
frameSentence =
|
|
813
|
+
frameSentence = binding.frames(firstFrameOrdinal, lastFrameOrdinal);
|
|
800
814
|
} else if (firstFrameOrdinal > 0) {
|
|
801
815
|
frameSentence = binding.frame(firstFrameOrdinal, "opening");
|
|
802
816
|
} else if (lastFrameOrdinal > 0) {
|
|
803
817
|
frameSentence = binding.frame(lastFrameOrdinal, "closing");
|
|
804
818
|
}
|
|
805
|
-
const identitySentence = args.bindIdentities && kept.length > 0 && frameCount > 0 ?
|
|
819
|
+
const identitySentence = args.bindIdentities && kept.length > 0 && frameCount > 0 ? binding.identities(1, kept.length) : "";
|
|
806
820
|
const promptSuffix = [frameSentence, identitySentence].filter(Boolean).join(" ");
|
|
807
821
|
return { imageUrls, promptSuffix, droppedRefImages, firstFrameOrdinal, lastFrameOrdinal };
|
|
808
822
|
}
|
|
@@ -988,5 +1002,5 @@ exports.resolveSeedance2Inputs = resolveSeedance2Inputs;
|
|
|
988
1002
|
exports.resolveVeoI2vInputs = resolveVeoI2vInputs;
|
|
989
1003
|
exports.resolveVideoReferenceCore = resolveVideoReferenceCore;
|
|
990
1004
|
exports.truncateForField = truncateForField;
|
|
991
|
-
//# sourceMappingURL=chunk-
|
|
992
|
-
//# sourceMappingURL=chunk-
|
|
1005
|
+
//# sourceMappingURL=chunk-EKRO42T3.cjs.map
|
|
1006
|
+
//# sourceMappingURL=chunk-EKRO42T3.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ref-binding.ts","../src/ref-id-tokens.ts","../src/video-reference-resolver.ts","../src/sound-aggregator.ts","../src/assemble-suno-input.ts","../src/assemble-image-input.ts","../src/assemble-video-input.ts","../src/frame-reference-slots.ts","../src/seedance-2-inputs.ts","../src/gemini-omni-inputs.ts","../src/veo-i2v-inputs.ts","../src/surround-fill.ts","../src/frame-delivery.ts"],"names":["tidyReferenceTokenGaps","unboundReferenceTokenText","REFERENCE_ROLE_PRESETS","resolveDefaultRole","roleToPhrase","buildIdentityLockLine","withForcedIdentityLock","referenceDescriptionLine","renderDescribedReferenceLines","renderReferenceCaptionLines","resolved","appendReferenceLines","findCharacterMentionTokens","resolveCharacterMentions","DEFAULT_USAGE_MODE","usageModeDirective","insertBeforeStyleSection","applyReferenceOrderToVideo","getParameterPromptHint","getMusicEra","getMusicGenre","buildMusicMoodHints","asBodyClauses","renderSubjectHints","SUBJECT_IMAGE_HINT_MODE_DEFAULT","partitionStyleClauses","IMAGE_HINT_MODE_DEFAULT","renderStructuredFields","composeSectionedPrompt","imageReferenceLimit","styleReferenceDescription","buildImagePromptWithOverflow","sectionedClauseCosts","keepableDirectionHints","SUBJECT_VIDEO_HINT_MODE_DEFAULT","VIDEO_HINT_MODE_DEFAULT","kept","body","SEEDANCE_2_REF_LIMITS","VIDEO_REF_LIMITS_BY_PROVIDER","resolveFrameDelivery"],"mappings":";;;;;;;;;;;;;AA8BO,SAAS,oBAAA,CACd,YAAA,EACA,WAAA,EACA,OAAA,GAAiC,YAAY,OAAA,EACrC;AACR,EAAA,OAAO,YAAA,KAAiB,WAAA,GACpB,CAAA,EAAG,OAAA,CAAQ,YAAY,CAAC,CAAA,kHAAA,CAAA,GACxB,CAAA,EAAG,OAAA,CAAQ,YAAY,CAAC,CAAA,SAAA,EAAY,OAAA,CAAQ,WAAW,CAAC,CAAA,oHAAA,CAAA;AAC9D;AAEO,IAAM,WAAA,GAAc;AAAA,EACzB,OAAO,CAAC,KAAA,EAAe,MAAc,CAAA,IAAA,EAAO,KAAK,gBAAgB,CAAC,CAAA,CAAA;AAAA,EAClE,OAAO,CAAC,KAAA,EAAe,MAAc,CAAA,IAAA,EAAO,KAAK,gBAAgB,CAAC,CAAA,CAAA;AAAA,EAClE,OAAO,CAAC,KAAA,EAAe,MAAc,CAAA,IAAA,EAAO,KAAK,gBAAgB,CAAC,CAAA,CAAA;AAAA;AAAA,EAElE,OAAA,EAAS,CAAC,CAAA,KAAc,CAAA,OAAA,EAAU,CAAC,CAAA,CAAA;AAAA,EACnC,KAAA,EAAO,CAAC,CAAA,EAAW,IAAA,KACjB,CAAA,WAAA,EAAc,CAAC,CAAA,QAAA,EAAW,IAAI,CAAA,EAAA,EAAK,IAAA,KAAS,SAAA,GAAY,OAAA,GAAU,MAAM,CAAA,qBAAA;AAC5E;AAiBA,IAAM,SAAA,GAAY,CAAC,IAAA,KAAgC,CAAA,EAAG,IAAI,CAAA,EAAA,EAAK,IAAA,KAAS,SAAA,GAAY,OAAA,GAAU,MAAM,CAAA,CAAA,CAAA;AAGpG,SAAS,SAAA,CAAU,OAAe,IAAA,EAAsB;AACtD,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,CAAK,EAAE,QAAQ,IAAA,GAAO,KAAA,GAAQ,CAAA,EAAE,EAAG,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,MAAA,EAAS,KAAA,GAAQ,CAAC,CAAA,CAAE,CAAA;AACrF,EAAA,OAAO,KAAA,CAAM,WAAW,CAAA,GAAI,KAAA,CAAM,CAAC,CAAA,GAAI,CAAA,EAAG,MAAM,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,KAAA,EAAQ,MAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAC,CAAA,CAAA;AACxG;AAeO,SAAS,gBAAgB,MAAA,EAA4C;AAC1E,EAAA,IAAI,WAAW,SAAA,EAAW;AACxB,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,CAAC,CAAA,KAAM,CAAA,MAAA,EAAS,CAAC,CAAA,CAAA;AAAA,MAC1B,KAAA,EAAO,CAAC,CAAA,EAAG,IAAA,KAAS,SAAS,CAAC,CAAA,QAAA,EAAW,SAAA,CAAU,IAAI,CAAC,CAAA,oBAAA,CAAA;AAAA,MACxD,MAAA,EAAQ,CAAC,KAAA,EAAO,IAAA,KAAS,SAAS,KAAK,CAAA,QAAA,EAAW,SAAA,CAAU,SAAS,CAAC,CAAA,iBAAA,EAAoB,IAAI,CAAA,QAAA,EAAW,SAAA,CAAU,SAAS,CAAC,CAAA,oBAAA,CAAA;AAAA,MAC7H,UAAA,EAAY,CAAC,KAAA,EAAO,IAAA,KAAS,KAAA,KAAU,IAAA,GACnC,CAAA,MAAA,EAAS,KAAK,CAAA,mDAAA,CAAA,GACd,CAAA,EAAG,SAAA,CAAU,KAAA,EAAO,IAAI,CAAC,CAAA,qDAAA;AAAA,KAC/B;AAAA,EACF;AACA,EAAA,MAAM,OAAA,GAAU,MAAA,KAAW,UAAA,GACvB,WAAA,CAAY,UACZ,MAAA,KAAW,WAAA,GAAc,CAAC,CAAA,KAAc,UAAU,CAAC,CAAA,CAAA,CAAA,GAAM,CAAC,CAAA,KAAc,UAAU,CAAC,CAAA,CAAA,CAAA;AACvF,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,KAAA,EAAO,MAAA,KAAW,UAAA,GAAa,WAAA,CAAY,QAAQ,CAAC,CAAA,EAAG,IAAA,KAAS,CAAA,IAAA,EAAO,QAAQ,CAAC,CAAC,CAAA,QAAA,EAAW,SAAA,CAAU,IAAI,CAAC,CAAA,oBAAA,CAAA;AAAA,IAC3G,QAAQ,CAAC,KAAA,EAAO,SAAS,CAAA,IAAA,EAAO,OAAA,CAAQ,KAAK,CAAC,CAAA,QAAA,EAAW,UAAU,SAAS,CAAC,cAAc,OAAA,CAAQ,IAAI,CAAC,CAAA,QAAA,EAAW,SAAA,CAAU,SAAS,CAAC,CAAA,oBAAA,CAAA;AAAA,IACvI,YAAY,CAAC,KAAA,EAAO,SAAS,oBAAA,CAAqB,KAAA,EAAO,MAAM,OAAO;AAAA,GACxE;AACF;AAUO,IAAM,0BAAA,GAA6B;AAKnC,SAAS,6BAA6B,MAAA,EAAoC;AAC/E,EAAA,MAAM,IAAA,GAAA,CAAQ,MAAA,IAAU,EAAA,EAAI,IAAA,EAAK;AACjC,EAAA,IAAI,oDAAA,CAAqD,IAAA,CAAK,IAAI,CAAA,EAAG,OAAO,IAAA;AAC5E,EAAA,OAAO,CAAA,EAAG,0BAA0B,CAAA,EAAG,IAAI,CAAA,CAAA;AAC7C;;;AC1GA,IAAM,kBAAA,GAAqB,mBAAA;AAE3B,IAAM,mBAAA,GAAsB,SAAA;AAU5B,IAAM,eAAA,GAAkB,4BAAA;AAMxB,IAAM,yBAAA,GAA4B,6BAAA;AAqBlC,SAAS,WAAW,OAAA,EAAiD;AACnE,EAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,WAAA,CAAY,GAAG,CAAA;AAClC,EAAA,IAAI,EAAA,KAAO,EAAA,EAAI,OAAO,EAAE,IAAI,OAAA,EAAQ;AACpC,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,KAAA,CAAM,EAAA,GAAK,CAAC,CAAA;AACjC,EAAA,IAAI,CAAC,mBAAmB,IAAA,CAAK,IAAI,GAAG,OAAO,EAAE,IAAI,OAAA,EAAQ;AACzD,EAAA,OAAO,EAAE,IAAI,OAAA,CAAQ,KAAA,CAAM,GAAG,EAAE,CAAA,EAAG,OAAO,IAAA,EAAK;AACjD;AA0BO,SAAS,kBAAA,CACd,QACA,GAAA,EACoB;AACpB,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,oBAAoB,IAAA,CAAK,MAAM,GAAG,OAAO,MAAA;AACzD,EAAA,MAAM,KAAA,GAAQ,CAAC,EAAA,KAAwB,EAAA,CAAG,SAAS,CAAA,KAAM,GAAA,CAAI,QAAA,CAAS,GAAA,CAAI,EAAE,CAAA,IAAK,GAAA,CAAI,QAAA,CAAS,IAAI,EAAE,CAAA,CAAA;AACpG,EAAA,MAAM,IAAA,GAAO,CAAC,EAAA,EAAY,KAAA,KAAsC;AAC9D,IAAA,MAAM,IAAA,GAAO,GAAA,CAAI,QAAA,CAAS,GAAA,CAAI,EAAE,CAAA;AAChC,IAAA,IAAI,SAAS,MAAA,IAAa,IAAA,IAAQ,CAAA,IAAK,IAAA,IAAQ,IAAI,UAAA,EAAY;AAC7D,MAAA,OAAO,KAAA,GAAQ,YAAY,KAAA,CAAM,KAAA,EAAO,IAAI,CAAA,GAAI,WAAA,CAAY,QAAQ,IAAI,CAAA;AAAA,IAC1E;AACA,IAAA,OAAO,KAAA,IAAS,GAAA,CAAI,QAAA,CAAS,GAAA,CAAI,EAAE,CAAA,IAAK,EAAA;AAAA,EAC1C,CAAA;AACA,EAAA,OAAO,MAAA,CACJ,OAAA,CAAQ,eAAA,EAAiB,CAAC,QAAQ,OAAA,KAAoB;AACrD,IAAA,IAAI,MAAM,OAAO,CAAA,EAAG,OAAO,IAAA,CAAK,SAAS,MAAS,CAAA;AAClD,IAAA,MAAM,EAAE,EAAA,EAAI,KAAA,EAAM,GAAI,WAAW,OAAO,CAAA;AACxC,IAAA,IAAI,MAAM,EAAE,CAAA,EAAG,OAAO,IAAA,CAAK,IAAI,KAAK,CAAA;AACpC,IAAA,OAAO,KAAA,IAAS,EAAA;AAAA,EAClB,CAAC,CAAA,CACA,OAAA,CAAQ,yBAAA,EAA2B,EAAE,CAAA;AAC1C;;;ACpCA,IAAM,kBAAA,GAAqB,wDAAA;AA+BpB,SAAS,sBAAA,CACd,QACA,MAAA,EACoB;AACpB,EAAA,IAAI,CAAC,QAAQ,OAAO,MAAA;AACpB,EAAA,OAAOA,wCAAA;AAAA,IACL,OAAO,OAAA,CAAQ,kBAAA,EAAoB,CAAC,MAAA,EAAQ,OAAA,EAAiB,MAAc,KAAA,KAAmB;AAC5F,MAAA,MAAM,IAAA,GAAO,QAAQ,WAAA,EAAY;AACjC,MAAA,MAAM,CAAA,GAAI,QAAA,CAAS,IAAA,EAAM,EAAE,CAAA;AAC3B,MAAA,IAAI,CAAA,GAAI,KAAK,CAAA,GAAI,MAAA,CAAO,IAAI,CAAA,EAAG,OAAOC,4CAA0B,KAAK,CAAA;AACrE,MAAA,IAAI,OAAO,OAAO,WAAA,CAAY,IAAI,CAAA,CAAE,OAAO,CAAC,CAAA;AAC5C,MAAA,OAAO,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,EAAI,CAAC,CAAA,CAAA;AAAA,IACtB,CAAC;AAAA,GACH,IAAK,MAAA;AACP;AAmMA,SAAS,mCAAA,CACP,MAAA,EACA,MAAA,EACA,IAAA,EACA,MAAA,EACqB;AACrB,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAgC;AACnD,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAgC;AACtD,EAAA,KAAA,MAAW,KAAK,IAAA,EAAM;AACpB,IAAA,IAAI,CAAC,EAAE,aAAA,EAAe;AACtB,IAAA,IAAI,CAAC,CAAA,CAAE,WAAA,SAAoB,GAAA,CAAI,CAAA,CAAE,eAAe,CAAC,CAAA;AAAA,SAC5C,SAAA,CAAU,IAAI,CAAA,EAAG,CAAA,CAAE,aAAa,CAAA,CAAA,EAAI,CAAA,CAAE,WAAW,CAAA,CAAA,EAAI,CAAC,CAAA;AAAA,EAC7D;AAEA,EAAA,MAAM,OAAA,GAAUC,gCAAuB,iBAAiB,CAAA;AAExD,EAAA,MAAM,uBAAA,uBAA8B,GAAA,EAAY;AAChD,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAgC;AAKrD,EAAA,MAAM,iBAAA,uBAAwB,GAAA,EAAqB;AACnD,EAAA,MAAM,UAA+E,EAAC;AACtF,EAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AAGtB,IAAA,MAAM,YAAA,GAAe,CAAA,CAAE,WAAA,GACnB,SAAA,CAAU,GAAA,CAAI,CAAA,EAAG,CAAA,CAAE,aAAa,CAAA,CAAA,EAAI,CAAA,CAAE,WAAW,CAAA,CAAE,CAAA,GACnD,MAAA;AACJ,IAAA,MAAM,KAAA,GAAQ,YAAA,IAAgB,MAAA,CAAO,GAAA,CAAI,EAAE,aAAa,CAAA;AACxD,IAAA,IAAI,CAAC,KAAA,IAAS,CAAC,KAAA,CAAM,GAAA,EAAK;AAC1B,IAAA,uBAAA,CAAwB,GAAA,CAAI,EAAE,aAAa,CAAA;AAC3C,IAAA,QAAA,CAAS,GAAA,CAAI,KAAA,CAAM,GAAA,EAAK,KAAK,CAAA;AAC7B,IAAA,IAAI,CAAA,CAAE,SAAS,MAAA,EAAW,iBAAA,CAAkB,IAAI,KAAA,CAAM,GAAA,EAAK,EAAE,IAAI,CAAA;AAIjE,IAAA,MAAM,YAAA,GAAA,CAAgB,CAAA,CAAE,IAAA,IAAQ,EAAA,EAAI,IAAA,EAAK;AACzC,IAAA,MAAM,WAAW,CAAA,CAAE,SAAA,IAAa,CAAA,CAAE,WAAA,IAAe,IAAI,IAAA,EAAK;AAU1D,IAAA,MAAM,OAAO,YAAA,GACT,YAAA,GACA,YAAY,OAAA,CAAQ,QAAA,CAAS,OAAO,CAAA,IAAM,CAAA,CAAE,aAAa,IAAA,IAAQ,CAAC,gBAChE,OAAA,GACAC,2BAAA,CAAmB,MAAM,WAAA,EAAa,KAAA,CAAM,kBAAkB,iBAAiB,CAAA;AACrF,IAAA,OAAA,CAAQ,IAAA,CAAK,EAAE,KAAA,EAAO,CAAA,CAAE,KAAA,EAAO,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAQ,GAAA,EAAK,KAAA,CAAM,GAAA,EAAK,IAAA,EAAM,CAAA;AAAA,EACzE;AAKA,EAAA,MAAM,iBAA2B,EAAC;AAClC,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAoB;AAC1C,EAAA,KAAA,MAAW,KAAK,OAAA,EAAS;AACvB,IAAA,IAAI,CAAC,SAAA,CAAU,GAAA,CAAI,CAAA,CAAE,GAAG,CAAA,EAAG;AACzB,MAAA,SAAA,CAAU,IAAI,CAAA,CAAE,GAAA,EAAK,MAAA,GAAS,SAAA,CAAU,OAAO,CAAC,CAAA;AAChD,MAAA,cAAA,CAAe,IAAA,CAAK,EAAE,GAAG,CAAA;AAAA,IAC3B;AAAA,EACF;AACA,EAAA,MAAM,UAAA,GAAa,CAAC,GAAA,KAAwB;AAC1C,IAAA,MAAM,IAAA,GAAO,SAAA,CAAU,GAAA,CAAI,GAAG,CAAA;AAC9B,IAAA,OAAO,IAAA,KAAS,SAAY,WAAA,CAAY,OAAA,CAAQ,IAAI,CAAA,GAAI,WAAA,CAAY,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAA;AAAA,EACxF,CAAA;AAGA,EAAA,IAAI,cAAA,GAAiB,MAAA;AACrB,EAAA,KAAA,MAAW,CAAA,IAAK,CAAC,GAAG,OAAO,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,MAAA,GAAS,CAAA,CAAE,MAAM,CAAA,EAAG;AAChE,IAAA,MAAM,SAASC,qBAAA,CAAa,CAAA,CAAE,MAAM,UAAA,CAAW,CAAA,CAAE,GAAG,CAAC,CAAA;AACrD,IAAA,cAAA,GACE,cAAA,CAAe,KAAA,CAAM,CAAA,EAAG,CAAA,CAAE,MAAM,CAAA,GAAI,MAAA,GAAS,cAAA,CAAe,KAAA,CAAM,CAAA,CAAE,MAAA,GAAS,CAAA,CAAE,MAAM,MAAM,CAAA;AAAA,EAC/F;AAGA,EAAA,MAAM,YAAsB,EAAC;AAC7B,EAAA,MAAM,oBAA8B,EAAC;AACrC,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAY;AACjC,EAAA,KAAA,MAAW,KAAK,OAAA,EAAS;AACvB,IAAA,IAAI,QAAA,CAAS,GAAA,CAAI,CAAA,CAAE,GAAG,CAAA,EAAG;AACzB,IAAA,QAAA,CAAS,GAAA,CAAI,EAAE,GAAG,CAAA;AAClB,IAAA,MAAM,GAAA,GAAM,QAAA,CAAS,GAAA,CAAI,CAAA,CAAE,GAAG,CAAA;AAC9B,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,MAAM,OAAA,GAAU,UAAA,CAAW,CAAA,CAAE,GAAG,CAAA;AAChC,IAAA,MAAM,IAAA,GAAOC,uCAAA,CAAsBC,wCAAA,CAAuB,GAAA,EAAK,iBAAA,CAAkB,IAAI,CAAA,CAAE,GAAG,CAAC,CAAA,EAAG,OAAO,CAAA;AACrG,IAAA,IAAI,IAAA,EAAM,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA;AAI7B,IAAA,MAAM,YAAA,GAAeC,0CAAA,CAAyB,OAAA,EAAS,GAAA,CAAI,mBAAmB,CAAA;AAC9E,IAAA,IAAI,YAAA,EAAc,iBAAA,CAAkB,IAAA,CAAK,YAAY,CAAA;AACrD,IAAA,MAAM,MAAA,GAAS,GAAA,CAAI,gBAAA,EAAkB,IAAA,EAAK;AAC1C,IAAA,IAAI,MAAA,EAAQ,iBAAA,CAAkB,IAAA,CAAK,MAAM,CAAA;AAAA,EAC3C;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,cAAA,EAAgB,cAAA,EAAgB,uBAAA,EAAyB,WAAW,iBAAA,EAAkB;AACzG;AAkBO,SAAS,0BACd,IAAA,EAC0D;AAQ1D,EAAA,MAAM,iBAA2B,EAAC;AAClC,EAAA;AACE,IAAA,MAAM,QAAA,uBAAe,GAAA,EAAY;AACjC,IAAA,KAAA,MAAW,CAAA,IAAK,IAAA,CAAK,cAAA,IAAkB,EAAC,EAAG;AACzC,MAAA,MAAM,CAAA,GAAI,GAAG,IAAA,EAAK;AAClB,MAAA,IAAI,CAAA,IAAK,CAAC,QAAA,CAAS,GAAA,CAAI,CAAC,CAAA,EAAG;AAAE,QAAA,QAAA,CAAS,IAAI,CAAC,CAAA;AAAG,QAAA,cAAA,CAAe,KAAK,CAAC,CAAA;AAAA,MAAE;AAAA,IACvE;AAAA,EACF;AACA,EAAA,MAAM,UAAA,GAAa,KAAK,cAAA,IAAkB,IAAA;AAO1C,EAAA,MAAM,MAAA,GAAS,UAAA,GAAa,cAAA,CAAe,MAAA,GAAU,KAAK,aAAA,IAAiB,CAAA;AAC3E,EAAA,MAAM,UAAA,GAAa,UAAA,IAAc,IAAA,CAAK,aAAA,IAAiB,IAAA;AAOvD,EAAA,MAAM,WAAA,GAAc,CAAC,SAAA,MAAwC;AAAA,IAC3D,OAAO,UAAA,GAAc,MAAA,GAAS,YAAY,cAAA,CAAe,MAAA,GAAW,KAAK,aAAA,IAAiB,SAAA;AAAA,IAC1F,KAAA,EAAO,KAAK,aAAA,IAAiB,CAAA;AAAA,IAC7B,KAAA,EAAO,KAAK,aAAA,IAAiB;AAAA,GAC/B,CAAA;AAMA,EAAA,MAAM,wBAAA,GAA2B;AAAA,IAC/B,GAAGC,+CAAA,CAA8B,IAAA,CAAK,mBAAmB,CAAA;AAAA,IACzD,GAAGC,6CAAA,CAA4B,IAAA,CAAK,aAAA,EAAe,KAAK,aAAA,EAAe;AAAA,MACrE,KAAA,EAAO,KAAK,aAAA,IAAiB,CAAA;AAAA,MAC7B,KAAA,EAAO,KAAK,aAAA,IAAiB;AAAA,KAC9B;AAAA,GACH;AACA,EAAA,IAAI,aAAA,GAAgB,CAAC,GAAG,IAAA,CAAK,aAAa,CAAA;AAC1C,EAAA,MAAM,kBAAkB,IAAI,GAAA,CAAI,IAAA,CAAK,+BAAA,IAAmC,EAAE,CAAA;AAC1E,EAAA,IAAI,eAAA,CAAgB,OAAO,CAAA,EAAG;AAC5B,IAAA,aAAA,GAAgB,aAAA,CAAc,MAAA,CAAO,CAAC,CAAA,KAAM;AAC1C,MAAA,IAAI,CAAA,CAAE,MAAA,KAAW,iBAAA,EAAmB,OAAO,IAAA;AAC3C,MAAA,IAAI,CAAC,CAAA,CAAE,aAAA,EAAe,OAAO,IAAA;AAC7B,MAAA,IAAI,CAAA,CAAE,aAAa,OAAO,IAAA;AAC1B,MAAA,OAAO,CAAC,eAAA,CAAgB,GAAA,CAAI,CAAA,CAAE,aAAa,CAAA;AAAA,IAC7C,CAAC,CAAA;AAAA,EACH;AAKA,EAAA,MAAM,SAAA,GAAA,CAAa,IAAA,CAAK,SAAA,EAAW,MAAA,IAAU,CAAA,IAAK,CAAA;AAKlD,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAoB;AAC5C,EAAA,KAAA,MAAW,CAAA,IAAK,KAAK,aAAA,EAAe;AAClC,IAAA,IAAI,EAAE,EAAA,IAAM,CAAC,WAAA,CAAY,GAAA,CAAI,EAAE,EAAE,CAAA,EAAG,WAAA,CAAY,GAAA,CAAI,EAAE,EAAA,EAAI,CAAA,CAAE,WAAA,IAAe,CAAA,CAAE,iBAAiB,EAAE,CAAA;AAAA,EAClG;AACA,EAAA,KAAA,MAAW,EAAA,IAAM,IAAA,CAAK,SAAA,IAAa,EAAC,EAAG;AACrC,IAAA,IAAI,EAAA,CAAG,EAAA,IAAM,CAAC,WAAA,CAAY,GAAA,CAAI,EAAA,CAAG,EAAE,CAAA,EAAG,WAAA,CAAY,GAAA,CAAI,EAAA,CAAG,EAAA,EAAI,EAAE,CAAA;AAAA,EACjE;AACA,EAAA,KAAA,MAAW,CAAC,EAAA,EAAI,IAAI,KAAK,IAAA,CAAK,YAAA,IAAgB,EAAC,EAAG;AAChD,IAAA,IAAI,EAAA,EAAI,WAAA,CAAY,GAAA,CAAI,EAAA,EAAI,IAAI,CAAA;AAAA,EAClC;AAIA,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAoB;AAC5C,EAAA,IAAI,aAAA,CAAc,MAAA,KAAW,CAAA,IAAK,CAAC,SAAA,EAAW;AAM5C,IAAA,MAAM,MAAA,GAAS,WAAA,CAAY,cAAA,CAAe,MAAM,CAAA;AAIhD,IAAA,MAAMC,SAAAA,GAAW,sBAAA;AAAA,MACf,kBAAA,CAAmB,IAAA,CAAK,MAAA,EAAQ,EAAE,QAAA,EAAU,WAAA,EAAa,QAAA,EAAU,WAAA,EAAa,UAAA,EAAY,MAAA,CAAO,KAAA,EAAO,CAAA;AAAA,MAC1G;AAAA,KACF;AACA,IAAA,OAAO;AAAA;AAAA;AAAA;AAAA,MAIL,MAAA,EAAQ,wBAAA,CAAyB,MAAA,GAAS,CAAA,GACtCC,sCAAA,CAAqBD,SAAAA,IAAY,EAAA,EAAI,wBAAA,EAA0B,IAAA,CAAK,WAAA,KAAgB,IAAA,GAAO,QAAA,GAAW,QAAQ,CAAA,GAC9GA,SAAAA;AAAA,MACJ,cAAA,EAAgB,CAAC,GAAG,cAAc;AAAA,KACpC;AAAA,EACF;AACA,EAAA,MAAM,iBAAiB,KAAA,CAAM,IAAA;AAAA,IAC3B,IAAI,GAAA;AAAA,MACF,aAAA,CACG,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,aAAa,CAAA,CAC1B,MAAA,CAAO,CAAC,MAAmB,OAAO,CAAA,KAAM,QAAA,IAAY,CAAA,CAAE,SAAS,CAAC;AAAA;AACrE,GACF;AAIA,EAAA,MAAM,mBAAA,GAAsB,KAAK,MAAA,IAAU,EAAA;AAC3C,EAAA,MAAM,aAAA,GAAgB,eAAe,MAAA,GAAS,CAAA,GAC1CE,oCAA2B,mBAAA,EAAqB,cAAc,IAC9D,EAAC;AAKL,EAAA,MAAM,MAAA,GAAS,KAAK,WAAA,KAAgB,IAAA;AAEpC,EAAA,IAAI,QAAA;AAGJ,EAAA,IAAI,mBAA6B,EAAC;AAClC,EAAA,IAAI,2BAAqC,EAAC;AAC1C,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAM,CAAA,GAAI,mCAAA,CAAoC,mBAAA,EAAqB,aAAA,EAAe,eAAe,MAAM,CAAA;AACvG,IAAA,QAAA,GAAW,EAAE,QAAQ,CAAA,CAAE,MAAA,EAAQ,gBAAgB,CAAA,CAAE,cAAA,EAAgB,uBAAA,EAAyB,CAAA,CAAE,uBAAA,EAAwB;AACpH,IAAA,gBAAA,GAAmB,CAAA,CAAE,SAAA;AACrB,IAAA,wBAAA,GAA2B,CAAA,CAAE,iBAAA;AAAA,EAC/B,CAAA,MAAO;AACL,IAAA,QAAA,GAAW,cAAc,MAAA,GAAS,CAAA,GAC9BC,0CAAA,CAAyB,mBAAA,EAAqB,eAAe,aAAa,CAAA,GAC1E,EAAE,MAAA,EAAQ,qBAAqB,cAAA,EAAgB,IAAgB,uBAAA,kBAAyB,IAAI,KAAY,EAAE;AAAA,EAChH;AASA,EAAA,MAAM,eAAyB,EAAC;AAChC,EAAA,MAAM,yBAAmC,EAAC;AAI1C,EAAA,MAAM,mBAA6B,EAAC;AACpC,EAAA,MAAM,qBAA+B,EAAC;AACtC,EAAA,MAAM,6BAAuC,EAAC;AAC9C,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAY;AAIlC,EAAA,MAAM,eAAA,uBAAsB,GAAA,EAAoB;AAKhD,EAAA,IAAI,QAAA,GAAW,MAAA;AACf,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,QAAA,CAAS,cAAA,CAAe,QAAQ,CAAA,EAAA,EAAK;AACvD,IAAA,QAAA,IAAY,CAAA;AACZ,IAAA,MAAM,GAAA,GAAM,QAAA,CAAS,cAAA,CAAe,CAAC,CAAA;AAErC,IAAA,MAAM,MAAM,aAAA,CAAc,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,QAAQ,GAAG,CAAA;AACnD,IAAA,MAAM,OAAO,GAAA,EAAK,aAAA;AAClB,IAAA,IAAI,IAAA,IAAQ,CAAC,eAAA,CAAgB,GAAA,CAAI,IAAI,CAAA,EAAG,eAAA,CAAgB,GAAA,CAAI,IAAA,EAAM,QAAQ,CAAA;AAI1E,IAAA,KAAA,MAAW,KAAK,aAAA,EAAe;AAC7B,MAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,GAAA,IAAO,CAAA,CAAE,MAAM,CAAC,WAAA,CAAY,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA,EAAG,WAAA,CAAY,GAAA,CAAI,CAAA,CAAE,IAAI,QAAQ,CAAA;AAAA,IACrF;AAAA,EACF;AACA,EAAA,KAAA,MAAW,KAAK,aAAA,EAAe;AAC7B,IAAA,IAAI,CAAA,CAAE,WAAW,iBAAA,EAAmB;AACpC,IAAA,IAAI,CAAC,EAAE,aAAA,EAAe;AACtB,IAAA,IAAI,QAAA,CAAS,uBAAA,CAAwB,GAAA,CAAI,CAAA,CAAE,aAAa,CAAA,EAAG;AAC3D,IAAA,IAAI,SAAA,CAAU,GAAA,CAAI,CAAA,CAAE,aAAa,CAAA,EAAG;AACpC,IAAA,IAAI,EAAE,WAAA,EAAa;AACnB,IAAA,IAAI,CAAC,EAAE,GAAA,EAAK;AACZ,IAAA,SAAA,CAAU,GAAA,CAAI,EAAE,aAAa,CAAA;AAC7B,IAAA,YAAA,CAAa,IAAA,CAAK,EAAE,GAAG,CAAA;AACvB,IAAA,QAAA,IAAY,CAAA;AACZ,IAAA,IAAI,CAAC,eAAA,CAAgB,GAAA,CAAI,CAAA,CAAE,aAAa,GAAG,eAAA,CAAgB,GAAA,CAAI,CAAA,CAAE,aAAA,EAAe,QAAQ,CAAA;AACxF,IAAA,IAAI,CAAA,CAAE,EAAA,IAAM,CAAC,WAAA,CAAY,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA,EAAG,WAAA,CAAY,GAAA,CAAI,CAAA,CAAE,EAAA,EAAI,QAAQ,CAAA;AAKlE,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,MAAM,OAAA,GAAU,WAAA,CAAY,OAAA,CAAQ,QAAQ,CAAA;AAI5C,MAAA,gBAAA,CAAiB,IAAA,CAAKT,qBAAA,CAAaD,2BAAA,CAAmB,CAAA,CAAE,WAAA,EAAa,CAAA,CAAE,gBAAA,EAAkB,CAAA,CAAE,MAAM,CAAA,EAAG,OAAO,CAAC,CAAA;AAC5G,MAAA,MAAM,IAAA,GAAOE,uCAAA,CAAsB,CAAA,EAAG,OAAO,CAAA;AAC7C,MAAA,IAAI,IAAA,EAAM,kBAAA,CAAmB,IAAA,CAAK,IAAI,CAAA;AACtC,MAAA,MAAM,YAAA,GAAeE,0CAAA,CAAyB,OAAA,EAAS,CAAA,CAAE,mBAAmB,CAAA;AAC5E,MAAA,IAAI,YAAA,EAAc,0BAAA,CAA2B,IAAA,CAAK,YAAY,CAAA;AAC9D,MAAA,MAAM,MAAA,GAAS,CAAA,CAAE,gBAAA,EAAkB,IAAA,EAAK;AACxC,MAAA,IAAI,MAAA,EAAQ,0BAAA,CAA2B,IAAA,CAAK,MAAM,CAAA;AAClD,MAAA;AAAA,IACF;AACA,IAAA,MAAM,WAAA,GAAc,CAAA,CAAE,WAAA,IAAe,CAAA,CAAE,aAAA;AACvC,IAAA,MAAM,aAAA,GAAgB,EAAE,gBAAA,IAAoBO,2BAAA;AAI5C,IAAA,IAAI,kBAAkB,MAAA,EAAQ;AAC5B,MAAA;AAAA,IACF;AACA,IAAA,IAAI,kBAAkB,MAAA,EAAQ;AAC5B,MAAA,sBAAA,CAAuB,IAAA,CAAK,KAAK,WAAA,CAAY,OAAA,CAAQ,QAAQ,CAAC,CAAA,EAAA,EAAK,WAAW,CAAA,CAAA,CAAG,CAAA;AACjF,MAAA;AAAA,IACF;AACA,IAAA,MAAM,SAAA,GAAYC,4BAAmB,aAAa,CAAA;AAClD,IAAA,MAAM,oBAAA,GAAuB,aAAA,KAAkB,WAAA,IAAe,aAAA,KAAkB,WAAA;AAIhF,IAAA,MAAM,gBAA0B,EAAC;AAIjC,IAAA,MAAM,iBAAA,GAAoB,CAAA,CAAE,mBAAA,EAAqB,IAAA,EAAK;AACtD,IAAA,IAAI,iBAAA,EAAmB;AACrB,MAAA,aAAA,CAAc,KAAK,iBAAiB,CAAA;AAAA,IACtC,CAAA,MAAA,IAAW,oBAAA,IAAwB,CAAA,CAAE,6BAAA,EAA+B,MAAK,EAAG;AAC1E,MAAA,aAAA,CAAc,IAAA,CAAK,CAAA,CAAE,6BAAA,CAA8B,IAAA,EAAM,CAAA;AAAA,IAC3D;AACA,IAAA,IAAI,CAAA,CAAE,kBAAkB,IAAA,EAAK,gBAAiB,IAAA,CAAK,CAAA,CAAE,gBAAA,CAAiB,IAAA,EAAM,CAAA;AAC5E,IAAA,MAAM,QAAA,GAAW,aAAA,CAAc,MAAA,GAAS,CAAA,GACpC,CAAA,EAAG,WAAW,CAAA,QAAA,EAAM,aAAA,CAAc,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,GAC5C,WAAA;AACJ,IAAA,sBAAA,CAAuB,IAAA,CAAK,KAAK,QAAQ,CAAA,CAAA,EAAI,YAAY,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA,GAAK,EAAE,CAAA,CAAE,CAAA;AAAA,EACjF;AAeA,EAAA,MAAM,YAAsB,EAAC;AAC7B,EAAA,MAAM,sBAAgC,EAAC;AAIvC,EAAA,MAAM,mBAA6B,EAAC;AACpC,EAAA,MAAM,uBAAiC,EAAC;AACxC,EAAA,MAAM,+BAAyC,EAAC;AAChD,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,KAAA,MAAW,EAAA,IAAM,KAAK,SAAA,EAAY;AAChC,MAAA,IAAI,CAAC,GAAG,GAAA,EAAK;AACb,MAAA,QAAA,IAAY,CAAA;AACZ,MAAA,IAAI,EAAA,CAAG,EAAA,IAAM,CAAC,WAAA,CAAY,GAAA,CAAI,EAAA,CAAG,EAAE,CAAA,EAAG,WAAA,CAAY,GAAA,CAAI,EAAA,CAAG,EAAA,EAAI,QAAQ,CAAA;AAGrE,MAAA,MAAM,IAAA,GAAA,CAAQ,GAAG,mBAAA,IAAuB,EAAA,EAAI,MAAK,IAAA,CAAM,EAAA,CAAG,WAAA,IAAe,EAAA,EAAI,IAAA,EAAK;AAClF,MAAA,IAAI,GAAG,aAAA,EAAe;AAKpB,QAAA,MAAM,IAAA,GAAO,IAAA,CAAK,qBAAA,GAAwB,EAAA,CAAG,aAAa,CAAA;AAC1D,QAAA,MAAM,kBAAkB,IAAA,EAAM,gBAAA;AAC9B,QAAA,MAAM,aAAA,GAAgB,EAAA,CAAG,SAAA,IAAa,eAAA,IAAmBD,2BAAA;AACzD,QAAA,MAAM,UAAA,GAAa,eAAA,CAAgB,GAAA,CAAI,EAAA,CAAG,aAAa,CAAA;AACvD,QAAA,IAAI,MAAA,EAAQ;AAMV,UAAA,MAAM,OAAA,GAAU,WAAA,CAAY,OAAA,CAAQ,QAAQ,CAAA;AAC5C,UAAA,IAAI,eAAe,MAAA,EAAW;AAE5B,YAAA,MAAM,IAAA,GAAO,IAAA,GAAO,CAAA,EAAA,EAAK,IAAI,CAAA,CAAA,GAAK,EAAA;AAClC,YAAA,gBAAA,CAAiB,IAAA;AAAA,cACf,CAAA,EAAG,OAAO,CAAA,wBAAA,EAA2B,WAAA,CAAY,QAAQ,UAAU,CAAC,GAAG,IAAI,CAAA,CAAA;AAAA,aAC7E;AAAA,UACF,CAAA,MAAO;AAcL,YAAA,MAAM,IAAA,GAAOX,2BAAA;AAAA;AAAA;AAAA;AAAA,cAIX,EAAA,CAAG,WAAA,KAAgB,EAAA,CAAG,SAAA,GAAY,SAAY,IAAA,EAAM,WAAA,CAAA;AAAA,cACpD,aAAA;AAAA,cACA;AAAA,aACF;AACA,YAAA,MAAM,MAAA,GAASC,qBAAA,CAAa,IAAA,EAAM,OAAO,CAAA;AACzC,YAAA,gBAAA,CAAiB,IAAA,CAAK,OAAO,CAAA,EAAG,MAAM,KAAK,IAAI,CAAA,CAAA,CAAA,GAAM,CAAA,EAAG,MAAM,CAAA,CAAA,CAAG,CAAA;AAGjE,YAAA,MAAM,IAAA,GAAOC,uCAAA;AAAA,cACX;AAAA,gBACE,IAAI,EAAA,CAAG,GAAA;AAAA,gBACP,WAAA,EAAa,IAAA,EAAM,aAAA,IAAiB,EAAA,CAAG,aAAA;AAAA,gBACvC,MAAA,EAAQ,iBAAA;AAAA,gBACR,KAAK,EAAA,CAAG,GAAA;AAAA,gBACR,eAAe,EAAA,CAAG,aAAA;AAAA,gBAClB,aAAa,EAAA,CAAG,WAAA;AAAA,gBAChB,YAAA,EAAc,EAAA,CAAG,YAAA,IAAgB,IAAA,EAAM;AAAA,eACzC;AAAA,cACA;AAAA,aACF;AACA,YAAA,IAAI,IAAA,EAAM,oBAAA,CAAqB,IAAA,CAAK,IAAI,CAAA;AACxC,YAAA,MAAM,MAAA,GAAS,EAAA,CAAG,gBAAA,EAAkB,IAAA,EAAK;AACzC,YAAA,IAAI,MAAA,EAAQ,4BAAA,CAA6B,IAAA,CAAK,MAAM,CAAA;AACpD,YAAA,eAAA,CAAgB,GAAA,CAAI,EAAA,CAAG,aAAA,EAAe,QAAQ,CAAA;AAAA,UAChD;AAAA,QACF,CAAA,MAAA,IAAW,eAAe,MAAA,EAAW;AAGnC,UAAA,IAAI,kBAAkB,MAAA,EAAQ;AAC5B,YAAA,MAAM,IAAA,GAAO,IAAA,GAAO,CAAA,EAAA,EAAK,IAAI,CAAA,CAAA,GAAK,EAAA;AAClC,YAAA,mBAAA,CAAoB,IAAA;AAAA,cAClB,CAAA,EAAA,EAAK,WAAA,CAAY,OAAA,CAAQ,QAAQ,CAAC,CAAA,wBAAA,EAA2B,WAAA,CAAY,OAAA,CAAQ,UAAU,CAAC,CAAA,EAAG,IAAI,CAAA,CAAA;AAAA,aACrG;AAAA,UACF;AAAA,QACF,CAAA,MAAA,IAAW,kBAAkB,MAAA,EAAQ;AAGnC,UAAA,eAAA,CAAgB,GAAA,CAAI,EAAA,CAAG,aAAA,EAAe,QAAQ,CAAA;AAAA,QAChD,CAAA,MAAA,IAAW,kBAAkB,MAAA,EAAQ;AACnC,UAAA,MAAM,WAAA,GAAc,IAAA,EAAM,aAAA,IAAiB,EAAA,CAAG,aAAA;AAC9C,UAAA,MAAM,UAAU,CAAA,EAAG,WAAA,CAAY,QAAQ,QAAQ,CAAC,KAAK,WAAW,CAAA,CAAA,CAAA;AAChE,UAAA,MAAM,WAAW,IAAA,GAAO,CAAA,EAAG,OAAO,CAAA,QAAA,EAAM,IAAI,CAAA,CAAA,GAAK,OAAA;AACjD,UAAA,mBAAA,CAAoB,IAAA,CAAK,CAAA,EAAA,EAAK,QAAQ,CAAA,CAAA,CAAG,CAAA;AACzC,UAAA,eAAA,CAAgB,GAAA,CAAI,EAAA,CAAG,aAAA,EAAe,QAAQ,CAAA;AAAA,QAChD,CAAA,MAAO;AACL,UAAA,MAAM,SAAA,GAAYU,4BAAmB,aAAa,CAAA;AAClD,UAAA,MAAM,WAAA,GAAc,IAAA,EAAM,aAAA,IAAiB,EAAA,CAAG,aAAA;AAC9C,UAAA,MAAM,UAAU,CAAA,EAAG,WAAA,CAAY,QAAQ,QAAQ,CAAC,KAAK,WAAW,CAAA,CAAA,CAAA;AAChE,UAAA,MAAM,oBAAA,GAAuB,aAAA,KAAkB,WAAA,IAAe,aAAA,KAAkB,WAAA;AAChF,UAAA,MAAM,gBAAgB,IAAA,EAAM,oBAAA;AAC5B,UAAA,IAAI,QAAA,GAAW,OAAA;AACf,UAAA,IAAI,IAAA,EAAM,QAAA,GAAW,CAAA,EAAG,OAAO,WAAM,IAAI,CAAA,CAAA;AAAA,eAAA,IAChC,oBAAA,IAAwB,aAAA,EAAe,IAAA,EAAK,EAAG,QAAA,GAAW,GAAG,OAAO,CAAA,QAAA,EAAM,aAAA,CAAc,IAAA,EAAM,CAAA,CAAA;AACvG,UAAA,mBAAA,CAAoB,IAAA,CAAK,KAAK,QAAQ,CAAA,CAAA,EAAI,YAAY,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA,GAAK,EAAE,CAAA,CAAE,CAAA;AAC5E,UAAA,eAAA,CAAgB,GAAA,CAAI,EAAA,CAAG,aAAA,EAAe,QAAQ,CAAA;AAAA,QAChD;AAAA,MACF,WAAW,MAAA,EAAQ;AAKjB,QAAA,MAAM,OAAA,GAAU,WAAA,CAAY,OAAA,CAAQ,QAAQ,CAAA;AAC5C,QAAA,gBAAA,CAAiB,IAAA,CAAK,OAAO,CAAA,EAAG,IAAI,KAAK,OAAO,CAAA,EAAA,CAAA,GAAO,CAAA,EAAG,OAAO,CAAA,aAAA,CAAe,CAAA;AAAA,MAClF,CAAA,MAAO;AAGL,QAAA,IAAI,IAAA,EAAM;AACR,UAAA,mBAAA,CAAoB,IAAA,CAAK,KAAK,WAAA,CAAY,OAAA,CAAQ,QAAQ,CAAC,CAAA,cAAA,EAAiB,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,QACrF,CAAA,MAAO;AACL,UAAA,mBAAA,CAAoB,KAAK,CAAA,EAAA,EAAK,WAAA,CAAY,OAAA,CAAQ,QAAQ,CAAC,CAAA,aAAA,CAAe,CAAA;AAAA,QAC5E;AAAA,MACF;AACA,MAAA,SAAA,CAAU,IAAA,CAAK,GAAG,GAAG,CAAA;AAAA,IACvB;AAAA,EACF;AAEA,EAAA,IAAI,cAAc,QAAA,CAAS,MAAA;AAC3B,EAAA,IAAI,MAAA,EAAQ;AAYV,IAAA,MAAM,SAAA,GAAY,CAAC,mBAAG,IAAI,GAAA,CAAI,CAAC,GAAG,gBAAA,EAAkB,GAAG,kBAAA,EAAoB,GAAG,oBAAoB,CAAC,CAAC,CAAA;AACpG,IAAA,MAAM,aAAA,GAAgB;AAAA,MACpB,GAAG,wBAAA;AAAA,MACH,GAAG,gBAAA;AAAA,MACH,GAAG,0BAAA;AAAA,MACH,GAAG,gBAAA;AAAA,MACH,GAAG;AAAA,KACL;AACA,IAAA,MAAM,SAAA,GAAY,UAAU,MAAA,GAAS,CAAA,GAAI,GAAG,SAAA,CAAU,IAAA,CAAK,IAAI,CAAC;;AAAA,CAAA,GAAS,EAAA;AAIzE,IAAA,WAAA,GAAc,GAAG,SAAS,CAAA,EAAGC,0CAAA,CAAyB,WAAA,EAAa,aAAa,CAAC,CAAA,CAAA;AAAA,EACnF,CAAA,MAAO;AACL,IAAA,MAAM,gBAAA,GAAmB,CAAC,GAAG,sBAAA,EAAwB,GAAG,mBAAmB,CAAA;AAC3E,IAAA,IAAI,gBAAA,CAAiB,SAAS,CAAA,EAAG;AAI/B,MAAA,IAAI,WAAA,IAAe,WAAA,CAAY,UAAA,CAAW,yBAAyB,CAAA,EAAG;AACpE,QAAA,MAAM,QAAA,GAAW,WAAA,CAAY,OAAA,CAAQ,MAAM,CAAA;AAC3C,QAAA,IAAI,aAAa,EAAA,EAAI;AACnB,UAAA,MAAM,MAAA,GAAS,WAAA,CAAY,KAAA,CAAM,CAAA,EAAG,QAAQ,CAAA;AAC5C,UAAA,MAAM,IAAA,GAAO,WAAA,CAAY,KAAA,CAAM,QAAQ,CAAA;AACvC,UAAA,WAAA,GAAc,GAAG,MAAM;AAAA,EAAK,gBAAA,CAAiB,IAAA,CAAK,IAAI,CAAC,GAAG,IAAI,CAAA,CAAA;AAAA,QAChE,CAAA,MAAO;AACL,UAAA,WAAA,GAAc,GAAG,WAAW;AAAA,EAAK,gBAAA,CAAiB,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,QAC9D;AAAA,MACF,CAAA,MAAO;AACL,QAAA,MAAM,KAAA,GAAQ,CAAA;AAAA,EAA0B,gBAAA,CAAiB,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AACnE,QAAA,WAAA,GAAc,WAAA,GAAc,GAAG,KAAK;;AAAA,EAAO,WAAW,CAAA,CAAA,GAAK,KAAA;AAAA,MAC7D;AAAA,IACF;AAAA,EACF;AAKA,EAAA,MAAM,SAAmB,EAAC;AAC1B,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,KAAA,MAAW,KAAK,cAAA,EAAgB;AAC9B,IAAA,IAAI,CAAA,IAAK,CAAC,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,EAAG;AAAE,MAAA,IAAA,CAAK,IAAI,CAAC,CAAA;AAAG,MAAA,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,IAAE;AAAA,EACvD;AACA,EAAA,KAAA,MAAW,CAAA,IAAK,SAAS,cAAA,EAAgB;AACvC,IAAA,IAAI,CAAA,IAAK,CAAC,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,EAAG;AAAE,MAAA,IAAA,CAAK,IAAI,CAAC,CAAA;AAAG,MAAA,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,IAAE;AAAA,EACvD;AACA,EAAA,KAAA,MAAW,KAAK,YAAA,EAAc;AAC5B,IAAA,IAAI,CAAA,IAAK,CAAC,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,EAAG;AAAE,MAAA,IAAA,CAAK,IAAI,CAAC,CAAA;AAAG,MAAA,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,IAAE;AAAA,EACvD;AACA,EAAA,KAAA,MAAW,KAAK,SAAA,EAAW;AACzB,IAAA,IAAI,CAAA,IAAK,CAAC,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,EAAG;AAAE,MAAA,IAAA,CAAK,IAAI,CAAC,CAAA;AAAG,MAAA,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,IAAE;AAAA,EACvD;AAOA,EAAA,WAAA,GAAcL,sCAAA;AAAA,IACZ,WAAA;AAAA,IACA,wBAAA;AAAA,IACA,SAAS,QAAA,GAAW;AAAA,GACtB;AAYA,EAAA,WAAA,GACE,mBAAmB,WAAA,EAAa;AAAA,IAC9B,QAAA,EAAU,WAAA;AAAA,IACV,QAAA,EAAU,WAAA;AAAA,IACV,UAAA,EAAY,WAAA,CAAY,MAAA,CAAO,MAAM,CAAA,CAAE;AAAA,GACxC,CAAA,IAAK,WAAA;AAIR,EAAA,MAAM,iBAAiB,IAAA,CAAK,cAAA;AAK5B,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,KAAA,CAAM,cAAA,CAAe,MAAM,CAAA;AACpD,EAAA,IAAI,kBAAkB,cAAA,CAAe,MAAA,GAAS,CAAA,IAAK,SAAA,CAAU,SAAS,CAAA,EAAG;AACvE,IAAA,MAAM,eAAA,GAAwC,CAAC,GAAG,aAAa,CAAA;AAC/D,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,KAAA,MAAW,EAAA,IAAM,KAAK,SAAA,EAAY;AAChC,QAAA,IAAI,CAAC,GAAG,GAAA,EAAK;AACb,QAAA,eAAA,CAAgB,IAAA,CAAK;AAAA,UACnB,IAAI,EAAA,CAAG,GAAA;AAAA,UACP,WAAA,EAAa,GAAG,aAAA,IAAiB,OAAA;AAAA,UACjC,MAAA,EAAQ,EAAA,CAAG,aAAA,GAAgB,iBAAA,GAAoB,QAAA;AAAA,UAC/C,KAAK,EAAA,CAAG,GAAA;AAAA,UACR,eAAe,EAAA,CAAG,aAAA;AAAA,UAClB,aAAa,EAAA,CAAG,WAAA;AAAA,UAChB,UAAA,EAAY;AAAA,SACb,CAAA;AAAA,MACH;AAAA,IACF;AACA,IAAA,MAAM,YAAYM,4CAAA,CAA2B,SAAA,EAAW,aAAa,eAAA,EAAiB,cAAA,EAAgB,QAAW,MAAM,CAAA;AAOvH,IAAA,OAAO;AAAA,MACL,QAAQ,sBAAA,CAAuB,SAAA,CAAU,QAAQ,WAAA,CAAY,MAAA,CAAO,MAAM,CAAC,CAAA;AAAA,MAC3E,gBAAgB,CAAC,GAAG,cAAA,EAAgB,GAAG,UAAU,IAAI;AAAA,KACvD;AAAA,EACF;AAIA,EAAA,OAAO;AAAA,IACL,QAAQ,sBAAA,CAAuB,WAAA,EAAa,WAAA,CAAY,MAAA,CAAO,MAAM,CAAC,CAAA;AAAA,IACtE,cAAA,EAAgB;AAAA,GAClB;AACF;;;ACv5BA,IAAM,8BAAc,IAAI,GAAA,CAAI,CAAC,aAAA,EAAe,YAAA,EAAc,iBAAiB,CAAC,CAAA;AAC5E,IAAM,8BAAc,IAAI,GAAA,CAAI,CAAC,iBAAA,EAAmB,gBAAgB,CAAC,CAAA;AAEjE,SAAS,QAAQ,CAAA,EAAuB;AAAE,EAAA,OAAO,CAAC,CAAC,CAAA,IAAK,WAAA,CAAY,IAAI,CAAC,CAAA;AAAE;AAC3E,SAAS,QAAQ,CAAA,EAAuB;AAAE,EAAA,OAAO,CAAC,CAAC,CAAA,IAAK,WAAA,CAAY,IAAI,CAAC,CAAA;AAAE;AAc3E,IAAM,6CAAkD,IAAI,GAAA,CAAI,CAAC,aAAA,EAAe,OAAO,CAAC,CAAA;AAExF,SAAS,wBAAA,CACP,UACA,GAAA,EACgB;AAChB,EAAA,MAAM,UAA0B,EAAC;AACjC,EAAA,KAAA,MAAW,IAAA,IAAQ,IAAI,KAAA,EAAO;AAC5B,IAAA,IAAI,IAAA,CAAK,MAAA,KAAW,QAAA,CAAS,EAAA,EAAI;AACjC,IAAA,IAAI,IAAA,CAAK,gBAAgB,IAAA,IAAQ,CAAC,2BAA2B,GAAA,CAAI,IAAA,CAAK,YAAY,CAAA,EAAG;AACrF,IAAA,MAAM,MAAA,GAAS,IAAI,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,IAAA,CAAK,MAAM,CAAA;AACzD,IAAA,IAAI,MAAA,EAAQ,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAA;AAAA,EACjC;AACA,EAAA,OAAO,OAAA;AACT;AAEO,SAAS,+BAAA,CACd,QAAA,EACA,YAAA,EACA,GAAA,EACkB;AAClB,EAAA,MAAM,OAAA,GAAU,wBAAA,CAAyB,QAAA,EAAU,GAAG,CAAA;AACtD,EAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,IAAA,OAAO,EAAE,MAAM,EAAA,EAAI,MAAA,EAAQ,EAAC,EAAG,QAAA,EAAU,EAAC,EAAE;AAAA,EAC9C;AAEA,EAAA,MAAM,WAAqB,EAAC;AAC5B,EAAA,MAAM,gBAA0B,EAAC;AACjC,EAAA,MAAM,SAAiC,EAAC;AAYxC,EAAA,KAAA,MAAW,OAAO,OAAA,EAAS;AACzB,IAAA,MAAM,IAAI,GAAA,CAAI,IAAA;AACd,IAAA,IAAI,YAAA,KAAiB,cAAA,IAAkB,YAAA,KAAiB,aAAA,EAAe;AACrE,MAAA,IAAI,OAAA,CAAQ,CAAC,CAAA,EAAG;AACd,QAAA,QAAA,CAAS,IAAA,CAAK,gBAAgB,CAAC,CAAA,iBAAA,EAAoB,iBAAiB,cAAA,GAAiB,cAAA,GAAiB,aAAa,CAAA,CAAA,CAAG,CAAA;AACtH,QAAA;AAAA,MACF;AAAA,IACF;AACA,IAAA,IAAI,iBAAiB,eAAA,EAAiB;AACpC,MAAA,IAAI,OAAA,CAAQ,CAAC,CAAA,EAAG;AACd,QAAA,QAAA,CAAS,IAAA,CAAK,CAAA,aAAA,EAAgB,CAAC,CAAA,+BAAA,CAAiC,CAAA;AAChE,QAAA;AAAA,MACF;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,GAAOC,yCAAuB,GAAG,CAAA;AACvC,IAAA,IAAI,IAAA,EAAM,aAAA,CAAc,IAAA,CAAK,IAAI,CAAA;AAOjC,IAAA,IAAA,CACG,iBAAiB,eAAA,IAAmB,YAAA,KAAiB,qBACtD,CAAA,KAAM,iBAAA,IACN,IAAI,IAAA,EACJ;AACA,MAAA,MAAM,MAAA,GAAU,IAAI,IAAA,CAAiC,MAAA;AACrD,MAAA,IAAA,CAAK,WAAW,MAAA,IAAU,MAAA,KAAW,QAAA,KAAa,CAAC,OAAO,WAAA,EAAa;AACrE,QAAA,MAAA,CAAO,MAAA,CAAO,MAAA,EAAQ,EAAE,WAAA,EAAa,QAAQ,CAAA;AAAA,MAC/C;AAAA,IACF;AAGA,IAAA,IAAI,iBAAiB,gBAAA,IAAoB,QAAA,CAAS,QAAS,QAAA,CAAS,IAAA,CAAiC,aAAa,SAAA,EAAW;AAC3H,MAAA,MAAM,OAAO,GAAA,CAAI,IAAA;AACjB,MAAA,IAAI,CAAA,KAAM,iBAAiB,IAAA,EAAM;AAC/B,QAAA,MAAM,MAAM,OAAO,IAAA,CAAK,QAAA,KAAa,QAAA,GAAW,KAAK,QAAA,GAAW,MAAA;AAChE,QAAA,MAAM,UAAU,OAAO,IAAA,CAAK,KAAA,KAAU,QAAA,GAAW,KAAK,KAAA,GAAQ,MAAA;AAC9D,QAAA,MAAM,QAAQ,OAAO,IAAA,CAAK,GAAA,KAAQ,QAAA,GAAW,KAAK,GAAA,GAAM,MAAA;AACxD,QAAA,MAAM,OAAA,GAAUC,6BAAA,CAAY,KAAK,CAAA,EAAG,UAAA;AACpC,QAAA,MAAM,KAAA,GAAQC,gCAAc,OAAO,CAAA;AACnC,QAAA,MAAM,OAAA,GAAU,KAAA,EAAO,SAAA,CAAU,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,GAAG,CAAA,EAAG,UAAA,IAAc,KAAA,EAAO,UAAA;AACjF,QAAA,MAAM,QAAA,GAAW,CAAC,OAAA,EAAS,OAAO,EAAE,MAAA,CAAO,OAAO,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAC5D,QAAA,IAAI,QAAA,IAAY,CAAC,MAAA,CAAO,KAAA,EAAO,MAAA,CAAO,OAAO,MAAA,EAAQ,EAAE,KAAA,EAAO,QAAA,EAAU,CAAA;AAAA,MAC1E;AACA,MAAA,IAAI,CAAA,KAAM,gBAAgB,IAAA,EAAM;AAC9B,QAAA,MAAM,WAAWC,qCAAA,CAAoB;AAAA,UACnC,QAAQ,OAAO,IAAA,CAAK,MAAA,KAAW,QAAA,GAAW,KAAK,MAAA,GAAS,MAAA;AAAA,UACxD,SAAS,IAAA,CAAK,OAAA;AAAA,UACd,MAAM,IAAA,CAAK;AAAA,SACZ,CAAA;AACD,QAAA,IAAI,QAAA,IAAY,CAAC,MAAA,CAAO,IAAA,EAAM,MAAA,CAAO,OAAO,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAU,CAAA;AAAA,MACxE;AACA,MAAA,IAAI,CAAA,KAAM,qBAAqB,IAAA,EAAM;AACnC,QAAA,MAAM,KAAK,OAAO,IAAA,CAAK,aAAA,KAAkB,QAAA,GAAW,KAAK,aAAA,GAAgB,MAAA;AACzE,QAAA,IAAI,EAAA,KAAO,gBAAgB,MAAA,CAAO,MAAA,CAAO,QAAQ,EAAE,YAAA,EAAc,MAAM,CAAA;AAAA,MACzE;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,IAAA,GAAO,aAAA,CAAc,IAAA,CAAK,IAAI,CAAA;AAIpC,EAAA,IAAA,CAAK,YAAA,KAAiB,cAAA,IAAkB,YAAA,KAAiB,aAAA,KAAkB,IAAA,EAAM;AAC/E,IAAA,MAAA,CAAO,MAAA,CAAO,MAAA,EAAQ,EAAE,gBAAA,EAAkB,MAAM,CAAA;AAAA,EAClD;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,QAAA,EAAS;AAClC;AAMO,SAAS,WAAA,CAAY,UAAkB,YAAA,EAA8B;AAC1E,EAAA,IAAI,CAAC,cAAc,OAAO,QAAA;AAC1B,EAAA,IAAI,CAAC,UAAU,OAAO,YAAA;AACtB,EAAA,OAAO,CAAA,EAAG,QAAQ,CAAA,EAAA,EAAK,YAAY,CAAA,CAAA;AACrC;AAOO,SAAS,gBAAA,CAAiB,YAAA,EAAsB,QAAA,EAAkB,WAAA,EAA6B;AACpG,EAAA,IAAI,CAAC,cAAc,OAAO,EAAA;AAC1B,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,MAAA,GAAS,CAAA,GAAI,CAAA,GAAI,CAAA;AACzC,EAAA,MAAM,MAAA,GAAS,WAAA,GAAc,QAAA,CAAS,MAAA,GAAS,MAAA;AAC/C,EAAA,IAAI,MAAA,IAAU,GAAG,OAAO,EAAA;AACxB,EAAA,IAAI,YAAA,CAAa,MAAA,IAAU,MAAA,EAAQ,OAAO,YAAA;AAC1C,EAAA,MAAM,GAAA,GAAM,YAAA,CAAa,KAAA,CAAM,CAAA,EAAG,MAAM,CAAA;AACxC,EAAA,MAAM,EAAA,GAAK,GAAA,CAAI,WAAA,CAAY,GAAG,CAAA;AAC9B,EAAA,OAAO,KAAK,CAAA,GAAI,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GAAI,GAAA;AACrC;AAaO,SAAS,2BACd,IAAA,EACS;AACT,EAAA,IAAI,OAAO,IAAA,CAAK,UAAA,KAAe,SAAA,SAAkB,IAAA,CAAK,UAAA;AACtD,EAAA,OAAO,CAAC,EAAE,IAAA,CAAK,KAAA,IAAS,IAAA,CAAK,SAAS,IAAA,CAAK,MAAA,CAAA;AAC7C;AAaO,SAAS,eAAA,CACd,YACA,IAAA,EACQ;AACR,EAAA,MAAM,KAAA,GAAQ,CAAC,UAAU,CAAA;AACzB,EAAA,IAAI,IAAA,CAAK,KAAA,EAAO,KAAA,CAAM,IAAA,CAAK,KAAK,KAAK,CAAA;AACrC,EAAA,IAAI,IAAA,CAAK,IAAA,EAAM,KAAA,CAAM,IAAA,CAAK,KAAK,IAAI,CAAA;AACnC,EAAA,IAAI,IAAA,CAAK,YAAA,EAAc,KAAA,CAAM,IAAA,CAAK,yBAAyB,CAAA;AAC3D,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;;;AClMA,IAAM,YAAA,GAAe,GAAA;AACrB,IAAM,aAAA,GAAgB,GAAA;AAuDf,SAAS,kBAAkB,KAAA,EAA8C;AAC9E,EAAA,MAAM,IAAA,GAAQ,KAAA,CAAM,IAAA,CAAK,IAAA,IAAQ,EAAC;AAElC,EAAA,MAAM,UAAA,GAAa,2BAA2B,IAAI,CAAA;AAGlD,EAAA,MAAM,aAAa,+BAAA,CAAgC,KAAA,CAAM,IAAA,EAAM,eAAA,EAAiB,MAAM,KAAK,CAAA;AAK3F,EAAA,IAAI,MAAM,YAAA,IAAgB,CAAC,MAAM,UAAA,IAAc,CAAC,WAAW,IAAA,EAAM;AAC/D,IAAA,MAAM,IAAI,MAAM,uBAAuB,CAAA;AAAA,EACzC;AAEA,EAAA,MAAM,SAAA,GAAa,KAAK,KAAA,IAAgC,EAAA;AAIxD,EAAA,MAAM,WAAA,GAAc,MAAM,UAAA,IAAc,EAAA;AAIxC,EAAA,IAAI,UAAA,GAAa,SAAA;AACjB,EAAA,IAAI,WAAA,GAAc,WAAA;AAClB,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,UAAA,GAAa,SAAA,GACT,WAAA,CAAY,SAAA,EAAW,gBAAA,CAAiB,UAAA,CAAW,MAAM,SAAA,EAAW,YAAY,CAAC,CAAA,GACjF,UAAA,CAAW,IAAA;AAAA,EACjB,CAAA,MAAO;AACL,IAAA,WAAA,GAAc,WAAA,GACV,WAAA,CAAY,WAAA,EAAa,gBAAA,CAAiB,UAAA,CAAW,MAAM,WAAA,EAAa,aAAa,CAAC,CAAA,GACtF,UAAA,CAAW,IAAA;AAAA,EACjB;AAEA,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,WAAA;AAAA,IACR,KAAA,EAAQ,KAAK,KAAA,IAAgC,MAAA;AAAA;AAAA,IAE7C,MAAA,EAAQ,MAAM,MAAA,IAAU,MAAA;AAAA,IACxB,OAAO,UAAA,IAAc,MAAA;AAAA,IACrB,KAAA,EAAQ,KAAK,KAAA,IAAgC,MAAA;AAAA,IAC7C,aAAA,EAAgB,KAAK,aAAA,IAAwC,MAAA;AAAA;AAAA,IAE7D,WAAA,EAAc,IAAA,CAAK,WAAA,IAAsC,UAAA,CAAW,OAAO,WAAA,IAAe,MAAA;AAAA,IAC1F,aAAa,IAAA,CAAK,WAAA;AAAA,IAClB,qBAAqB,IAAA,CAAK,mBAAA;AAAA,IAC1B,aAAa,IAAA,CAAK,WAAA;AAAA,IAClB,UAAU,IAAA,CAAK,QAAA;AAAA,IACf,UAAA;AAAA,IACA,YAAA,EAAe,KAAK,YAAA,IAAwC,KAAA;AAAA,IAC5D,GAAI,KAAA,CAAM,OAAA,IAAW;AAAC,GACxB;AACF;AC8EA,SAAS,qBAAA,CACP,OAAA,EACA,SAAA,EACA,UAAA,EACA,aAAA,EACiB;AACjB,EAAA,OAAO;AAAA,IACL,WAAA,EAAa;AAAA,MACX,GAAGC,+BAAA;AAAA,QACDC,qCAAmB,OAAA,EAAS;AAAA,UAC1B,OAAA,EAAS,OAAA;AAAA,UACT,IAAA,EAAMC;AAAA,SACP;AAAA,OACH;AAAA,MACA,GAAGC,wCAAsB,SAAA,EAAW;AAAA,QAClC,OAAA,EAAS,OAAA;AAAA,QACT,IAAA,EAAMC,yCAAA;AAAA,QACN,GAAI,aAAA,KAAkB,MAAA,GAAY,EAAE,IAAA,EAAM,aAAA,KAAkB;AAAC,OAC9D;AAAA,MACD,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,IAAA,CAAK,SAAS,CAAC,CAAA;AAAA,IACjC,kBAAA,EAAoB,UAAA,GAAaC,wCAAA,CAAuB,UAAU,CAAA,GAAI;AAAA,GACxE;AACF;AAwBA,SAAS,iBAAA,CACP,UAAA,EACA,MAAA,EACA,eAAA,EACQ;AACR,EAAA,OAAOC,wCAAA;AAAA,IACL,UAAA;AAAA,IACA,MAAA,CAAO,WAAA,CAAY,KAAA,CAAM,CAAA,EAAG,eAAe,CAAA;AAAA,IAC3C,MAAA,CAAO;AAAA,GACT;AACF;AA2BO,SAAS,mBACd,KAAA,EACqB;AACrB,EAAA,MAAM,QAAQ,KAAA,CAAM,eAAA;AACpB,EAAA,MAAM,MAAA,GAAS,KAAA,KAAU,MAAA,IAAa,KAAA,CAAM,MAAA,GAAS,CAAA,GACjD,mBAAA,CAAoB,KAAA,EAAO,KAAK,CAAA,GAChC,YAAA,CAAa,KAAK,CAAA;AAMtB,EAAA,IAAI,MAAM,YAAA,IAAgB,CAAC,MAAA,CAAO,MAAA,CAAO,MAAK,EAAG;AAC/C,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AA4BA,SAAS,mBAAA,CACP,OACA,KAAA,EACqB;AACrB,EAAA,MAAM,IAAA,GAAO,aAAa,KAAK,CAAA;AAC/B,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,kBAAA,IAAsB,EAAC;AACxC,EAAA,MAAM,KAAA,GAAQC,0BAAA,CAAoB,KAAA,CAAM,QAAQ,IAAI,GAAA,CAAI,MAAA;AACxD,EAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,GAAG,CAAA;AACxB,EAAA,MAAM,aAAoC,EAAC;AAC3C,EAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,IAAA,IAAI,OAAO,CAAA,CAAE,GAAA,KAAQ,QAAA,IAAY,CAAA,CAAE,GAAA,CAAI,MAAA,KAAW,CAAA,IAAK,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,GAAG,CAAA,EAAG;AACxE,IAAA,IAAA,CAAK,GAAA,CAAI,EAAE,GAAG,CAAA;AACd,IAAA,UAAA,CAAW,KAAK,CAAC,CAAA;AAAA,EACnB;AACA,EAAA,KAAA,IAAS,CAAA,GAAI,KAAK,GAAA,CAAI,KAAA,EAAO,WAAW,MAAM,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AAC3D,IAAA,MAAM,SAAS,UAAA,CAAW,KAAA,CAAM,GAAG,CAAC,CAAA,CAAE,IAAI,uBAAuB,CAAA;AACjE,IAAA,MAAM,SAAS,YAAA,CAAa;AAAA,MAC1B,GAAG,KAAA;AAAA,MACH,mBAAA,EAAqB,CAAC,GAAI,KAAA,CAAM,uBAAuB,EAAC,EAAI,GAAG,MAAM;AAAA,KACtE,CAAA;AACD,IAAA,MAAM,IAAA,GAAO,MAAA,CAAO,kBAAA,IAAsB,EAAC;AAC3C,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,MAAA,KAAW,GAAA,CAAI,MAAA,GAAS,CAAA,IACzC,GAAA,CAAI,KAAA,CAAM,CAAC,GAAA,EAAK,CAAA,KAAM,IAAA,CAAK,CAAC,CAAA,KAAM,GAAG,CAAA,IACrC,MAAA,CAAO,KAAA,CAAM,CAAC,GAAA,EAAK,CAAA,KAAM,IAAA,CAAK,GAAA,CAAI,MAAA,GAAS,CAAC,CAAA,KAAM,GAAA,CAAI,GAAG,CAAA;AAC9D,IAAA,IAAI,UAAU,OAAO,EAAE,GAAG,MAAA,EAAQ,qBAAqB,CAAA,EAAE;AAAA,EAC3D;AACA,EAAA,OAAO,EAAE,GAAG,IAAA,EAAM,mBAAA,EAAqB,CAAA,EAAE;AAC3C;AAGA,SAAS,uBAAA,CAAwB,GAAwB,KAAA,EAAmC;AAC1F,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,CAAA,gBAAA,EAAmB,KAAA,GAAQ,CAAC,CAAA,CAAA;AAAA,IAChC,WAAA,EAAa,CAAA,gBAAA,EAAmB,KAAA,GAAQ,CAAC,CAAA,CAAA;AAAA,IACzC,MAAA,EAAQ,QAAA;AAAA,IACR,KAAK,CAAA,CAAE,GAAA;AAAA,IACP,UAAA,EAAY,IAAA;AAAA,IACZ,WAAA,EAAa,OAAA;AAAA,IACb,WAAA,EAAaC,2CAAA,CAA0B,KAAA,EAAO,CAAA,CAAE,OAAO;AAAA,GACzD;AACF;AAGA,SAAS,aAAa,KAAA,EAAmD;AACvE,EAAA,MAAM,MAAA,GAAS,sBAAsB,KAAA,CAAM,OAAA,EAAS,MAAM,SAAA,EAAW,KAAA,CAAM,UAAA,EAAY,KAAA,CAAM,aAAa,CAAA;AAE1G,EAAA,MAAM,YAAA,GAAe,CAAC,eAAA,KAA4BC,8CAAA,CAA6B;AAAA,IAC7E,MAAA,EAAQ,iBAAA,CAAkB,KAAA,CAAM,UAAA,EAAY,QAAQ,eAAe,CAAA;AAAA,IACnE,UAAU,KAAA,CAAM,QAAA;AAAA,IAChB,GAAI,MAAM,mBAAA,KAAwB,MAAA,GAC9B,EAAE,mBAAA,EAAqB,KAAA,CAAM,mBAAA,EAAoB,GACjD,EAAC;AAAA,IACL,GAAI,MAAM,mBAAA,KAAwB,MAAA,GAC9B,EAAE,mBAAA,EAAqB,KAAA,CAAM,mBAAA,EAAoB,GACjD,EAAC;AAAA;AAAA;AAAA;AAAA;AAAA,IAKL,GAAI,MAAM,uBAAA,KAA4B,MAAA,GAClC,EAAE,kBAAA,EAAoB,KAAA,CAAM,uBAAA,EAAwB,GACpD,EAAC;AAAA,IACL,GAAI,MAAM,cAAA,KAAmB,MAAA,GAAY,EAAE,cAAA,EAAgB,KAAA,CAAM,cAAA,EAAe,GAAI,EAAC;AAAA,IACrF,GAAI,MAAM,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,EAAO,KAAA,CAAM,KAAA,EAAM,GAAI,EAAC;AAAA,IAC1D,GAAI,MAAM,cAAA,KAAmB,MAAA,GAAY,EAAE,cAAA,EAAgB,KAAA,CAAM,cAAA,EAAe,GAAI,EAAC;AAAA,IACrF,GAAI,MAAM,YAAA,KAAiB,MAAA,GAAY,EAAE,YAAA,EAAc,KAAA,CAAM,YAAA,EAAa,GAAI,EAAC;AAAA,IAC/E,GAAI,MAAM,+BAAA,KAAoC,MAAA,GAC1C,EAAE,+BAAA,EAAiC,KAAA,CAAM,+BAAA,EAAgC,GACzE,EAAC;AAAA,IACL,GAAI,MAAM,8BAAA,KAAmC,MAAA,GACzC,EAAE,8BAAA,EAAgC,KAAA,CAAM,8BAAA,EAA+B,GACvE,EAAC;AAAA,IACL,GAAI,MAAM,eAAA,KAAoB,MAAA,GAAY,EAAE,eAAA,EAAiB,KAAA,CAAM,eAAA,EAAgB,GAAI,EAAC;AAAA,IACxF,GAAI,MAAM,oBAAA,KAAyB,MAAA,GAAY,EAAE,oBAAA,EAAsB,KAAA,CAAM,oBAAA,EAAqB,GAAI,EAAC;AAAA,IACvG,GAAI,MAAM,aAAA,KAAkB,MAAA,GAAY,EAAE,aAAA,EAAe,KAAA,CAAM,aAAA,EAAc,GAAI,EAAC;AAAA,IAClF,GAAI,MAAM,aAAA,KAAkB,MAAA,GAAY,EAAE,aAAA,EAAe,KAAA,CAAM,aAAA,EAAc,GAAI,EAAC;AAAA,IAClF,GAAI,MAAM,aAAA,KAAkB,MAAA,GAAY,EAAE,aAAA,EAAe,KAAA,CAAM,aAAA,EAAc,GAAI,EAAC;AAAA,IAClF,GAAI,MAAM,YAAA,KAAiB,MAAA,GAAY,EAAE,YAAA,EAAc,KAAA,CAAM,YAAA,EAAa,GAAI,EAAC;AAAA,IAC/E,GAAI,MAAM,gBAAA,KAAqB,MAAA,GAAY,EAAE,gBAAA,EAAkB,KAAA,CAAM,gBAAA,EAAiB,GAAI,EAAC;AAAA,IAC3F,GAAI,MAAM,qBAAA,KAA0B,MAAA,GAChC,EAAE,qBAAA,EAAuB,KAAA,CAAM,qBAAA,EAAsB,GACrD;AAAC,GACN,CAAA;AASD,EAAA,IAAI,IAAA,GAAO,OAAO,WAAA,CAAY,MAAA;AAC9B,EAAA,IAAI,MAAA,GAAS,aAAa,IAAI,CAAA;AAC9B,EAAA,IAAI,MAAA,CAAO,gBAAgB,CAAA,EAAG;AAG5B,IAAA,MAAM,KAAA,GAAQC,sCAAA;AAAA,MACZ,KAAA,CAAM,UAAA;AAAA,MACN,MAAA,CAAO,WAAA;AAAA,MACP,MAAA,CAAO;AAAA,KACT;AACA,IAAA,MAAM,QAAQ,MAAA,CAAO,WAAA,CAAY,IAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAClD,IAAA,OAAO,MAAA,CAAO,aAAA,GAAgB,CAAA,IAAK,IAAA,GAAO,CAAA,EAAG;AAC3C,MAAA,IAAA,GAAOC,wCAAA,CAAuB,KAAA,EAAO,IAAA,EAAM,MAAA,CAAO,eAAe,KAAK,CAAA;AACtE,MAAA,MAAA,GAAS,aAAa,IAAI,CAAA;AAAA,IAC5B;AAAA,EACF;AAEA,EAAA,MAAM,EAAE,aAAA,EAAe,GAAG,MAAA,EAAO,GAAI,MAAA;AACrC,EAAA,OAAO,MAAA;AACT;;;ACtRO,SAAS,sBAAA,CACd,UAAA,EACA,SAAA,EACA,UAAA,EACA,IAAA,EAKoB;AAWpB,EAAA,MAAM,WAAA,GAAc;AAAA,IAClB,GAAGX,+BAAA;AAAA,MACDC,oCAAA,CAAmB,MAAM,OAAA,EAAS;AAAA,QAChC,OAAA,EAAS,OAAA;AAAA,QACT,IAAA,EAAM,MAAM,eAAA,IAAmBW;AAAA,OAChC;AAAA,KACH;AAAA,IACA,GAAGT,wCAAsB,SAAA,EAAW;AAAA,MAClC,OAAA,EAAS,OAAA;AAAA,MACT,IAAA,EAAM,MAAM,QAAA,IAAYU;AAAA,KACzB;AAAA,IACD,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,IAAA,CAAK,SAAS,CAAC,CAAA;AAGjC,EAAA,MAAM,kBAAA,GAAqB,UAAA,GAAaR,wCAAA,CAAuB,UAAU,CAAA,GAAI,EAAA;AAO7E,EAAA,MAAM,WAAA,GAAc,CAACS,KAAAA,KACnBR,wCAAA,CAAuB,UAAA,EAAY,YAAY,KAAA,CAAM,CAAA,EAAGQ,KAAI,CAAA,EAAG,kBAAkB,CAAA;AAEnF,EAAA,MAAM,MAAM,IAAA,EAAM,GAAA;AAClB,EAAA,IAAI,GAAA,KAAQ,MAAA,EAAW,OAAO,WAAA,CAAY,YAAY,MAAM,CAAA;AAQ5D,EAAA,MAAM,KAAA,GAAQ,IAAA,EAAM,KAAA,KAAU,CAACC,KAAAA,KAA6BA,KAAAA,CAAAA;AAC5D,EAAA,IAAI,OAAO,WAAA,CAAY,MAAA;AACvB,EAAA,IAAI,IAAA,GAAO,YAAY,IAAI,CAAA;AAC3B,EAAA,IAAI,YAAA,GAAe,KAAA,CAAM,IAAI,CAAA,EAAG,MAAA,IAAU,CAAA;AAC1C,EAAA,IAAI,YAAA,IAAgB,KAAK,OAAO,IAAA;AAGhC,EAAA,MAAM,KAAA,GAAQL,sCAAA,CAAqB,UAAA,EAAY,WAAA,EAAa,kBAAkB,CAAA;AAC9E,EAAA,MAAM,QAAQ,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAC3C,EAAA,OAAO,YAAA,GAAe,GAAA,IAAO,IAAA,GAAO,CAAA,EAAG;AACrC,IAAA,IAAA,GAAOC,wCAAA,CAAuB,KAAA,EAAO,IAAA,EAAM,YAAA,GAAe,KAAK,KAAK,CAAA;AACpE,IAAA,IAAA,GAAO,YAAY,IAAI,CAAA;AACvB,IAAA,YAAA,GAAe,KAAA,CAAM,IAAI,CAAA,EAAG,MAAA,IAAU,CAAA;AAAA,EACxC;AACA,EAAA,OAAO,IAAA;AACT;;;ACzNO,SAAS,2BAA2B,IAAA,EAA0D;AACnG,EAAA,MAAM,IAAA,GAAA,CAAQ,IAAA,CAAK,YAAA,IAAgB,IAAI,MAAA,CAAO,CAAC,CAAA,KAAmB,OAAO,MAAM,QAAA,IAAY,CAAA,CAAE,IAAA,EAAK,CAAE,SAAS,CAAC,CAAA;AAC9G,EAAA,MAAM,cAAc,IAAA,CAAK,aAAA,GAAgB,IAAI,CAAA,KAAM,IAAA,CAAK,eAAe,CAAA,GAAI,CAAA,CAAA;AAC3E,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,EAAG,IAAA,CAAK,QAAA,GAAW,UAAU,CAAC,CAAA;AAClE,EAAA,MAAM,gBAAA,GAAmB,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,MAAA;AAE5C,EAAA,MAAM,SAAA,GAAY,CAAC,GAAG,IAAI,CAAA;AAC1B,EAAA,IAAI,iBAAA,GAAoB,CAAA;AACxB,EAAA,IAAI,gBAAA,GAAmB,CAAA;AACvB,EAAA,IAAI,KAAK,aAAA,EAAe;AAAE,IAAA,SAAA,CAAU,IAAA,CAAK,KAAK,aAAa,CAAA;AAAG,IAAA,iBAAA,GAAoB,SAAA,CAAU,MAAA;AAAA,EAAO;AACnG,EAAA,IAAI,KAAK,YAAA,EAAc;AAAE,IAAA,SAAA,CAAU,IAAA,CAAK,KAAK,YAAY,CAAA;AAAG,IAAA,gBAAA,GAAmB,SAAA,CAAU,MAAA;AAAA,EAAO;AAEhG,EAAA,MAAM,MAAA,GAAS,KAAK,MAAA,IAAU,UAAA;AAC9B,EAAA,MAAM,OAAA,GAAU,gBAAgB,MAAM,CAAA;AAGtC,EAAA,MAAM,gBAAA,GAAmB,MAAA,KAAW,UAAA,IAAc,qBAAA,CAAsB,KAAK,MAAM,CAAA;AACnF,EAAA,IAAI,aAAA,GAAgB,EAAA;AACpB,EAAA,IAAI,gBAAA,IAAoB,iBAAA,GAAoB,CAAA,IAAK,gBAAA,KAAqB,CAAA,EAAG,CAEzE,MAAA,IAAW,iBAAA,GAAoB,CAAA,IAAK,gBAAA,GAAmB,CAAA,EAAG;AACxD,IAAA,aAAA,GAAgB,OAAA,CAAQ,MAAA,CAAO,iBAAA,EAAmB,gBAAgB,CAAA;AAAA,EACpE,CAAA,MAAA,IAAW,oBAAoB,CAAA,EAAG;AAChC,IAAA,aAAA,GAAgB,OAAA,CAAQ,KAAA,CAAM,iBAAA,EAAmB,SAAS,CAAA;AAAA,EAC5D,CAAA,MAAA,IAAW,mBAAmB,CAAA,EAAG;AAC/B,IAAA,aAAA,GAAgB,OAAA,CAAQ,KAAA,CAAM,gBAAA,EAAkB,SAAS,CAAA;AAAA,EAC3D;AACA,EAAA,MAAM,gBAAA,GAAmB,IAAA,CAAK,cAAA,IAAkB,IAAA,CAAK,MAAA,GAAS,CAAA,IAAK,UAAA,GAAa,CAAA,GAC5E,OAAA,CAAQ,UAAA,CAAW,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,GACjC,EAAA;AACJ,EAAA,MAAM,YAAA,GAAe,CAAC,aAAA,EAAe,gBAAgB,EAAE,MAAA,CAAO,OAAO,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAC/E,EAAA,OAAO,EAAE,SAAA,EAAW,YAAA,EAAc,gBAAA,EAAkB,mBAAmB,gBAAA,EAAiB;AAC1F;AAUO,SAAS,sBAAsB,MAAA,EAAqC;AACzE,EAAA,IAAI,CAAC,QAAQ,OAAO,KAAA;AACpB,EAAA,OAAO,iEAAA,CAAkE,KAAK,MAAM,CAAA;AACtF;;;AC1DA,IAAM,KAAA,GAAQ,CAAC,CAAA,KAAmC;AAChD,EAAA,MAAM,CAAA,GAAI,GAAG,IAAA,EAAK;AAClB,EAAA,OAAO,CAAA,IAAK,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,CAAA,GAAI,MAAA;AACjC,CAAA;AACA,IAAM,SAAA,GAAY,CAAC,EAAA,KAAA,CAChB,EAAA,IAAM,EAAC,EAAG,GAAA,CAAI,KAAK,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,KAAmB,MAAM,MAAS,CAAA;AAY3D,SAAS,uBAAuB,IAAA,EAAkD;AACvF,EAAA,MAAM,MAAA,GAAS,KAAK,MAAA,IAAUK,4BAAA;AAC9B,EAAA,MAAM,aAAA,GAAgB,KAAA,CAAM,IAAA,CAAK,aAAa,CAAA;AAC9C,EAAA,MAAM,YAAA,GAAe,KAAA,CAAM,IAAA,CAAK,YAAY,CAAA;AAC5C,EAAA,MAAM,SAAA,GAAY,SAAA,CAAU,IAAA,CAAK,YAAY,CAAA;AAC7C,EAAA,MAAM,SAAA,GAAY,UAAU,IAAA,CAAK,YAAY,EAAE,KAAA,CAAM,CAAA,EAAG,OAAO,MAAM,CAAA;AACrE,EAAA,MAAM,SAAA,GAAY,UAAU,IAAA,CAAK,YAAY,EAAE,KAAA,CAAM,CAAA,EAAG,OAAO,KAAK,CAAA;AAEpE,EAAA,MAAM,eAAA,GAAkB,UAAU,MAAA,GAAS,CAAA,IAAK,UAAU,MAAA,GAAS,CAAA,IAAK,UAAU,MAAA,GAAS,CAAA;AAQ3F,EAAA,MAAM,mBAAmB,CAAC,eAAA,KAAoB,OAAA,CAAQ,aAAa,KAAK,CAAC,YAAA,CAAA;AAEzE,EAAA,IAAI,gBAAA,EAAkB;AACpB,IAAA,IAAI,iBAAiB,YAAA,EAAc;AACjC,MAAA,OAAO,EAAE,IAAA,EAAM,kBAAA,EAAoB,aAAA,EAAe,YAAA,EAAc,oBAAoB,EAAC,EAAG,kBAAA,EAAoB,IAAI,kBAAA,EAAoB,IAAI,YAAA,EAAc,EAAA,EAAI,kBAAkB,CAAA,EAAE;AAAA,IAChL;AACA,IAAA,OAAO,EAAE,IAAA,EAAM,aAAA,EAAe,eAAe,YAAA,EAAc,MAAA,EAAW,oBAAoB,EAAC,EAAG,kBAAA,EAAoB,IAAI,kBAAA,EAAoB,IAAI,YAAA,EAAc,EAAA,EAAI,kBAAkB,CAAA,EAAE;AAAA,EACtL;AAMA,EAAA,MAAM,SAAS,0BAAA,CAA2B;AAAA,IACxC,QAAQ,IAAA,CAAK,MAAA;AAAA,IACb,aAAA;AAAA,IACA,YAAA;AAAA,IACA,YAAA,EAAc,SAAA;AAAA,IACd,UAAU,MAAA,CAAO;AAAA,GAClB,CAAA;AACD,EAAA,MAAM,qBAAqB,MAAA,CAAO,SAAA;AAClC,EAAA,MAAM,eAAe,MAAA,CAAO,YAAA;AAC5B,EAAA,MAAM,mBAAmB,MAAA,CAAO,gBAAA;AAEhC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,WAAA;AAAA,IACN,aAAA,EAAe,MAAA;AAAA,IACf,YAAA,EAAc,MAAA;AAAA,IACd,kBAAA;AAAA,IACA,kBAAA,EAAoB,SAAA;AAAA,IACpB,kBAAA,EAAoB,SAAA;AAAA,IACpB,YAAA;AAAA,IACA;AAAA,GACF;AACF;ACrCA,IAAM,4BAAA,GAA+B,mBAAA;AACrC,SAAS,qBAAqB,QAAA,EAAsC;AAClE,EAAA,OAAOC,mCAAA,CAA6B,QAAA,IAAY,4BAA4B,CAAA,EAAG,MAAA,IAAU,CAAA;AAC3F;AAEO,SAAS,2BAA2B,IAAA,EAA0D;AACnG,EAAA,MAAM,QAAQ,oBAAA,CAAqB,IAAA,CAAK,QAAQ,CAAA,IAAK,IAAA,CAAK,iBAAiB,CAAA,GAAI,CAAA,CAAA;AAC/E,EAAA,MAAM,SAAS,0BAAA,CAA2B;AAAA,IACxC,QAAQ,IAAA,CAAK,MAAA;AAAA,IACb,eAAe,IAAA,CAAK,aAAA;AAAA,IACpB,cAAc,IAAA,CAAK,YAAA;AAAA,IACnB,QAAA,EAAU,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAK,CAAA;AAAA,IAC3B,cAAA,EAAgB;AAAA,GACjB,CAAA;AACD,EAAA,IAAI,MAAA,CAAO,SAAA,CAAU,MAAA,KAAW,CAAA,EAAG;AACjC,IAAA,OAAO,EAAE,WAAW,MAAA,CAAO,SAAA,EAAW,cAAc,EAAA,EAAI,gBAAA,EAAkB,OAAO,gBAAA,EAAiB;AAAA,EACpG;AACA,EAAA,OAAO,EAAE,WAAW,MAAA,CAAO,SAAA,EAAW,cAAc,MAAA,CAAO,YAAA,EAAc,gBAAA,EAAkB,MAAA,CAAO,gBAAA,EAAiB;AACrH;;;AClCA,IAAM,oBAAA,GAAuB,CAAA;AAEtB,SAAS,oBAAoB,IAAA,EAA4C;AAC9E,EAAA,MAAM,IAAA,GAAA,CAAQ,IAAA,CAAK,YAAA,IAAgB,EAAC,EAAG,MAAA,CAAO,CAAC,CAAA,KAAmB,OAAO,CAAA,KAAM,QAAA,IAAY,CAAA,CAAE,SAAS,CAAC,CAAA;AACvG,EAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACrB,IAAA,OAAO;AAAA,MACL,SAAA,EAAW,IAAA,CAAK,WAAA,GAAc,CAAC,IAAA,CAAK,aAAA,EAAe,IAAA,CAAK,WAAW,CAAA,GAAI,CAAC,IAAA,CAAK,aAAa,CAAA;AAAA,MAC1F,YAAA,EAAc,EAAA;AAAA,MACd,gBAAA,EAAkB,CAAA;AAAA,MAClB,eAAA,EAAiB;AAAA,KACnB;AAAA,EACF;AACA,EAAA,MAAM,SAAS,0BAAA,CAA2B;AAAA,IACxC,QAAQ,IAAA,CAAK,MAAA;AAAA,IACb,eAAe,IAAA,CAAK,aAAA;AAAA,IACpB,cAAc,IAAA,CAAK,WAAA;AAAA,IACnB,YAAA,EAAc,IAAA;AAAA,IACd,QAAA,EAAU,oBAAA;AAAA,IACV,cAAA,EAAgB;AAAA,GACjB,CAAA;AACD,EAAA,OAAO;AAAA,IACL,WAAW,MAAA,CAAO,SAAA;AAAA,IAClB,cAAA,EAAgB,mBAAA;AAAA,IAChB,cAAc,MAAA,CAAO,YAAA;AAAA,IACrB,kBAAkB,MAAA,CAAO,gBAAA;AAAA,IACzB,eAAA,EAAiB;AAAA,GACnB;AACF;;;AC9DA,IAAM,IAAA,GAAwE;AAAA,EAC5E,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,SAAS,OAAA,EAAQ;AAAA,EAC3C,IAAA,EAAM,EAAE,OAAA,EAAS,OAAA,EAAS,SAAS,MAAA,EAAO;AAAA,EAC1C,EAAA,EAAI,EAAE,OAAA,EAAS,QAAA,EAAU,SAAS,KAAA,EAAM;AAAA,EACxC,IAAA,EAAM,EAAE,OAAA,EAAS,KAAA,EAAO,SAAS,QAAA;AACnC,CAAA;AAGA,IAAM,YAAA,GAAwF;AAAA,EAC5F,EAAA,EAAI;AAAA,IACF,IAAA,EAAM,IAAA;AAAA,IACN,OAAA,EAAS,+FAAA;AAAA,IACT,KAAA,EAAO;AAAA,GACT;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,MAAA;AAAA,IACN,OAAA,EAAS,mEAAA;AAAA,IACT,KAAA,EAAO;AAAA;AAEX,CAAA;AAWO,SAAS,uBAAA,CAAwB,WAA8B,UAAA,EAA6B;AACjG,EAAA,MAAM,KAAA,GAAQ,cAAc,UAAA,CAAW,IAAA,KAAS,CAAA,EAAG,UAAA,CAAW,IAAA,EAAM,CAAA,EAAA,CAAA,GAAO,EAAA;AAC3E,EAAA,MAAM,EAAE,OAAA,EAAS,OAAA,EAAQ,GAAI,KAAK,SAAS,CAAA;AAE3C,EAAA,IAAI,SAAA,KAAc,IAAA,IAAQ,SAAA,KAAc,MAAA,EAAQ;AAC9C,IAAA,MAAM,CAAA,GAAI,aAAa,SAAS,CAAA;AAChC,IAAA,OACE,CAAA,EAAG,KAAK,CAAA,iCAAA,EAC4B,CAAA,CAAE,IAAI,CAAA,0BAAA,EAA6B,OAAO,6EAA6E,OAAO,CAAA,4CAAA,EAA+C,EAAE,OAAO,CAAA,2BAAA,EAC9L,EAAE,KAAK,CAAA,sJAAA,EACb,OAAO,CAAA,gIAAA,EAA8H,OAAO,qGACvI,OAAO,CAAA,0EAAA,CAAA;AAAA,EAEtC;AAGA,EAAA,OACE,GAAG,KAAK,CAAA,6BAAA,EACwB,OAAO,CAAA,gDAAA,EAAmD,OAAO,CAAA,8DAAA,EAC/E,OAAO,CAAA,wDAAA,EAA2D,OAAO,2IAC/E,OAAO,CAAA,wHAAA,EACyE,OAAO,CAAA,2OAAA,EAC3E,OAAO,QAAQ,OAAO,CAAA,0EAAA,CAAA;AAElD;ACDO,SAAS,kBAAkB,IAAA,EAAgD;AAChF,EAAA,MAAM,YAAY,IAAA,CAAK,iBAAA,IAAqB,EAAC,EAAG,OAAO,OAAO,CAAA;AAC9D,EAAA,MAAM,SAAA,GAAY,KAAK,SAAA,IAAa,MAAA;AACpC,EAAA,MAAM,WAAWC,0BAAA,CAAqB;AAAA,IACpC,UAAU,IAAA,CAAK,QAAA;AAAA,IACf,SAAA;AAAA,IACA,yBAAyB,IAAA,CAAK;AAAA,GAC/B,CAAA;AAED,EAAA,MAAM,MAAA,GAAS,CAAC,IAAA,CAAK,aAAA,EAAe,IAAA,CAAK,WAAW,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,KAAmB,OAAA,CAAQ,CAAC,CAAC,CAAA;AAE3F,EAAA,IAAI,QAAA,KAAa,OAAA,IAAW,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG;AAC/C,IAAA,OAAO;AAAA,MACL,QAAA,EAAU,OAAA;AAAA,MACV,kBAAA,EAAoB,QAAA;AAAA,MACpB,YAAA,EAAc,EAAA;AAAA,MACd,GAAI,SAAA,KAAc,WAAA,IAAe,CAAC,IAAA,CAAK,0BACnC,EAAE,aAAA,EAAe,sBAAA,EAAgC,GACjD;AAAC,KACP;AAAA,EACF;AAEA,EAAA,MAAM,GAAA,GAAMD,mCAAAA,CAA6B,IAAA,CAAK,QAAA,IAAY,EAAE,CAAA,EAAG,MAAA;AAC/D,EAAA,IAAI,QAAQ,MAAA,IAAa,QAAA,CAAS,MAAA,GAAS,MAAA,CAAO,SAAS,GAAA,EAAK;AAC9D,IAAA,OAAO,EAAE,UAAU,OAAA,EAAS,kBAAA,EAAoB,UAAU,YAAA,EAAc,EAAA,EAAI,eAAe,WAAA,EAAY;AAAA,EACzG;AAEA,EAAA,MAAM,kBAAA,GAAqB,CAAC,GAAG,QAAQ,CAAA;AACvC,EAAA,MAAM,YAAsB,EAAC;AAC7B,EAAA,IAAI,KAAK,aAAA,EAAe;AACtB,IAAA,kBAAA,CAAmB,IAAA,CAAK,KAAK,aAAa,CAAA;AAC1C,IAAA,IAAI,CAAC,qBAAA,CAAsB,IAAA,CAAK,MAAM,CAAA,EAAG;AACvC,MAAA,SAAA,CAAU,KAAK,WAAA,CAAY,KAAA,CAAM,kBAAA,CAAmB,MAAA,EAAQ,SAAS,CAAC,CAAA;AAAA,IACxE;AAAA,EACF;AACA,EAAA,IAAI,KAAK,WAAA,EAAa;AACpB,IAAA,kBAAA,CAAmB,IAAA,CAAK,KAAK,WAAW,CAAA;AACxC,IAAA,SAAA,CAAU,KAAK,WAAA,CAAY,KAAA,CAAM,kBAAA,CAAmB,MAAA,EAAQ,SAAS,CAAC,CAAA;AAAA,EACxE;AAEA,EAAA,OAAO,EAAE,UAAU,WAAA,EAAa,kBAAA,EAAoB,cAAc,SAAA,CAAU,IAAA,CAAK,GAAG,CAAA,EAAE;AACxF","file":"chunk-EKRO42T3.cjs","sourcesContent":["/**\n * The reference-binding surface string — `@image_N` / `@video_N` / `@audio_N`\n * — and the one identity sentence built on it. Split out of\n * `video-reference-resolver.ts` so the id-addressed token resolver\n * (`ref-id-tokens.ts`) can bind through the same arrows without a module\n * cycle; the resolver re-exports both, so importers are unaffected.\n */\n\n/**\n * The SINGLE swap-point for the reference-binding surface-string (design D1/D7).\n *\n * Every place that renders an `@image_N`-style binding into a video prompt — the\n * per-image subject phrasing, the bare ordinal in a \"Use these characters\" /\n * pair-back bullet, and the opening/closing frame directive — MUST go through\n * these five arrows. The default form is `@image_N`; if the D7 probe shows a\n * provider prefers the legacy `Image N` form, flipping is editing ONLY these five\n * arrows (`@image_${n}` → `Image ${n}`), nothing downstream.\n *\n * This IS the live swap-point: `resolveVideoReferenceCore` routes the per-image\n * subject phrasing, the \"Use these characters\" / pair-back bullet ordinals, and\n * the frame directive through these arrows, and `resolveReferenceTokens` resolves\n * the body `{image:N}` tokens through `REF_BINDING[kind]` — so the five arrows\n * are the ONLY emission sites for the binding surface string.\n */\n/**\n * The identity-reference binding sentence shared by the flat-image-list\n * resolvers (gemini-omni, veo i2v): names the ordinal span as identities and\n * says the two things a multimodal model needs to hear — match exactly, and\n * these are not frames. One spelling; both resolvers ride it.\n */\nexport function identityRefsSentence(\n firstOrdinal: number,\n lastOrdinal: number,\n ordinal: (n: number) => string = REF_BINDING.ordinal,\n): string {\n return firstOrdinal === lastOrdinal\n ? `${ordinal(firstOrdinal)} is an identity reference for this shot's subjects — match its subject's exact appearance; it is not a frame.`\n : `${ordinal(firstOrdinal)} through ${ordinal(lastOrdinal)} are identity references for this shot's subjects — match each subject's exact appearance; they are not frames.`\n}\n\nexport const REF_BINDING = {\n image: (label: string, n: number) => `the ${label} from @image_${n}`,\n video: (label: string, n: number) => `the ${label} from @video_${n}`,\n audio: (label: string, n: number) => `the ${label} from @audio_${n}`,\n /** ordinal as it appears in a \"Use these characters\" bullet / pair-back */\n ordinal: (n: number) => `@image_${n}`,\n frame: (n: number, role: \"opening\" | \"closing\") =>\n `Use @image_${n} as the ${role} (${role === \"opening\" ? \"first\" : \"last\"}) frame of the video.`,\n} as const\n\n/** How a video provider addresses its images in the prompt, by array order\n * (`VideoReferenceSyntax` in @nodaro/shared). */\nexport type ImageReferenceSyntax = \"@image_N\" | \"@image(n)\" | \"[Image N]\" | \"image N\"\n\n/** The binding sentences — frames and identity references — in one provider syntax. */\nexport interface ImageBinding {\n ordinal: (n: number) => string\n /** One frame: the opening or the closing one. */\n frame: (n: number, role: \"opening\" | \"closing\") => string\n /** A start and an end frame together. */\n frames: (first: number, last: number) => string\n /** Seats `first`..`last` are references, not frames. */\n identities: (first: number, last: number) => string\n}\n\nconst roleWords = (role: \"opening\" | \"closing\") => `${role} (${role === \"opening\" ? \"first\" : \"last\"})`\n\n/** \"image 1\", \"image 1 and image 2\", \"image 1, image 2 and image 3\". */\nfunction plainList(first: number, last: number): string {\n const names = Array.from({ length: last - first + 1 }, (_, i) => `image ${first + i}`)\n return names.length === 1 ? names[0] : `${names.slice(0, -1).join(\", \")} and ${names[names.length - 1]}`\n}\n\n/**\n * The image binding in a provider's OWN reference syntax\n * (`videoProviderReferenceSyntax` in @nodaro/shared). `@image_N` IS\n * {@link REF_BINDING} — the platform swap point, byte for byte; `@image(n)` and\n * `[Image N]` spell the same sentences in the form the provider documents.\n *\n * `image N` (Gemini Omni Flash 1.2) is plain words, each sentence stating what\n * an image IS: \"image 3 is the opening (first) frame of the video. image 1 and\n * image 2 are references for the subject; they are not frames.\" It is the form\n * KIE's own example uses. In staging runs neither wording made 1.2 open on the\n * frame: beside references the frame is one more reference, so the sentence\n * guides the clip and never makes the frame exact.\n */\nexport function imageBindingFor(syntax: ImageReferenceSyntax): ImageBinding {\n if (syntax === \"image N\") {\n return {\n ordinal: (n) => `image ${n}`,\n frame: (n, role) => `image ${n} is the ${roleWords(role)} frame of the video.`,\n frames: (first, last) => `image ${first} is the ${roleWords(\"opening\")} frame and image ${last} is the ${roleWords(\"closing\")} frame of the video.`,\n identities: (first, last) => first === last\n ? `image ${first} is a reference for the subject; it is not a frame.`\n : `${plainList(first, last)} are references for the subject; they are not frames.`,\n }\n }\n const ordinal = syntax === \"@image_N\"\n ? REF_BINDING.ordinal\n : syntax === \"@image(n)\" ? (n: number) => `@image(${n})` : (n: number) => `[Image ${n}]`\n return {\n ordinal,\n frame: syntax === \"@image_N\" ? REF_BINDING.frame : (n, role) => `Use ${ordinal(n)} as the ${roleWords(role)} frame of the video.`,\n frames: (first, last) => `Use ${ordinal(first)} as the ${roleWords(\"opening\")} frame and ${ordinal(last)} as the ${roleWords(\"closing\")} frame of the video.`,\n identities: (first, last) => identityRefsSentence(first, last, ordinal),\n }\n}\n\n/**\n * The instruction that makes Seedance EDIT a wired clip rather than use it as a\n * style reference — the Video to Video node's Seedance lane prepends it to the\n * user's own sentence. Written with the EDITOR token (`{video:1}`) so it goes\n * through the same reference resolver as any prompt: the source clip is the\n * run's first reference video, so it resolves to `@video_1` on the wire. The\n * instruction lands on its own line after it.\n */\nexport const SEEDANCE_VIDEO_EDIT_PREFIX = \"edit {video:1} as follows:\\n\"\n\n/** The user's sentence, framed as an edit of the source clip. Idempotent: a\n * prompt that already opens with the instruction (a preset's pre text, a\n * hand-written one, either token spelling) is left alone. */\nexport function buildSeedanceVideoEditPrompt(prompt: string | undefined): string {\n const body = (prompt ?? \"\").trim()\n if (/^edit\\s+(?:\\{video:1(?::[^}]*)?\\}|@video_1(?!\\w))/i.test(body)) return body\n return `${SEEDANCE_VIDEO_EDIT_PREFIX}${body}`\n}\n","/**\n * `{ref:<id>}` / `{ref:<id>:<label>}` — id-addressed reference tokens.\n *\n * The API/Studio form of the positional `{image:N}` token: the client names a\n * reference by its OWN `connectedReferences[].id` and the platform substitutes\n * the `@image_N` seat after IT has done the numbering. Without it a client that\n * wanted the binding inline had to mirror the numbering walk client-side — a\n * duplicated rule that misbinds pictures the moment the walk changes.\n *\n * `resolveVideoReferenceCore` builds the `RefIdTokenContext` DURING its walk\n * and calls `resolveRefIdTokens` before the `referenceOrder` reorder (so the\n * binding follows the reference to its final seat); the video routes call it\n * standalone on their no-image-reference early return (nothing seated, so\n * every token degrades).\n */\n\nimport { REF_BINDING } from \"./ref-binding.js\"\n\n/** The label class of `REFERENCE_TOKEN_RE` (`{image:N:label}`), shared. */\nconst REF_TOKEN_LABEL_RE = /^[a-zA-Z0-9_ -]+$/\n/** Cheap gate for the whole pass — a prompt without it is untouched. */\nconst HAS_REF_ID_TOKEN_RE = /\\{ref:/i\n/**\n * One well-formed `{ref:…}` token: everything between `{ref:` and the next\n * `}` that contains no brace. Greedy over a brace-free class, so the scan is\n * LINEAR in the prompt length whatever the content — `prompt` is up to\n * `PROMPT_HARD_CEILING` (30k) chars of caller-controlled text, so a lazy\n * quantifier with a nested optional label group here would be a quadratic-time\n * ReDoS surface. The id / label split happens in code (`splitLabel`), not in\n * the regex. `ref` is case-insensitive; ids are not.\n */\nconst REF_ID_TOKEN_RE = /\\{[rR][eE][fF]:([^{}]*)\\}/g\n/**\n * Last-resort net for a MALFORMED `{ref:` (a brace inside the id, or no\n * closing `}`): drop the `{ref:` run up to the next whitespace or brace, so\n * the prefix can never reach a model, without eating prose past the token.\n */\nconst MALFORMED_REF_ID_TOKEN_RE = /\\{[rR][eE][fF]:[^\\s{}]*\\}?/g\n\n/** What `resolveRefIdTokens` resolves against — the numbering walk's output. */\nexport interface RefIdTokenContext {\n /** Reference id → the 1-based `@image_N` seat the walk gave it. */\n readonly slotById: ReadonlyMap<string, number>\n /** Reference id → display name, the degrade target of a token that cannot bind. */\n readonly nameById: ReadonlyMap<string, string>\n /**\n * How many image references actually ship — the same range gate `{image:N}`\n * uses. A seat past it (a capped-out or duplicate-URL ref) must not bind.\n */\n readonly imageCount: number\n}\n\n/**\n * Split a token's content into `<id>` and an optional `<label>` at the LAST\n * colon — only when the tail is a well-formed label. Ids are opaque and may\n * themselves contain `:` (`slug:variant`) or `/` (a URL), so nothing before\n * the last colon is ever interpreted.\n */\nfunction splitLabel(content: string): { id: string; label?: string } {\n const at = content.lastIndexOf(\":\")\n if (at === -1) return { id: content }\n const tail = content.slice(at + 1)\n if (!REF_TOKEN_LABEL_RE.test(tail)) return { id: content }\n return { id: content.slice(0, at), label: tail }\n}\n\n/**\n * Rewrite id-addressed reference tokens into the `@image_N` binding of the\n * reference the caller sent under that id.\n *\n * Ids are matched by IDENTITY against the known ids (seated or named), never\n * parsed by character class: the whole content is tried as an id first (the\n * longest reading — an id may itself end in something label-shaped), then\n * `<id>:<label>` split at the last colon, then the token is unknown. The label\n * class is the one `REFERENCE_TOKEN_RE` uses. An id containing `{`, `}`, an\n * `@name:N` mention or a `{image:N}` token is unsupported (the mention pass\n * runs first and would rewrite it; a brace ends the token).\n *\n * Per token:\n * - id seated in range → `REF_BINDING.image(label, N)` when labeled, else the\n * bare `REF_BINDING.ordinal(N)` — exactly what `{image:N[:label]}` emits.\n * - otherwise (unknown id, ref skipped by the walk, capped out, or no image\n * references at all) → the label if given, else the ref's display name if\n * the id is known, else \"\". A token never ships raw — a malformed one is\n * dropped by the last-resort net.\n *\n * No whitespace tidy here: every caller runs `resolveReferenceTokens` after\n * this (the core does at every return), and that collapses the gap a dropped\n * token leaves. Returns the input untouched when it carries no `{ref:` at all.\n */\nexport function resolveRefIdTokens(\n prompt: string | undefined,\n ctx: RefIdTokenContext,\n): string | undefined {\n if (!prompt || !HAS_REF_ID_TOKEN_RE.test(prompt)) return prompt\n const known = (id: string): boolean => id.length > 0 && (ctx.slotById.has(id) || ctx.nameById.has(id))\n const bind = (id: string, label: string | undefined): string => {\n const slot = ctx.slotById.get(id)\n if (slot !== undefined && slot >= 1 && slot <= ctx.imageCount) {\n return label ? REF_BINDING.image(label, slot) : REF_BINDING.ordinal(slot)\n }\n return label ?? ctx.nameById.get(id) ?? \"\"\n }\n return prompt\n .replace(REF_ID_TOKEN_RE, (_match, content: string) => {\n if (known(content)) return bind(content, undefined)\n const { id, label } = splitLabel(content)\n if (known(id)) return bind(id, label)\n return label ?? \"\"\n })\n .replace(MALFORMED_REF_ID_TOKEN_RE, \"\")\n}\n","/**\n * Shared video-reference resolver CORE — the ONE pure implementation of the\n * video-prompt `@-mention` + canonical-fallback + extras assembly that BOTH the\n * frontend (`frontend/src/lib/video-prompt-assembly.ts`) and backend\n * (`backend/src/services/workflow-engine/payload-builder.ts`) resolvers delegate\n * to.\n *\n * Historically the FE and BE each hand-rolled a byte-for-byte copy of this logic\n * (they had to stay in lock-step or single-node FE runs and orchestrator runs\n * would diverge). This module is the lift-and-shift of the FRONTEND resolver's\n * post-expansion body into one place so there is a single source of truth.\n *\n * Purity contract: NO FE/BE-only dependencies. The caller is responsible for the\n * layer-specific work BEFORE calling in:\n * - expanding wired Character upstreams into `ConnectedReference[]`\n * (`wiredCharRefs`), and\n * - looking up an extra-ref's character metadata by slug\n * (`lookupCharacterBySlug` — FE: `nodes.find(...)`, BE: `buildCtx`).\n *\n * Behavior-preserving when first extracted (the FE resolver's post-expansion\n * body was lifted verbatim). The ONE intended output change since is the\n * reference-binding surface string: the per-image subject phrasing, the bullet\n * ordinals, and the frame directive now emit `@image_N` / `@video_N` /\n * `@audio_N` (the legacy form was `Image N`) — all routed through the single\n * `REF_BINDING` swap-point below. The structural strings (\"Use these\n * characters:\", the bullet layout, \"… is the same subject as …\") are otherwise\n * unchanged.\n */\n\nimport {\n DEFAULT_USAGE_MODE,\n usageModeDirective,\n type UsageMode,\n findCharacterMentionTokens,\n type CharacterMentionTokenInfo,\n roleToPhrase,\n REFERENCE_ROLE_PRESETS,\n resolveDefaultRole\n} from \"@nodaro/shared/entities\"\nimport { resolveCharacterMentions, applyReferenceOrderToVideo } from \"./prompt-builder.js\"\nimport { buildIdentityLockLine, withForcedIdentityLock } from \"./identity-lock.js\"\nimport type { ConnectedReference, DescribedReference } from \"@nodaro/shared/types\"\nimport {\n appendReferenceLines,\n referenceDescriptionLine,\n renderDescribedReferenceLines,\n renderReferenceCaptionLines,\n} from \"./described-references.js\"\nimport { REF_BINDING } from \"./ref-binding.js\"\nimport { resolveRefIdTokens } from \"./ref-id-tokens.js\"\nimport { insertBeforeStyleSection } from \"./prompt-style-section.js\"\nimport { tidyReferenceTokenGaps, unboundReferenceTokenText } from \"./unbound-reference-tokens.js\"\n\n// The binding surface string and the id-addressed token resolver live in their\n// own modules (see them for the contracts); re-exported here so every existing\n// importer of this module — and the package index's `export *` — keeps working.\nexport { REF_BINDING, identityRefsSentence, imageBindingFor, SEEDANCE_VIDEO_EDIT_PREFIX, buildSeedanceVideoEditPrompt, type ImageBinding, type ImageReferenceSyntax } from \"./ref-binding.js\"\nexport { resolveRefIdTokens, type RefIdTokenContext } from \"./ref-id-tokens.js\"\n\n\n/**\n * Positional reference counts the editor tokens are resolved against — how many\n * image / video / audio references are wired into the node, in worker-payload\n * order. `{image:N:…}` is 1-based against `image`, `{video:N:…}` against `video`,\n * etc. A token whose N exceeds its count (or whose kind has count 0) is dropped.\n */\nexport interface ReferenceCounts {\n image: number\n video: number\n audio: number\n}\n\n/** Matches an editor reference token: `{image:1}`, `{video:2:clip}`, `{audio:3:my song}`.\n * Group 1 = kind, group 2 = 1-based slot N, group 3 = optional label\n * (alphanumerics, underscore, space, hyphen). Case-insensitive on the kind. */\nconst REFERENCE_TOKEN_RE = /\\{(image|video|audio):(\\d+)(?::([a-zA-Z0-9_ -]+))?\\}/gi\n\n/**\n * Rewrite the editor's `{image:N:label}` / `{video:N:label}` / `{audio:N:label}`\n * reference tokens into `@image_N` / `@video_N` / `@audio_N` subject bindings.\n *\n * The token's `N` is POSITIONAL (1-based) against the matching `counts` entry —\n * the worker-payload order of wired references of that kind. Per match:\n * - `N < 1` or `N > counts[kind]` (out of range / no such reference) → drop to\n * the bare `label` (or empty if label-less). This is the legacy\n * `stripVideoImageTokens` strip behavior: drop the token, keep the label text.\n * The rendering is `unboundReferenceTokenText` — shared with the image prompt\n * builder, which applies the same rule to its own unwired `{image:N}` tokens.\n * - in range, label present → `REF_BINDING[kind](label, N)`\n * (e.g. `the person from @image_2`).\n * - in range, no label → the bare `@${kind}_${N}` (the ref-only default): the\n * binding lands with no descriptive wrapper.\n *\n * Runs of 2+ HORIZONTAL whitespace (left behind by a dropped label-less token)\n * collapse to one space, the result is trimmed (`tidyReferenceTokenGaps`, also\n * shared with the image builder), and an empty result becomes `undefined` —\n * matching the `stripVideoImageTokens` contract so this can replace it cleanly.\n * The collapse class is `[^\\S\\r\\n]` (NOT `\\s`) on purpose: the core applies this\n * to the FULLY-ASSEMBLED prompt, which carries `\\n\\n` block separators between\n * the \"Use these characters:\" directive block and the body — a `\\s{2,}` collapse\n * would silently merge those paragraphs. Horizontal-only collapse preserves\n * newline structure while still tidying the dropped-token gap.\n * The `kind` is lowercased before indexing `counts`/`REF_BINDING`, so a\n * case-variant token (`{Image:1}`) resolves to the same binding rather than\n * mis-classifying as out-of-range.\n */\nexport function resolveReferenceTokens(\n prompt: string | undefined,\n counts: ReferenceCounts,\n): string | undefined {\n if (!prompt) return prompt\n return tidyReferenceTokenGaps(\n prompt.replace(REFERENCE_TOKEN_RE, (_match, rawKind: string, nStr: string, label?: string) => {\n const kind = rawKind.toLowerCase() as keyof ReferenceCounts\n const n = parseInt(nStr, 10)\n if (n < 1 || n > counts[kind]) return unboundReferenceTokenText(label)\n if (label) return REF_BINDING[kind](label, n)\n return `@${kind}_${n}`\n }),\n ) || undefined\n}\n\n\n/**\n * A user-attached \"extra reference image\" row. Layer-agnostic shape of the\n * frontend `ExtraRef` / backend extras: only the fields this core reads.\n */\nexport interface VideoExtraRef {\n /**\n * The caller's own id for this reference (`connectedReferences[].id` on the\n * route, `extraRefs[].id` on the canvas) — what a `{ref:<id>}` token in the\n * prompt names. Slot-map only: the reorder's tile id stays `wired:<url>`.\n * Absent → the extra cannot be addressed by id (it still numbers normally).\n */\n id?: string\n url: string\n description?: string\n characterSlug?: string\n variantSlug?: string\n usageMode?: UsageMode\n /**\n * Wired scene-composition fragment (held-prop / styling / text) for this\n * extra — the `ConnectedReference.elementInjection` analogue. Surfaced ONLY\n * in hybrid mode on a FIRST-SIGHT character extra, as its own trailing scene\n * directive (mirrors the canonical/mention hybrid paths). Absent → unchanged.\n */\n elementInjection?: string | null\n /**\n * Optional, opt-in per-extra identity-lock (the `ConnectedReference.identityLock`\n * analogue). Emitted ONLY in hybrid mode on a first-sight character extra, via\n * `buildIdentityLockLine`. Default OFF (absent / `enabled === false`) → no lock.\n * When absent, the first-sight branch falls back to the node's mapped lock\n * from `CharacterMeta.identityLock`.\n */\n identityLock?: { enabled: boolean; text?: string }\n /**\n * The `ConnectedReference.defaultRole` analogue for ROUTE-supplied extras\n * (generate-video / text-to-video / MCP verbs), whose adapter can only feed\n * the COALESCED `defaultUsageMode` as `usageMode` — which would otherwise\n * trip the meta-suppression gate and permanently ignore the node default.\n * Preferred over `CharacterMeta.defaultRole` at the first-sight role site.\n * The expander (`expandExtraRefsToConnectedReferences`) already suppresses\n * the source field when a true per-ref override existed, so passing it\n * through verbatim preserves the override-wins precedence. Canvas /\n * orchestrator extras leave this unset (they resolve via `CharacterMeta`).\n */\n defaultRole?: string\n /**\n * The `ConnectedReference.descriptionOverride` analogue — the PER-USE identity\n * description for this extra. Fills the row's own description slot (the\n * pair-back tail, the name-mode bullet, the first-sight descriptor), winning\n * over `description`, which keeps its label semantics. Absent → unchanged.\n */\n descriptionOverride?: string\n}\n\n/**\n * Character metadata resolved by the caller for an extra-ref's `characterSlug`.\n * Mirrors the per-layer upstream lookup: FE reads `CharacterNodeData`\n * (`characterName` / `defaultUsageMode` / `canonicalDescription`); BE reads its\n * `buildExtraRefCharacterContextLookup` context (`displayName` →\n * `characterName`, etc.).\n */\nexport interface CharacterMeta {\n characterName?: string\n defaultUsageMode?: UsageMode\n canonicalDescription?: string\n /** Character node's hybrid `defaultRole` — takes precedence over\n * `defaultUsageMode` at the video extras first-sight role site (via\n * `resolveDefaultRole`). Absent when the node never set a role. */\n defaultRole?: string\n /** Character node's `identityLock`, already mapped to the per-reference lock\n * shape (`characterLockToRefLock`). Read at the video extras first-sight lock\n * site; a per-extra `identityLock` still wins over it. */\n identityLock?: { enabled: boolean; text?: string }\n}\n\nexport interface ResolveVideoReferenceCoreArgs {\n prompt: string | undefined\n /** Already expanded by the caller's layer-specific expander. */\n wiredCharRefs: ConnectedReference[]\n extraRefs?: readonly VideoExtraRef[]\n /** Look up an extra-ref's character metadata by slug (FE: nodes.find; BE: buildCtx). */\n lookupCharacterBySlug?: (slug: string) => CharacterMeta | undefined\n referenceOrder?: readonly string[]\n suppressedCanonicalCharacterIds?: readonly string[]\n /**\n * Positional counts the editor numbers `{image:N}` / `{video:N}` / `{audio:N}`\n * body tokens against — the TOTAL reference-handle count of each kind wired\n * into the node (base reference images + mention additions), in worker-payload\n * order. Supplied by the callers in Tasks 3.2/4.1; when omitted, `image` falls\n * back to the core's own merged-URL count and `video`/`audio` to 0 (the core\n * only attaches image URLs).\n */\n imageRefCount?: number\n videoRefCount?: number\n audioRefCount?: number\n /**\n * Plain image-reference URLs that LEAD the unified `@image_N` numbering\n * (D5: image-refs-first). They occupy `@image_1 … @image_offset`; every\n * character/asset directive this core emits is numbered AFTER them, and they\n * are prepended to the returned `additionalUrls` so the worker payload order\n * matches the ordinals. When provided, they OWN the image token count (so\n * `imageRefCount` is ignored for the image modality); omitted → legacy\n * behaviour (`imageRefCount ?? mergedLen`, asset URLs numbered from 1).\n * Already deduped/reordered by the caller (e.g. `connectedRefImageOrder`).\n */\n leadingRefUrls?: readonly string[]\n /**\n * Number of leading image-refs the CALLER owns + merges itself (so the core\n * offsets asset directive ordinals + the `{image:N}` count by this much WITHOUT\n * prepending any URLs to `additionalUrls`). Used by i2v, whose frame-promotion\n * consumes the asset URLs directly and can't have leading refs spliced in.\n * Ignored when `leadingRefUrls` is set. D5 unified-asset-references.\n */\n ordinalOffset?: number\n /**\n * Render character / canonical-fallback / extra-ref directives in the hybrid\n * role-phrase form (`the {role} from @image_N`) instead of the legacy\n * `Use these characters:` block; default false = today's behavior, unchanged.\n * Wired in Phase B Tasks 2-3.\n */\n hybridRoles?: boolean\n /**\n * Display names for EVERY reference the caller knows by id — including the\n * ones it did NOT hand to this walk (the route caps `connectedReferences` to\n * the provider's image budget before calling in). A `{ref:<id>}` token that\n * cannot bind degrades to `label ?? refNamesById[id] ?? \"\"`, so a capped-out\n * or skipped reference keeps its name in the prose instead of vanishing.\n * The wired character refs' `defaultName`s are known without this.\n */\n refNamesById?: ReadonlyMap<string, string>\n /**\n * References the caller can NAME and DESCRIBE but has no media for — an\n * un-bound cast role, an analysis slot. They attach no URL and claim no\n * `@image_N` seat: they are rendered as prose by `renderDescribedReferenceLines`\n * and joined the way this lane joins its trailing directives. The\n * described-ONLY request (no wired characters, no extras) is the normal case,\n * so the early return below carries the join too.\n */\n describedReferences?: readonly DescribedReference[]\n /**\n * Captions for the node's video / audio rail references, INDEX-ALIGNED with\n * the caller's `referenceVideoUrls` / `referenceAudioUrls`. Rendered as\n * `@video_N: <caption>.` / `@audio_N: <caption>.` and bounded by\n * `videoRefCount` / `audioRefCount`, so a caption for a url the provider cap\n * dropped never binds a slot the payload does not ship.\n */\n videoCaptions?: readonly string[]\n audioCaptions?: readonly string[]\n}\n\n/** Result of the HYBRID mention pass — inline role phrases + surfaced opt-in\n * locks + wired element injections (no \"Use these characters:\" block). */\ninterface HybridMentionResult {\n /** Body with each `@`-mention replaced INLINE by its role phrase\n * (e.g. \"the face from @image_1\"). */\n prompt: string\n /** Matched character URLs in mention order, DEDUPED. The deduped order +\n * `offset` define each URL's `@image_N` slot, so the per-mention binding and\n * the core's `position` walk agree with the final merged URL list. */\n additionalUrls: string[]\n /** Slugs that had at least one resolved mention. */\n mentionedCharacterSlugs: Set<string>\n /** Per-reference opt-in identity-lock lines (non-null, one per unique URL).\n * The caller prepends them as ONE block. Empty by default (opt-in). */\n lockLines: string[]\n /** Non-empty `elementInjection` fragments (one per unique URL). The caller\n * appends them as trailing scene directives. */\n elementDirectives: string[]\n}\n\n/**\n * HYBRID-mode character mention convergence for the VIDEO core (Unified\n * Reference Roles, Phase B — Task 2). The video analogue of\n * `resolveCharacterMentionsHybrid` in `prompt-builder.ts`: where the legacy\n * `resolveCharacterMentions` prepends the `\"Use these characters:\"` bullet block\n * and replaces tokens with bare display names, this renders each `@`-mention as\n * the inline role phrase `roleToPhrase(role, REF_BINDING.ordinal(slot))`\n * (e.g. \"the face from @image_1\") and surfaces the optional identity-lock + the\n * wired `elementInjection` SEPARATELY for the caller to assemble. No block.\n *\n * Slot/binding: a character URL's 1-based position in the DEDUPED mention-URL\n * list, OFFSET by `offset` (the count of leading plain image-refs that precede\n * the assets in the unified `@image_N` numbering — D5). Deduping keeps the\n * binding aligned with both the core's `position` walk and the final merged URL\n * list (a character mentioned N times attaches ONE URL → ONE slot).\n *\n * Role: the mention's `usageMode` (3rd-segment) — or `variantSlug` when no mode\n * parsed — is the role when it's a curated `REFERENCE_ROLE_PRESETS[\"wired-character\"]`\n * entry (`person`/`face`/`clothes`/`hair`/`pose`/`expression`/`style`); otherwise\n * the source default (`\"person\"`). Matching is variant-first, canonical-fallback,\n * so a role-word 3rd segment that parsed as a variant slug still attaches the\n * canonical reference instead of being dropped.\n */\nfunction resolveVideoCharacterMentionsHybrid(\n prompt: string,\n tokens: readonly CharacterMentionTokenInfo[],\n refs: readonly ConnectedReference[],\n offset: number,\n): HybridMentionResult {\n const bySlug = new Map<string, ConnectedReference>()\n const byVariant = new Map<string, ConnectedReference>()\n for (const r of refs) {\n if (!r.characterSlug) continue\n if (!r.variantSlug) bySlug.set(r.characterSlug, r)\n else byVariant.set(`${r.characterSlug}:${r.variantSlug}`, r)\n }\n\n const presets = REFERENCE_ROLE_PRESETS[\"wired-character\"]\n\n const mentionedCharacterSlugs = new Set<string>()\n const refByUrl = new Map<string, ConnectedReference>()\n // Per-mention `~lock` / `~nolock` (Task 4 + F4): the tri-state lock OVERRIDE\n // per attached URL — `true` (force on) / `false` (force off, suppressing a\n // ref-level enabled lock) / absent (inherit). Fed to `withForcedIdentityLock`\n // before `buildIdentityLockLine`. Mirrors the image-side hybrid resolvers.\n const lockOverrideByUrl = new Map<string, boolean>()\n const matched: Array<{ token: string; offset: number; url: string; role: string }> = []\n for (const t of tokens) {\n // Variant-first, canonical-fallback. `variantMatch` (a REAL matched variant\n // URL) doubles as the \"this segment selected a variant, not a role\" signal.\n const variantMatch = t.variantSlug\n ? byVariant.get(`${t.characterSlug}:${t.variantSlug}`)\n : undefined\n const match = variantMatch ?? bySlug.get(t.characterSlug)\n if (!match || !match.url) continue\n mentionedCharacterSlugs.add(t.characterSlug)\n refByUrl.set(match.url, match)\n if (t.lock !== undefined) lockOverrideByUrl.set(match.url, t.lock)\n // Variant + Role Separation: an explicit 4th-segment ROLE\n // (`@kira:1:walking:clothes`) wins outright and coexists with the variant —\n // used VERBATIM, in lock-step with the image-side resolver.\n const explicitRole = (t.role ?? \"\").trim()\n const segment = (t.usageMode ?? t.variantSlug ?? \"\").trim()\n // Custom roles survive VERBATIM — the SAME relaxation as the image-side\n // `resolveCharacterMentionsHybrid` (Unified Reference Roles, Phase D), kept\n // in lock-step so FE single-node + orchestrator video runs never diverge. A\n // preset OR a free-form value in the variant/role slot that didn't resolve to\n // a real variant URL → role; a real variant slug or a directive-only usage\n // mode → the NODE default (`defaultRole` verbatim → `defaultUsageMode`-derived\n // → \"person\", via `resolveDefaultRole` — Character Node Role+Lock), mirroring\n // the image-side mention fallback. Precedence: seg4 role → seg3 token role →\n // node default chain.\n const role = explicitRole\n ? explicitRole\n : segment && (presets.includes(segment) || (t.usageMode == null && !variantMatch))\n ? segment\n : resolveDefaultRole(match.defaultRole, match.defaultUsageMode, \"wired-character\")\n matched.push({ token: t.token, offset: t.offset, url: match.url, role })\n }\n\n // Deduped URL list + slot map together. Slot = offset + 1-based dedup index →\n // `@image_N` (REF_BINDING.ordinal), so the binding lands AFTER the leading refs\n // and aligns with the core's `position` walk over `additionalUrls`.\n const additionalUrls: string[] = []\n const slotByUrl = new Map<string, number>()\n for (const m of matched) {\n if (!slotByUrl.has(m.url)) {\n slotByUrl.set(m.url, offset + slotByUrl.size + 1)\n additionalUrls.push(m.url)\n }\n }\n const bindingFor = (url: string): string => {\n const slot = slotByUrl.get(url)\n return slot !== undefined ? REF_BINDING.ordinal(slot) : REF_BINDING.ordinal(offset + 1)\n }\n\n // Replace mention tokens right-to-left so earlier offsets stay valid.\n let resolvedPrompt = prompt\n for (const m of [...matched].sort((a, b) => b.offset - a.offset)) {\n const phrase = roleToPhrase(m.role, bindingFor(m.url))\n resolvedPrompt =\n resolvedPrompt.slice(0, m.offset) + phrase + resolvedPrompt.slice(m.offset + m.token.length)\n }\n\n // One opt-in lock + one element directive per UNIQUE attached URL.\n const lockLines: string[] = []\n const elementDirectives: string[] = []\n const seenUrls = new Set<string>()\n for (const m of matched) {\n if (seenUrls.has(m.url)) continue\n seenUrls.add(m.url)\n const ref = refByUrl.get(m.url)\n if (!ref) continue\n const binding = bindingFor(m.url)\n const lock = buildIdentityLockLine(withForcedIdentityLock(ref, lockOverrideByUrl.get(m.url)), binding)\n if (lock) lockLines.push(lock)\n // A per-use `descriptionOverride` has no slot in a hybrid role phrase, so it\n // rides the trailing-directive channel as its own binding-subject line —\n // exactly as on the image lane (`pushOverrideDirective`).\n const overrideLine = referenceDescriptionLine(binding, ref.descriptionOverride)\n if (overrideLine) elementDirectives.push(overrideLine)\n const inject = ref.elementInjection?.trim()\n if (inject) elementDirectives.push(inject)\n }\n\n return { prompt: resolvedPrompt, additionalUrls, mentionedCharacterSlugs, lockLines, elementDirectives }\n}\n\n/**\n * Resolve `@kira:N` / `@kira:N:smile` mentions in a video-node prompt against\n * the caller-supplied wired Character references AND apply the per-character\n * canonical fallback for unmentioned wired characters, plus manual/extra refs.\n *\n * Per-character behavior contract (parity with image-side + backend):\n * - wired-character with at least one `@-mention` → contribute ONLY the\n * mentioned variant URLs (no canonical auto-attach), prepend the\n * mention-derived directive block.\n * - wired-character with NO `@-mention` → contribute the canonical URL\n * + a strong identity directive (mode-aware via `defaultUsageMode`).\n *\n * Returns the mutated prompt + the asset URLs to slot into the worker payload.\n * The caller decides where (i2v has both `imageUrl` and `referenceImageUrls`;\n * v2v has only a single `referenceImageUrl`; t2v has `referenceImageUrls` only).\n */\nexport function resolveVideoReferenceCore(\n args: ResolveVideoReferenceCoreArgs,\n): { prompt: string | undefined; additionalUrls: string[] } {\n // Counts the editor numbers `{image:N}` / `{video:N}` / `{audio:N}` body tokens\n // against. `image` falls back to the core's own merged-URL count (passed by the\n // caller as `imageFallback`); `video`/`audio` to 0 since the core attaches only\n // image URLs. Applied at every return so token resolution is uniform.\n // Leading plain image-refs (D5 image-refs-first): they occupy `@image_1 …\n // @image_offset`, lead the merged URL list, and OWN the image token count.\n // Deduped + trimmed defensively (the caller already orders them).\n const leadingRefUrls: string[] = []\n {\n const seenLead = new Set<string>()\n for (const u of args.leadingRefUrls ?? []) {\n const t = u?.trim()\n if (t && !seenLead.has(t)) { seenLead.add(t); leadingRefUrls.push(t) }\n }\n }\n const hasLeading = args.leadingRefUrls != null\n // `offset` = how many image-refs precede the assets in the unified numbering.\n // Two ways to specify it: `leadingRefUrls` (the core OWNS the URLs — prepends\n // them to `merged`, offset = their count) OR `ordinalOffset` (the CALLER owns\n // the leading URLs and merges them itself — e.g. i2v's frame-promotion path —\n // so the core only offsets the numbering + count, no prepend). They're mutually\n // exclusive; `leadingRefUrls` wins if both are (mistakenly) passed.\n const offset = hasLeading ? leadingRefUrls.length : (args.ordinalOffset ?? 0)\n const hasUnified = hasLeading || args.ordinalOffset != null\n // image token count: the FULL unified count = offset (leading) + asset count,\n // where asset count = mergedLen − the core-owned leading URLs. (leadingRefUrls\n // mode: offset == leadingRefUrls.length → count == mergedLen; ordinalOffset\n // mode: leadingRefUrls is empty → count == offset + mergedLen.) Without a\n // unified offset, keep the legacy `imageRefCount ??` fallback. video/audio\n // always from args (this core attaches only image URLs).\n const tokenCounts = (mergedLen: number): ReferenceCounts => ({\n image: hasUnified ? (offset + mergedLen - leadingRefUrls.length) : (args.imageRefCount ?? mergedLen),\n video: args.videoRefCount ?? 0,\n audio: args.audioRefCount ?? 0,\n })\n // Described references (name + description, no media) and the video/audio rail\n // captions — rendered ONCE, ahead of every exit, because both the early return\n // below and the main assembly have to carry them. They attach no URL and claim\n // no `@image_N` seat, so they take no part in the numbering walk. Empty for\n // every caller that sends neither → every existing output is untouched.\n const describedAndCaptionLines = [\n ...renderDescribedReferenceLines(args.describedReferences),\n ...renderReferenceCaptionLines(args.videoCaptions, args.audioCaptions, {\n video: args.videoRefCount ?? 0,\n audio: args.audioRefCount ?? 0,\n }),\n ]\n let wiredCharRefs = [...args.wiredCharRefs]\n const suppressedSlugs = new Set(args.suppressedCanonicalCharacterIds ?? [])\n if (suppressedSlugs.size > 0) {\n wiredCharRefs = wiredCharRefs.filter((r) => {\n if (r.source !== \"wired-character\") return true\n if (!r.characterSlug) return true\n if (r.variantSlug) return true\n return !suppressedSlugs.has(r.characterSlug)\n })\n }\n // Extras are valid even WITHOUT any wired character upstream (e.g. the user\n // uploaded loose reference photos and typed per-row descriptions). The\n // early-return below is gated on (no chars AND no extras) so we don't skip\n // extras-only setups.\n const hasExtras = (args.extraRefs?.length ?? 0) > 0\n // `{ref:<id>}` degrade names — every id this call knows: the wired refs'\n // display names, the extras' ids (name-less, so they match by identity rather\n // than falling to the catch-all), then the caller's own map on top (it knows\n // the refs it capped out before calling in).\n const nameByRefId = new Map<string, string>()\n for (const r of args.wiredCharRefs) {\n if (r.id && !nameByRefId.has(r.id)) nameByRefId.set(r.id, r.defaultName || r.characterSlug || \"\")\n }\n for (const ex of args.extraRefs ?? []) {\n if (ex.id && !nameByRefId.has(ex.id)) nameByRefId.set(ex.id, \"\")\n }\n for (const [id, name] of args.refNamesById ?? []) {\n if (id) nameByRefId.set(id, name)\n }\n // id → seat, recorded by the walk below as `position` advances — never\n // recovered from URLs afterwards (the walk counts a duplicate-URL extra that\n // `merged` dedups, so URL → index is ambiguous; id → position is not).\n const slotByRefId = new Map<string, number>()\n if (wiredCharRefs.length === 0 && !hasExtras) {\n // No wired chars / extras, but the node can still carry plain base reference\n // images (leadingRefUrls), so `{image:N}` body tokens MUST still resolve. The\n // count is the leading-ref count (or the legacy `imageRefCount` when no\n // leading refs were passed); the leading URLs are returned for the payload.\n // Nothing was seated, so every `{ref:}` degrades (label → name → \"\").\n const counts = tokenCounts(leadingRefUrls.length)\n // tokenCounts(leadingRefUrls.length) → image count == offset (no assets here):\n // leadingRefUrls mode counts the leading refs; ordinalOffset mode counts the\n // caller-owned leading refs the offset stands in for.\n const resolved = resolveReferenceTokens(\n resolveRefIdTokens(args.prompt, { slotById: slotByRefId, nameById: nameByRefId, imageCount: counts.image }),\n counts,\n )\n return {\n // Described references and rail captions are the ONE thing this branch can\n // still contribute: they need no seat, so a request that carries only them\n // (a story landing before any entity exists) reaches the model here.\n prompt: describedAndCaptionLines.length > 0\n ? appendReferenceLines(resolved ?? \"\", describedAndCaptionLines, args.hybridRoles === true ? \"hybrid\" : \"legacy\")\n : resolved,\n additionalUrls: [...leadingRefUrls],\n }\n }\n const knownCharSlugs = Array.from(\n new Set(\n wiredCharRefs\n .map((r) => r.characterSlug)\n .filter((s): s is string => typeof s === \"string\" && s.length > 0),\n ),\n )\n // Empty user prompt is allowed — canonical fallback / mention resolution\n // can fill the prompt entirely. Treat undefined/empty as `\"\"` so the\n // resolver flows through to mention + canonical-fallback assembly below.\n const promptForResolution = args.prompt ?? \"\"\n const mentionTokens = knownCharSlugs.length > 0\n ? findCharacterMentionTokens(promptForResolution, knownCharSlugs)\n : []\n // Hybrid (Phase B) emits inline role phrases (`the {role} from @image_N`) +\n // surfaced opt-in locks / element injections; legacy emits the shared \"Use\n // these characters:\" block. Gated on `args.hybridRoles` (default false →\n // byte-identical legacy output).\n const hybrid = args.hybridRoles === true\n // Resolve any mentions (may be empty); always check fallback after.\n let resolved: { prompt: string; additionalUrls: string[]; mentionedCharacterSlugs: Set<string> }\n // Hybrid mention-pass byproducts (empty in legacy mode): the lock lines the\n // caller prepends once + the element injections it appends as trailing lines.\n let mentionLockLines: string[] = []\n let mentionElementDirectives: string[] = []\n if (hybrid) {\n const h = resolveVideoCharacterMentionsHybrid(promptForResolution, mentionTokens, wiredCharRefs, offset)\n resolved = { prompt: h.prompt, additionalUrls: h.additionalUrls, mentionedCharacterSlugs: h.mentionedCharacterSlugs }\n mentionLockLines = h.lockLines\n mentionElementDirectives = h.elementDirectives\n } else {\n resolved = mentionTokens.length > 0\n ? resolveCharacterMentions(promptForResolution, mentionTokens, wiredCharRefs)\n : { prompt: promptForResolution, additionalUrls: [] as string[], mentionedCharacterSlugs: new Set<string>() }\n }\n\n // Canonical fallback for any wired character NOT @-mentioned. Single\n // canonical URL + strong directive per unmentioned character — mirrors\n // `buildCanonicalFallback` from the shared prompt-builder and the backend\n // `resolveVideoPromptMentions`. The directive's wording is mode-aware:\n // resolves through the character node's `defaultUsageMode` → global\n // `DEFAULT_USAGE_MODE` so a character configured for \"face\" emits a\n // face-only directive instead of the identity-lock language.\n const fallbackUrls: string[] = []\n const fallbackDirectiveLines: string[] = []\n // Hybrid canonical-fallback byproducts (empty in legacy mode): the role\n // phrases appended to the body, the opt-in lock lines, and the wired element\n // injections — mirrors `renderCanonicalFallbackHybrid` in prompt-builder.ts.\n const canonicalPhrases: string[] = []\n const canonicalLockLines: string[] = []\n const canonicalElementDirectives: string[] = []\n const seenSlugs = new Set<string>()\n // Character-slug → first emitted position, used by extras to pair back via\n // \"Image B is the same subject as Image A, …\". Built from mention URLs +\n // canonical fallback URLs as they're emitted.\n const positionsByChar = new Map<string, number>()\n // `position` walks the FINAL merged URL list (leading plain refs first, then\n // mention URLs, canonical fallback, then extras). Seeded past the leading refs\n // (D5) so directive ordinals number AFTER them and align with the worker's\n // `referenceImageUrls` order.\n let position = offset\n for (let i = 0; i < resolved.additionalUrls.length; i++) {\n position += 1\n const url = resolved.additionalUrls[i]\n // Look up which ref this URL came from to learn its characterSlug.\n const ref = wiredCharRefs.find((r) => r.url === url)\n const slug = ref?.characterSlug\n if (slug && !positionsByChar.has(slug)) positionsByChar.set(slug, position)\n // `{ref:<id>}`: every wired ref sharing this URL sits in this seat (the\n // merge dedups by URL). First sight wins — the legacy mention pass does\n // not dedup, so a re-mentioned URL advances `position` but keeps its seat.\n for (const r of wiredCharRefs) {\n if (r.url === url && r.id && !slotByRefId.has(r.id)) slotByRefId.set(r.id, position)\n }\n }\n for (const r of wiredCharRefs) {\n if (r.source !== \"wired-character\") continue\n if (!r.characterSlug) continue\n if (resolved.mentionedCharacterSlugs.has(r.characterSlug)) continue\n if (seenSlugs.has(r.characterSlug)) continue\n if (r.variantSlug) continue\n if (!r.url) continue\n seenSlugs.add(r.characterSlug)\n fallbackUrls.push(r.url)\n position += 1\n if (!positionsByChar.has(r.characterSlug)) positionsByChar.set(r.characterSlug, position)\n if (r.id && !slotByRefId.has(r.id)) slotByRefId.set(r.id, position)\n // Hybrid: emit the inline role phrase (`the person from @image_N`) + opt-in\n // lock + wired element injection instead of a \"Use these characters:\"\n // bullet. The selection above (canonical entry only, deduped, skip mentioned)\n // is shared with the legacy branch so both paths attach the SAME URLs.\n if (hybrid) {\n const binding = REF_BINDING.ordinal(position)\n // Node-default role chain (Character Node Role+Lock): `defaultRole`\n // (hybrid dropdown pick, verbatim) → `defaultUsageMode`-derived → source\n // default — mirroring the image `renderCanonicalFallbackHybrid`.\n canonicalPhrases.push(roleToPhrase(resolveDefaultRole(r.defaultRole, r.defaultUsageMode, r.source), binding))\n const lock = buildIdentityLockLine(r, binding)\n if (lock) canonicalLockLines.push(lock)\n const overrideLine = referenceDescriptionLine(binding, r.descriptionOverride)\n if (overrideLine) canonicalElementDirectives.push(overrideLine)\n const inject = r.elementInjection?.trim()\n if (inject) canonicalElementDirectives.push(inject)\n continue\n }\n const displayName = r.defaultName || r.characterSlug\n const effectiveMode = r.defaultUsageMode ?? DEFAULT_USAGE_MODE\n // Minimal-intervention modes:\n // - \"none\": URL attached, NO bullet emitted.\n // - \"name\": one bullet with the name, no trailing directive.\n if (effectiveMode === \"none\") {\n continue\n }\n if (effectiveMode === \"name\") {\n fallbackDirectiveLines.push(`- ${REF_BINDING.ordinal(position)} (${displayName})`)\n continue\n }\n const directive = usageModeDirective(effectiveMode)\n const includeCanonicalDesc = effectiveMode === \"identical\" || effectiveMode === \"face-pose\"\n // Wired elements ride the bullet alongside the (mode-gated) canonical desc —\n // mirrors the shared image-side `composeIdentityDescPart`. The subject here\n // is the bare display name (video numbering is applied separately above).\n const descBodyParts: string[] = []\n // The per-use override IS the caller describing this subject for this run,\n // so it fills the identity slot ahead of the stored canonical description —\n // and rides every mode that emits a bullet at all (\"none\" returned above).\n const canonicalOverride = r.descriptionOverride?.trim()\n if (canonicalOverride) {\n descBodyParts.push(canonicalOverride)\n } else if (includeCanonicalDesc && r.characterCanonicalDescription?.trim()) {\n descBodyParts.push(r.characterCanonicalDescription.trim())\n }\n if (r.elementInjection?.trim()) descBodyParts.push(r.elementInjection.trim())\n const descPart = descBodyParts.length > 0\n ? `${displayName} — ${descBodyParts.join(\". \")}`\n : displayName\n fallbackDirectiveLines.push(`- ${descPart}.${directive ? ` ${directive}` : \"\"}`)\n }\n\n // Extras: emit one directive per row. Numbering continues from `position`\n // so the worker's `referenceImageUrls` order lines up with `@image_N` in\n // the assembled prompt. Pair-back (\"same subject as @image_M, …\") fires\n // when the same `characterSlug` was already attached as a mention or\n // canonical fallback.\n //\n // Two render shapes, gated on `hybrid`:\n // - legacy (`!hybrid`) → `- @image_N (reference): …` / `- @image_N is the\n // same subject as @image_M.` bullets in `extraDirectiveLines` (byte-\n // identical to today; consolidated into the \"Use these characters:\" block).\n // - hybrid → trailing role-phrase / pair-back / manual directives in\n // `extraHybridLines` (+ opt-in lock lines + first-sight element\n // injections), mirroring Task 2's canonical branch. NO block, no bullets.\n const extraUrls: string[] = []\n const extraDirectiveLines: string[] = []\n // Hybrid extras byproducts (empty in legacy mode): trailing directives, the\n // opt-in identity-lock lines (prepended once with the mention/canonical locks),\n // and first-sight wired element injections (appended as trailing scene lines).\n const extraHybridLines: string[] = []\n const extraHybridLockLines: string[] = []\n const extraHybridElementDirectives: string[] = []\n if (hasExtras) {\n for (const ex of args.extraRefs!) {\n if (!ex.url) continue\n position += 1\n if (ex.id && !slotByRefId.has(ex.id)) slotByRefId.set(ex.id, position)\n // Per-use override fills this row's description slot; `description` keeps\n // its own label semantics (mirrors the image extras).\n const desc = (ex.descriptionOverride ?? \"\").trim() || (ex.description ?? \"\").trim()\n if (ex.characterSlug) {\n // First sight of this character via an extra. Resolution chain\n // matches the image side: per-ref override → upstream character\n // default → global identical. The caller supplies the upstream\n // character's metadata via `lookupCharacterBySlug`.\n const meta = args.lookupCharacterBySlug?.(ex.characterSlug)\n const charDefaultMode = meta?.defaultUsageMode\n const effectiveMode = ex.usageMode ?? charDefaultMode ?? DEFAULT_USAGE_MODE\n const earlierPos = positionsByChar.get(ex.characterSlug)\n if (hybrid) {\n // Hybrid character extra — mirrors Task 2's mention/canonical role\n // shape. Mode-agnostic (the role comes from the usageMode/variant\n // mapping, not from a none/name/else split): the hybrid path never\n // emits a \"Use these characters:\" bullet, so the legacy minimal-\n // intervention suppression doesn't apply.\n const binding = REF_BINDING.ordinal(position)\n if (earlierPos !== undefined) {\n // Pair-back: `@image_N is the same subject as @image_M[, <desc>].`\n const tail = desc ? `, ${desc}` : \"\"\n extraHybridLines.push(\n `${binding} is the same subject as ${REF_BINDING.ordinal(earlierPos)}${tail}.`,\n )\n } else {\n // First-sight: mapped role phrase + opt-in identity-lock + wired\n // element injection (the latter two surfaced as their own lines,\n // mirroring `renderCanonicalFallbackHybrid` / the mention hybrid).\n // Role chain (Character Node Role+Lock): the node's `defaultRole`\n // (`meta.defaultRole`, verbatim — applied only when the extra\n // carries NO per-ref `usageMode` override, which would win) → the\n // COALESCED effective mode (per-ref override → node default →\n // \"identical\"), preset-mapped → source default — via the shared\n // `resolveDefaultRole` helper. The image `renderExtraRefsHybrid`\n // feeds the same helper the expander-stamped `defaultRole` +\n // coalesced `defaultUsageMode`, so image and video stay fully\n // converged (same helper, same precedence); pinned by\n // `character-convergence-image.test.ts`.\n const role = resolveDefaultRole(\n // Per-extra `defaultRole` (route-supplied CR extras) wins; else\n // the node default via meta — suppressed when the extra carries a\n // true per-ref usageMode override (the override wins).\n ex.defaultRole ?? (ex.usageMode ? undefined : meta?.defaultRole),\n effectiveMode,\n \"wired-character\",\n )\n const phrase = roleToPhrase(role, binding)\n extraHybridLines.push(desc ? `${phrase}, ${desc}.` : `${phrase}.`)\n // Lock: per-extra `identityLock` wins; else the node's mapped lock\n // (`meta.identityLock`, from `characterLockToRefLock`).\n const lock = buildIdentityLockLine(\n {\n id: ex.url,\n defaultName: meta?.characterName || ex.characterSlug,\n source: \"wired-character\",\n url: ex.url,\n characterSlug: ex.characterSlug,\n variantSlug: ex.variantSlug,\n identityLock: ex.identityLock ?? meta?.identityLock,\n },\n binding,\n )\n if (lock) extraHybridLockLines.push(lock)\n const inject = ex.elementInjection?.trim()\n if (inject) extraHybridElementDirectives.push(inject)\n positionsByChar.set(ex.characterSlug, position)\n }\n } else if (earlierPos !== undefined) {\n // Pair-back. Suppressed for \"none\" so the extras-side respects the\n // same minimal-intervention contract as primary mentions.\n if (effectiveMode !== \"none\") {\n const tail = desc ? `, ${desc}` : \"\"\n extraDirectiveLines.push(\n `- ${REF_BINDING.ordinal(position)} is the same subject as ${REF_BINDING.ordinal(earlierPos)}${tail}.`,\n )\n }\n } else if (effectiveMode === \"none\") {\n // URL attached, no bullet. Record the slot for any later same-\n // character extras that pair-back via \"same subject as Image N\".\n positionsByChar.set(ex.characterSlug, position)\n } else if (effectiveMode === \"name\") {\n const displayName = meta?.characterName || ex.characterSlug\n const subject = `${REF_BINDING.ordinal(position)} (${displayName})`\n const descPart = desc ? `${subject} — ${desc}` : subject\n extraDirectiveLines.push(`- ${descPart}.`)\n positionsByChar.set(ex.characterSlug, position)\n } else {\n const directive = usageModeDirective(effectiveMode)\n const displayName = meta?.characterName || ex.characterSlug\n const subject = `${REF_BINDING.ordinal(position)} (${displayName})`\n const includeCanonicalDesc = effectiveMode === \"identical\" || effectiveMode === \"face-pose\"\n const canonicalDesc = meta?.canonicalDescription\n let descPart = subject\n if (desc) descPart = `${subject} — ${desc}`\n else if (includeCanonicalDesc && canonicalDesc?.trim()) descPart = `${subject} — ${canonicalDesc.trim()}`\n extraDirectiveLines.push(`- ${descPart}.${directive ? ` ${directive}` : \"\"}`)\n positionsByChar.set(ex.characterSlug, position)\n }\n } else if (hybrid) {\n // Hybrid manual extra: surface the description tied to its `@image_N`\n // (\"<desc> (@image_N).\") as a trailing directive; a description-less\n // extra still emits a bare positional marker so the model knows what\n // `@image_N` is. No bullet, no block.\n const binding = REF_BINDING.ordinal(position)\n extraHybridLines.push(desc ? `${desc} (${binding}).` : `${binding} (reference).`)\n } else {\n // Manual extra. Description goes in the bullet; absent description\n // still emits a positional marker so the model knows what Image N is.\n if (desc) {\n extraDirectiveLines.push(`- ${REF_BINDING.ordinal(position)} (reference): ${desc}.`)\n } else {\n extraDirectiveLines.push(`- ${REF_BINDING.ordinal(position)} (reference).`)\n }\n }\n extraUrls.push(ex.url)\n }\n }\n\n let finalPrompt = resolved.prompt\n if (hybrid) {\n // Hybrid assembly (mirrors `buildImagePrompt`'s hybrid branch): prepend ONE\n // identity-lock block (mention + canonical + extra first-sight, opt-in →\n // usually empty) and append the body's trailing scene directives (mention\n // element injections, canonical role phrases, canonical element injections,\n // then the extras' hybrid directives + their first-sight element\n // injections). Every extra line carries an `@image_N` binding, so the\n // numbering stays consistent with the core's `position` walk. The\n // `\"Use these characters:\"` block is NEVER assembled here.\n // Set-dedup — mirrors the image assembler: identical lock lines (same ref\n // locked via mention + canonical/extra) are emitted once. {ref}-bound\n // texts keep distinct references distinct.\n const lockLines = [...new Set([...mentionLockLines, ...canonicalLockLines, ...extraHybridLockLines])]\n const trailingLines = [\n ...mentionElementDirectives,\n ...canonicalPhrases,\n ...canonicalElementDirectives,\n ...extraHybridLines,\n ...extraHybridElementDirectives,\n ]\n const lockBlock = lockLines.length > 0 ? `${lockLines.join(\"\\n\")}\\n\\n` : \"\"\n // The trailing lines are scene content, so they extend the BODY: a `[style]`\n // section has no terminator, and appended flat they would read as look\n // clauses under its header instead of as the bindings they are.\n finalPrompt = `${lockBlock}${insertBeforeStyleSection(finalPrompt, trailingLines)}`\n } else {\n const allFallbackLines = [...fallbackDirectiveLines, ...extraDirectiveLines]\n if (allFallbackLines.length > 0) {\n // Mirror shared `buildImagePrompt`'s consolidation: append fallback\n // bullets into an existing \"Use these characters:\" block when present,\n // otherwise create a new one.\n if (finalPrompt && finalPrompt.startsWith(\"Use these characters:\\n\")) {\n const splitIdx = finalPrompt.indexOf(\"\\n\\n\")\n if (splitIdx !== -1) {\n const header = finalPrompt.slice(0, splitIdx)\n const rest = finalPrompt.slice(splitIdx)\n finalPrompt = `${header}\\n${allFallbackLines.join(\"\\n\")}${rest}`\n } else {\n finalPrompt = `${finalPrompt}\\n${allFallbackLines.join(\"\\n\")}`\n }\n } else {\n const block = `Use these characters:\\n${allFallbackLines.join(\"\\n\")}`\n finalPrompt = finalPrompt ? `${block}\\n\\n${finalPrompt}` : block\n }\n }\n }\n\n // Dedup combined URLs while preserving order (LEADING plain refs first, then\n // mentions, fallback, then extras — D5). The `@image_N` labels in the prompt\n // assume this exact order BEFORE any user-defined `referenceOrder` reorder below.\n const merged: string[] = []\n const seen = new Set<string>()\n for (const u of leadingRefUrls) {\n if (u && !seen.has(u)) { seen.add(u); merged.push(u) }\n }\n for (const u of resolved.additionalUrls) {\n if (u && !seen.has(u)) { seen.add(u); merged.push(u) }\n }\n for (const u of fallbackUrls) {\n if (u && !seen.has(u)) { seen.add(u); merged.push(u) }\n }\n for (const u of extraUrls) {\n if (u && !seen.has(u)) { seen.add(u); merged.push(u) }\n }\n\n // Described references + rail captions — joined the way this format joins its\n // own trailing directives (hybrid: ahead of the `[style]` section; legacy:\n // bullets consolidated into the \"Use these characters:\" block the branch above\n // may just have created). Ahead of the reorder, like every other assembled\n // line; they carry no `@image_N` binding for the renumber to move.\n finalPrompt = appendReferenceLines(\n finalPrompt,\n describedAndCaptionLines,\n hybrid ? \"hybrid\" : \"legacy\",\n )\n\n // `{ref:<id>}` tokens resolve HERE — after the walk has seated every reference\n // (the slot map is complete) and BEFORE the user reorder below, so the\n // reorder's `@image_N` renumber pass carries the freshly emitted binding to\n // the ref's final seat. That is the point of an id token: \"this reference,\n // wherever it lands\". `{image:N}` is deliberately the opposite — resolved\n // LAST, after the reorder, so the author's literal N is kept (see the note at\n // the reorder return). Range-gated by the same image count `{image:N}` uses,\n // so a seat the payload never ships (capped out, duplicate URL) degrades to\n // the name instead of binding a phantom `@image_N`. A prompt with no `{ref:`\n // is untouched — byte-identical to before this token existed.\n finalPrompt =\n resolveRefIdTokens(finalPrompt, {\n slotById: slotByRefId,\n nameById: nameByRefId,\n imageCount: tokenCounts(merged.length).image,\n }) ?? finalPrompt\n\n // Apply user-defined reorder + renumber `Image N` tokens — parity with the\n // backend `resolveVideoPromptMentions` and the shared image builder.\n const referenceOrder = args.referenceOrder\n // Reorder ONLY the asset tail (merged minus the core-owned leading plain refs).\n // Leading refs are already in the caller's chosen order and stay fixed; the\n // renumber offsets asset ordinals by `offset` (D5). Slice by the core-owned\n // leading count (0 in ordinalOffset mode, where the caller owns the leading URLs).\n const assetUrls = merged.slice(leadingRefUrls.length)\n if (referenceOrder && referenceOrder.length > 0 && assetUrls.length > 1) {\n const refsForOrdering: ConnectedReference[] = [...wiredCharRefs]\n if (hasExtras) {\n for (const ex of args.extraRefs!) {\n if (!ex.url) continue\n refsForOrdering.push({\n id: ex.url,\n defaultName: ex.characterSlug || \"Extra\",\n source: ex.characterSlug ? \"wired-character\" : \"manual\",\n url: ex.url,\n characterSlug: ex.characterSlug,\n variantSlug: ex.variantSlug,\n isExtraRef: true,\n })\n }\n }\n const reordered = applyReferenceOrderToVideo(assetUrls, finalPrompt, refsForOrdering, referenceOrder, undefined, offset)\n // Resolve body tokens LAST — AFTER the reorder's `@image_N` renumber pass, so\n // it can't miscorrect a freshly-resolved binding (the curly `{image:N}` tokens\n // are invisible to the reorder's `(@image_|Image )` regex, so they ride through\n // untouched and keep their author-typed N — documented v1 behavior). The\n // id-addressed `{ref:<id>}` tokens are the deliberate opposite: resolved\n // BEFORE the reorder (above), so their binding follows the reference.\n return {\n prompt: resolveReferenceTokens(reordered.prompt, tokenCounts(merged.length)),\n additionalUrls: [...leadingRefUrls, ...reordered.urls],\n }\n }\n\n // Same as the reorder branch but no user reorder ran — resolve the body tokens\n // on the assembled prompt as the final step.\n return {\n prompt: resolveReferenceTokens(finalPrompt, tokenCounts(merged.length)),\n additionalUrls: merged,\n }\n}\n","/**\n * Aggregator that walks a consumer node's `audio-style` target handle, collects\n * incoming Sound parameter nodes, and composes a SoundComposition (text +\n * optional structured fields + warnings) used to enrich the consumer's prompt\n * fields. Parallel to how cinematography hints aggregate via\n * `collectCinematographyHints` in front+back, but exposes structured-field\n * outputs for typed targets like MiniMax (genre/mood/instrumental).\n *\n * Called from:\n * - frontend: `frontend/src/lib/audio-style-hints.ts` (canvas executor)\n * - backend: `backend/src/services/workflow-engine/payload-builder.ts`\n */\n\nimport type { HintGraphContext, HintNodeLike } from \"./parameter-prompt-hint.js\"\nimport { getParameterPromptHint } from \"./parameter-prompt-hint.js\"\nimport { getMusicGenre, getMusicEra } from \"./music-genre.js\"\nimport { buildMusicMoodHints } from \"./music-mood.js\"\n\nexport type SoundConsumerType =\n | \"suno-generate\"\n | \"generate-music\"\n | \"voice-design\"\n | \"voice-remix\"\n | \"text-to-audio\"\n\nexport interface SoundCompositionFields {\n readonly genre?: string\n readonly mood?: string\n readonly instrumental?: boolean\n readonly voiceDescription?: string\n /**\n * Suno's binary vocal-gender control. Extracted from a connected\n * voice-character node's `gender` field when it's \"male\" or \"female\"\n * (\"androgynous\" is left to prompt-only since Suno doesn't accept it).\n * Set on suno-generate and generate-music consumers.\n */\n readonly vocalGender?: \"male\" | \"female\"\n}\n\nexport interface SoundComposition {\n readonly text: string\n readonly fields: SoundCompositionFields\n readonly warnings: ReadonlyArray<string>\n}\n\nconst MUSIC_TYPES = new Set([\"music-genre\", \"music-mood\", \"instrumentation\"])\nconst VOICE_TYPES = new Set([\"voice-character\", \"voice-delivery\"])\n\nfunction isMusic(t: string | undefined) { return !!t && MUSIC_TYPES.has(t) }\nfunction isVoice(t: string | undefined) { return !!t && VOICE_TYPES.has(t) }\n\n/**\n * Target handles whose wired pickers contribute an audio-style hint. Before the\n * typed-handle split, suno-generate had ONE picker handle; the split into\n * prompt / audio-style / voice fragmented collection, so a descriptive\n * voice-character picker dropped onto the \"Voice\" pip silently vanished from the\n * output. The voice pip's other accepted sources (suno-voice / voice-design) are\n * persona producers — `getParameterPromptHint` returns \"\" for them, so they're\n * skipped here and still route to `personaId` via the input-resolver. Only\n * suno-generate exposes a `voice` handle among the fold consumers, so adding it\n * here is naturally scoped to suno without affecting generate-music / voice-* /\n * text-to-audio (they have no voice edges to collect).\n */\nconst AUDIO_STYLE_SOURCE_HANDLES: ReadonlySet<string> = new Set([\"audio-style\", \"voice\"])\n\nfunction collectAudioStyleSources(\n consumer: HintNodeLike,\n ctx: HintGraphContext,\n): HintNodeLike[] {\n const sources: HintNodeLike[] = []\n for (const edge of ctx.edges) {\n if (edge.target !== consumer.id) continue\n if (edge.targetHandle == null || !AUDIO_STYLE_SOURCE_HANDLES.has(edge.targetHandle)) continue\n const source = ctx.nodes.find((n) => n.id === edge.source)\n if (source) sources.push(source)\n }\n return sources\n}\n\nexport function composeSoundHintFromConnections(\n consumer: HintNodeLike,\n consumerType: SoundConsumerType,\n ctx: HintGraphContext,\n): SoundComposition {\n const sources = collectAudioStyleSources(consumer, ctx)\n if (sources.length === 0) {\n return { text: \"\", fields: {}, warnings: [] }\n }\n\n const warnings: string[] = []\n const acceptedHints: string[] = []\n const fields: SoundCompositionFields = {}\n\n // Identify which sources are accepted vs warned-about by consumer type.\n //\n // Music consumers (suno-generate, generate-music) accept BOTH music nodes\n // AND voice nodes — voice description (gender, age, accent, language,\n // timbre, delivery archetype) is valid input for music with vocals. Suno\n // V6 in particular benefits from rich voice description; the typed\n // `vocalGender` field on Suno is also extracted below from voice-character.\n //\n // Voice Design rejects music nodes (different domain). Text-to-Audio\n // (sound effects) rejects voice nodes (sound effects aren't vocal).\n for (const src of sources) {\n const t = src.type\n if (consumerType === \"voice-design\" || consumerType === \"voice-remix\") {\n if (isMusic(t)) {\n warnings.push(`Music nodes (${t}) are ignored on ${consumerType === \"voice-design\" ? \"Voice Design\" : \"Voice Remix\"}.`)\n continue\n }\n }\n if (consumerType === \"text-to-audio\") {\n if (isVoice(t)) {\n warnings.push(`Voice nodes (${t}) are ignored on Text to Audio.`)\n continue\n }\n }\n\n const hint = getParameterPromptHint(src)\n if (hint) acceptedHints.push(hint)\n\n // Music consumers — extract vocalGender from connected voice-character\n // node (when gender is \"male\" or \"female\"; \"androgynous\" doesn't map to\n // Suno's binary vocalGender field, so it's left to the prompt-only path).\n // Applies to BOTH suno-generate and generate-music since the Suno\n // provider routes both.\n if (\n (consumerType === \"suno-generate\" || consumerType === \"generate-music\") &&\n t === \"voice-character\" &&\n src.data\n ) {\n const gender = (src.data as Record<string, unknown>).gender\n if ((gender === \"male\" || gender === \"female\") && !fields.vocalGender) {\n Object.assign(fields, { vocalGender: gender })\n }\n }\n\n // Generate Music — populate typed fields when provider is minimax.\n if (consumerType === \"generate-music\" && consumer.data && (consumer.data as Record<string, unknown>).provider === \"minimax\") {\n const data = src.data as Record<string, unknown> | undefined\n if (t === \"music-genre\" && data) {\n const sub = typeof data.subgenre === \"string\" ? data.subgenre : undefined\n const genreId = typeof data.genre === \"string\" ? data.genre : undefined\n const eraId = typeof data.era === \"string\" ? data.era : undefined\n const eraHint = getMusicEra(eraId)?.promptHint\n const genre = getMusicGenre(genreId)\n const subHint = genre?.subgenres.find((s) => s.id === sub)?.promptHint ?? genre?.promptHint\n const composed = [eraHint, subHint].filter(Boolean).join(\" \")\n if (composed && !fields.genre) Object.assign(fields, { genre: composed })\n }\n if (t === \"music-mood\" && data) {\n const composed = buildMusicMoodHints({\n energy: typeof data.energy === \"string\" ? data.energy : undefined,\n emotion: data.emotion as string | ReadonlyArray<string> | undefined,\n vibe: data.vibe as string | ReadonlyArray<string> | undefined,\n })\n if (composed && !fields.mood) Object.assign(fields, { mood: composed })\n }\n if (t === \"instrumentation\" && data) {\n const vp = typeof data.vocalPresence === \"string\" ? data.vocalPresence : undefined\n if (vp === \"instrumental\") Object.assign(fields, { instrumental: true })\n }\n }\n }\n\n const text = acceptedHints.join(\", \")\n\n // Voice Design / Voice Remix: also surface the composed text as\n // voiceDescription so callers can drop it straight into the API field.\n if ((consumerType === \"voice-design\" || consumerType === \"voice-remix\") && text) {\n Object.assign(fields, { voiceDescription: text })\n }\n\n return { text, fields, warnings }\n}\n\n/**\n * Append `composedText` to `userText` with \", \" separator. Empty inputs\n * are tolerated; the non-empty side is returned alone.\n */\nexport function appendField(userText: string, composedText: string): string {\n if (!composedText) return userText\n if (!userText) return composedText\n return `${userText}, ${composedText}`\n}\n\n/**\n * Truncate `composedText` so the final string `userText + \", \" + composedText`\n * fits within `maxTotalLen`. Returns \"\" when the budget is non-positive (user\n * text already at limit). Cuts on word boundary when possible.\n */\nexport function truncateForField(composedText: string, userText: string, maxTotalLen: number): string {\n if (!composedText) return \"\"\n const sepLen = userText.length > 0 ? 2 : 0\n const budget = maxTotalLen - userText.length - sepLen\n if (budget <= 0) return \"\"\n if (composedText.length <= budget) return composedText\n const cut = composedText.slice(0, budget)\n const ws = cut.lastIndexOf(\" \")\n return ws > 0 ? cut.slice(0, ws) : cut\n}\n\n/**\n * Suno Generate auto-detects custom mode when the user typed style/title/lyrics,\n * even without explicitly toggling `customMode`. The frontend executor, backend\n * payload-builder, and the FinalAudioPromptPreview all need to agree on the\n * resolved value or the preview lies about which field receives the audio-style\n * hint.\n *\n * Accepts an unknown-keyed record so callers in both the typed frontend\n * (SunoGenerateData) and the loosely-typed backend (WorkflowNodeData with\n * `[k: string]: unknown`) can hand the node `data` straight in without casts.\n */\nexport function getEffectiveSunoCustomMode(\n data: { readonly [k: string]: unknown },\n): boolean {\n if (typeof data.customMode === \"boolean\") return data.customMode\n return !!(data.style || data.title || data.lyrics)\n}\n\n/**\n * Fold a Generate Music node's genre / mood / instrumental selections into the\n * prompt text. The music worker only reads `prompt`, so this enrichment is the\n * ONLY channel by which those three controls reach the provider.\n *\n * Shared by the single-node route (`/v1/generate-music`) and the workflow\n * orchestrator (`payload-builder.ts` `generate-music`) so a workflow / app /\n * webhook run produces the same prompt — and the same music — as a Run-button\n * run. Mirrors the route's original inline `[prompt, genre, mood, …].join(\", \")`\n * exactly (locked by `routes/__tests__/generate-music.test.ts`).\n */\nexport function appendMusicMeta(\n basePrompt: string,\n meta: { genre?: string; mood?: string; instrumental?: boolean },\n): string {\n const parts = [basePrompt]\n if (meta.genre) parts.push(meta.genre)\n if (meta.mood) parts.push(meta.mood)\n if (meta.instrumental) parts.push(\"instrumental, no vocals\")\n return parts.join(\", \")\n}\n","/**\n * `assembleSunoInput` — the single source of truth for turning a\n * `suno-generate` node's inputs (a prompt + connected Sound pickers + the\n * per-field levers) into the flat field set the Suno provider receives.\n *\n * WHY THIS EXISTS: the same custom-mode / audio-style fold —\n * 1. resolve effective custom mode (`getEffectiveSunoCustomMode`),\n * 2. compose the connected-picker hint (`composeSoundHintFromConnections`),\n * 3. fold it into `style` (custom, 500-char budget) OR `prompt` (non-custom,\n * 3000-char budget), skipping `truncateForField` when the user field is\n * empty,\n * 4. normalize the secondary fields,\n * — was duplicated in THREE places kept in lockstep by hand: the frontend\n * `execute-node.ts` `suno-generate` branch (the DESIGNATED SOURCE OF TRUTH),\n * the backend `payload-builder.ts` `suno-generate` case, and the editor Final\n * preview. This wrapper collapses them into one so preview == run.\n *\n * THE 5 FE↔BE DIVERGENCES, all reconciled toward the FE:\n * (A) `throwOnEmpty` — the FE run rejects an empty prompt+hint; the BE run and\n * the preview do not. Opt-in via the flag (default `false` = BE/preview).\n * (B) skip-when-empty — when the user's `style`/`prompt` field is empty the\n * bare hint is returned (NOT run through `truncateForField`), matching the\n * FE / `foldStyle` behavior. The BE always-truncated.\n * (C) `lyrics` is CALLER-PRE-RESOLVED (`input.lyrics`) — the fn never reads\n * `data.lyrics` for the value (the caller resolves `{}`-refs first; this\n * is the FE bugfix of adopting the BE's ref-resolution). `data.lyrics` is\n * still consulted by `getEffectiveSunoCustomMode` for mode auto-detection.\n * (D) `vocalGender` precedence: `data.vocalGender || fields.vocalGender ||\n * undefined` (manual wins, else the connected voice-character gender).\n * (E) `|| undefined` normalization on `model` / `style` / `title` /\n * `negativeStyle` (and `lyrics`), so empty strings drop out of the payload.\n *\n * CALLER-ONLY fields (deliberately NOT in the result, mirroring how\n * `assembleImageInput` leaves `userId`/`jobId` out): the FE adds `userId`; the\n * BE adds `jobId` / `usageLogId` / the credit id. The caller spreads `...result`\n * and appends those itself.\n */\nimport {\n getEffectiveSunoCustomMode,\n composeSoundHintFromConnections,\n appendField,\n truncateForField,\n} from \"./sound-aggregator.js\"\nimport type { HintGraphContext, HintNodeLike } from \"./parameter-prompt-hint.js\"\n\n/** Suno's server-side caps the fold budgets mirror. */\nconst STYLE_BUDGET = 500\nconst PROMPT_BUDGET = 3000\n\nexport interface AssembleSunoInput {\n /** The `suno-generate` node; `data` is ALREADY field-mapped by the caller. */\n node: HintNodeLike\n /** `{ nodes, edges }` for the connected-picker audio-style fold. */\n graph: HintGraphContext\n /**\n * Caller-resolved user prompt (FE: `overridePrompt ?? inputs.prompt ??\n * resolveTextRefs(data.prompt)`). Folded into `prompt` in non-custom mode.\n */\n userPrompt: string\n /**\n * Caller-resolved lyrics (divergence C — refMap-resolved by the caller, NOT\n * read from `data.lyrics` here).\n */\n lyrics?: string\n /** Persona fields (FE/BE `resolvePersona(...)`), spread onto the result. */\n persona?: { personaId?: string; personaModel?: \"voice_persona\" | \"style_persona\" }\n /**\n * Reject an empty prompt+hint with `throw` (divergence A). FE run passes\n * `true`; the BE run + the preview leave it `false`.\n */\n throwOnEmpty?: boolean\n}\n\nexport interface AssembleSunoResult {\n prompt: string\n style?: string\n lyrics?: string\n title?: string\n negativeStyle?: string\n vocalGender?: string\n styleWeight?: number\n weirdnessConstraint?: number\n audioWeight?: number\n customMode: boolean\n instrumental: boolean\n model?: string\n /**\n * Requested song length in seconds (KIE: 10–360). Passed through from\n * `data.duration` unconditionally — the provider client is the single\n * gate that only sends it when `customMode && sunoModelHonoursDuration(model)`\n * (the V6 family — V6 / V6_MINI / V6_WILD; KIE ignores it on every earlier\n * version), so the assembler stays a faithful field carrier.\n */\n duration?: number\n personaId?: string\n personaModel?: \"voice_persona\" | \"style_persona\"\n}\n\n/**\n * Assemble a `suno-generate` node's inputs into the flat field set the Suno\n * provider receives. Byte-faithful to the FE `execute-node.ts` inline block.\n */\nexport function assembleSunoInput(input: AssembleSunoInput): AssembleSunoResult {\n const data = (input.node.data ?? {}) as { readonly [k: string]: unknown }\n\n const customMode = getEffectiveSunoCustomMode(data)\n // Fold connected Sound pickers (music-genre / music-mood / instrumentation /\n // voice-character / voice-delivery) into style (custom) or prompt (non-custom).\n const audioStyle = composeSoundHintFromConnections(input.node, \"suno-generate\", input.graph)\n\n // Divergence A — opt-in empty-prompt guard. Checked BEFORE the fold so an\n // upstream picker that supplies the whole prompt still runs (matches the FE\n // `if (!typedPrompt && !audioStyle.text)` gate).\n if (input.throwOnEmpty && !input.userPrompt && !audioStyle.text) {\n throw new Error(\"Suno: no prompt found\")\n }\n\n const userStyle = (data.style as string | undefined) ?? \"\"\n // `?? \"\"` mirrors the FE source-of-truth (`finalPrompt = typedPrompt ?? \"\"`) and\n // makes the `prompt: string` postcondition self-enforcing — in custom mode the\n // prompt branch is never rewritten, so a stray `undefined` would otherwise leak.\n const typedPrompt = input.userPrompt ?? \"\"\n\n // Divergence B — skip `truncateForField` when the user field is empty (return\n // the bare hint). Identical to the FE inline fold / `foldStyle`.\n let finalStyle = userStyle\n let finalPrompt = typedPrompt\n if (customMode) {\n finalStyle = userStyle\n ? appendField(userStyle, truncateForField(audioStyle.text, userStyle, STYLE_BUDGET))\n : audioStyle.text\n } else {\n finalPrompt = typedPrompt\n ? appendField(typedPrompt, truncateForField(audioStyle.text, typedPrompt, PROMPT_BUDGET))\n : audioStyle.text\n }\n\n return {\n prompt: finalPrompt,\n model: (data.model as string | undefined) || undefined,\n // Divergence C — lyrics is caller-pre-resolved; Divergence E — `|| undefined`.\n lyrics: input.lyrics || undefined,\n style: finalStyle || undefined,\n title: (data.title as string | undefined) || undefined,\n negativeStyle: (data.negativeStyle as string | undefined) || undefined,\n // Divergence D — manual vocalGender wins; else the connected voice-character.\n vocalGender: (data.vocalGender as string | undefined) || audioStyle.fields.vocalGender || undefined,\n styleWeight: data.styleWeight as number | undefined,\n weirdnessConstraint: data.weirdnessConstraint as number | undefined,\n audioWeight: data.audioWeight as number | undefined,\n duration: data.duration as number | undefined,\n customMode,\n instrumental: (data.instrumental as boolean | undefined) ?? false,\n ...(input.persona ?? {}),\n }\n}\n","/**\n * `assembleImageInput` — the single source of truth for turning a node's\n * image-generation inputs (a prompt + cinematic direction + connected\n * references + the per-provider levers) into the flat `{ prompt,\n * nativeNegativePrompt, referenceImageUrls }` the `generate-image` route\n * expects.\n *\n * WHY THIS EXISTS (WI-1a): the same three-step assembly —\n * 1. compose the prompt text (fold cinematic id-hints + structured fields),\n * 2. call `buildImagePrompt(...)` (the pure core; the per-provider reference\n * gate lives INSIDE it),\n * 3. (optionally) reject a truly-empty FINAL prompt,\n * — was duplicated in THREE places kept in lockstep by hand: the frontend\n * `execute-node.ts` `generate-image` branch, the backend\n * `payload-builder.ts` `generate-image` case, and Studio's `assembly.ts`.\n * This wrapper collapses them into one.\n *\n * THE NO-OP CONTRACT (load-bearing — the platform-caller parity relies on it):\n * a node that carries NO stored `subject` / `direction` / `structured` (every\n * workflow authored before the canvas honored them) still reaches here with all\n * three absent, and `composePromptText` MUST return the caller's `userPrompt`\n * byte-for-byte unchanged, so the wrapper degenerates to exactly the\n * `buildImagePrompt(...)` call those sites made before. The platform callers\n * (`execute-node` / `payload-builder`) compose their prompt from the canvas GRAPH themselves and\n * ALSO forward a node's STORED `subject` / `direction` / `structured` when it\n * carries them (`readSubjectFields` / `readDirectionFields` /\n * `readStructuredFields`); those nodes get the id-hint composition on top,\n * ADDITIVE to the graph-wired cinematography hints the caller already folded\n * into `userPrompt`. Studio and the MCP route supply the levers directly.\n *\n * THE EMPTY-CHECK FLAG (also load-bearing for parity): `execute-node` rejects a\n * truly-empty assembled prompt (its \"type one, mention a character, or connect\n * a cinematography source\" guard); `payload-builder` does NOT (it never threw\n * there). So the post-assembly throw is OPT-IN via `throwOnEmpty` — defaulting\n * to `false` preserves the backend's no-throw behavior. Callers that want the\n * guard (frontend, Studio, route) pass `throwOnEmpty: true`.\n */\nimport {\n buildImagePromptWithOverflow,\n type BuildImagePromptResult,\n} from \"./prompt-builder.js\"\nimport {\n renderStructuredFields,\n type StructuredPromptFields,\n} from \"./prompt-builder-structured-fields.js\"\nimport {\n IMAGE_HINT_MODE_DEFAULT,\n type DirectionFields,\n} from \"./direction-registry.js\"\nimport {\n renderSubjectHints,\n SUBJECT_IMAGE_HINT_MODE_DEFAULT,\n type SubjectFields,\n} from \"./subject-registry.js\"\nimport {\n asBodyClauses,\n composeSectionedPrompt,\n partitionStyleClauses,\n sectionedClauseCosts,\n type SlottedPromptClause,\n} from \"./prompt-style-section.js\"\nimport { keepableDirectionHints } from \"./hint-shedding.js\"\nimport { styleReferenceDescription, type StyleReferenceInput } from \"./user-direction-refs.js\"\nimport { imageReferenceLimit } from \"@nodaro/shared/models\"\nimport type { CharacterDef, ConnectedReference, DescribedReference, IdentityMeta } from \"@nodaro/shared/types\"\n\n/**\n * Flat cinematic-direction ids the Studio framing UI, the MCP route and the\n * canvas node data expose — all optional. The dimensions, their canonical fold\n * ORDER and their per-catalog rendering live in `direction-registry.ts`; this\n * re-export keeps the import path stable for existing consumers. The platform\n * callers fold their GRAPH-WIRED hints into `userPrompt` themselves and pass\n * these only when the node carries them as stored data (Studio-emitted graphs,\n * spec D3).\n */\nexport type { DirectionFields }\n\n/**\n * Flat SUBJECT ids (Person / Styling / prop catalogs) — the companion channel\n * to `direction`, describing WHO is in the shot rather than how it is shot. Its\n * table, fold order and per-catalog rendering live in `subject-registry.ts`;\n * this re-export keeps one import path for a consumer that takes both levers.\n */\nexport type { SubjectFields }\n\n/**\n * Input to `assembleImageInput`. A faithful SUPERSET of what the two platform\n * callers pass to `buildImagePrompt` today (so they can route through this\n * wrapper with byte-identical output), plus the id-based `direction` /\n * `structured` composition levers that Studio + the MCP route use.\n */\nexport interface AssembleImageInput {\n /** Pre-composed (caller's graph) or raw user prompt text. */\n userPrompt: string\n /** Image model id / provider key (the catalog enum value, e.g. \"flux-2-max\"). */\n provider: string\n /**\n * Connected references with URLs ALREADY resolved by the caller. Become\n * `referenceImageUrls` + identity directives inside `buildImagePrompt`\n * (gated per provider there). Omit when the caller wires only raw URLs.\n */\n connectedReferences?: ConnectedReference[]\n /**\n * References the caller can NAME and DESCRIBE but has no media for — an\n * un-bound cast role, an analysis slot. They attach no URL and claim no\n * `Image N` slot; `buildImagePrompt` renders them as prose. Present with no\n * `connectedReferences` is the normal case, so they are forwarded on their own.\n */\n describedReferences?: readonly DescribedReference[]\n /**\n * Flat cinematic-direction ids → folded into the prompt as hints. Studio /\n * MCP-route use, and the platform callers' narrow-read of a node's STORED\n * `data.direction`; absent on a node that carries none (so `composePromptText`\n * is a no-op for it and the result is byte-identical to today).\n */\n direction?: DirectionFields\n /**\n * Rendered clauses for the caller's OWN saved resources named in `direction`\n * (`colorLook: \"palette:<id>\"`, `style: \"moodboard:<id>\"`), keyed by the\n * direction value. Resolving them is a database read, so the platform does it\n * BEFORE this pure assembly and hands the clauses in; each one folds in its\n * row's slot exactly like a catalog clause (the `[style]` film line), sheds\n * like one, and a reference missing from the map folds nothing.\n */\n directionRefs?: ReadonlyMap<string, string>\n /**\n * Style reference images (a moodboard's images). They only FILL the\n * reference seats the caller's own references leave free — after them, never\n * displacing, renumbering or evicting one — up to the model's reference\n * limit; the rest are not sent. `styleReferencesSent` on the result says how\n * many went. Each carries a short style line (`styleReferenceDescription`).\n */\n styleReferences?: readonly StyleReferenceInput[]\n /**\n * Flat subject ids (Person / Styling / props) → folded into the prompt AHEAD\n * of the direction clauses: the subject is the noun phrase the cinematography\n * then modifies. Same provenance as `direction` — Studio / MCP-route, or the\n * platform callers' narrow-read of a node's STORED `data.subject`.\n */\n subject?: SubjectFields\n /** Path-1 structured fields → composed fragment appended to the prompt. */\n structured?: StructuredPromptFields\n /**\n * Reference image URLs from direct connections / manual uploads — ride\n * `buildImagePrompt`'s reference-URL channel so they pass through the SAME\n * per-provider reference-image gate + ordering as `connectedReferences`.\n * (This is `buildImagePrompt`'s `referenceImageUrls` config field, named\n * `extra…` here to reflect that bound entities already carry their URLs.)\n */\n extraReferenceImageUrls?: string[]\n /** Negative prompt text (routed to native vs. \"Avoid:\" by `buildImagePrompt`). */\n negativePrompt?: string\n /** Style text to append (e.g. \"cinematic\"). */\n style?: string\n /** User-defined reorder of the injected reference list (stable tile ids). */\n referenceOrder?: readonly string[]\n /** Per-identity (imageIndex+label) user overrides for fidelity / custom text. */\n identityMeta?: readonly IdentityMeta[]\n /** Character slugs whose canonical-fallback the user explicitly hid. */\n suppressedCanonicalCharacterIds?: readonly string[]\n /** Location slugs whose canonical-fallback the user explicitly hid. */\n suppressedCanonicalLocationIds?: readonly string[]\n /**\n * Reference-prompt assembly format for the `{image:N:label}` path. Forwarded\n * verbatim to `buildImagePrompt` (default legacy). \"hybrid\" = images-only\n * reference-lock snippet + lettered inline scene.\n */\n referenceFormat?: \"legacy\" | \"hybrid\"\n /** Override the hybrid reference-lock snippet (forwarded to buildImagePrompt). */\n referenceLockSnippet?: string\n /** Character definitions selected for this node (legacy `buildImagePrompt` path). */\n characterDefs?: CharacterDef[]\n /** User-level prompt template overrides. */\n userTemplates?: Record<string, string>\n /** Flow-level prompt template overrides. */\n flowTemplates?: Record<string, string>\n /** Ancestor reference image URLs (fallback when no direct refs exist). */\n ancestorRefs?: string[]\n /** Map of `connectedReferences[i].id → sourceNodeId` for wired-raw tile ids. */\n sourceNodeIdById?: ReadonlyMap<string, string>\n /**\n * LoRA inference path: strip `@`-mention tokens + skip the connected-reference\n * machinery (the trigger word + LoRA carry identity).\n */\n skipCharacterMentions?: boolean\n /**\n * Reject a truly-empty FINAL (post-assembly) prompt with `throw`. OFF by\n * default to match the backend `payload-builder`, which never threw. The\n * frontend / Studio / route pass `true` to keep their \"type one, bind a\n * character, or pick a cinematography direction\" guard. Checked POST-assembly\n * so a bound entity / `@`-mention / direction chip that filled an otherwise-\n * empty prompt still runs.\n */\n throwOnEmpty?: boolean\n}\n\n/**\n * The hint pieces a fold contributes, split by whether the assembler may SHED\n * them under a provider prompt cap.\n *\n * `hintClauses` are the catalog-rendered clauses — the SUBJECT fold first, then\n * the cinematic direction fold — decorative garnish next to a reference\n * directive or the user's own prose, and the only thing this assembler drops\n * when the prompt won't fit. They are ONE list because the shed walks it from\n * the TAIL: direction leaves before subject, which is the right order (who is\n * in the shot outranks how it is lit). `structuredFragment` is user CONTENT (a\n * Path-1 structured field the caller populated), so it is sticky and always\n * ends the BODY, exactly as before — the `[style]` section reads after it.\n *\n * Each clause carries the SLOT it reads in. The list order is the SURVIVAL\n * order; it stopped being the string order when the look clauses lifted into\n * the section (`prompt-style-section.ts`). On this surface every direction row\n * is `look` — the registry has no image-surface motion row — so an image\n * `[style]` section carries the whole direction fold.\n */\ninterface ImageHintPieces {\n /** Subject clauses then direction clauses, each in its registry's fold order. */\n readonly hintClauses: readonly SlottedPromptClause[]\n /** The structured-field fragment (\"\" when nothing is populated). */\n readonly structuredFragment: string\n}\n\n/**\n * Render the fold's hint pieces once, so the cap-aware retry can re-compose a\n * SUBSET of them without re-rendering the catalogs. Each renderer folds its own\n * channel in its registry's canonical table order (unknown keys and unknown ids\n * contribute nothing), and `renderStructuredFields` returns \"\" when nothing is\n * populated. Never mutates inputs.\n *\n * SUBJECT LEADS DIRECTION: the subject is the noun phrase (\"a woman in her\n * 30s, …\") the cinematographic clauses then modify. With no `subject` the list\n * IS the direction fold, so every existing caller sheds identically.\n */\nfunction renderImageHintPieces(\n subject: SubjectFields | undefined,\n direction: DirectionFields | undefined,\n structured: StructuredPromptFields | undefined,\n directionRefs: ReadonlyMap<string, string> | undefined,\n): ImageHintPieces {\n return {\n hintClauses: [\n ...asBodyClauses(\n renderSubjectHints(subject, {\n surface: \"image\",\n mode: SUBJECT_IMAGE_HINT_MODE_DEFAULT,\n }),\n ),\n ...partitionStyleClauses(direction, {\n surface: \"image\",\n mode: IMAGE_HINT_MODE_DEFAULT,\n ...(directionRefs !== undefined ? { refs: directionRefs } : {}),\n }),\n ].filter((c) => c.text.length > 0),\n structuredFragment: structured ? renderStructuredFields(structured) : \"\",\n }\n}\n\n/**\n * Compose the subject + cinematic-direction hints and the structured-field\n * fragment with the user's prompt, keeping the FIRST `keptHintClauses` clauses\n * (the full count on the first pass; fewer only when the provider cap forced a\n * shed). The structured fragment always ends the body; the look clauses that\n * survived follow it in the `[style]` section.\n *\n * EXACT NO-OP CONTRACT: when there are no subject/cinematic/structured hint\n * pieces (the platform-caller case for a node that carries no stored `subject`/\n * `direction`/`structured` — every workflow authored before the canvas honored\n * them), the\n * user's prompt is returned **verbatim, untrimmed**. This is load-bearing for\n * parity: the old platform path passed the prompt straight to\n * `buildImagePrompt`, which never trims, so trimming here would change the\n * assembled prompt (and the recorded `jobs.input_data`) byte-for-byte. Never\n * mutates inputs.\n *\n * A node that DOES carry `direction`/`structured` takes the fold branch and is\n * therefore trimmed — a `[style]` section counts as folded even when the body\n * gained nothing. Intended, and asserted at the caller level by the\n * payload-builder before/after test.\n */\nfunction composePromptText(\n userPrompt: string,\n pieces: ImageHintPieces,\n keptHintClauses: number,\n): string {\n return composeSectionedPrompt(\n userPrompt,\n pieces.hintClauses.slice(0, keptHintClauses),\n pieces.structuredFragment,\n )\n}\n\n/**\n * Assemble a node's image-generation inputs into a `BuildImagePromptResult`\n * (`{ prompt, nativeNegativePrompt, referenceImageUrls }`).\n *\n * Order: (1) compose the prompt text (no-op when no subject/direction/structured),\n * (2) `buildImagePrompt(...)` — exactly the call the three sites make today,\n * (3) shed hint clauses and re-assemble while the provider cap overflows,\n * (4) with `styleReferences`, fill the free reference seats with them\n * (`fillStyleReferences`; absent = this step does not run at all),\n * (5) optional post-assembly empty-prompt throw (gated by `throwOnEmpty`).\n *\n * TRUNCATION ORDERING (step 3): `buildImagePrompt`'s cap clamp cuts the TAIL,\n * which is ORDER-BLIND — on a low-cap provider (seedream = 3000) a maximal\n * direction fold renders ~3.3K characters of clauses and the cut can sever a\n * reference directive, mention-resolved text or the user's own prose while a\n * decorative clause survives. So the ASSEMBLER decides instead: it knows which\n * clauses are hints because it just built them, and drops them last-folded\n * first until the prompt fits. Everything else — references, prose, the\n * structured fragment, the Style/Avoid suffixes — outranks a hint. A body that\n * still overflows with ZERO hints (long prose or many directives on its own)\n * falls back to the builder's clamp, unchanged.\n *\n * UNDER-CAP PARITY: the first pass folds every hint, so a prompt that fits is\n * byte-identical to before — the retry only ever runs on an over-cap assembly.\n */\nexport function assembleImageInput(\n input: AssembleImageInput,\n): AssembleImageResult {\n const style = input.styleReferences\n const result = style !== undefined && style.length > 0\n ? fillStyleReferences(input, style)\n : assembleOnce(input)\n\n // Post-assembly empty-prompt check (opt-in): a bound entity / `@`-mention /\n // subject or direction chip — or a style reference's line — could have\n // filled the assembled prompt even if the user typed nothing, so only reject\n // when the FINAL prompt is truly empty.\n if (input.throwOnEmpty && !result.prompt.trim()) {\n throw new Error(\n \"No prompt — type one, bind a character, or pick a cinematography direction\",\n )\n }\n\n return result\n}\n\n/** `assembleImageInput`'s result: the builder's, plus the style-reference count. */\nexport interface AssembleImageResult extends BuildImagePromptResult {\n /**\n * How many of `styleReferences` were sent (they follow the caller's own\n * references at the end of `referenceImageUrls`). Present only when\n * `styleReferences` were given, so every other result is unchanged.\n */\n styleReferencesSent?: number\n}\n\n/**\n * Fill the reference seats the caller's own references leave free with style\n * references, without touching the caller's own.\n *\n * PASS 1 assembles the caller's input alone: its `referenceImageUrls` are the\n * references the caller's request really sends (after the per-provider cap,\n * mention resolution and dedupe). The free seats are the model's limit minus\n * those. PASS 2 appends k style references as manual extra references — the\n * builder's own mechanism, so they get its numbered/lettered lines in either\n * reference format — for the LARGEST k that leaves the caller's references\n * exactly where pass 1 put them, followed by the k style images. Why a check\n * and not arithmetic: the builder caps the RAW reference list with an\n * attach-priority slice (non-variants first), so extra entries can push out a\n * character variant the prompt mentions even when seats looked free. Assembly\n * is pure and k ≤ 12, so trying each k downward is cheap and exact.\n */\nfunction fillStyleReferences(\n input: AssembleImageInput,\n style: readonly StyleReferenceInput[],\n): AssembleImageResult {\n const base = assembleOnce(input)\n const own = base.referenceImageUrls ?? []\n const seats = imageReferenceLimit(input.provider) - own.length\n const seen = new Set(own)\n const candidates: StyleReferenceInput[] = []\n for (const s of style) {\n if (typeof s.url !== \"string\" || s.url.length === 0 || seen.has(s.url)) continue\n seen.add(s.url)\n candidates.push(s)\n }\n for (let k = Math.min(seats, candidates.length); k > 0; k--) {\n const extras = candidates.slice(0, k).map(styleConnectedReference)\n const filled = assembleOnce({\n ...input,\n connectedReferences: [...(input.connectedReferences ?? []), ...extras],\n })\n const urls = filled.referenceImageUrls ?? []\n const keepsOwn = urls.length === own.length + k\n && own.every((url, i) => urls[i] === url)\n && extras.every((ref, j) => urls[own.length + j] === ref.url)\n if (keepsOwn) return { ...filled, styleReferencesSent: k }\n }\n return { ...base, styleReferencesSent: 0 }\n}\n\n/** A style reference as the builder's manual extra reference. */\nfunction styleConnectedReference(s: StyleReferenceInput, index: number): ConnectedReference {\n return {\n id: `style-reference-${index + 1}`,\n defaultName: `Style reference ${index + 1}`,\n source: \"manual\",\n url: s.url,\n isExtraRef: true,\n defaultRole: \"style\",\n description: styleReferenceDescription(index, s.caption),\n }\n}\n\n/** One assembly of `input` as given (no style-reference fill, no empty check). */\nfunction assembleOnce(input: AssembleImageInput): BuildImagePromptResult {\n const pieces = renderImageHintPieces(input.subject, input.direction, input.structured, input.directionRefs)\n\n const assembleWith = (keptHintClauses: number) => buildImagePromptWithOverflow({\n prompt: composePromptText(input.userPrompt, pieces, keptHintClauses),\n provider: input.provider,\n ...(input.connectedReferences !== undefined\n ? { connectedReferences: input.connectedReferences }\n : {}),\n ...(input.describedReferences !== undefined\n ? { describedReferences: input.describedReferences }\n : {}),\n // Manual uploads / direct refs ride the builder's reference-URL channel so\n // the per-provider reference gate filters them alongside bound entities.\n // Omit the field entirely when absent so the builder's default ([]) kicks\n // in — byte-identical to a caller that didn't set it.\n ...(input.extraReferenceImageUrls !== undefined\n ? { referenceImageUrls: input.extraReferenceImageUrls }\n : {}),\n ...(input.negativePrompt !== undefined ? { negativePrompt: input.negativePrompt } : {}),\n ...(input.style !== undefined ? { style: input.style } : {}),\n ...(input.referenceOrder !== undefined ? { referenceOrder: input.referenceOrder } : {}),\n ...(input.identityMeta !== undefined ? { identityMeta: input.identityMeta } : {}),\n ...(input.suppressedCanonicalCharacterIds !== undefined\n ? { suppressedCanonicalCharacterIds: input.suppressedCanonicalCharacterIds }\n : {}),\n ...(input.suppressedCanonicalLocationIds !== undefined\n ? { suppressedCanonicalLocationIds: input.suppressedCanonicalLocationIds }\n : {}),\n ...(input.referenceFormat !== undefined ? { referenceFormat: input.referenceFormat } : {}),\n ...(input.referenceLockSnippet !== undefined ? { referenceLockSnippet: input.referenceLockSnippet } : {}),\n ...(input.characterDefs !== undefined ? { characterDefs: input.characterDefs } : {}),\n ...(input.userTemplates !== undefined ? { userTemplates: input.userTemplates } : {}),\n ...(input.flowTemplates !== undefined ? { flowTemplates: input.flowTemplates } : {}),\n ...(input.ancestorRefs !== undefined ? { ancestorRefs: input.ancestorRefs } : {}),\n ...(input.sourceNodeIdById !== undefined ? { sourceNodeIdById: input.sourceNodeIdById } : {}),\n ...(input.skipCharacterMentions !== undefined\n ? { skipCharacterMentions: input.skipCharacterMentions }\n : {}),\n })\n\n // Fold everything first (the under-cap byte-parity pass), then shed hints\n // from the tail of the COMBINED fold order (subject clauses first in the list,\n // therefore last to leave) while the assembled prompt overflows the provider\n // cap. `keepableDirectionHints` — the one shed arithmetic, shared with\n // `composeVideoPromptText` — strictly decreases `kept` whenever there IS an\n // overflow, so this terminates at `kept === 0` in the worst case, at which\n // point the body overflows on its own and the builder's clamp stands.\n let kept = pieces.hintClauses.length\n let fitted = assembleWith(kept)\n if (fitted.overflowChars > 0) {\n // Priced only on the overflow path: the deltas cost a composition per\n // clause and the fits-first-time case is the common one.\n const costs = sectionedClauseCosts(\n input.userPrompt,\n pieces.hintClauses,\n pieces.structuredFragment,\n )\n const texts = pieces.hintClauses.map((c) => c.text)\n while (fitted.overflowChars > 0 && kept > 0) {\n kept = keepableDirectionHints(texts, kept, fitted.overflowChars, costs)\n fitted = assembleWith(kept)\n }\n }\n // `overflowChars` is assembly bookkeeping, not part of the callers' contract.\n const { overflowChars, ...result } = fitted\n return result\n}\n","/**\n * `composeVideoPromptText` — the video twin of `assemble-image-input.ts`'s\n * `composePromptText`: fold cinematic-direction picker IDS into the prompt BODY,\n * server-side, at the model call.\n *\n * WHY THIS EXISTS: `/v1/generate-video` had no structured direction channel, so\n * every client baked the hint TEXT itself. A copied scene then carried stale\n * catalog wording forever, a re-generate double-baked it, and each client\n * re-implemented the fold with its own separator and its own order. The wire\n * now carries ids; the platform renders the clauses.\n *\n * WHERE IT RUNS (load-bearing): on the prompt BODY, BEFORE\n * `resolveVideoReferenceCore`. That resolver FRAMES the body — legacy prepends\n * its `Use these characters:` block, hybrid prepends the lock lines and extends\n * the body's END with the canonical role phrases and extras (spliced in ahead of\n * the `[style]` section, which stays last). Folding afterwards would push the\n * scene/look description PAST the identity directives, a worse version of the\n * bug this channel exists to fix. The image side is structurally identical\n * (`assembleImageInput` = `composePromptText` → `buildImagePrompt`).\n *\n * That ordering is also why cap-aware shedding here takes a `frame` callback\n * rather than a provider id: the shed must run at the FOLD site (before the\n * resolver) but be decided on the RESOLVED length (after it), so the binding\n * text the resolver adds is inside the budget and can never be the thing that\n * gets dropped. See {@link VideoPromptCapOptions}. Both catalog channels —\n * SUBJECT and direction — fold into the one sheddable list that budget walks.\n *\n * THE SHAPE IT EMITS: the body (prose, subject fold, MOTION clauses, structured\n * fragment) and then a trailing `[style]` section carrying every LOOK clause —\n * `prompt-style-section.ts` owns those bytes. Camera motion is shot prose, not\n * style, so the whole motion family stays in the body; the boundary is the\n * registry's `family` column, deliberately the same column the verbosity policy\n * below splits on.\n *\n * THE VERBOSITY POLICY LIVES HERE, NOT IN THE CLIENT: motion dimensions render\n * their compact professional term, look dimensions their full clause\n * (`VIDEO_HINT_MODE_DEFAULT`, resolved per row's `family` by the registry). The\n * SUBJECT fold has its own policy — compact on video\n * (`SUBJECT_VIDEO_HINT_MODE_DEFAULT`), because a fully specified person at full\n * verbosity is ~30 paragraph clauses and the start frame already carries the\n * subject's identity into the clip.\n * It is a threaded PARAMETER with a pure default — never deployment state:\n * `__tests__/content-free-contract.test.ts` hard-fails any environment read\n * under `packages/prompts/src`, and this module has nothing to read anyway.\n *\n * EXACT NO-OP CONTRACT: with no subject, no direction and no structured fields\n * the caller's\n * `userPrompt` comes back VERBATIM AND UNTRIMMED — `undefined` included, since\n * a video prompt is optional on the route. That is what keeps every existing\n * caller byte-identical (the \"backward-compatible: no connectedReferences →\n * prompt + flat refs pass through unchanged\" oracle in\n * `backend/src/routes/__tests__/generate-video.test.ts`, restated locally in\n * `__tests__/assemble-video-input.test.ts`).\n *\n * WHAT IS DELIBERATELY NOT HERE: the dimension table, the fold order, the\n * dedupe and the surface filter all live in `direction-registry.ts` (and\n * `subject-registry.ts` for the subject channel) — ONE renderer per channel\n * serves both surfaces, so the image and video folds cannot drift.\n * Clients render their \"will inject into prompt\" preview by importing\n * `renderSubjectHints` + `partitionStyleClauses` + `composeSectionedPrompt`\n * directly (or `renderStyleSection` for the section alone).\n */\nimport {\n VIDEO_HINT_MODE_DEFAULT,\n type DirectionFields,\n type DirectionHintMode,\n} from \"./direction-registry.js\"\nimport {\n renderSubjectHints,\n SUBJECT_VIDEO_HINT_MODE_DEFAULT,\n type SubjectFields,\n type SubjectHintMode,\n} from \"./subject-registry.js\"\nimport {\n asBodyClauses,\n composeSectionedPrompt,\n partitionStyleClauses,\n sectionedClauseCosts,\n} from \"./prompt-style-section.js\"\nimport { keepableDirectionHints } from \"./hint-shedding.js\"\nimport {\n renderStructuredFields,\n type StructuredPromptFields,\n} from \"./prompt-builder-structured-fields.js\"\n\n/**\n * Cap-aware shedding, opt-in. Absent → the composer is exactly what it always\n * was (every existing caller stays byte-identical, and the no-op path below is\n * never even reached differently).\n *\n * WHY A NUMBER AND A CALLBACK, NOT A PROVIDER ID — the two halves of the video\n * surface's problem, which the image half did not have:\n *\n * - `cap` is the caller's EFFECTIVE ceiling, not `getMaxVideoPromptChars` read\n * here. The routes compute it with `effectiveVideoPromptCeiling`, which\n * mirrors `applyVideoNegativePrompt`'s reservation of the `\"\\nAvoid: …\"`\n * suffix for a provider with no native negative param. Re-deriving the cap\n * inside this package would put a second copy of that reservation one\n * refactor away from drifting from the clamp it is supposed to predict.\n *\n * - `frame` is the REFERENCE RESOLVER, and it is what makes the shed correct\n * end-to-end. The fold runs BEFORE `resolveVideoReferenceCore` (see the\n * module header — folding afterwards strands the scene description past the\n * identity directives). The resolver then ADDS binding text: legacy's\n * \"Use these characters:\" block, hybrid's lock lines and the canonical role\n * phrases that end its body. None of it is sheddable and all of it is inside\n * what the clamp measures, so a budget blind to it under-sheds and hands the\n * remainder to the order-blind cut. Measuring THROUGH the caller's framing\n * gives both properties at once:\n * the shed decision sees the final length, while the only thing it can drop\n * is a hint clause it rendered itself.\n *\n * Re-framing a SUBSET of the hints is sound because a hint can never change how\n * the resolver reads the rest of the body: no registered catalog hint, term or\n * label contains a `{image:N}` / `{ref:` / `@slug:N` shape\n * (`__tests__/direction-hint-token-safety.test.ts` pins that for every catalog),\n * so dropping one cannot renumber or unbind a reference.\n */\nexport interface VideoPromptCapOptions {\n /**\n * The maximum length the FRAMED prompt may reach. Sheds only while the framed\n * body exceeds it; `undefined` (the default) disables shedding entirely.\n */\n readonly cap?: number\n /**\n * The downstream framing the cap is measured through — the caller's reference\n * assembly. Identity when omitted (a caller with a cap but no references).\n * Must be PURE: it is called once per shed iteration, and the caller re-runs\n * its own real assembly on the returned body afterwards.\n */\n readonly frame?: (body: string | undefined) => string | undefined\n}\n\n/**\n * Fold a video run's subject and cinematic-direction ids (and optional\n * structured fields) into its prompt body.\n *\n * IN THE BODY: the SUBJECT hints first (who is in the shot — the noun phrase the\n * cinematography modifies), then the MOTION direction hints in the registry's\n * canonical table order (camera motion leads), then the structured fragment —\n * the same ordering `composePromptText` uses for stills. The LOOK hints leave\n * the body for the `[style]` section that follows it.\n *\n * `subject` rides `opts` rather than a fourth positional parameter on purpose:\n * every existing caller passes `(prompt, direction)` or\n * `(prompt, direction, structured)` positionally, and a new positional would\n * have made the two levers' order a memorization test.\n *\n * TRUNCATION ORDERING (opt-in via `opts.cap`): the provider clamp\n * (`applyVideoNegativePrompt`) slices the prompt TAIL, which is ORDER-BLIND —\n * on a low-cap provider (kling = 1000) a broad direction renders more than the\n * whole ceiling and the cut severs reference bindings and the end of the user's\n * prose while decorative clauses survive. With a cap the composer decides\n * instead: it knows which clauses are hints because it just rendered them, and\n * drops them LAST-FOLDED FIRST until the framed prompt fits. Everything else —\n * the user's prose, the structured fragment (user CONTENT, never a garnish) and\n * every byte the resolver's framing adds — outranks a hint.\n *\n * SUBJECT CLAUSES ARE SHED CANDIDATES TOO, and they shed AFTER the direction\n * clauses. Both folds are catalog decoration of the same class — ids the\n * platform rendered into wording — so exempting one would just move the\n * overflow into the order-blind clamp, which is the bug this machinery exists\n * to prevent. They ride the SAME `hintClauses` list the shed already walks\n * (subject first, direction second, tail-first shedding), so there is exactly\n * one shed arithmetic (`hint-shedding.ts`) across both channels and both\n * surfaces. Neither channel ever sheds before the prose, the references or the\n * structured fragment.\n *\n * WHAT THE BUDGET DELIBERATELY EXCLUDES: the route's later opt-in identity\n * injection (an async DB read that appends a canonical description) and any\n * registered `applyPromptPolicies` transform both run AFTER the reference\n * assembly and are not modelled here. Pricing them in would mean folding an\n * await into this pure composer; instead the provider clamp stays their last\n * resort, exactly as today. Same for a body that still overflows with ZERO\n * hints left — long prose, or many bound references on their own.\n *\n * UNDER-CAP PARITY: the first pass folds every hint, so a prompt that fits is\n * byte-identical to a capless call, and a caller with no\n * `subject`/`direction`/`structured` takes the same exact no-op path it always\n * did.\n *\n * @param userPrompt The user's prompt. Optional: an image-to-video run may\n * legitimately have none, and it is returned as-is when nothing folds.\n * @param direction Flat catalog ids. Unknown keys, off-surface keys (an\n * image-only dimension sent to a video run) and unknown ids all contribute\n * nothing — never a throw.\n * @param structured Path-1 structured fields. Not a `/v1/generate-video` wire\n * field today; the canvas orchestrator passes it directly.\n * @param opts.hintMode Override the direction verbosity policy (a whole-fold\n * `PickerHintMode`, or a `{ look, motion }` split).\n * @param opts.subject Flat subject ids (Person / Styling / props), same\n * inertness contract as `direction`.\n * @param opts.subjectHintMode Override the subject verbosity policy.\n * @param opts.cap / `opts.frame` See {@link VideoPromptCapOptions}.\n */\nexport function composeVideoPromptText(\n userPrompt: string | undefined,\n direction: DirectionFields | undefined,\n structured?: StructuredPromptFields,\n opts?: {\n readonly hintMode?: DirectionHintMode\n readonly subject?: SubjectFields\n readonly subjectHintMode?: SubjectHintMode\n } & VideoPromptCapOptions,\n): string | undefined {\n // ONE sheddable list, subject FIRST then direction — because the shed walks it\n // from the TAIL, so this order IS the survival order: a direction clause\n // leaves before a subject clause. Deliberate, and the same order the image\n // side uses (`renderImageHintPieces`): the subject is the noun phrase the\n // cinematography modifies, so losing \"who is in the shot\" to keep a\n // decorative grade would be the wrong trade. With no `subject` the list IS\n // the direction fold, so every pre-subject caller sheds identically.\n //\n // Each clause carries the SLOT it reads in, because survival order and string\n // order are two different things once the look clauses lift into `[style]`.\n const hintClauses = [\n ...asBodyClauses(\n renderSubjectHints(opts?.subject, {\n surface: \"video\",\n mode: opts?.subjectHintMode ?? SUBJECT_VIDEO_HINT_MODE_DEFAULT,\n }),\n ),\n ...partitionStyleClauses(direction, {\n surface: \"video\",\n mode: opts?.hintMode ?? VIDEO_HINT_MODE_DEFAULT,\n }),\n ].filter((c) => c.text.length > 0)\n // User CONTENT, not a garnish: never sheddable, always last IN THE BODY (the\n // `[style]` section reads after it).\n const structuredFragment = structured ? renderStructuredFields(structured) : \"\"\n\n // Nothing folded — no body hint AND no section — returns the caller's value\n // straight back, `undefined` included. A FULL shed lands there too, which is\n // what keeps the no-op contract intact at `kept === 0`: the route's\n // `composed !== prompt` guard then correctly leaves `input_data.userPrompt`\n // unpinned.\n const composeWith = (kept: number): string | undefined =>\n composeSectionedPrompt(userPrompt, hintClauses.slice(0, kept), structuredFragment)\n\n const cap = opts?.cap\n if (cap === undefined) return composeWith(hintClauses.length)\n\n // Fold everything first (the under-cap byte-parity pass), then shed from the\n // tail of the fold order while the FRAMED prompt overflows the ceiling.\n // `keepableDirectionHints` — the ONE shed arithmetic, shared with the image\n // assembler — strictly decreases `kept` whenever there is a deficit, so this\n // terminates at `kept === 0` in the worst case, at which point nothing\n // droppable is left and the provider clamp stands.\n const frame = opts?.frame ?? ((body: string | undefined) => body)\n let kept = hintClauses.length\n let body = composeWith(kept)\n let framedLength = frame(body)?.length ?? 0\n if (framedLength <= cap) return body\n // Priced only on the overflow path: the deltas cost a composition per clause\n // and the fits-first-time case is the common one.\n const costs = sectionedClauseCosts(userPrompt, hintClauses, structuredFragment)\n const texts = hintClauses.map((c) => c.text)\n while (framedLength > cap && kept > 0) {\n kept = keepableDirectionHints(texts, kept, framedLength - cap, costs)\n body = composeWith(kept)\n framedLength = frame(body)?.length ?? 0\n }\n return body\n}\n","import { imageBindingFor, type ImageReferenceSyntax } from \"./ref-binding.js\"\n\n/**\n * WHERE A START / END FRAME GOES WHEN IT SHARES A MODEL'S REFERENCE LIST — the\n * one platform convention (2026-10-18), for every video model whose frames and\n * reference images travel in a single image array (`videoProviderAppendsFrames`\n * in @nodaro/shared):\n *\n * - the caller's own reference images come FIRST, in their order, so the\n * caller's image token N (`{image:N}`, `{ref:<id>}`, a typed ordinal)\n * always means their Nth reference;\n * - the frames are appended AFTER them (start, then end) and named in the\n * prompt as the opening / closing frame, in the provider's own syntax;\n * - at the image cap the frames are KEPT — they are explicit intent — and\n * trailing caller references are dropped and reported.\n *\n * `bindIdentities` adds the identity sentence the flat-image-list models carry\n * (Gemini Omni, VEO, Grok, HappyHorse: nothing in their payload says which\n * entries are identities, so the prompt says it). The Seedance-family resolver\n * calls this without it — byte for byte what it always emitted.\n */\nexport interface FrameReferenceSlotsArgs {\n /** Consulted only to skip a duplicate opening-frame sentence. */\n prompt?: string\n firstFrameUrl?: string\n lastFrameUrl?: string\n /** The caller's own reference images, in priority order. */\n refImageUrls?: readonly (string | undefined)[]\n /** The provider's image cap — frames and references together. */\n imageCap: number\n /** The provider's reference syntax (`videoProviderReferenceSyntax`). */\n syntax?: ImageReferenceSyntax\n bindIdentities?: boolean\n}\n\nexport interface FrameReferenceSlotsResult {\n /** `[...keptRefs, start?, end?]` — never longer than `imageCap` (bar the frames themselves). */\n imageUrls: string[]\n promptSuffix: string\n /** Trailing caller references dropped to keep the frames. */\n droppedRefImages: number\n /** 1-based seats of the frames in `imageUrls`; 0 when absent. */\n firstFrameOrdinal: number\n lastFrameOrdinal: number\n}\n\nexport function placeFramesAfterReferences(args: FrameReferenceSlotsArgs): FrameReferenceSlotsResult {\n const refs = (args.refImageUrls ?? []).filter((u): u is string => typeof u === \"string\" && u.trim().length > 0)\n const frameCount = (args.firstFrameUrl ? 1 : 0) + (args.lastFrameUrl ? 1 : 0)\n const kept = refs.slice(0, Math.max(0, args.imageCap - frameCount))\n const droppedRefImages = refs.length - kept.length\n\n const imageUrls = [...kept]\n let firstFrameOrdinal = 0\n let lastFrameOrdinal = 0\n if (args.firstFrameUrl) { imageUrls.push(args.firstFrameUrl); firstFrameOrdinal = imageUrls.length }\n if (args.lastFrameUrl) { imageUrls.push(args.lastFrameUrl); lastFrameOrdinal = imageUrls.length }\n\n const syntax = args.syntax ?? \"@image_N\"\n const binding = imageBindingFor(syntax)\n // The duplicate-binding check reads the platform's `@image_N` form, which is\n // the only form a caller's prompt can already carry it in.\n const promptBindsStart = syntax === \"@image_N\" && promptBindsFirstFrame(args.prompt)\n let frameSentence = \"\"\n if (promptBindsStart && firstFrameOrdinal > 0 && lastFrameOrdinal === 0) {\n // The prompt already binds the first frame at its own (working) position.\n } else if (firstFrameOrdinal > 0 && lastFrameOrdinal > 0) {\n frameSentence = binding.frames(firstFrameOrdinal, lastFrameOrdinal)\n } else if (firstFrameOrdinal > 0) {\n frameSentence = binding.frame(firstFrameOrdinal, \"opening\")\n } else if (lastFrameOrdinal > 0) {\n frameSentence = binding.frame(lastFrameOrdinal, \"closing\")\n }\n const identitySentence = args.bindIdentities && kept.length > 0 && frameCount > 0\n ? binding.identities(1, kept.length)\n : \"\"\n const promptSuffix = [frameSentence, identitySentence].filter(Boolean).join(\" \")\n return { imageUrls, promptSuffix, droppedRefImages, firstFrameOrdinal, lastFrameOrdinal }\n}\n\n/** True when the prompt ALREADY binds an image as the first/opening frame —\n * e.g. the gvp overlap transport's colon-position directive (\"use @image_1\n * as the first frame, it is the last keyframe of @video_1\"). Field finding\n * (2026-07-20): the first-frame directive only works adjacent to the extend\n * colon; a duplicate sentence at the END of the prompt (this resolver's\n * default position) dilutes it back into coin-flip behavior — so when the\n * prompt carries its own binding, the suffix is suppressed (the frame image\n * itself still rides the reference list). */\nexport function promptBindsFirstFrame(prompt: string | undefined): boolean {\n if (!prompt) return false\n return /@image_\\d+\\s+as\\s+the\\s+(first|opening)\\s*(\\(first\\))?\\s*frame/i.test(prompt)\n}\n","import { SEEDANCE_2_REF_LIMITS } from \"@nodaro/shared/models\"\nimport { placeFramesAfterReferences } from \"./frame-reference-slots.js\"\n\nexport type Seedance2Mode = \"first-frame\" | \"first-last-frame\" | \"reference\"\n\nexport interface Seedance2InputsArgs {\n /** The generation prompt — used ONLY to detect an existing first-frame\n * binding and suppress the duplicate trailing sentence. */\n prompt?: string\n firstFrameUrl?: string\n lastFrameUrl?: string\n refImageUrls?: readonly string[]\n refVideoUrls?: readonly string[]\n refAudioUrls?: readonly string[]\n /** Per-provider input caps (2026-08-15). The Seedance 2.x GENERATIONS share\n * this resolver's whole mode logic but not their caps — 2.5 takes the same\n * three kinds at 30/10/10 where 2.0 stops at 9/3/3. Defaults to the 2.0\n * caps so every existing caller is byte-identical; the adapter passes the\n * provider's own entry from VIDEO_REF_LIMITS_BY_PROVIDER. */\n limits?: { images: number; videos: number; audio: number }\n}\n\nexport interface Seedance2InputsResult {\n mode: Seedance2Mode\n firstFrameUrl?: string\n lastFrameUrl?: string\n referenceImageUrls: string[]\n referenceVideoUrls: string[]\n referenceAudioUrls: string[]\n promptSuffix: string\n droppedRefImages: number\n}\n\nconst clean = (u?: string): string | undefined => {\n const t = u?.trim()\n return t && t.length > 0 ? t : undefined\n}\nconst cleanList = (xs?: readonly string[]): string[] =>\n (xs ?? []).map(clean).filter((x): x is string => x !== undefined)\n\n/**\n * Single source of truth for how Seedance 2's three mutually-exclusive KIE input\n * modes are selected from connected inputs. Strict first/last-frame mode is used\n * only when nothing but frames is connected; any reference (image, video, OR\n * audio) switches to multimodal Reference mode, where the frames are appended to\n * reference_image_urls (after the user's own images, so their @Image ordinals do\n * not shift) and named in a doctrine-compliant prompt suffix.\n */\nexport { promptBindsFirstFrame } from \"./frame-reference-slots.js\"\n\nexport function resolveSeedance2Inputs(args: Seedance2InputsArgs): Seedance2InputsResult {\n const limits = args.limits ?? SEEDANCE_2_REF_LIMITS\n const firstFrameUrl = clean(args.firstFrameUrl)\n const lastFrameUrl = clean(args.lastFrameUrl)\n const refImages = cleanList(args.refImageUrls)\n const refVideos = cleanList(args.refVideoUrls).slice(0, limits.videos)\n const refAudios = cleanList(args.refAudioUrls).slice(0, limits.audio)\n\n const hasAnyReference = refImages.length > 0 || refVideos.length > 0 || refAudios.length > 0\n\n // Strict first/last-frame mode is only expressible when a first frame is\n // present (KIE has no last-frame-only strict mode) and nothing but frames is\n // connected. A lone last frame (no first frame, no references) therefore falls\n // through to Reference mode below, where it becomes the sole reference image\n // with a closing-frame hint. The truly-empty case (no frames at all) stays in\n // the degenerate first-frame branch with no URLs.\n const canUseStrictMode = !hasAnyReference && (Boolean(firstFrameUrl) || !lastFrameUrl)\n\n if (canUseStrictMode) {\n if (firstFrameUrl && lastFrameUrl) {\n return { mode: \"first-last-frame\", firstFrameUrl, lastFrameUrl, referenceImageUrls: [], referenceVideoUrls: [], referenceAudioUrls: [], promptSuffix: \"\", droppedRefImages: 0 }\n }\n return { mode: \"first-frame\", firstFrameUrl, lastFrameUrl: undefined, referenceImageUrls: [], referenceVideoUrls: [], referenceAudioUrls: [], promptSuffix: \"\", droppedRefImages: 0 }\n }\n\n // Reference mode: keep both frames (explicit intent), drop trailing user images\n // if the image cap is exceeded. Frames are appended AFTER the kept user images\n // so existing user @Image ordinals are preserved — the platform convention\n // (placeFramesAfterReferences), shared with every frames-in-the-list model.\n const placed = placeFramesAfterReferences({\n prompt: args.prompt,\n firstFrameUrl,\n lastFrameUrl,\n refImageUrls: refImages,\n imageCap: limits.images,\n })\n const referenceImageUrls = placed.imageUrls\n const promptSuffix = placed.promptSuffix\n const droppedRefImages = placed.droppedRefImages\n\n return {\n mode: \"reference\",\n firstFrameUrl: undefined,\n lastFrameUrl: undefined,\n referenceImageUrls,\n referenceVideoUrls: refVideos,\n referenceAudioUrls: refAudios,\n promptSuffix,\n droppedRefImages,\n }\n}\n","import { VIDEO_REF_LIMITS_BY_PROVIDER } from \"@nodaro/shared/models\"\n\nimport { placeFramesAfterReferences } from \"./frame-reference-slots.js\"\n\n/**\n * Gemini Omni Video i2v input resolution — the sibling of\n * `resolveSeedance2Inputs` for a model whose multimodal channel is ONE flat\n * `image_urls` list.\n *\n * THE PLATFORM CONVENTION (2026-10-18, `placeFramesAfterReferences`): the\n * caller's identity references come first, so their image token N is their Nth\n * reference, and the start frame is appended after them. Until then the frame\n * led the list, so a caller's `@image_1` landed on the frame.\n *\n * WHY BINDING IS LOAD-BEARING: Gemini Omni receives the start frame and the\n * identity references in the same array, with nothing in the payload marking\n * which is which. A multimodal model treats unbound images as loose context —\n * field finding (recast keyframes run, 2026-08-14): the identity references\n * rode every call and the cast still drifted part to part, because the prompt\n * never said the images WERE identities to keep. So the resolver names the\n * roles in a prompt suffix: the references are identity references, explicitly\n * not frames, and the last image is the opening frame.\n *\n * BUDGETED, NEVER REJECTED, for the list this resolver assembles: KIE's quota\n * is `images + 2×videos ≤ 7`, and `runGeminiOmni` hard-rejects overflow. That\n * reject is right for a caller-assembled list (the user's own images should\n * not silently thin out) and wrong for THIS merge, where the overflow is our\n * own construction — so trailing references are dropped to fit, the start\n * frame always kept (the convention's tie-break), and the drop count is\n * reported for the caller to log.\n *\n * BYTE-IDENTICAL when there is nothing to bind: no references ⇒ no suffix and\n * a single-image list — exactly what every plain gemini-omni i2v call has\n * always sent.\n */\n\nexport interface GeminiOmniI2vInputsArgs {\n /** The composed prompt, used only to detect an existing first-frame binding. */\n prompt?: string\n /** The start frame — always kept, appended after the references. */\n firstFrameUrl: string\n /** Identity references, in priority order (trailing ones drop first). */\n refImageUrls?: Array<string | undefined>\n /** A connected source video occupies 2 of the 7 input slots (KIE quota). */\n videoConnected?: boolean\n /** The Omni SKU this run targets — defaults to `gemini-omni-video` for\n * back-compat. Both SKUs cap at 7 today, so passing it is behaviour-neutral;\n * it stops the flash path from silently reading the pro model's quota if the\n * two ever diverge. */\n provider?: string\n}\n\nexport interface GeminiOmniI2vInputsResult {\n /** `[...keptRefs, firstFrameUrl]` — the `image_urls` payload, quota-fitted. */\n imageUrls: string[]\n /** The role-binding sentences; empty when no reference survived the budget. */\n promptSuffix: string\n /** References dropped to fit the quota — surface in a log, never silently. */\n droppedRefImages: number\n}\n\n/** The catalog-declared cap (7) — read PER SKU from the shared limits map so the\n * wire-contract number has one home; the literal is only the safety net. */\nconst DEFAULT_GEMINI_OMNI_PROVIDER = \"gemini-omni-video\"\nfunction geminiOmniInputSlots(provider: string | undefined): number {\n return VIDEO_REF_LIMITS_BY_PROVIDER[provider ?? DEFAULT_GEMINI_OMNI_PROVIDER]?.images ?? 7\n}\n\nexport function resolveGeminiOmniI2vInputs(args: GeminiOmniI2vInputsArgs): GeminiOmniI2vInputsResult {\n const slots = geminiOmniInputSlots(args.provider) - (args.videoConnected ? 2 : 0)\n const placed = placeFramesAfterReferences({\n prompt: args.prompt,\n firstFrameUrl: args.firstFrameUrl,\n refImageUrls: args.refImageUrls,\n imageCap: Math.max(1, slots),\n bindIdentities: true,\n })\n if (placed.imageUrls.length === 1) {\n return { imageUrls: placed.imageUrls, promptSuffix: \"\", droppedRefImages: placed.droppedRefImages }\n }\n return { imageUrls: placed.imageUrls, promptSuffix: placed.promptSuffix, droppedRefImages: placed.droppedRefImages }\n}\n","import { placeFramesAfterReferences } from \"./frame-reference-slots.js\"\n\n/**\n * VEO 3.x i2v input resolution — the mutually-exclusive sibling of\n * `resolveGeminiOmniI2vInputs`. VEO's API carries ONE `imageUrls` array\n * (≤3) whose meaning flips with `generationType`: plain i2v reads it as\n * [first(, last)] frames; REFERENCE_2_VIDEO reads every entry as a\n * reference ingredient. Frames and identities cannot ride separate\n * channels, so an anchored call that must carry identity references moves\n * to REFERENCE_2_VIDEO with the frames bound in prose (requested, not\n * pixel-guaranteed — the accepted trade, same as seedance-2's reference mode).\n *\n * THE PLATFORM CONVENTION (2026-10-18, `placeFramesAfterReferences`): the\n * caller's references take the first seats, so their image token N is their\n * Nth reference; the start frame — and the end frame, VEO takes one — follow\n * them, bound as the opening / closing frame. At the 3-ingredient cap the\n * frames are kept (explicit intent) and trailing references drop, reported.\n * (Until then the anchor took seat 1 and an end frame was surrendered.)\n *\n * BYTE-IDENTICAL with no references: plain frame mode, frames kept, no\n * generationType, no suffix — exactly what every veo i2v call has always\n * sent.\n */\n\nexport interface VeoI2vInputsArgs {\n /** Used only to detect an existing first-frame binding (seedance rule). */\n prompt?: string\n firstFrameUrl: string\n endFrameUrl?: string\n refImageUrls?: Array<string | undefined>\n}\n\nexport interface VeoI2vInputsResult {\n /** The `imageUrls` payload: frames in plain mode, [...refs, start, end?] in\n * reference mode — never more than VEO's 3-ingredient cap. */\n imageUrls: string[]\n /** Present (REFERENCE_2_VIDEO) exactly when references ride. */\n generationType?: \"REFERENCE_2_VIDEO\"\n promptSuffix: string\n droppedRefImages: number\n /** Always false since the frames-after-references convention: an end frame\n * is kept in reference mode. Kept for callers that log it. */\n droppedEndFrame: boolean\n}\n\n/** VEO's ingredient cap — the adapter's REFERENCE_2_VIDEO path has always\n * sliced to 3 (kie/video.ts), mirrored in VIDEO_REF_LIMITS_BY_PROVIDER. */\nconst VEO_INGREDIENT_SLOTS = 3\n\nexport function resolveVeoI2vInputs(args: VeoI2vInputsArgs): VeoI2vInputsResult {\n const refs = (args.refImageUrls ?? []).filter((u): u is string => typeof u === \"string\" && u.length > 0)\n if (refs.length === 0) {\n return {\n imageUrls: args.endFrameUrl ? [args.firstFrameUrl, args.endFrameUrl] : [args.firstFrameUrl],\n promptSuffix: \"\",\n droppedRefImages: 0,\n droppedEndFrame: false,\n }\n }\n const placed = placeFramesAfterReferences({\n prompt: args.prompt,\n firstFrameUrl: args.firstFrameUrl,\n lastFrameUrl: args.endFrameUrl,\n refImageUrls: refs,\n imageCap: VEO_INGREDIENT_SLOTS,\n bindIdentities: true,\n })\n return {\n imageUrls: placed.imageUrls,\n generationType: \"REFERENCE_2_VIDEO\",\n promptSuffix: placed.promptSuffix,\n droppedRefImages: placed.droppedRefImages,\n droppedEndFrame: false,\n }\n}\n","/**\n * Surround continuation — the fill prompt.\n *\n * Prompt engineering, so it lives here and not in `@nodaro/shared`: that\n * package is published to npm under Apache-2.0, where every release is an\n * irrevocable grant. This package is never published (`\"private\": true`).\n * `@nodaro/shared` keeps only the wire contract — the direction enum and the\n * carried-fraction defaults the route Zod schema and the SDK input type need.\n */\nimport type { SurroundDirection } from \"@nodaro/shared/entities\"\n\n/** Which edge of the NEW frame holds the carried pixels vs the painted region. */\nconst EDGE: Record<SurroundDirection, { carried: string; painted: string }> = {\n right: { carried: \"left\", painted: \"right\" },\n left: { carried: \"right\", painted: \"left\" },\n up: { carried: \"bottom\", painted: \"top\" },\n down: { carried: \"top\", painted: \"bottom\" },\n}\n\n/** What a tilt must actually render (NOT a continuation of the landscape). */\nconst TILT_SUBJECT: Record<\"up\" | \"down\", { word: string; subject: string; where: string }> = {\n up: {\n word: \"up\",\n subject: \"the open sky directly overhead — sky, clouds, or (for an interior) the canopy or ceiling\",\n where: \"overhead\",\n },\n down: {\n word: \"down\",\n subject: \"the ground directly below — terrain, floor, or water surface\",\n where: \"below\",\n },\n}\n\n/**\n * Build the fill prompt the model receives alongside the half-carry composite.\n *\n * `userPrompt` (an optional scene hint from the caller) is woven in front. PAN\n * directions get the seamless-continuation prompt (with the anti-golden-hour\n * negative that fights the documented warm-regrade drift). TILT directions get a\n * subject-forcing prompt — render the sky / ground overhead / below, explicitly\n * NOT a mirrored landscape — which is what stops the vertical echo.\n */\nexport function buildSurroundFillPrompt(direction: SurroundDirection, userPrompt?: string): string {\n const scene = userPrompt && userPrompt.trim() ? `${userPrompt.trim()}. ` : \"\"\n const { carried, painted } = EDGE[direction]\n\n if (direction === \"up\" || direction === \"down\") {\n const t = TILT_SUBJECT[direction]\n return (\n `${scene}` +\n `This is a camera tilted straight ${t.word} from the same scene. The ${carried} strip holds real, finished pixels from the edge of the horizon view; the ${painted} region is flat gray and MUST be painted as ${t.subject}. ` +\n `Render what is genuinely ${t.where} — do NOT repeat, mirror, or continue the landscape, and do NOT draw a horizon line or distant scenery in the painted region. ` +\n `CRITICAL: keep the ${carried} strip unchanged and match the scene's EXACT lighting, time of day, white balance, and color grade — the same light as the ${carried} strip; no golden hour, no sunset, no warm relight, no cinematic regrade. ` +\n `Blend smoothly into the ${carried} strip with no visible seam. No people, no text, no labels, no watermarks.`\n )\n }\n\n // pan (right / left)\n return (\n `${scene}` +\n `This is a partial frame: the ${carried} portion contains real, finished pixels and the ${painted} portion is flat gray that MUST be painted in. ` +\n `Paint ONLY the ${painted} gray region as a natural, seamless continuation of the ${carried} portion — same scene, same perspective, continuing the horizon, geometry, and content across the boundary with no break. ` +\n `Keep the ${carried} portion completely unchanged. ` +\n `CRITICAL: do NOT change the lighting, exposure, white balance, or time of day. Match the ${carried} portion's EXACT light, color temperature, and contrast across the whole frame — if it is flat overcast daylight, keep flat overcast daylight. No golden hour, no sunset, no warm relight, no cinematic regrade. ` +\n `The seam between the ${carried} and ${painted} portions must be invisible. No people, no text, no labels, no watermarks.`\n )\n}\n","/**\n * HOW A START/END FRAME TRAVELS — one plan, read by both ends.\n *\n * A frame can reach a model two ways: as a real frame, or as a reference image\n * bound in prose as the opening (or closing) frame. Which one is right is\n * measured per model — the Seedance 2.0 family crop-zooms a frame 2% and drifts\n * 11-26% darker within six frames, but holds its look from frame 0 when the same\n * image rides as a reference; every other model measured reproduces the opening\n * frame better as a frame (see `resolveFrameDelivery` in @nodaro/shared).\n *\n * The composition — where the frames sit in the reference list, which sentence\n * binds them, when the switch is refused — lives HERE rather than at the\n * dispatch site, because two places need the same answer:\n *\n * - `backend/src/lib/video-frame-dispatch.ts` builds the actual request,\n * - the editor's config panel tells the user which mode their node will run in.\n *\n * When those two disagree the panel lies, which is worse than having no panel\n * text at all. One function, two callers, no drift.\n */\nimport { VIDEO_REF_LIMITS_BY_PROVIDER } from \"@nodaro/shared/models\"\nimport { resolveFrameDelivery, type FrameDelivery } from \"@nodaro/shared/video\"\nimport { REF_BINDING } from \"./ref-binding.js\"\nimport { promptBindsFirstFrame } from \"./seedance-2-inputs.js\"\n\nexport interface FrameDeliveryPlanArgs {\n readonly provider: string | undefined\n /** The node's / caller's choice. `auto` (or absent) resolves per model. */\n readonly requested?: FrameDelivery\n /** Whether the model accepts reference images at all. */\n readonly supportsReferenceImages: boolean\n readonly startFrameUrl?: string\n readonly endFrameUrl?: string\n /** The user's OWN reference images, in their existing order. */\n readonly userReferenceUrls?: readonly string[]\n /** Consulted only to avoid a duplicate opening-frame sentence. */\n readonly prompt?: string\n}\n\nexport interface FrameDeliveryPlan {\n /** What will actually happen — never `auto`. */\n readonly delivery: Exclude<FrameDelivery, \"auto\">\n /** The full reference list to send. Equals the user's own list under `frame`. */\n readonly referenceImageUrls: readonly string[]\n /** Sentence(s) to append to the prompt. Empty under `frame`. */\n readonly promptSuffix: string\n /** Set when reference delivery was WANTED but refused, so a caller can say why. */\n readonly refusedReason?: \"no-reference-support\" | \"image-cap\"\n}\n\n/**\n * Resolve delivery and compose the request shape that follows from it.\n *\n * Two refusals, both deliberate:\n * - a model with no reference-image support keeps frame mode (there is nowhere\n * for the frame to go);\n * - a switch that would push past the model's image cap keeps frame mode,\n * because the alternative is dropping one of the USER's reference images to\n * make room for ours.\n *\n * The frames are appended AFTER the user's own images so their `@image_N`\n * ordinals never shift, and the opening sentence is skipped when the prompt\n * already binds its own first frame — a second binding at another position\n * dilutes the first back into coin-flip behaviour (field finding 2026-07-20).\n */\nexport function planFrameDelivery(args: FrameDeliveryPlanArgs): FrameDeliveryPlan {\n const userRefs = (args.userReferenceUrls ?? []).filter(Boolean)\n const requested = args.requested ?? \"auto\"\n const delivery = resolveFrameDelivery({\n provider: args.provider,\n requested,\n supportsReferenceImages: args.supportsReferenceImages,\n })\n\n const frames = [args.startFrameUrl, args.endFrameUrl].filter((u): u is string => Boolean(u))\n\n if (delivery === \"frame\" || frames.length === 0) {\n return {\n delivery: \"frame\",\n referenceImageUrls: userRefs,\n promptSuffix: \"\",\n ...(requested === \"reference\" && !args.supportsReferenceImages\n ? { refusedReason: \"no-reference-support\" as const }\n : {}),\n }\n }\n\n const cap = VIDEO_REF_LIMITS_BY_PROVIDER[args.provider ?? \"\"]?.images\n if (cap !== undefined && userRefs.length + frames.length > cap) {\n return { delivery: \"frame\", referenceImageUrls: userRefs, promptSuffix: \"\", refusedReason: \"image-cap\" }\n }\n\n const referenceImageUrls = [...userRefs]\n const sentences: string[] = []\n if (args.startFrameUrl) {\n referenceImageUrls.push(args.startFrameUrl)\n if (!promptBindsFirstFrame(args.prompt)) {\n sentences.push(REF_BINDING.frame(referenceImageUrls.length, \"opening\"))\n }\n }\n if (args.endFrameUrl) {\n referenceImageUrls.push(args.endFrameUrl)\n sentences.push(REF_BINDING.frame(referenceImageUrls.length, \"closing\"))\n }\n\n return { delivery: \"reference\", referenceImageUrls, promptSuffix: sentences.join(\" \") }\n}\n"]}
|
|
@@ -20,12 +20,26 @@ var REF_BINDING = {
|
|
|
20
20
|
ordinal: (n) => `@image_${n}`,
|
|
21
21
|
frame: (n, role) => `Use @image_${n} as the ${role} (${role === "opening" ? "first" : "last"}) frame of the video.`
|
|
22
22
|
};
|
|
23
|
+
var roleWords = (role) => `${role} (${role === "opening" ? "first" : "last"})`;
|
|
24
|
+
function plainList(first, last) {
|
|
25
|
+
const names = Array.from({ length: last - first + 1 }, (_, i) => `image ${first + i}`);
|
|
26
|
+
return names.length === 1 ? names[0] : `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
|
|
27
|
+
}
|
|
23
28
|
function imageBindingFor(syntax) {
|
|
24
|
-
if (syntax === "
|
|
25
|
-
|
|
29
|
+
if (syntax === "image N") {
|
|
30
|
+
return {
|
|
31
|
+
ordinal: (n) => `image ${n}`,
|
|
32
|
+
frame: (n, role) => `image ${n} is the ${roleWords(role)} frame of the video.`,
|
|
33
|
+
frames: (first, last) => `image ${first} is the ${roleWords("opening")} frame and image ${last} is the ${roleWords("closing")} frame of the video.`,
|
|
34
|
+
identities: (first, last) => first === last ? `image ${first} is a reference for the subject; it is not a frame.` : `${plainList(first, last)} are references for the subject; they are not frames.`
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const ordinal = syntax === "@image_N" ? REF_BINDING.ordinal : syntax === "@image(n)" ? (n) => `@image(${n})` : (n) => `[Image ${n}]`;
|
|
26
38
|
return {
|
|
27
39
|
ordinal,
|
|
28
|
-
frame: (n, role) => `Use ${ordinal(n)} as the ${
|
|
40
|
+
frame: syntax === "@image_N" ? REF_BINDING.frame : (n, role) => `Use ${ordinal(n)} as the ${roleWords(role)} frame of the video.`,
|
|
41
|
+
frames: (first, last) => `Use ${ordinal(first)} as the ${roleWords("opening")} frame and ${ordinal(last)} as the ${roleWords("closing")} frame of the video.`,
|
|
42
|
+
identities: (first, last) => identityRefsSentence(first, last, ordinal)
|
|
29
43
|
};
|
|
30
44
|
}
|
|
31
45
|
var SEEDANCE_VIDEO_EDIT_PREFIX = "edit {video:1} as follows:\n";
|
|
@@ -794,13 +808,13 @@ function placeFramesAfterReferences(args) {
|
|
|
794
808
|
const promptBindsStart = syntax === "@image_N" && promptBindsFirstFrame(args.prompt);
|
|
795
809
|
let frameSentence = "";
|
|
796
810
|
if (promptBindsStart && firstFrameOrdinal > 0 && lastFrameOrdinal === 0) ; else if (firstFrameOrdinal > 0 && lastFrameOrdinal > 0) {
|
|
797
|
-
frameSentence =
|
|
811
|
+
frameSentence = binding.frames(firstFrameOrdinal, lastFrameOrdinal);
|
|
798
812
|
} else if (firstFrameOrdinal > 0) {
|
|
799
813
|
frameSentence = binding.frame(firstFrameOrdinal, "opening");
|
|
800
814
|
} else if (lastFrameOrdinal > 0) {
|
|
801
815
|
frameSentence = binding.frame(lastFrameOrdinal, "closing");
|
|
802
816
|
}
|
|
803
|
-
const identitySentence = args.bindIdentities && kept.length > 0 && frameCount > 0 ?
|
|
817
|
+
const identitySentence = args.bindIdentities && kept.length > 0 && frameCount > 0 ? binding.identities(1, kept.length) : "";
|
|
804
818
|
const promptSuffix = [frameSentence, identitySentence].filter(Boolean).join(" ");
|
|
805
819
|
return { imageUrls, promptSuffix, droppedRefImages, firstFrameOrdinal, lastFrameOrdinal };
|
|
806
820
|
}
|
|
@@ -964,5 +978,5 @@ function planFrameDelivery(args) {
|
|
|
964
978
|
}
|
|
965
979
|
|
|
966
980
|
export { REF_BINDING, SEEDANCE_VIDEO_EDIT_PREFIX, appendField, appendMusicMeta, assembleImageInput, assembleSunoInput, buildSeedanceVideoEditPrompt, buildSurroundFillPrompt, composeSoundHintFromConnections, composeVideoPromptText, getEffectiveSunoCustomMode, identityRefsSentence, imageBindingFor, placeFramesAfterReferences, planFrameDelivery, promptBindsFirstFrame, resolveGeminiOmniI2vInputs, resolveRefIdTokens, resolveReferenceTokens, resolveSeedance2Inputs, resolveVeoI2vInputs, resolveVideoReferenceCore, truncateForField };
|
|
967
|
-
//# sourceMappingURL=chunk-
|
|
968
|
-
//# sourceMappingURL=chunk-
|
|
981
|
+
//# sourceMappingURL=chunk-XR7RAHZI.js.map
|
|
982
|
+
//# sourceMappingURL=chunk-XR7RAHZI.js.map
|