@gentbajko/slopify 3.25.8 → 4.0.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.
Files changed (604) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/cache.js +3 -5
  3. package/dist/adapters/alignment/ctc.js +1 -2
  4. package/dist/adapters/alignment/lock.js +4 -3
  5. package/dist/adapters/alignment/multilingual.js +0 -3
  6. package/dist/adapters/alignment/prefetch.js +2 -1
  7. package/dist/adapters/alignment/worker.js +2 -1
  8. package/dist/adapters/explain.js +0 -3
  9. package/dist/adapters/ffmpeg.js +19 -1
  10. package/dist/adapters/host-cli/index.js +19 -3
  11. package/dist/adapters/http-failure.js +53 -0
  12. package/dist/adapters/image/codex.js +36 -4
  13. package/dist/adapters/image/fal.js +17 -50
  14. package/dist/adapters/image/google.js +31 -55
  15. package/dist/adapters/image/openai.js +20 -47
  16. package/dist/adapters/image/reference.js +36 -0
  17. package/dist/adapters/image/replicate.js +18 -47
  18. package/dist/adapters/interest/search-interest.js +113 -0
  19. package/dist/adapters/key-probes.js +53 -2
  20. package/dist/adapters/llm/antigravity.js +4 -0
  21. package/dist/adapters/llm/assistant-cli.js +388 -0
  22. package/dist/adapters/llm/child-session.js +83 -0
  23. package/dist/adapters/llm/claude-code-models.js +51 -98
  24. package/dist/adapters/llm/claude-code.js +13 -2
  25. package/dist/adapters/llm/codex-limits.js +27 -68
  26. package/dist/adapters/llm/codex-server-models.js +61 -109
  27. package/dist/adapters/llm/codex.js +12 -1
  28. package/dist/adapters/llm/openrouter.js +10 -47
  29. package/dist/adapters/llm/run-cli.js +6 -22
  30. package/dist/adapters/llm/sse-lines.js +5 -1
  31. package/dist/adapters/model-access.js +37 -0
  32. package/dist/adapters/tts/cartesia.js +15 -46
  33. package/dist/adapters/tts/elevenlabs.js +20 -52
  34. package/dist/adapters/tts/gemini.js +19 -40
  35. package/dist/adapters/tts/inworld-async.js +4 -1
  36. package/dist/adapters/tts/inworld.js +3 -0
  37. package/dist/adapters/tts/openai.js +20 -50
  38. package/dist/assets/models-lifecycle.yaml +6 -0
  39. package/dist/assistant-wiring.js +60 -0
  40. package/dist/autopilot-timers.js +59 -0
  41. package/dist/catalog/lifecycle.js +52 -0
  42. package/dist/catalog/propose.js +204 -0
  43. package/dist/catalog/registry.js +3 -1
  44. package/dist/catalog/schema.js +2 -0
  45. package/dist/catalog/store.js +39 -0
  46. package/dist/edge/autostart/native.js +3 -5
  47. package/dist/edge/autostart/prompt.js +2 -1
  48. package/dist/edge/autostart/service.js +3 -4
  49. package/dist/edge/cli-args.js +3 -4
  50. package/dist/edge/cli.js +2 -1
  51. package/dist/edge/docker-install/apply.js +11 -11
  52. package/dist/edge/docker-install/run.js +2 -1
  53. package/dist/edge/docker-install/state.js +4 -3
  54. package/dist/edge/docker-install/tree.js +2 -4
  55. package/dist/edge/docker-install/volume.js +3 -2
  56. package/dist/edge/docker.js +5 -4
  57. package/dist/edge/events/hub.js +5 -5
  58. package/dist/edge/http/actions.js +4 -25
  59. package/dist/edge/http/actor.js +5 -3
  60. package/dist/edge/http/app.js +85 -10
  61. package/dist/edge/http/assistant.js +127 -0
  62. package/dist/edge/http/audio-preview.js +4 -11
  63. package/dist/edge/http/auditions.js +1 -1
  64. package/dist/edge/http/autostart.js +5 -5
  65. package/dist/edge/http/backups.js +1 -3
  66. package/dist/edge/http/channel-memory.js +7 -20
  67. package/dist/edge/http/channels.js +13 -26
  68. package/dist/edge/http/checkpoints.js +33 -17
  69. package/dist/edge/http/design-templates.js +167 -0
  70. package/dist/edge/http/designs.js +178 -0
  71. package/dist/edge/http/diagnostics.js +5 -1
  72. package/dist/edge/http/document-themes.js +2 -11
  73. package/dist/edge/http/draft-files.js +1 -3
  74. package/dist/edge/http/draft-problem.js +4 -9
  75. package/dist/edge/http/drafts.js +3 -3
  76. package/dist/edge/http/entries.js +6 -9
  77. package/dist/edge/http/extension-auth.js +27 -0
  78. package/dist/edge/http/external-reviews.js +12 -11
  79. package/dist/edge/http/files.js +5 -11
  80. package/dist/edge/http/folder-location.js +1 -4
  81. package/dist/edge/http/fonts.js +0 -7
  82. package/dist/edge/http/home.js +38 -13
  83. package/dist/edge/http/host-cli.js +18 -8
  84. package/dist/edge/http/ids.js +8 -0
  85. package/dist/edge/http/keywords.js +35 -0
  86. package/dist/edge/http/model-checks.js +132 -0
  87. package/dist/edge/http/narration-peaks.js +5 -13
  88. package/dist/edge/http/onboarding.js +1 -14
  89. package/dist/edge/http/open-folder.js +5 -10
  90. package/dist/edge/http/outputs.js +4 -12
  91. package/dist/edge/http/patch-notes.js +4 -10
  92. package/dist/edge/http/planning.js +2 -6
  93. package/dist/edge/http/problem.js +2 -4
  94. package/dist/edge/http/project-create.js +1 -0
  95. package/dist/edge/http/project-templates.js +29 -17
  96. package/dist/edge/http/projects.js +2 -12
  97. package/dist/edge/http/prompts.js +7 -14
  98. package/dist/edge/http/pronunciations.js +1 -2
  99. package/dist/edge/http/providers.js +39 -24
  100. package/dist/edge/http/refusal.js +29 -0
  101. package/dist/edge/http/remote-access.js +40 -0
  102. package/dist/edge/http/remote-guard.js +71 -0
  103. package/dist/edge/http/request-guard.js +91 -0
  104. package/dist/edge/http/reviews.js +11 -24
  105. package/dist/edge/http/revision-files.js +5 -10
  106. package/dist/edge/http/revisions.js +10 -15
  107. package/dist/edge/http/run-cost.js +1 -2
  108. package/dist/edge/http/same-page.js +80 -0
  109. package/dist/edge/http/schedules.js +59 -32
  110. package/dist/edge/http/settings.js +22 -13
  111. package/dist/edge/http/social.js +282 -0
  112. package/dist/edge/http/staging.js +2 -4
  113. package/dist/edge/http/storage-files.js +5 -7
  114. package/dist/edge/http/storage.js +23 -23
  115. package/dist/edge/http/studio.js +23 -50
  116. package/dist/edge/http/style-preview.js +1 -4
  117. package/dist/edge/http/subtitles.js +3 -17
  118. package/dist/edge/http/template-studio.js +433 -0
  119. package/dist/edge/http/trash.js +10 -11
  120. package/dist/edge/http/tutorial.js +1 -2
  121. package/dist/edge/http/tutorials.js +5 -11
  122. package/dist/edge/http/update.js +7 -13
  123. package/dist/edge/http/waveform.js +3 -6
  124. package/dist/edge/http/youtube-edits.js +2 -3
  125. package/dist/edge/mcp/assistant.js +279 -0
  126. package/dist/edge/mcp/attribution.js +8 -2
  127. package/dist/edge/mcp/call-route.js +11 -2
  128. package/dist/edge/mcp/excluded-routes.js +25 -0
  129. package/dist/edge/mcp/http.js +11 -2
  130. package/dist/edge/mcp/instructions.js +5 -3
  131. package/dist/edge/mcp/library-tools.js +1 -0
  132. package/dist/edge/mcp/route-tool.js +35 -15
  133. package/dist/edge/mcp/server.js +13 -8
  134. package/dist/edge/mcp/tools-channels.js +2 -0
  135. package/dist/edge/mcp/tools-designs.js +165 -0
  136. package/dist/edge/mcp/tools-library.js +14 -2
  137. package/dist/edge/mcp/tools-planning.js +23 -4
  138. package/dist/edge/mcp/tools-projects.js +28 -1
  139. package/dist/edge/mcp/tools-runs.js +14 -1
  140. package/dist/edge/mcp/tools-schedules.js +28 -4
  141. package/dist/edge/mcp/tools-setup.js +61 -3
  142. package/dist/edge/mcp/tools-social.js +46 -0
  143. package/dist/edge/mcp/tools-storage.js +8 -0
  144. package/dist/edge/mcp/tools-template-studio.js +166 -0
  145. package/dist/edge/mcp/tools.js +6 -0
  146. package/dist/edge/remote-access/cli.js +71 -0
  147. package/dist/edge/remote-access/model.js +32 -0
  148. package/dist/edge/remote-access/rules.js +207 -0
  149. package/dist/edge/remote-access/service.js +203 -0
  150. package/dist/edge/remote-access/store.js +44 -0
  151. package/dist/edge/remote-access/views.js +43 -0
  152. package/dist/extension/slopify-studio-chrome.zip +0 -0
  153. package/dist/extension/slopify-studio-firefox.zip +0 -0
  154. package/dist/host-cli/install.js +2 -2
  155. package/dist/host-cli/open-folder.js +2 -2
  156. package/dist/host-cli/paths.js +2 -4
  157. package/dist/host-cli/server.js +4 -4
  158. package/dist/host-cli/service.js +3 -3
  159. package/dist/host-cli/status.js +7 -7
  160. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  161. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  162. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  163. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  164. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  165. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  166. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  167. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  168. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  169. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  170. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  171. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  172. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  173. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  174. package/dist/kernel/db/tx.js +2 -4
  175. package/dist/kernel/errors.js +27 -1
  176. package/dist/kernel/json-answer.js +15 -0
  177. package/dist/kernel/lock.js +4 -9
  178. package/dist/kernel/paths.js +8 -0
  179. package/dist/kernel/ports/assistant-cli.js +4 -0
  180. package/dist/kernel/ports/host-cli.js +3 -0
  181. package/dist/kernel/ports/interest.js +4 -0
  182. package/dist/kernel/ports/languages.js +0 -3
  183. package/dist/kernel/ports/model.js +2 -0
  184. package/dist/kernel/runner/attempt.js +3 -1
  185. package/dist/kernel/runner/audition.js +3 -1
  186. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  187. package/dist/kernel/runner/index.js +8 -10
  188. package/dist/kernel/runner/model-access.js +40 -0
  189. package/dist/kernel/runner/model-check.js +153 -0
  190. package/dist/kernel/runner/providers.js +15 -10
  191. package/dist/kernel/runner/standalone.js +9 -5
  192. package/dist/main.js +334 -19
  193. package/dist/model-checks.js +166 -0
  194. package/dist/patch-notes/3.25.9.md +5 -0
  195. package/dist/patch-notes/4.0.0.md +51 -0
  196. package/dist/patch-notes/index.json +12 -0
  197. package/dist/slices/admission/heads.js +2 -1
  198. package/dist/slices/admission/repo.js +2 -1
  199. package/dist/slices/admission/rules.js +43 -8
  200. package/dist/slices/admission/schema.js +8 -0
  201. package/dist/slices/admission/stage-writes.js +0 -3
  202. package/dist/slices/admission/start.js +48 -31
  203. package/dist/slices/admission/substitute.js +7 -4
  204. package/dist/slices/agents/attention-posts.js +31 -0
  205. package/dist/slices/agents/attention-templates.js +33 -0
  206. package/dist/slices/agents/attention.js +10 -1
  207. package/dist/slices/agents/connection.js +2 -6
  208. package/dist/slices/agents/model.js +6 -3
  209. package/dist/slices/agents/repo.js +15 -7
  210. package/dist/slices/article/continuation.js +0 -63
  211. package/dist/slices/article/plain.js +3 -2
  212. package/dist/slices/article/segments.js +2 -62
  213. package/dist/slices/assistant/access.js +1 -0
  214. package/dist/slices/assistant/asks.js +80 -0
  215. package/dist/slices/assistant/cost.js +54 -0
  216. package/dist/slices/assistant/gate.js +222 -0
  217. package/dist/slices/assistant/model.js +79 -0
  218. package/dist/slices/assistant/names.js +23 -0
  219. package/dist/slices/assistant/prompt.js +74 -0
  220. package/dist/slices/assistant/repo.js +74 -0
  221. package/dist/slices/assistant/tokens.js +18 -0
  222. package/dist/slices/assistant/turn.js +342 -0
  223. package/dist/slices/autopilot/buffer.js +76 -0
  224. package/dist/slices/autopilot/model.js +14 -0
  225. package/dist/slices/autopilot/notify.js +27 -0
  226. package/dist/slices/autopilot/repo.js +34 -0
  227. package/dist/slices/autopilot/signals.js +194 -0
  228. package/dist/slices/autopilot/warnings.js +85 -0
  229. package/dist/slices/autopilot/weekly.js +100 -0
  230. package/dist/slices/backups/files.js +2 -6
  231. package/dist/slices/backups/repo.js +3 -15
  232. package/dist/slices/backups/service.js +4 -3
  233. package/dist/slices/channels/cast-images.js +2 -1
  234. package/dist/slices/channels/model.js +0 -1
  235. package/dist/slices/channels/rebrand.js +0 -3
  236. package/dist/slices/channels/repo.js +4 -0
  237. package/dist/slices/channels/schema.js +6 -0
  238. package/dist/slices/channels/service.js +10 -0
  239. package/dist/slices/channels/videos.js +2 -14
  240. package/dist/slices/checkpoints/index.js +0 -1
  241. package/dist/slices/control/providers.js +0 -26
  242. package/dist/slices/designs/artwork.js +66 -0
  243. package/dist/slices/designs/budget.js +23 -0
  244. package/dist/slices/designs/calls.js +124 -0
  245. package/dist/slices/designs/check-context.js +38 -0
  246. package/dist/slices/designs/check-text.js +43 -0
  247. package/dist/slices/designs/fields.js +88 -0
  248. package/dist/slices/designs/frames.js +34 -0
  249. package/dist/slices/designs/gate.js +128 -0
  250. package/dist/slices/designs/layout.js +293 -0
  251. package/dist/slices/designs/model.js +35 -0
  252. package/dist/slices/designs/pdf-job.js +23 -0
  253. package/dist/slices/designs/pdf-pages.js +30 -0
  254. package/dist/slices/designs/pdf-runner.js +68 -0
  255. package/dist/slices/designs/pdf-worker.js +24 -0
  256. package/dist/slices/designs/picture-size.js +79 -0
  257. package/dist/slices/designs/pictures.js +55 -0
  258. package/dist/slices/designs/quote.js +24 -0
  259. package/dist/slices/designs/render-args.js +36 -0
  260. package/dist/slices/designs/render-input.js +59 -0
  261. package/dist/slices/designs/render.js +215 -0
  262. package/dist/slices/designs/repo.js +101 -0
  263. package/dist/slices/designs/requests.js +80 -0
  264. package/dist/slices/designs/runner.js +239 -0
  265. package/dist/slices/designs/sample-repo.js +91 -0
  266. package/dist/slices/designs/sample.js +222 -0
  267. package/dist/slices/designs/schema.js +87 -0
  268. package/dist/slices/designs/sections.js +44 -0
  269. package/dist/slices/designs/service.js +233 -0
  270. package/dist/slices/designs/steps-repo.js +102 -0
  271. package/dist/slices/designs/studio-kind.js +39 -0
  272. package/dist/slices/designs/template-checks.js +44 -0
  273. package/dist/slices/designs/template-make.js +118 -0
  274. package/dist/slices/designs/templates.js +218 -0
  275. package/dist/slices/document/fonts.js +2 -1
  276. package/dist/slices/episodes/summarize.js +5 -1
  277. package/dist/slices/estimate/index.js +45 -7
  278. package/dist/slices/fonts/catalog.js +3 -2
  279. package/dist/slices/fonts/discovery.js +3 -2
  280. package/dist/slices/fonts/files.js +2 -6
  281. package/dist/slices/fonts/index.js +0 -1
  282. package/dist/slices/fonts/sfnt.js +45 -10
  283. package/dist/slices/images/appearance.js +6 -15
  284. package/dist/slices/images/scenes.js +100 -27
  285. package/dist/slices/keywords/digest.js +84 -0
  286. package/dist/slices/keywords/facts.js +92 -0
  287. package/dist/slices/keywords/fill.js +80 -0
  288. package/dist/slices/keywords/model.js +75 -0
  289. package/dist/slices/keywords/rules.js +187 -0
  290. package/dist/slices/keywords/schema.js +28 -0
  291. package/dist/slices/keywords/specs.js +260 -0
  292. package/dist/slices/keywords/values.js +74 -0
  293. package/dist/slices/library/lint.js +29 -12
  294. package/dist/slices/library/model.js +2 -0
  295. package/dist/slices/library/photorealistic.js +4 -12
  296. package/dist/slices/library/rename.js +9 -7
  297. package/dist/slices/library/save.js +14 -10
  298. package/dist/slices/library/slots.js +37 -11
  299. package/dist/slices/library/transfer.js +2 -1
  300. package/dist/slices/library/used-by.js +25 -10
  301. package/dist/slices/loudness/loudnorm.js +51 -11
  302. package/dist/slices/model-checks/cost.js +61 -0
  303. package/dist/slices/model-checks/listing.js +124 -0
  304. package/dist/slices/model-checks/model.js +20 -0
  305. package/dist/slices/model-checks/record.js +32 -0
  306. package/dist/slices/model-checks/repo.js +120 -0
  307. package/dist/slices/model-checks/rules.js +112 -0
  308. package/dist/slices/model-checks/run.js +143 -0
  309. package/dist/slices/model-checks/service.js +198 -0
  310. package/dist/slices/model-upkeep/switch.js +70 -25
  311. package/dist/slices/model-upkeep/usage.js +26 -11
  312. package/dist/slices/narration/breaths.js +2 -1
  313. package/dist/slices/narration/clean-pieces.js +2 -1
  314. package/dist/slices/narration/concat.js +2 -1
  315. package/dist/slices/narration/peaks.js +3 -4
  316. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  317. package/dist/slices/narration/pronunciation.js +3 -2
  318. package/dist/slices/narration/steering.js +4 -3
  319. package/dist/slices/notifications/notifier.js +4 -3
  320. package/dist/slices/notifications/rules.js +5 -2
  321. package/dist/slices/notifications/settings.js +4 -12
  322. package/dist/slices/notifications/subject.js +2 -1
  323. package/dist/slices/onboarding/copy.js +2 -1
  324. package/dist/slices/onboarding/first-run.js +3 -2
  325. package/dist/slices/onboarding/full-video.js +3 -2
  326. package/dist/slices/onboarding/install.js +4 -3
  327. package/dist/slices/onboarding/model.js +0 -16
  328. package/dist/slices/onboarding/packs.js +22 -22
  329. package/dist/slices/onboarding/quick-short.js +2 -1
  330. package/dist/slices/onboarding/sample.js +0 -1
  331. package/dist/slices/onboarding/state.js +2 -11
  332. package/dist/slices/onboarding/template.js +2 -1
  333. package/dist/slices/patch-notes/library.js +3 -2
  334. package/dist/slices/play-drafts/convert.js +4 -0
  335. package/dist/slices/play-drafts/review-inputs.js +8 -2
  336. package/dist/slices/play-drafts/review.js +5 -4
  337. package/dist/slices/play-drafts/schema.js +4 -0
  338. package/dist/slices/project-templates/from-project.js +9 -2
  339. package/dist/slices/project-templates/repo.js +6 -1
  340. package/dist/slices/project-templates/setup.js +8 -0
  341. package/dist/slices/project-templates/stand-ins.js +22 -0
  342. package/dist/slices/rebuild/activity.js +2 -0
  343. package/dist/slices/rebuild/admission-repo.js +3 -2
  344. package/dist/slices/rebuild/narration-history.js +4 -5
  345. package/dist/slices/rebuild/narration-retry.js +2 -1
  346. package/dist/slices/rebuild/preview-details.js +27 -9
  347. package/dist/slices/rebuild/preview-plan.js +3 -8
  348. package/dist/slices/rebuild/recipe-build.js +8 -3
  349. package/dist/slices/rebuild/recipe-describe.js +3 -3
  350. package/dist/slices/rebuild/recipe-digest.js +58 -0
  351. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  352. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  353. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  354. package/dist/slices/rebuild/recipe-model.js +4 -0
  355. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  356. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  357. package/dist/slices/rebuild/recipe-reference.js +8 -4
  358. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  359. package/dist/slices/rebuild/recipe-save.js +6 -3
  360. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  361. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  362. package/dist/slices/rebuild/recipe-social.js +46 -0
  363. package/dist/slices/rebuild/recipe-text.js +64 -20
  364. package/dist/slices/rebuild/recipe-validation.js +98 -28
  365. package/dist/slices/rebuild/recipe-visual.js +55 -16
  366. package/dist/slices/rebuild/recipe-work.js +15 -3
  367. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  368. package/dist/slices/rebuild/recovery-selection.js +3 -1
  369. package/dist/slices/rebuild/recovery.js +3 -2
  370. package/dist/slices/rebuild/repo.js +4 -3
  371. package/dist/slices/rebuild/review-promise.js +25 -0
  372. package/dist/slices/rebuild/review-redo.js +2 -1
  373. package/dist/slices/rebuild/runtime-admission.js +4 -3
  374. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  375. package/dist/slices/rebuild/runtime-animate.js +9 -8
  376. package/dist/slices/rebuild/runtime-article.js +3 -2
  377. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  378. package/dist/slices/rebuild/runtime-document.js +8 -11
  379. package/dist/slices/rebuild/runtime-export-bed.js +6 -11
  380. package/dist/slices/rebuild/runtime-export-edit.js +6 -15
  381. package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
  382. package/dist/slices/rebuild/runtime-export-short.js +8 -4
  383. package/dist/slices/rebuild/runtime-export.js +34 -12
  384. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  385. package/dist/slices/rebuild/runtime-image.js +4 -9
  386. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  387. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  388. package/dist/slices/rebuild/runtime-lines.js +2 -11
  389. package/dist/slices/rebuild/runtime-local.js +14 -20
  390. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  391. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  392. package/dist/slices/rebuild/runtime-narration-text.js +10 -15
  393. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  394. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  395. package/dist/slices/rebuild/runtime-plan.js +31 -5
  396. package/dist/slices/rebuild/runtime-provider.js +60 -19
  397. package/dist/slices/rebuild/runtime-publication.js +10 -9
  398. package/dist/slices/rebuild/runtime-review.js +33 -28
  399. package/dist/slices/rebuild/runtime-run.js +4 -1
  400. package/dist/slices/rebuild/runtime-shorts.js +34 -50
  401. package/dist/slices/rebuild/runtime-store.js +3 -8
  402. package/dist/slices/rebuild/runtime-subtitles.js +16 -14
  403. package/dist/slices/rebuild/runtime-voices.js +8 -15
  404. package/dist/slices/rebuild/runtime-youtube.js +11 -29
  405. package/dist/slices/rebuild/transition-repo.js +6 -5
  406. package/dist/slices/rebuild/work-records.js +6 -0
  407. package/dist/slices/reruns/cascade.js +1 -2
  408. package/dist/slices/reruns/index.js +6 -9
  409. package/dist/slices/reviews/external-model.js +3 -0
  410. package/dist/slices/reviews/external-repo.js +2 -1
  411. package/dist/slices/reviews/external-rows.js +2 -3
  412. package/dist/slices/reviews/model.js +26 -8
  413. package/dist/slices/reviews/repo.js +2 -1
  414. package/dist/slices/reviews/rules.js +1 -0
  415. package/dist/slices/reviews/view.js +5 -2
  416. package/dist/slices/revisions/downloads.js +4 -5
  417. package/dist/slices/revisions/mutation-assets.js +7 -4
  418. package/dist/slices/revisions/mutation-cues.js +2 -5
  419. package/dist/slices/revisions/mutations.js +0 -1
  420. package/dist/slices/revisions/repo.js +3 -1
  421. package/dist/slices/revisions/schema.js +1 -0
  422. package/dist/slices/revisions/view.js +20 -0
  423. package/dist/slices/run-cost/limits.js +5 -2
  424. package/dist/slices/run-cost/meter.js +12 -5
  425. package/dist/slices/run-cost/panel.js +8 -74
  426. package/dist/slices/run-cost/plan-readings.js +127 -0
  427. package/dist/slices/run-cost/plan-use.js +169 -0
  428. package/dist/slices/run-cost/pricing.js +3 -3
  429. package/dist/slices/run-cost/week.js +32 -4
  430. package/dist/slices/schedules/agenda.js +11 -2
  431. package/dist/slices/schedules/by-date.js +42 -0
  432. package/dist/slices/schedules/calendar.js +5 -0
  433. package/dist/slices/schedules/design-agenda.js +36 -0
  434. package/dist/slices/schedules/design-check.js +41 -0
  435. package/dist/slices/schedules/design-run.js +126 -0
  436. package/dist/slices/schedules/next-run.js +62 -0
  437. package/dist/slices/schedules/prepare.js +18 -2
  438. package/dist/slices/schedules/repo.js +24 -16
  439. package/dist/slices/schedules/rescore.js +35 -0
  440. package/dist/slices/schedules/scheduler.js +13 -3
  441. package/dist/slices/schedules/schema.js +54 -3
  442. package/dist/slices/schedules/score.js +107 -0
  443. package/dist/slices/schedules/service.js +57 -24
  444. package/dist/slices/schedules/topic-list.js +15 -0
  445. package/dist/slices/schedules/topic-scores.js +104 -0
  446. package/dist/slices/schedules/topics.js +48 -36
  447. package/dist/slices/settings/cli-paths.js +4 -6
  448. package/dist/slices/settings/first-run.js +2 -11
  449. package/dist/slices/settings/model-reach.js +3 -17
  450. package/dist/slices/settings/model.js +10 -0
  451. package/dist/slices/settings/playback.js +33 -22
  452. package/dist/slices/settings/probe-read.js +16 -0
  453. package/dist/slices/settings/repo.js +33 -0
  454. package/dist/slices/settings/tutorial.js +2 -2
  455. package/dist/slices/settings/voices.js +4 -12
  456. package/dist/slices/shorts/pick.js +2 -15
  457. package/dist/slices/shorts/prompts.js +2 -13
  458. package/dist/slices/shorts/render.js +3 -0
  459. package/dist/slices/social/channel.js +44 -0
  460. package/dist/slices/social/defaults.js +17 -0
  461. package/dist/slices/social/due.js +36 -0
  462. package/dist/slices/social/extension.js +18 -0
  463. package/dist/slices/social/fit.js +115 -0
  464. package/dist/slices/social/limits.js +186 -0
  465. package/dist/slices/social/links.js +21 -0
  466. package/dist/slices/social/media.js +131 -0
  467. package/dist/slices/social/model.js +75 -0
  468. package/dist/slices/social/notice.js +19 -0
  469. package/dist/slices/social/pack.js +176 -0
  470. package/dist/slices/social/post-prompt.js +55 -0
  471. package/dist/slices/social/queue.js +31 -0
  472. package/dist/slices/social/repo.js +133 -0
  473. package/dist/slices/social/reshape.js +97 -0
  474. package/dist/slices/social/rules.js +5 -0
  475. package/dist/slices/social/schema.js +70 -0
  476. package/dist/slices/social/serve.js +51 -0
  477. package/dist/slices/social/service.js +128 -0
  478. package/dist/slices/social/settings.js +21 -0
  479. package/dist/slices/social/sources.js +114 -0
  480. package/dist/slices/social/state.js +110 -0
  481. package/dist/slices/social/text.js +149 -0
  482. package/dist/slices/storage/arrange.js +3 -1
  483. package/dist/slices/storage/assets.js +8 -10
  484. package/dist/slices/storage/backup-designs.js +74 -0
  485. package/dist/slices/storage/backup-export.js +8 -3
  486. package/dist/slices/storage/backup-format.js +10 -0
  487. package/dist/slices/storage/backup-import.js +20 -2
  488. package/dist/slices/storage/delete-project.js +6 -5
  489. package/dist/slices/storage/files-location.js +6 -8
  490. package/dist/slices/storage/layout.js +13 -2
  491. package/dist/slices/storage/model.js +3 -0
  492. package/dist/slices/storage/portable.js +16 -11
  493. package/dist/slices/storage/schema.js +2 -0
  494. package/dist/slices/storage/staging-refs.js +2 -1
  495. package/dist/slices/storage/staging.js +6 -9
  496. package/dist/slices/storage/trim.js +10 -3
  497. package/dist/slices/studio/calendar.js +4 -1
  498. package/dist/slices/studio/pick.js +2 -11
  499. package/dist/slices/studio/plan.js +6 -15
  500. package/dist/slices/studio/queue.js +3 -14
  501. package/dist/slices/studio/releases.js +26 -0
  502. package/dist/slices/studio/settings.js +14 -51
  503. package/dist/slices/studio/videos.js +12 -0
  504. package/dist/slices/style-preview/render.js +34 -13
  505. package/dist/slices/style-preview/schema.js +13 -0
  506. package/dist/slices/style-preview/service.js +2 -1
  507. package/dist/slices/style-preview/settings.js +33 -5
  508. package/dist/slices/telemetry/collector-client.js +2 -4
  509. package/dist/slices/telemetry/flush.js +2 -4
  510. package/dist/slices/telemetry/model.js +0 -7
  511. package/dist/slices/telemetry/record.js +10 -3
  512. package/dist/slices/template-studio/anatomy-view.js +118 -0
  513. package/dist/slices/template-studio/anatomy.js +73 -0
  514. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  515. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  516. package/dist/slices/template-studio/bakeoff.js +333 -0
  517. package/dist/slices/template-studio/budget.js +103 -0
  518. package/dist/slices/template-studio/checks.js +273 -0
  519. package/dist/slices/template-studio/document-sample.js +56 -0
  520. package/dist/slices/template-studio/edit.js +15 -0
  521. package/dist/slices/template-studio/for-all.js +161 -0
  522. package/dist/slices/template-studio/free-video.js +31 -0
  523. package/dist/slices/template-studio/free.js +176 -0
  524. package/dist/slices/template-studio/gate.js +40 -0
  525. package/dist/slices/template-studio/hidden.js +20 -0
  526. package/dist/slices/template-studio/jobs.js +79 -0
  527. package/dist/slices/template-studio/loudness.js +111 -0
  528. package/dist/slices/template-studio/make.js +40 -0
  529. package/dist/slices/template-studio/model.js +54 -0
  530. package/dist/slices/template-studio/pack-import.js +253 -0
  531. package/dist/slices/template-studio/pack-samples.js +60 -0
  532. package/dist/slices/template-studio/pack.js +205 -0
  533. package/dist/slices/template-studio/paid-view.js +66 -0
  534. package/dist/slices/template-studio/paid.js +348 -0
  535. package/dist/slices/template-studio/quote.js +148 -0
  536. package/dist/slices/template-studio/ready.js +104 -0
  537. package/dist/slices/template-studio/repo.js +134 -0
  538. package/dist/slices/template-studio/reviews-path.js +58 -0
  539. package/dist/slices/template-studio/runner.js +84 -0
  540. package/dist/slices/template-studio/save.js +236 -0
  541. package/dist/slices/template-studio/say-it-as.js +248 -0
  542. package/dist/slices/template-studio/schedule-gate.js +34 -0
  543. package/dist/slices/template-studio/schedule-price.js +30 -0
  544. package/dist/slices/template-studio/schedule-sample.js +87 -0
  545. package/dist/slices/template-studio/schema.js +218 -0
  546. package/dist/slices/template-studio/scope.js +124 -0
  547. package/dist/slices/template-studio/sections.js +176 -0
  548. package/dist/slices/template-studio/service.js +173 -0
  549. package/dist/slices/template-studio/status.js +174 -0
  550. package/dist/slices/template-studio/topic.js +223 -0
  551. package/dist/slices/trash/designs.js +76 -0
  552. package/dist/slices/trash/model.js +13 -4
  553. package/dist/slices/trash/service.js +52 -2
  554. package/dist/slices/tutorials/library.js +3 -2
  555. package/dist/slices/video/ass-text.js +17 -0
  556. package/dist/slices/video/audio-export.js +2 -64
  557. package/dist/slices/video/encoding.js +160 -0
  558. package/dist/slices/video/ffmpeg.js +23 -21
  559. package/dist/slices/video/figure-card.js +1 -15
  560. package/dist/slices/video/slideshow.js +43 -18
  561. package/dist/slices/voices/palette.js +0 -3
  562. package/dist/slices/youtube/answer.js +2 -15
  563. package/dist/slices/youtube/edits-repo.js +2 -11
  564. package/dist/tutorials/Agents-MCP.md +2 -1
  565. package/dist/tutorials/Assistant.md +42 -0
  566. package/dist/tutorials/Channels.md +2 -0
  567. package/dist/tutorials/Home-and-Projects.md +19 -0
  568. package/dist/tutorials/Install.md +1 -0
  569. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  570. package/dist/tutorials/Models.md +33 -1
  571. package/dist/tutorials/Notifications.md +6 -0
  572. package/dist/tutorials/Play-Images.md +4 -0
  573. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  574. package/dist/tutorials/Prompts.md +10 -6
  575. package/dist/tutorials/Remote-Access.md +63 -0
  576. package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
  577. package/dist/tutorials/Schedules.md +21 -1
  578. package/dist/tutorials/Settings-Reference.md +50 -8
  579. package/dist/tutorials/Template-Studio.md +125 -0
  580. package/dist/tutorials/Templates.md +3 -0
  581. package/dist/tutorials/_Sidebar.md +5 -0
  582. package/dist/updater/work-in-progress.js +42 -12
  583. package/dist/web/assets/index-CkAI_2E7.css +1 -0
  584. package/dist/web/assets/index-DcD0cdfo.js +334 -0
  585. package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
  586. package/dist/web/index.html +2 -2
  587. package/package.json +5 -1
  588. package/dist/slices/article/provided-entries.js +0 -66
  589. package/dist/slices/article/run.js +0 -145
  590. package/dist/slices/images/run.js +0 -234
  591. package/dist/slices/narration/legacy-plan.js +0 -58
  592. package/dist/slices/narration/run.js +0 -270
  593. package/dist/slices/rebuild/runtime-actions.js +0 -90
  594. package/dist/slices/research/run.js +0 -224
  595. package/dist/slices/subtitles/prepare.js +0 -194
  596. package/dist/slices/subtitles/transcript.js +0 -34
  597. package/dist/slices/thumbnail/run.js +0 -219
  598. package/dist/slices/video/audio-inputs.js +0 -33
  599. package/dist/slices/video/reuse-audio.js +0 -62
  600. package/dist/slices/video/run.js +0 -78
  601. package/dist/slices/video/write-export.js +0 -158
  602. package/dist/slices/video/write-subtitles.js +0 -59
  603. package/dist/web/assets/index-BadsoORk.css +0 -1
  604. package/dist/web/assets/index-D8LqNsCl.js +0 -326
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  import { catalogueSchema } from "../../catalog/schema.js";
3
3
  import { stageKinds } from "../../kernel/pipeline.js";
4
4
  import { estimateRequests } from "../estimate/index.js";
5
+ import { assetPathOrUndefined } from "../revisions/mutation-assets.js";
5
6
  import { requestHash } from "../revisions/mutation-request.js";
6
7
  import { previewDetails } from "./preview-details.js";
7
8
  import { hasSubmittedRequest, requiresNewSubmission, retainedPreviewPlan, } from "./preview-retained.js";
@@ -99,23 +100,17 @@ export function planPreview(deps, view, catalogue, selection, id) {
99
100
  });
100
101
  for (const row of view.pieces)
101
102
  if (row.selected && selected.has(row.key) && row.assetId !== null) {
102
- const asset = deps.db
103
- .prepare("SELECT path FROM project_assets WHERE id=? AND project_id=?")
104
- .get(row.assetId, view.revision.projectId);
105
103
  relevantAssets.set(row.assetId, {
106
104
  id: row.assetId,
107
- path: String(asset?.path ?? ""),
105
+ path: assetPathOrUndefined(deps, view.revision.projectId, row.assetId) ?? "",
108
106
  available: row.available,
109
107
  });
110
108
  }
111
109
  for (const recipe of recipes)
112
110
  if (recipe.input.kind === "provided" && recipe.input.assetId !== null) {
113
- const asset = deps.db
114
- .prepare("SELECT path FROM project_assets WHERE id=? AND project_id=?")
115
- .get(recipe.input.assetId, view.revision.projectId);
116
111
  relevantAssets.set(recipe.input.assetId, {
117
112
  id: recipe.input.assetId,
118
- path: String(asset?.path ?? ""),
113
+ path: assetPathOrUndefined(deps, view.revision.projectId, recipe.input.assetId) ?? "",
119
114
  available: !recipe.unresolved,
120
115
  });
121
116
  }
@@ -4,6 +4,7 @@ import { audioRecipes } from "./recipe-audio.js";
4
4
  import { documentRecipes } from "./recipe-document.js";
5
5
  import { editPlan } from "./recipe-edit.js";
6
6
  import { exportRecipes } from "./recipe-exports.js";
7
+ import { fillPrompt } from "./recipe-keywords.js";
7
8
  import { linePlan } from "./recipe-lines.js";
8
9
  import { masterPlan } from "./recipe-loudness.js";
9
10
  import { resourceIdentity } from "./recipe-model.js";
@@ -11,6 +12,7 @@ import { imageReference, referenceRecipe } from "./recipe-reference.js";
11
12
  import { withReviews } from "./recipe-reviews.js";
12
13
  import { imageScenes, thumbnailScenes } from "./recipe-scenes.js";
13
14
  import { shortsRecipes } from "./recipe-shorts.js";
15
+ import { socialRecipes } from "./recipe-social.js";
14
16
  import { unsoftenedTimelines } from "./recipe-softening.js";
15
17
  import { textRecipes } from "./recipe-text.js";
16
18
  import { thumbnailRecipes, visualAssets, visualRecipes } from "./recipe-visual.js";
@@ -50,8 +52,10 @@ export function buildRecipes(context) {
50
52
  return built;
51
53
  }
52
54
  const descriptionKey = "youtube:description";
53
- function buildEach(context) {
54
- const text = textRecipes(context);
55
+ function buildEach(plain) {
56
+ const text = textRecipes(plain);
57
+ // Every later step fills its prompts' keywords from what the text steps write.
58
+ const context = { ...plain, keywords: text.keywords };
55
59
  const audio = audioRecipes(context, text);
56
60
  const exports = exportRecipes(context, audio, () => unsoftenedTimelines(context, text));
57
61
  const captions = exports.find((value) => value.key === "subtitles:files");
@@ -72,11 +76,12 @@ function buildEach(context) {
72
76
  ...audio.recipes,
73
77
  ...exports,
74
78
  ...youtube,
79
+ ...socialRecipes(context, text),
75
80
  ...(reference === undefined ? [] : [reference]),
76
81
  ...shortsRecipes(context, exports, drawnFrom, audio.cards ?? [], masterPlan(context, audio.levels, "video"), appearance),
77
82
  ...thumbnail,
78
83
  ...documentRecipes(context, text, thumbnail),
79
- ...visualAssets(context, visualRecipes(context.config, context.content, audio.mediaFingerprint, captions?.fingerprint ?? null, (images) => editPlan(context, exports, youtube, images, audio.cards ?? []), drawnFrom, timing === undefined ? null : resourceIdentity(context, timing), withLines(masterPlan(context, audio.levels, "video"), linePlan(context, timing)), scenes, appearance)),
84
+ ...visualAssets(context, visualRecipes(context.config, context.content, audio.mediaFingerprint, captions?.fingerprint ?? null, (images) => editPlan(context, exports, youtube, images, audio.cards ?? []), drawnFrom, timing === undefined ? null : resourceIdentity(context, timing), withLines(masterPlan(context, audio.levels, "video"), linePlan(context, timing)), scenes, appearance, (text) => fillPrompt(context, text, "image"))),
80
85
  ...(scenes === undefined ? [] : [scenes.recipe]),
81
86
  ...(thumbnailScened === undefined ? [] : [thumbnailScened.recipe]),
82
87
  ...(appearance === undefined ? [] : [appearance.recipe]),
@@ -2,7 +2,7 @@ import { usesDescribedNarration, usesFigureCards, usesShorts } from "../admissio
2
2
  import { describedBlocks, narrationBlocks, spokenNarration, } from "../narration/blocks.js";
3
3
  import { describeMessages, spokenPassage } from "../narration/describe.js";
4
4
  import { recipe } from "./recipe-model.js";
5
- import { llmInput, matchingText, renderedPrompt } from "./recipe-text.js";
5
+ import { llmInput, matchingText, promptOf } from "./recipe-text.js";
6
6
  // "Describe tables and figures in the narration": one text-model step per table, figure,
7
7
  // diagram, equation or code block (`narration:describe:<n>`, in reading order), whose answer
8
8
  // is said in the block's place. The narration is chunked, prepared and spoken from that
@@ -19,7 +19,7 @@ export function describing(context) {
19
19
  // the model and the style guidance. The future step's template, so a change to any of them
20
20
  // reaches the narration still to be written.
21
21
  export function describeTemplate(context) {
22
- const input = llmInput(context, describeMessages({ kind: "table", index: 1, source: "", section: null }, renderedPrompt(context, "narration"), context.config.language));
22
+ const input = llmInput(context, describeMessages({ kind: "table", index: 1, source: "", section: null }, promptOf(context, "narration").text, context.config.language));
23
23
  return [
24
24
  "narration-description-v1",
25
25
  JSON.parse(JSON.stringify({ ...input, thinkingConfig: null })),
@@ -43,7 +43,7 @@ export function describedText(context, markdown, dependsOn, offset = 0) {
43
43
  code: config.audio?.skipCode === true ? "skip" : "describe",
44
44
  language: config.language,
45
45
  });
46
- const style = renderedPrompt(context, "narration");
46
+ const style = promptOf(context, "narration").text;
47
47
  const passages = new Map();
48
48
  const cards = [];
49
49
  const recipes = describedBlocks(blocks).map((block) => {
@@ -0,0 +1,58 @@
1
+ import { withLanguage } from "../../kernel/ports/languages.js";
2
+ import { subjectOf } from "../admission/model.js";
3
+ import { digestMessages, digestNames } from "../keywords/digest.js";
4
+ import { keywordUsesOf } from "../keywords/rules.js";
5
+ import { recipe, selectedReference, } from "./recipe-model.js";
6
+ import { llmInput, llmInputFingerprint } from "./recipe-text.js";
7
+ import { keywordBodies } from "./recipe-validation.js";
8
+ export const digestKey = "article:digest";
9
+ // The keyword digest step, only while a prompt the version runs uses a digest keyword, and the
10
+ // digest it wrote once it answers this plan's request. It reads the article the pictures are
11
+ // drawn from, so editing the article by hand keeps it with them.
12
+ export function keywordDigest(context, text) {
13
+ const { config } = context;
14
+ if (keywordUsesOf(keywordBodies(config, context.content)).digest === undefined)
15
+ return undefined;
16
+ const messages = withLanguage(digestMessages({ title: subjectOf(config), article: text.pictureText ?? "" }), config.language);
17
+ const value = recipe(context, digestKey, "article", text.pictureText === null
18
+ ? {
19
+ kind: "deferred",
20
+ version: 1,
21
+ operation: "keyword-digest",
22
+ template: [llmInputFingerprint(context, messages), text.article.fingerprint],
23
+ }
24
+ : llmInput(context, messages), [text.article.key]);
25
+ return {
26
+ recipe: value,
27
+ producer: {
28
+ key: value.key,
29
+ fingerprint: value.fingerprint,
30
+ names: digestNames,
31
+ values: text.pictureText === null ? undefined : savedDigest(context, value),
32
+ },
33
+ };
34
+ }
35
+ // The digest saved for this request, by either fingerprint (see `savedScenes`).
36
+ function savedDigest(context, value) {
37
+ const logical = context.catalogue === undefined || value.input.kind !== "llm"
38
+ ? value.fingerprint
39
+ : recipe({ content: context.content }, value.key, value.stage, { ...value.input, thinkingConfig: null }, value.dependsOn).fingerprint;
40
+ const piece = context.manifest.pieces.find((row) => row.key === value.key &&
41
+ selectedReference(row) &&
42
+ row.piece.state === "done" &&
43
+ (row.fingerprint === value.fingerprint || row.fingerprint === logical));
44
+ if (piece?.piece.payload == null)
45
+ return undefined;
46
+ const payload = JSON.parse(piece.piece.payload);
47
+ const digest = payload.digest;
48
+ if (typeof digest !== "object" || digest === null)
49
+ return undefined;
50
+ const values = {};
51
+ for (const name of digestNames) {
52
+ const one = digest[name];
53
+ if (typeof one !== "string")
54
+ return undefined;
55
+ values[name] = one;
56
+ }
57
+ return values;
58
+ }
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  import { messageRoles, thinkingModes } from "../../kernel/ports/llm.js";
3
3
  import { llmDocumentsSchema } from "../../kernel/ports/llm-documents.js";
4
4
  import { describedKinds } from "../narration/blocks.js";
5
+ import { socialPlatforms } from "../social/model.js";
5
6
  import { deferredOperations, localOperations } from "./recipe-model.js";
6
7
  const value = z.lazy(() => z.union([
7
8
  z.null(),
@@ -59,6 +60,15 @@ export const recipeInputSchema = z.discriminatedUnion("kind", [
59
60
  })
60
61
  .strict()
61
62
  .optional(),
63
+ // A post for another site (`recipe-social.ts`): the limit its answer must keep.
64
+ social: z
65
+ .object({
66
+ platform: z.enum(socialPlatforms),
67
+ max: z.number().int().positive(),
68
+ unit: z.enum(["weighted", "graphemes", "characters"]),
69
+ })
70
+ .strict()
71
+ .optional(),
62
72
  })
63
73
  .strict(),
64
74
  z
@@ -0,0 +1,71 @@
1
+ import { subjectOf } from "../admission/model.js";
2
+ import { detectSlots } from "../admission/substitute.js";
3
+ import { splitEndMatter } from "../article/split.js";
4
+ import { fillKeywords, mentionsKeyword } from "../keywords/fill.js";
5
+ import { placeOfKey } from "../keywords/rules.js";
6
+ import { keywordOf } from "../keywords/specs.js";
7
+ import { factValues } from "../keywords/values.js";
8
+ import { topHeadings } from "../video/chapters.js";
9
+ import { usesScriptPrompt } from "../voices/model.js";
10
+ export const articleKey = "article:body";
11
+ // The article as this version has it, or null while it is still to be written.
12
+ export function articleMarkdownOf(context) {
13
+ const { config, content, resolved } = context;
14
+ return config.sources.article !== "generate" || content.articleEdited === true
15
+ ? (content.articleMarkdown ?? config.provided.article ?? "")
16
+ : resolved.articleMarkdown;
17
+ }
18
+ // The article's chapter titles, once it is written.
19
+ export function chaptersProducer(markdown, article) {
20
+ return {
21
+ key: article.key,
22
+ fingerprint: article.fingerprint,
23
+ names: ["Slopify_Chapters"],
24
+ values: markdown === null
25
+ ? undefined
26
+ : {
27
+ Slopify_Chapters: topHeadings(splitEndMatter(markdown).body)
28
+ .map((heading) => heading.title)
29
+ .join("\n"),
30
+ },
31
+ };
32
+ }
33
+ // A prompt with its keywords filled. A text without a `Slopify_` keyword is returned as the very
34
+ // string it was, so a project that uses none plans the requests it always did.
35
+ export function fillPrompt(context, text, place) {
36
+ if (place === undefined || !mentionsKeyword(text))
37
+ return { text, waits: [], dependsOn: [] };
38
+ const producers = context.keywords?.producers ?? [
39
+ chaptersProducer(articleMarkdownOf(context), { key: articleKey, fingerprint: "" }),
40
+ ];
41
+ const values = {
42
+ ...factValues(context.config.keywordFacts, subjectOf(context.config), place),
43
+ };
44
+ const used = new Set();
45
+ for (const producer of producers)
46
+ for (const name of producer.names)
47
+ if (usesName(text, name, place)) {
48
+ used.add(producer.key);
49
+ const value = producer.values?.[name];
50
+ if (value !== undefined)
51
+ values[name] = value;
52
+ }
53
+ const filled = fillKeywords(text, place, values);
54
+ const dependsOn = producers.filter((one) => used.has(one.key)).map((one) => one.key);
55
+ if (filled.ok)
56
+ return { text: filled.text, waits: [], dependsOn };
57
+ return {
58
+ text,
59
+ waits: producers
60
+ .filter((one) => one.names.some((name) => filled.waiting.includes(name)))
61
+ .map((one) => ({ key: one.key, fingerprint: one.fingerprint })),
62
+ dependsOn,
63
+ };
64
+ }
65
+ // A picked prompt (`library/slots.ts` names its key), rendered and filled.
66
+ export function filledPrompt(context, key, rendered) {
67
+ return fillPrompt(context, rendered, placeOfKey(key, { script: usesScriptPrompt(context.config) }));
68
+ }
69
+ function usesName(text, name, place) {
70
+ return (detectSlots(text).names.includes(name) && (keywordOf(name)?.places.includes(place) ?? false));
71
+ }
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { internalError } from "../../kernel/errors.js";
2
3
  import { referenceKey } from "../admission/model.js";
3
4
  import { buildRecipes } from "./recipe-build.js";
4
5
  export function baselineFingerprints(project, outputs, pieces, content, stages = []) {
@@ -92,6 +93,9 @@ export function legacyOutputWorkKey(output, config) {
92
93
  case "render_params":
93
94
  case "instructions":
94
95
  return `${output.stageKind}:${output.role}`;
96
+ // Only ever made by a revision.
97
+ case "social_post":
98
+ return `social:post:${output.meta.platform ?? "x"}`;
95
99
  }
96
100
  }
97
101
  export function legacyPieceKey(piece, stageKind, outputs = []) {
@@ -145,7 +149,7 @@ export function legacyImageOutput(piece, outputs) {
145
149
  row.meta.promptName === undefined ||
146
150
  row.meta.promptName === promptName)));
147
151
  if (matches.length > 1)
148
- throw new Error(`Slopify hit an internal error (an image from an older Slopify version, ${piece.id}, matches multiple outputs). Try again; if it happens again, use Download diagnostics in Settings and report it.`);
152
+ throw new Error(internalError(`an image from an older Slopify version, ${piece.id}, matches multiple outputs`));
149
153
  return matches[0];
150
154
  }
151
155
  const legacyImageMetadata = z
@@ -51,6 +51,10 @@ export const deferredOperations = [
51
51
  "thumbnail-scenes",
52
52
  // How the subject and characters look (`recipe-appearance.ts`), looked up on the web.
53
53
  "image-appearance",
54
+ // The keyword digest (`recipe-digest.ts`), written once the article is.
55
+ "keyword-digest",
56
+ // A post for another site written by a post prompt (`recipe-social.ts`), once the article is.
57
+ "social-post",
54
58
  ];
55
59
  export function recipe(context, key, stage, input, dependsOn = [], options = {}) {
56
60
  const requestFingerprint = input.kind === "tts" && input.dialogue !== undefined
@@ -1,3 +1,4 @@
1
+ import { internalError } from "../../kernel/errors.js";
1
2
  import { recipe, resourceIdentity, } from "./recipe-model.js";
2
3
  export function narrationFileRecipe(context, segment, parts) {
3
4
  const key = `narration:files:${segment}`;
@@ -26,7 +27,7 @@ export function narrationFileRecipe(context, segment, parts) {
26
27
  ];
27
28
  if (part.input.kind === "provided")
28
29
  return [resourceIdentity(context, part), part.input.semantic, null];
29
- throw new Error("Slopify hit an internal error (a narration chunk has an unexpected source). Try again; if it happens again, use Download diagnostics in Settings and report it.");
30
+ throw new Error(internalError("a narration chunk has an unexpected source"));
30
31
  }),
31
32
  },
32
33
  }, dependencies, { unresolved: parts.length === 0 });
@@ -2,9 +2,9 @@ import { z } from "zod";
2
2
  import { preparationMessages, validatePreparation, } from "../narration/preparation.js";
3
3
  import { prepareRequests } from "../narration/steering.js";
4
4
  import { recipe, selectedReference, } from "./recipe-model.js";
5
- import { llmInput, renderedPrompt } from "./recipe-text.js";
5
+ import { llmInput, promptOf } from "./recipe-text.js";
6
6
  export function preparationTemplate(context) {
7
- const input = llmInput(context, preparationMessages(renderedPrompt(context, "narration"), "", [], context.config.language));
7
+ const input = llmInput(context, preparationMessages(promptOf(context, "narration").text, "", [], context.config.language));
8
8
  return [
9
9
  "inworld-tts-2",
10
10
  1,
@@ -29,7 +29,7 @@ export function preparationForGroup(context, logicalKey, source, segment, depend
29
29
  // A delivery note for this chunk alone (Edit project → Narration), after the project's prompt.
30
30
  const override = context.content.narrationOverrides[logicalKey];
31
31
  const note = override?.kind === "text" ? override.direction : undefined;
32
- const prompt = renderedPrompt(context, "narration");
32
+ const prompt = promptOf(context, "narration").text;
33
33
  const preparation = recipe(context, `narration:prepare:${segment}:${logicalKey}`, "audio", {
34
34
  ...llmInput(context, preparationMessages(note === undefined ? prompt : `${prompt}\n\n${note}`, source, aliases, context.config.language, speaker)),
35
35
  preparation: { format: "inworld-tts-2", version: 1, source, logicalKey, segment },
@@ -1,9 +1,10 @@
1
1
  import { referenceKey, subjectOf } from "../admission/model.js";
2
2
  import { usesReference } from "../admission/rules.js";
3
+ import { withoutSlopifyScene } from "../images/scenes.js";
3
4
  import { lookWait, withLooks } from "./recipe-appearance.js";
4
5
  import { castFor } from "./recipe-cast.js";
5
6
  import { recipe, selectedAsset, } from "./recipe-model.js";
6
- import { renderedPrompt } from "./recipe-text.js";
7
+ import { promptOf } from "./recipe-text.js";
7
8
  // The establishing image (`RunDraft.reference`): one step of the Images stage, made before
8
9
  // every other image of the run and never part of the video. Made from its library prompt, whose
9
10
  // keywords are filled like every other image prompt's, or the file the user uploaded.
@@ -30,8 +31,10 @@ appearance) {
30
31
  assetId: content.provided.reference ?? null,
31
32
  semantic: "reference",
32
33
  }, [], { unresolved: content.provided.reference === undefined });
33
- const written = renderedPrompt(context, referencePromptKey);
34
- const prompt = withLooks(written, appearance);
34
+ const filled = promptOf(context, referencePromptKey);
35
+ // The establishing image has no scene of its own.
36
+ const written = withoutSlopifyScene(filled.text);
37
+ const prompt = filled.waits.length > 0 ? null : withLooks(written, appearance);
35
38
  if (prompt === null) {
36
39
  const wait = lookWait(written, appearance);
37
40
  return recipe(context, referenceKey, "images", {
@@ -45,8 +48,9 @@ appearance) {
45
48
  config.images?.model ?? null,
46
49
  config.format,
47
50
  ...(config.images?.thinking === undefined ? [] : [config.images.thinking]),
51
+ ...filled.waits.map((one) => one.fingerprint),
48
52
  ],
49
- }, wait.dependsOn);
53
+ }, [...wait.dependsOn, ...filled.waits.map((one) => one.key)]);
50
54
  }
51
55
  return recipe(context, referenceKey, "images", {
52
56
  kind: "image",
@@ -1,7 +1,7 @@
1
- import { narrationItemKey, reviewedByAgent, reviewKey, reviewModeOf, reviewPromptKey, reviewRetriesOf, } from "../reviews/model.js";
1
+ import { narrationItemKey, promiseItemKey, reviewedByAgent, reviewKey, reviewModeOf, reviewPromptKey, reviewRetriesOf, } from "../reviews/model.js";
2
2
  import { activeReviewStages } from "../reviews/rules.js";
3
3
  import { recipe, resourceIdentity, } from "./recipe-model.js";
4
- import { renderedPrompt } from "./recipe-text.js";
4
+ import { promptOf } from "./recipe-text.js";
5
5
  // A review is a step of its own, `review:<item>`, planned like any other: it waits for the
6
6
  // item, its fingerprint follows the item's output, the reviewer and the prompt, and it is
7
7
  // kept and reused while those stay the same. What depends on a reviewed item waits for its
@@ -13,16 +13,23 @@ export function withReviews(context, recipes) {
13
13
  if (settings === undefined || stages.length === 0)
14
14
  return recipes;
15
15
  const byKey = new Map(recipes.map((row) => [row.key, row]));
16
- const items = stages.flatMap((stage) => reviewedItems(stage, recipes, byKey));
16
+ const all = stages.flatMap((stage) => reviewedItems(stage, recipes, byKey));
17
+ // A template studio sample shows how each stage's review answers on its first item only.
18
+ const items = context.config.sample === undefined
19
+ ? all
20
+ : all.filter((item, index) => all.findIndex((one) => one.stage === item.stage) === index);
17
21
  const byAgent = reviewedByAgent(settings);
18
22
  const reviews = items.map((item) => {
19
23
  const inputs = item.inputs.map((input) => [input.key, resourceIdentity(context, input)]);
20
24
  // Blank means the stage's built-in prompt; the step fills it in, so a new built-in
21
25
  // wording ships as a new operation version rather than a silent change here.
22
- const prompt = settings.stages[item.stage]?.prompt?.trim()
23
- ? renderedPrompt(context, reviewPromptKey(item.stage))
24
- : "";
25
- const dependsOn = item.inputs.map((input) => input.key);
26
+ const filled = settings.stages[item.stage]?.prompt?.trim()
27
+ ? promptOf(context, reviewPromptKey(item.stage))
28
+ : undefined;
29
+ const prompt = filled?.text ?? "";
30
+ const dependsOn = [
31
+ ...new Set([...item.inputs.map((input) => input.key), ...(filled?.dependsOn ?? [])]),
32
+ ];
26
33
  // An external agent answers through MCP: no provider, no charge, nothing to be ready.
27
34
  if (byAgent)
28
35
  return recipe(context, reviewKey(item.itemKey), stageOf(item.stage), {
@@ -79,6 +86,18 @@ function reviewedItems(stage, recipes, byKey) {
79
86
  ? []
80
87
  : [one(article.key, [article], [article.key])];
81
88
  }
89
+ // The opening read against the title and thumbnail before the paid narration: everything
90
+ // that reads the article waits for it. A thumbnail brief a model writes is read too; the
91
+ // drawn thumbnail isn't, since it is made after the narration.
92
+ case "promise": {
93
+ const article = byKey.get("article:body");
94
+ if (article === undefined || article.kind !== "provider")
95
+ return [];
96
+ const brief = byKey.get("thumbnail:prompt");
97
+ return [
98
+ one(promiseItemKey, brief === undefined ? [article] : [article, brief], [article.key]),
99
+ ];
100
+ }
82
101
  case "images": {
83
102
  const reference = byKey.get("reference:image");
84
103
  return recipes
@@ -114,6 +133,7 @@ function reviewedItems(stage, recipes, byKey) {
114
133
  function stageOf(stage) {
115
134
  switch (stage) {
116
135
  case "article":
136
+ case "promise":
117
137
  return "article";
118
138
  case "images":
119
139
  return "images";
@@ -3,11 +3,12 @@ import { sourceOf } from "../admission/model.js";
3
3
  import { allowedSources, articleOffFields, edgeSilenceSecondsProblem, imageSecondsProblem, normaliseDraft, silenceGapSecondsMax, voicesFields, zoomPercentProblem, } from "../admission/rules.js";
4
4
  import { shortModeFields } from "../admission/short-mode.js";
5
5
  import { render } from "../admission/substitute.js";
6
+ import { keywordUsesOf } from "../keywords/rules.js";
6
7
  import { effectiveClips, pickedShortsOf, rangeProblem } from "../shorts/clips.js";
7
8
  import { buildRecipes } from "./recipe-build.js";
8
9
  import { selectedReference } from "./recipe-model.js";
9
10
  import { shortsPickKey } from "./recipe-shorts.js";
10
- import { validateRecipeInputs } from "./recipe-validation.js";
11
+ import { keywordBodies, validateRecipeInputs } from "./recipe-validation.js";
11
12
  import { finishedShorts, keepChosen, movedShorts, withKeep } from "./shorts-keep-save.js";
12
13
  // A range set by hand for a short is checked against the pick it was set on, while that is
13
14
  // still the project's pick; one left from an earlier pick no longer applies and is ignored.
@@ -47,10 +48,12 @@ export function normalizeArticleIntent(base, edit) {
47
48
  };
48
49
  }
49
50
  // The steps that are made from the article: each image's scene, the looks, the thumbnails'
50
- // scenes, the thumbnail prompt and an intro or outro the AI model writes from it.
51
+ // scenes, the keyword digest, the thumbnail prompt and an intro or outro the AI model writes
52
+ // from it.
51
53
  export const pictureWorkKeys = [
52
54
  "images:scenes",
53
55
  "thumbnail:scenes",
56
+ "article:digest",
54
57
  "images:appearance",
55
58
  "thumbnail:prompt",
56
59
  "entry:intro:text",
@@ -321,7 +324,7 @@ export function validateRevisionEdit(config, content) {
321
324
  if (config.sources.article === "provide" &&
322
325
  !(content.articleMarkdown ?? config.provided.article)?.trim())
323
326
  fields.push({ field: "provided.article", message: "Paste the article." });
324
- fields.push(...articleOffFields(config));
327
+ fields.push(...articleOffFields(config, keywordUsesOf(keywordBodies(config, content))));
325
328
  if (config.sources.audio === "off" &&
326
329
  config.subtitles !== undefined &&
327
330
  config.subtitles.mode !== "off")
@@ -1,28 +1,30 @@
1
1
  import { z } from "zod";
2
2
  import { withLanguage } from "../../kernel/ports/languages.js";
3
3
  import { subjectOf, thumbnailCountOf } from "../admission/model.js";
4
- import { pictureKind, sceneMessages, usesScene } from "../images/scenes.js";
4
+ import { pictureKind, sceneMessages, usesScene, usesSlopifyScene, wantsSceneDetails, } from "../images/scenes.js";
5
5
  import { recipe, selectedReference, } from "./recipe-model.js";
6
6
  import { llmInput, llmInputFingerprint } from "./recipe-text.js";
7
7
  export const imageScenesKey = "images:scenes";
8
8
  export const thumbnailScenesKey = "thumbnail:scenes";
9
9
  // With Scenes from the article on, a thumbnail drawn from a Library prompt whose text asks for
10
- // `{{Scene}}` takes one scene per thumbnail; any other thumbnail is drawn as it always was.
10
+ // `{{Scene}}` takes one scene per thumbnail; so does one whose prompt uses a `Slopify_` scene
11
+ // keyword, switch or no switch. Any other thumbnail is drawn as it always was.
11
12
  export function thumbnailScenes(context, text) {
12
13
  const { config, content } = context;
13
- if (config.sources.images !== "generate" || config.imageScenes !== true)
14
- return undefined;
15
14
  if (config.sources.thumbnail !== "from_prompt")
16
15
  return undefined;
17
16
  const body = content.promptTemplates.thumbnailPrompt ?? config.rendered.thumbnailPrompt;
18
- if (!usesScene(body))
17
+ const legacy = config.sources.images === "generate" && config.imageScenes === true && usesScene(body);
18
+ if (!legacy && !usesSlopifyScene(body))
19
19
  return undefined;
20
20
  const count = thumbnailCountOf(config);
21
+ const details = wantsSceneDetails(body);
21
22
  const messages = withLanguage(sceneMessages({
22
23
  title: subjectOf(config),
23
24
  article: text.pictureText ?? "",
24
25
  pictures: [],
25
26
  thumbnails: count,
27
+ ...(details ? { details } : {}),
26
28
  }), config.language);
27
29
  const value = recipe(context, thumbnailScenesKey, "thumbnail", text.pictureText === null
28
30
  ? {
@@ -32,18 +34,23 @@ export function thumbnailScenes(context, text) {
32
34
  template: [llmInputFingerprint(context, messages), text.article.fingerprint],
33
35
  }
34
36
  : llmInput(context, messages), [text.article.key]);
37
+ const saved = text.pictureText === null ? undefined : savedScenes(context, value, count);
35
38
  return {
36
39
  recipe: value,
37
40
  count,
38
- scenes: text.pictureText === null ? undefined : savedScenes(context, value, count),
41
+ scenes: saved?.scenes,
42
+ ...(saved?.details === undefined ? {} : { details: saved.details }),
43
+ legacy,
39
44
  };
40
45
  }
41
46
  export function imageScenes(context, text) {
42
47
  const { config, content } = context;
43
- if (config.sources.images !== "generate" || config.imageScenes !== true)
48
+ if (config.sources.images !== "generate")
44
49
  return undefined;
50
+ const legacy = config.imageScenes === true;
45
51
  const keys = [];
46
52
  const pictures = [];
53
+ let details = false;
47
54
  for (const key of content.imageOrder) {
48
55
  const image = content.imageDefinitions[key];
49
56
  if (image?.source !== "generate")
@@ -53,6 +60,10 @@ export function imageScenes(context, text) {
53
60
  : (content.promptTemplates[image.templateKey] ?? image.prompt);
54
61
  if (body == null || body.trim() === "")
55
62
  continue;
63
+ // With the switch off a picture takes a scene only for its own `Slopify_` keywords.
64
+ if (!legacy && !usesSlopifyScene(body))
65
+ continue;
66
+ details ||= wantsSceneDetails(body);
56
67
  const at = /^imagePrompts\.(\d+)$/.exec(image.templateKey ?? "")?.[1];
57
68
  const name = at === undefined ? "Image" : (config.imagePrompts[Number(at)]?.name ?? "Image");
58
69
  keys.push(key);
@@ -60,7 +71,12 @@ export function imageScenes(context, text) {
60
71
  }
61
72
  if (keys.length === 0)
62
73
  return undefined;
63
- const messages = withLanguage(sceneMessages({ title: subjectOf(config), article: text.pictureText ?? "", pictures }), config.language);
74
+ const messages = withLanguage(sceneMessages({
75
+ title: subjectOf(config),
76
+ article: text.pictureText ?? "",
77
+ pictures,
78
+ ...(details ? { details } : {}),
79
+ }), config.language);
64
80
  const value = recipe(context, imageScenesKey, "images", text.pictureText === null
65
81
  ? {
66
82
  kind: "deferred",
@@ -69,10 +85,13 @@ export function imageScenes(context, text) {
69
85
  template: [llmInputFingerprint(context, messages), text.article.fingerprint],
70
86
  }
71
87
  : llmInput(context, messages), [text.article.key]);
88
+ const saved = text.pictureText === null ? undefined : savedScenes(context, value, keys.length);
72
89
  return {
73
90
  recipe: value,
74
91
  keys,
75
- scenes: text.pictureText === null ? undefined : savedScenes(context, value, keys.length),
92
+ scenes: saved?.scenes,
93
+ ...(saved?.details === undefined ? {} : { details: saved.details }),
94
+ legacy,
76
95
  };
77
96
  }
78
97
  // The scenes saved for this request. The request carries the model's thinking settings from
@@ -89,7 +108,17 @@ function savedScenes(context, value, count) {
89
108
  if (piece?.piece.payload == null)
90
109
  return undefined;
91
110
  const parsed = z
92
- .object({ scenes: z.array(z.string()) })
111
+ .object({
112
+ scenes: z.array(z.string()),
113
+ details: z
114
+ .array(z.object({ setting: z.string().optional(), mood: z.string().optional() }))
115
+ .optional(),
116
+ })
93
117
  .safeParse(JSON.parse(piece.piece.payload));
94
- return parsed.success && parsed.data.scenes.length === count ? parsed.data.scenes : undefined;
118
+ if (!parsed.success || parsed.data.scenes.length !== count)
119
+ return undefined;
120
+ return {
121
+ scenes: parsed.data.scenes,
122
+ ...(parsed.data.details === undefined ? {} : { details: parsed.data.details }),
123
+ };
95
124
  }
@@ -11,7 +11,7 @@ import { castFor } from "./recipe-cast.js";
11
11
  import { noMaster } from "./recipe-loudness.js";
12
12
  import { recipe, resourceIdentity, selectedAsset, selectedReference, } from "./recipe-model.js";
13
13
  import { castField, imageChoice } from "./recipe-reference.js";
14
- import { llmInput, renderedPrompt } from "./recipe-text.js";
14
+ import { llmInput, promptOf } from "./recipe-text.js";
15
15
  // The Shorts step unfolds as its answers land, the way research's chapters unfold from the
16
16
  // plan. `shorts:pick` reads the word timing, so it waits for `subtitles:timing` and runs
17
17
  // beside the render; until its saved answer matches, one deferred `shorts:future` stands in
@@ -39,6 +39,10 @@ appearance) {
39
39
  const timing = exports.find((value) => value.key === "subtitles:timing");
40
40
  if (!usesShorts(config) || shorts === undefined || timing === undefined)
41
41
  return [];
42
+ const prompt = shorts.prompt?.trim() ? promptOf(context, "shorts") : undefined;
43
+ // A shorts image prompt may name `{{Scene}}` like the video's: the prompts call already writes
44
+ // each image's moment from what the clip says, so that line is left out here.
45
+ const styled = shorts.imagePrompt?.trim() ? promptOf(context, "shortsImage") : undefined;
42
46
  const pickFor = (timingIdentity) => recipe(context, shortsPickKey, "video", {
43
47
  kind: "local",
44
48
  version: 1,
@@ -50,7 +54,7 @@ appearance) {
50
54
  config.llm?.thinking ?? null,
51
55
  // Blank means the built-in prompt; the step fills it in, so a new built-in wording
52
56
  // ships as a new operation version rather than a silent change here.
53
- shorts.prompt?.trim() ? renderedPrompt(context, "shorts") : "",
57
+ prompt?.text ?? "",
54
58
  shorts.count,
55
59
  shorts.minSeconds,
56
60
  shorts.maxSeconds,
@@ -78,7 +82,7 @@ appearance) {
78
82
  ],
79
83
  ]),
80
84
  ],
81
- }, [timing.key],
85
+ }, [...new Set([timing.key, ...(prompt?.dependsOn ?? []), ...(styled?.dependsOn ?? [])])],
82
86
  // A paid LLM call: readiness, the charge warning and the estimate treat it like one.
83
87
  { kind: "provider" });
84
88
  const pick = pickFor(resourceIdentity(context, timing));
@@ -93,11 +97,7 @@ appearance) {
93
97
  selectedReference(row));
94
98
  const picked = savedPicked(context, pick, recorded === undefined ? [] : [pickFor([recorded.fingerprint, asset]).fingerprint]);
95
99
  const images = config.images ?? { provider: "", model: "" };
96
- // A shorts image prompt may name `{{Scene}}` like the video's: the prompts call already writes
97
- // each image's moment from what the clip says, so that line is left out here.
98
- const style = shorts.imagePrompt?.trim()
99
- ? withoutScene(renderedPrompt(context, "shortsImage"))
100
- : defaultShortsImagePrompt;
100
+ const style = styled === undefined ? defaultShortsImagePrompt : withoutScene(styled.text);
101
101
  if (picked === undefined)
102
102
  return [
103
103
  pick,