@gentbajko/slopify 3.25.9 → 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 (603) 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/4.0.0.md +51 -0
  195. package/dist/patch-notes/index.json +6 -0
  196. package/dist/slices/admission/heads.js +2 -1
  197. package/dist/slices/admission/repo.js +2 -1
  198. package/dist/slices/admission/rules.js +43 -8
  199. package/dist/slices/admission/schema.js +8 -0
  200. package/dist/slices/admission/stage-writes.js +0 -3
  201. package/dist/slices/admission/start.js +48 -31
  202. package/dist/slices/admission/substitute.js +7 -4
  203. package/dist/slices/agents/attention-posts.js +31 -0
  204. package/dist/slices/agents/attention-templates.js +33 -0
  205. package/dist/slices/agents/attention.js +10 -1
  206. package/dist/slices/agents/connection.js +2 -6
  207. package/dist/slices/agents/model.js +6 -3
  208. package/dist/slices/agents/repo.js +15 -7
  209. package/dist/slices/article/continuation.js +0 -63
  210. package/dist/slices/article/plain.js +3 -2
  211. package/dist/slices/article/segments.js +2 -62
  212. package/dist/slices/assistant/access.js +1 -0
  213. package/dist/slices/assistant/asks.js +80 -0
  214. package/dist/slices/assistant/cost.js +54 -0
  215. package/dist/slices/assistant/gate.js +222 -0
  216. package/dist/slices/assistant/model.js +79 -0
  217. package/dist/slices/assistant/names.js +23 -0
  218. package/dist/slices/assistant/prompt.js +74 -0
  219. package/dist/slices/assistant/repo.js +74 -0
  220. package/dist/slices/assistant/tokens.js +18 -0
  221. package/dist/slices/assistant/turn.js +342 -0
  222. package/dist/slices/autopilot/buffer.js +76 -0
  223. package/dist/slices/autopilot/model.js +14 -0
  224. package/dist/slices/autopilot/notify.js +27 -0
  225. package/dist/slices/autopilot/repo.js +34 -0
  226. package/dist/slices/autopilot/signals.js +194 -0
  227. package/dist/slices/autopilot/warnings.js +85 -0
  228. package/dist/slices/autopilot/weekly.js +100 -0
  229. package/dist/slices/backups/files.js +2 -6
  230. package/dist/slices/backups/repo.js +3 -15
  231. package/dist/slices/backups/service.js +4 -3
  232. package/dist/slices/channels/cast-images.js +2 -1
  233. package/dist/slices/channels/model.js +0 -1
  234. package/dist/slices/channels/rebrand.js +0 -3
  235. package/dist/slices/channels/repo.js +4 -0
  236. package/dist/slices/channels/schema.js +6 -0
  237. package/dist/slices/channels/service.js +10 -0
  238. package/dist/slices/channels/videos.js +2 -14
  239. package/dist/slices/checkpoints/index.js +0 -1
  240. package/dist/slices/control/providers.js +0 -26
  241. package/dist/slices/designs/artwork.js +66 -0
  242. package/dist/slices/designs/budget.js +23 -0
  243. package/dist/slices/designs/calls.js +124 -0
  244. package/dist/slices/designs/check-context.js +38 -0
  245. package/dist/slices/designs/check-text.js +43 -0
  246. package/dist/slices/designs/fields.js +88 -0
  247. package/dist/slices/designs/frames.js +34 -0
  248. package/dist/slices/designs/gate.js +128 -0
  249. package/dist/slices/designs/layout.js +293 -0
  250. package/dist/slices/designs/model.js +35 -0
  251. package/dist/slices/designs/pdf-job.js +23 -0
  252. package/dist/slices/designs/pdf-pages.js +30 -0
  253. package/dist/slices/designs/pdf-runner.js +68 -0
  254. package/dist/slices/designs/pdf-worker.js +24 -0
  255. package/dist/slices/designs/picture-size.js +79 -0
  256. package/dist/slices/designs/pictures.js +55 -0
  257. package/dist/slices/designs/quote.js +24 -0
  258. package/dist/slices/designs/render-args.js +36 -0
  259. package/dist/slices/designs/render-input.js +59 -0
  260. package/dist/slices/designs/render.js +215 -0
  261. package/dist/slices/designs/repo.js +101 -0
  262. package/dist/slices/designs/requests.js +80 -0
  263. package/dist/slices/designs/runner.js +239 -0
  264. package/dist/slices/designs/sample-repo.js +91 -0
  265. package/dist/slices/designs/sample.js +222 -0
  266. package/dist/slices/designs/schema.js +87 -0
  267. package/dist/slices/designs/sections.js +44 -0
  268. package/dist/slices/designs/service.js +233 -0
  269. package/dist/slices/designs/steps-repo.js +102 -0
  270. package/dist/slices/designs/studio-kind.js +39 -0
  271. package/dist/slices/designs/template-checks.js +44 -0
  272. package/dist/slices/designs/template-make.js +118 -0
  273. package/dist/slices/designs/templates.js +218 -0
  274. package/dist/slices/document/fonts.js +2 -1
  275. package/dist/slices/episodes/summarize.js +5 -1
  276. package/dist/slices/estimate/index.js +45 -7
  277. package/dist/slices/fonts/catalog.js +3 -2
  278. package/dist/slices/fonts/discovery.js +3 -2
  279. package/dist/slices/fonts/files.js +2 -6
  280. package/dist/slices/fonts/index.js +0 -1
  281. package/dist/slices/fonts/sfnt.js +45 -10
  282. package/dist/slices/images/appearance.js +6 -15
  283. package/dist/slices/images/scenes.js +100 -27
  284. package/dist/slices/keywords/digest.js +84 -0
  285. package/dist/slices/keywords/facts.js +92 -0
  286. package/dist/slices/keywords/fill.js +80 -0
  287. package/dist/slices/keywords/model.js +75 -0
  288. package/dist/slices/keywords/rules.js +187 -0
  289. package/dist/slices/keywords/schema.js +28 -0
  290. package/dist/slices/keywords/specs.js +260 -0
  291. package/dist/slices/keywords/values.js +74 -0
  292. package/dist/slices/library/lint.js +29 -12
  293. package/dist/slices/library/model.js +2 -0
  294. package/dist/slices/library/photorealistic.js +4 -12
  295. package/dist/slices/library/rename.js +9 -7
  296. package/dist/slices/library/save.js +14 -10
  297. package/dist/slices/library/slots.js +37 -11
  298. package/dist/slices/library/transfer.js +2 -1
  299. package/dist/slices/library/used-by.js +25 -10
  300. package/dist/slices/loudness/loudnorm.js +51 -11
  301. package/dist/slices/model-checks/cost.js +61 -0
  302. package/dist/slices/model-checks/listing.js +124 -0
  303. package/dist/slices/model-checks/model.js +20 -0
  304. package/dist/slices/model-checks/record.js +32 -0
  305. package/dist/slices/model-checks/repo.js +120 -0
  306. package/dist/slices/model-checks/rules.js +112 -0
  307. package/dist/slices/model-checks/run.js +143 -0
  308. package/dist/slices/model-checks/service.js +198 -0
  309. package/dist/slices/model-upkeep/switch.js +70 -25
  310. package/dist/slices/model-upkeep/usage.js +26 -11
  311. package/dist/slices/narration/breaths.js +2 -1
  312. package/dist/slices/narration/clean-pieces.js +2 -1
  313. package/dist/slices/narration/concat.js +2 -1
  314. package/dist/slices/narration/peaks.js +3 -4
  315. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  316. package/dist/slices/narration/pronunciation.js +3 -2
  317. package/dist/slices/narration/steering.js +4 -3
  318. package/dist/slices/notifications/notifier.js +4 -3
  319. package/dist/slices/notifications/rules.js +5 -2
  320. package/dist/slices/notifications/settings.js +4 -12
  321. package/dist/slices/notifications/subject.js +2 -1
  322. package/dist/slices/onboarding/copy.js +2 -1
  323. package/dist/slices/onboarding/first-run.js +3 -2
  324. package/dist/slices/onboarding/full-video.js +3 -2
  325. package/dist/slices/onboarding/install.js +4 -3
  326. package/dist/slices/onboarding/model.js +0 -16
  327. package/dist/slices/onboarding/packs.js +22 -22
  328. package/dist/slices/onboarding/quick-short.js +2 -1
  329. package/dist/slices/onboarding/sample.js +0 -1
  330. package/dist/slices/onboarding/state.js +2 -11
  331. package/dist/slices/onboarding/template.js +2 -1
  332. package/dist/slices/patch-notes/library.js +3 -2
  333. package/dist/slices/play-drafts/convert.js +4 -0
  334. package/dist/slices/play-drafts/review-inputs.js +8 -2
  335. package/dist/slices/play-drafts/review.js +5 -4
  336. package/dist/slices/play-drafts/schema.js +4 -0
  337. package/dist/slices/project-templates/from-project.js +9 -2
  338. package/dist/slices/project-templates/repo.js +6 -1
  339. package/dist/slices/project-templates/setup.js +8 -0
  340. package/dist/slices/project-templates/stand-ins.js +22 -0
  341. package/dist/slices/rebuild/activity.js +2 -0
  342. package/dist/slices/rebuild/admission-repo.js +3 -2
  343. package/dist/slices/rebuild/narration-history.js +4 -5
  344. package/dist/slices/rebuild/narration-retry.js +2 -1
  345. package/dist/slices/rebuild/preview-details.js +27 -9
  346. package/dist/slices/rebuild/preview-plan.js +3 -8
  347. package/dist/slices/rebuild/recipe-build.js +8 -3
  348. package/dist/slices/rebuild/recipe-describe.js +3 -3
  349. package/dist/slices/rebuild/recipe-digest.js +58 -0
  350. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  351. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  352. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  353. package/dist/slices/rebuild/recipe-model.js +4 -0
  354. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  355. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  356. package/dist/slices/rebuild/recipe-reference.js +8 -4
  357. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  358. package/dist/slices/rebuild/recipe-save.js +6 -3
  359. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  360. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  361. package/dist/slices/rebuild/recipe-social.js +46 -0
  362. package/dist/slices/rebuild/recipe-text.js +64 -20
  363. package/dist/slices/rebuild/recipe-validation.js +98 -28
  364. package/dist/slices/rebuild/recipe-visual.js +55 -16
  365. package/dist/slices/rebuild/recipe-work.js +15 -3
  366. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  367. package/dist/slices/rebuild/recovery-selection.js +3 -1
  368. package/dist/slices/rebuild/recovery.js +3 -2
  369. package/dist/slices/rebuild/repo.js +4 -3
  370. package/dist/slices/rebuild/review-promise.js +25 -0
  371. package/dist/slices/rebuild/review-redo.js +2 -1
  372. package/dist/slices/rebuild/runtime-admission.js +4 -3
  373. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  374. package/dist/slices/rebuild/runtime-animate.js +9 -8
  375. package/dist/slices/rebuild/runtime-article.js +3 -2
  376. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  377. package/dist/slices/rebuild/runtime-document.js +8 -11
  378. package/dist/slices/rebuild/runtime-export-bed.js +6 -11
  379. package/dist/slices/rebuild/runtime-export-edit.js +6 -15
  380. package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
  381. package/dist/slices/rebuild/runtime-export-short.js +8 -4
  382. package/dist/slices/rebuild/runtime-export.js +34 -12
  383. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  384. package/dist/slices/rebuild/runtime-image.js +4 -9
  385. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  386. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  387. package/dist/slices/rebuild/runtime-lines.js +2 -11
  388. package/dist/slices/rebuild/runtime-local.js +14 -20
  389. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  390. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  391. package/dist/slices/rebuild/runtime-narration-text.js +10 -15
  392. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  393. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  394. package/dist/slices/rebuild/runtime-plan.js +31 -5
  395. package/dist/slices/rebuild/runtime-provider.js +60 -19
  396. package/dist/slices/rebuild/runtime-publication.js +10 -9
  397. package/dist/slices/rebuild/runtime-review.js +33 -28
  398. package/dist/slices/rebuild/runtime-run.js +4 -1
  399. package/dist/slices/rebuild/runtime-shorts.js +25 -45
  400. package/dist/slices/rebuild/runtime-store.js +3 -8
  401. package/dist/slices/rebuild/runtime-subtitles.js +16 -14
  402. package/dist/slices/rebuild/runtime-voices.js +8 -15
  403. package/dist/slices/rebuild/runtime-youtube.js +11 -29
  404. package/dist/slices/rebuild/transition-repo.js +6 -5
  405. package/dist/slices/rebuild/work-records.js +6 -0
  406. package/dist/slices/reruns/cascade.js +1 -2
  407. package/dist/slices/reruns/index.js +6 -9
  408. package/dist/slices/reviews/external-model.js +3 -0
  409. package/dist/slices/reviews/external-repo.js +2 -1
  410. package/dist/slices/reviews/external-rows.js +2 -3
  411. package/dist/slices/reviews/model.js +26 -8
  412. package/dist/slices/reviews/repo.js +2 -1
  413. package/dist/slices/reviews/rules.js +1 -0
  414. package/dist/slices/reviews/view.js +5 -2
  415. package/dist/slices/revisions/downloads.js +4 -5
  416. package/dist/slices/revisions/mutation-assets.js +7 -4
  417. package/dist/slices/revisions/mutation-cues.js +2 -5
  418. package/dist/slices/revisions/mutations.js +0 -1
  419. package/dist/slices/revisions/repo.js +3 -1
  420. package/dist/slices/revisions/schema.js +1 -0
  421. package/dist/slices/revisions/view.js +20 -0
  422. package/dist/slices/run-cost/limits.js +5 -2
  423. package/dist/slices/run-cost/meter.js +12 -5
  424. package/dist/slices/run-cost/panel.js +8 -74
  425. package/dist/slices/run-cost/plan-readings.js +127 -0
  426. package/dist/slices/run-cost/plan-use.js +169 -0
  427. package/dist/slices/run-cost/pricing.js +3 -3
  428. package/dist/slices/run-cost/week.js +32 -4
  429. package/dist/slices/schedules/agenda.js +11 -2
  430. package/dist/slices/schedules/by-date.js +42 -0
  431. package/dist/slices/schedules/calendar.js +5 -0
  432. package/dist/slices/schedules/design-agenda.js +36 -0
  433. package/dist/slices/schedules/design-check.js +41 -0
  434. package/dist/slices/schedules/design-run.js +126 -0
  435. package/dist/slices/schedules/next-run.js +62 -0
  436. package/dist/slices/schedules/prepare.js +18 -2
  437. package/dist/slices/schedules/repo.js +24 -16
  438. package/dist/slices/schedules/rescore.js +35 -0
  439. package/dist/slices/schedules/scheduler.js +13 -3
  440. package/dist/slices/schedules/schema.js +54 -3
  441. package/dist/slices/schedules/score.js +107 -0
  442. package/dist/slices/schedules/service.js +57 -24
  443. package/dist/slices/schedules/topic-list.js +15 -0
  444. package/dist/slices/schedules/topic-scores.js +104 -0
  445. package/dist/slices/schedules/topics.js +48 -36
  446. package/dist/slices/settings/cli-paths.js +4 -6
  447. package/dist/slices/settings/first-run.js +2 -11
  448. package/dist/slices/settings/model-reach.js +3 -17
  449. package/dist/slices/settings/model.js +10 -0
  450. package/dist/slices/settings/playback.js +33 -22
  451. package/dist/slices/settings/probe-read.js +16 -0
  452. package/dist/slices/settings/repo.js +33 -0
  453. package/dist/slices/settings/tutorial.js +2 -2
  454. package/dist/slices/settings/voices.js +4 -12
  455. package/dist/slices/shorts/pick.js +2 -15
  456. package/dist/slices/shorts/prompts.js +2 -13
  457. package/dist/slices/shorts/render.js +3 -0
  458. package/dist/slices/social/channel.js +44 -0
  459. package/dist/slices/social/defaults.js +17 -0
  460. package/dist/slices/social/due.js +36 -0
  461. package/dist/slices/social/extension.js +18 -0
  462. package/dist/slices/social/fit.js +115 -0
  463. package/dist/slices/social/limits.js +186 -0
  464. package/dist/slices/social/links.js +21 -0
  465. package/dist/slices/social/media.js +131 -0
  466. package/dist/slices/social/model.js +75 -0
  467. package/dist/slices/social/notice.js +19 -0
  468. package/dist/slices/social/pack.js +176 -0
  469. package/dist/slices/social/post-prompt.js +55 -0
  470. package/dist/slices/social/queue.js +31 -0
  471. package/dist/slices/social/repo.js +133 -0
  472. package/dist/slices/social/reshape.js +97 -0
  473. package/dist/slices/social/rules.js +5 -0
  474. package/dist/slices/social/schema.js +70 -0
  475. package/dist/slices/social/serve.js +51 -0
  476. package/dist/slices/social/service.js +128 -0
  477. package/dist/slices/social/settings.js +21 -0
  478. package/dist/slices/social/sources.js +114 -0
  479. package/dist/slices/social/state.js +110 -0
  480. package/dist/slices/social/text.js +149 -0
  481. package/dist/slices/storage/arrange.js +3 -1
  482. package/dist/slices/storage/assets.js +8 -10
  483. package/dist/slices/storage/backup-designs.js +74 -0
  484. package/dist/slices/storage/backup-export.js +8 -3
  485. package/dist/slices/storage/backup-format.js +10 -0
  486. package/dist/slices/storage/backup-import.js +20 -2
  487. package/dist/slices/storage/delete-project.js +6 -5
  488. package/dist/slices/storage/files-location.js +6 -8
  489. package/dist/slices/storage/layout.js +13 -2
  490. package/dist/slices/storage/model.js +3 -0
  491. package/dist/slices/storage/portable.js +16 -11
  492. package/dist/slices/storage/schema.js +2 -0
  493. package/dist/slices/storage/staging-refs.js +2 -1
  494. package/dist/slices/storage/staging.js +6 -9
  495. package/dist/slices/storage/trim.js +10 -3
  496. package/dist/slices/studio/calendar.js +4 -1
  497. package/dist/slices/studio/pick.js +2 -11
  498. package/dist/slices/studio/plan.js +6 -15
  499. package/dist/slices/studio/queue.js +3 -14
  500. package/dist/slices/studio/releases.js +26 -0
  501. package/dist/slices/studio/settings.js +14 -51
  502. package/dist/slices/studio/videos.js +12 -0
  503. package/dist/slices/style-preview/render.js +34 -13
  504. package/dist/slices/style-preview/schema.js +13 -0
  505. package/dist/slices/style-preview/service.js +2 -1
  506. package/dist/slices/style-preview/settings.js +33 -5
  507. package/dist/slices/telemetry/collector-client.js +2 -4
  508. package/dist/slices/telemetry/flush.js +2 -4
  509. package/dist/slices/telemetry/model.js +0 -7
  510. package/dist/slices/telemetry/record.js +10 -3
  511. package/dist/slices/template-studio/anatomy-view.js +118 -0
  512. package/dist/slices/template-studio/anatomy.js +73 -0
  513. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  514. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  515. package/dist/slices/template-studio/bakeoff.js +333 -0
  516. package/dist/slices/template-studio/budget.js +103 -0
  517. package/dist/slices/template-studio/checks.js +273 -0
  518. package/dist/slices/template-studio/document-sample.js +56 -0
  519. package/dist/slices/template-studio/edit.js +15 -0
  520. package/dist/slices/template-studio/for-all.js +161 -0
  521. package/dist/slices/template-studio/free-video.js +31 -0
  522. package/dist/slices/template-studio/free.js +176 -0
  523. package/dist/slices/template-studio/gate.js +40 -0
  524. package/dist/slices/template-studio/hidden.js +20 -0
  525. package/dist/slices/template-studio/jobs.js +79 -0
  526. package/dist/slices/template-studio/loudness.js +111 -0
  527. package/dist/slices/template-studio/make.js +40 -0
  528. package/dist/slices/template-studio/model.js +54 -0
  529. package/dist/slices/template-studio/pack-import.js +253 -0
  530. package/dist/slices/template-studio/pack-samples.js +60 -0
  531. package/dist/slices/template-studio/pack.js +205 -0
  532. package/dist/slices/template-studio/paid-view.js +66 -0
  533. package/dist/slices/template-studio/paid.js +348 -0
  534. package/dist/slices/template-studio/quote.js +148 -0
  535. package/dist/slices/template-studio/ready.js +104 -0
  536. package/dist/slices/template-studio/repo.js +134 -0
  537. package/dist/slices/template-studio/reviews-path.js +58 -0
  538. package/dist/slices/template-studio/runner.js +84 -0
  539. package/dist/slices/template-studio/save.js +236 -0
  540. package/dist/slices/template-studio/say-it-as.js +248 -0
  541. package/dist/slices/template-studio/schedule-gate.js +34 -0
  542. package/dist/slices/template-studio/schedule-price.js +30 -0
  543. package/dist/slices/template-studio/schedule-sample.js +87 -0
  544. package/dist/slices/template-studio/schema.js +218 -0
  545. package/dist/slices/template-studio/scope.js +124 -0
  546. package/dist/slices/template-studio/sections.js +176 -0
  547. package/dist/slices/template-studio/service.js +173 -0
  548. package/dist/slices/template-studio/status.js +174 -0
  549. package/dist/slices/template-studio/topic.js +223 -0
  550. package/dist/slices/trash/designs.js +76 -0
  551. package/dist/slices/trash/model.js +13 -4
  552. package/dist/slices/trash/service.js +52 -2
  553. package/dist/slices/tutorials/library.js +3 -2
  554. package/dist/slices/video/ass-text.js +17 -0
  555. package/dist/slices/video/audio-export.js +2 -64
  556. package/dist/slices/video/encoding.js +160 -0
  557. package/dist/slices/video/ffmpeg.js +23 -21
  558. package/dist/slices/video/figure-card.js +1 -15
  559. package/dist/slices/video/slideshow.js +43 -18
  560. package/dist/slices/voices/palette.js +0 -3
  561. package/dist/slices/youtube/answer.js +2 -15
  562. package/dist/slices/youtube/edits-repo.js +2 -11
  563. package/dist/tutorials/Agents-MCP.md +2 -1
  564. package/dist/tutorials/Assistant.md +42 -0
  565. package/dist/tutorials/Channels.md +2 -0
  566. package/dist/tutorials/Home-and-Projects.md +19 -0
  567. package/dist/tutorials/Install.md +1 -0
  568. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  569. package/dist/tutorials/Models.md +33 -1
  570. package/dist/tutorials/Notifications.md +6 -0
  571. package/dist/tutorials/Play-Images.md +4 -0
  572. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  573. package/dist/tutorials/Prompts.md +10 -6
  574. package/dist/tutorials/Remote-Access.md +63 -0
  575. package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
  576. package/dist/tutorials/Schedules.md +21 -1
  577. package/dist/tutorials/Settings-Reference.md +50 -8
  578. package/dist/tutorials/Template-Studio.md +125 -0
  579. package/dist/tutorials/Templates.md +3 -0
  580. package/dist/tutorials/_Sidebar.md +5 -0
  581. package/dist/updater/work-in-progress.js +42 -12
  582. package/dist/web/assets/index-CkAI_2E7.css +1 -0
  583. package/dist/web/assets/index-DcD0cdfo.js +334 -0
  584. package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
  585. package/dist/web/index.html +2 -2
  586. package/package.json +5 -1
  587. package/dist/slices/article/provided-entries.js +0 -66
  588. package/dist/slices/article/run.js +0 -145
  589. package/dist/slices/images/run.js +0 -234
  590. package/dist/slices/narration/legacy-plan.js +0 -58
  591. package/dist/slices/narration/run.js +0 -270
  592. package/dist/slices/rebuild/runtime-actions.js +0 -90
  593. package/dist/slices/research/run.js +0 -224
  594. package/dist/slices/subtitles/prepare.js +0 -194
  595. package/dist/slices/subtitles/transcript.js +0 -34
  596. package/dist/slices/thumbnail/run.js +0 -219
  597. package/dist/slices/video/audio-inputs.js +0 -33
  598. package/dist/slices/video/reuse-audio.js +0 -62
  599. package/dist/slices/video/run.js +0 -78
  600. package/dist/slices/video/write-export.js +0 -158
  601. package/dist/slices/video/write-subtitles.js +0 -59
  602. package/dist/web/assets/index-BadsoORk.css +0 -1
  603. package/dist/web/assets/index-D8LqNsCl.js +0 -326
@@ -0,0 +1,46 @@
1
+ import { withLanguage } from "../../kernel/ports/languages.js";
2
+ import { subjectOf } from "../admission/model.js";
3
+ import { socialPlatforms } from "../social/model.js";
4
+ import { postMessages, postTarget } from "../social/post-prompt.js";
5
+ import { recipe } from "./recipe-model.js";
6
+ import { llmInput, llmInputFingerprint, promptOf } from "./recipe-text.js";
7
+ export const socialPostKey = (platform) => `social:post:${platform}`;
8
+ export const socialPromptKey = (platform) => `social.${platform}`;
9
+ // One post per site whose text comes from a post prompt (`config.social`): a paid call to the
10
+ // project's text model that reads the article. Every other text source is free and made when
11
+ // the pack is read, so a project without a post prompt plans exactly what it did before. The
12
+ // request waits for the article, and for the keywords its prompt uses.
13
+ export function socialRecipes(context, text) {
14
+ const { config } = context;
15
+ return socialPlatforms.flatMap((platform) => {
16
+ const choice = config.social?.[platform];
17
+ if (choice?.text !== "prompt")
18
+ return [];
19
+ const target = postTarget(platform);
20
+ const prompt = choice.prompt?.trim() === undefined || choice.prompt.trim() === ""
21
+ ? undefined
22
+ : promptOf(context, socialPromptKey(platform));
23
+ const messages = withLanguage(postMessages({
24
+ instruction: prompt?.text ?? "",
25
+ target,
26
+ title: subjectOf(config),
27
+ article: text.articleText ?? "",
28
+ }), config.language);
29
+ const ready = text.articleText !== null && (prompt?.waits.length ?? 0) === 0;
30
+ return [
31
+ recipe(context, socialPostKey(platform), "video", ready
32
+ ? { ...llmInput(context, messages), social: target }
33
+ : {
34
+ kind: "deferred",
35
+ version: 1,
36
+ operation: "social-post",
37
+ template: [
38
+ llmInputFingerprint(context, messages),
39
+ text.article.fingerprint,
40
+ platform,
41
+ ...(prompt?.waits ?? []).map((one) => one.fingerprint),
42
+ ],
43
+ }, [text.article.key, ...(prompt?.dependsOn ?? [])]),
44
+ ];
45
+ });
46
+ }
@@ -10,17 +10,21 @@ import { plainText } from "../article/plain.js";
10
10
  import { segmentMessages } from "../article/segments.js";
11
11
  import { splitEndMatter } from "../article/split.js";
12
12
  import { withEarlierEpisodes } from "../episodes/related.js";
13
+ import { withoutSlopifyScene } from "../images/scenes.js";
13
14
  import { parsePronunciationGlossary, pronunciationRulesOf, } from "../narration/pronunciation.js";
14
15
  import { withSharedGlossary } from "../narration/shared-glossary.js";
15
16
  import { researchDocuments } from "../research/documents.js";
16
17
  import { plannerMessages, subAgentMessages } from "../research/planner.js";
17
18
  import { synthesisMessages } from "../research/synthesis.js";
19
+ import { sampleArticleInstruction, sampleNarrationText } from "../template-studio/scope.js";
18
20
  import { thumbnailMessages } from "../thumbnail/by-llm.js";
19
21
  import { adaptationMessages } from "../voices/adaptation.js";
20
22
  import { attributionMessages } from "../voices/attribution.js";
21
23
  import { usesVoices } from "../voices/model.js";
22
24
  import { scriptMessages } from "../voices/script.js";
23
25
  import { describedText, describeFuture, describing } from "./recipe-describe.js";
26
+ import { keywordDigest } from "./recipe-digest.js";
27
+ import { articleMarkdownOf, chaptersProducer, filledPrompt, } from "./recipe-keywords.js";
24
28
  import { recipe, selectedReference, } from "./recipe-model.js";
25
29
  export function renderedPrompt(context, key) {
26
30
  const raw = context.content.promptTemplates[key];
@@ -28,6 +32,10 @@ export function renderedPrompt(context, key) {
28
32
  ? (context.config.rendered[key] ?? "")
29
33
  : render(raw, context.config.values);
30
34
  }
35
+ // The prompt rendered with the person's values, then with the keywords Slopify fills.
36
+ export function promptOf(context, key) {
37
+ return filledPrompt(context, key, renderedPrompt(context, key));
38
+ }
31
39
  export function llmInput(context, messages, webSearch = false, documents) {
32
40
  const choice = context.config.llm;
33
41
  const model = context.catalogue?.llm.find((row) => row.provider === choice?.provider &&
@@ -66,7 +74,7 @@ export function selectedText(context, key, field) {
66
74
  export function textRecipes(context) {
67
75
  const { config, content, resolved } = context;
68
76
  const recipes = [];
69
- const brief = { articlePrompt: renderedPrompt(context, "article"), values: config.values };
77
+ const brief = { articlePrompt: promptOf(context, "article").text, values: config.values };
70
78
  let research;
71
79
  if (config.sources.research === "generate") {
72
80
  const planner = recipe(context, "research:planner", "research", llmInput(context, plannerMessages(brief)));
@@ -113,7 +121,9 @@ export function textRecipes(context) {
113
121
  // The related earlier episodes go under the prompt only when the run carries them, so a
114
122
  // project made without them keeps its fingerprint. Either is written in the project's
115
123
  // language (`withLanguage`; English adds nothing).
116
- const writtenPrompt = withEarlierEpisodes(brief.articlePrompt, config.earlierEpisodes);
124
+ // A template studio sample asks for its outline and first chapter only (`config.sample`);
125
+ // nothing is added for any other project, so its fingerprint stays as it was.
126
+ const writtenPrompt = withSampleScope(withEarlierEpisodes(brief.articlePrompt, config.earlierEpisodes), config.sample);
117
127
  const messages = withLanguage(voices !== undefined && writesScript
118
128
  ? scriptMessages(voices.format, voices.speakers, writtenPrompt, articleNotes)
119
129
  : articleMessages({
@@ -157,6 +167,18 @@ export function textRecipes(context) {
157
167
  const pictureText = content.picturesArticle === undefined
158
168
  ? articleText
159
169
  : plainText(splitEndMatter(content.picturesArticle).body);
170
+ // What the keywords in the later prompts are filled from: the article's chapters and, when a
171
+ // prompt uses one, the keyword digest written from it.
172
+ const digest = keywordDigest(context, { pictureText, article });
173
+ if (digest !== undefined)
174
+ recipes.push(digest.recipe);
175
+ const keywords = {
176
+ producers: [
177
+ chaptersProducer(articleMarkdown, article),
178
+ ...(digest === undefined ? [] : [digest.producer]),
179
+ ],
180
+ };
181
+ const keyed = { ...context, keywords };
160
182
  let script;
161
183
  if (voices !== undefined && scriptCheck !== undefined) {
162
184
  if (writesScript)
@@ -189,7 +211,10 @@ export function textRecipes(context) {
189
211
  };
190
212
  }
191
213
  }
192
- let narrationText = articleText;
214
+ // A sample says only the narration's opening (`template-studio/scope.ts`).
215
+ let narrationText = config.sample === undefined || articleText === null
216
+ ? articleText
217
+ : sampleNarrationText(articleText);
193
218
  const descriptions = [];
194
219
  const cards = [];
195
220
  if (describing(context) && voices === undefined) {
@@ -201,7 +226,11 @@ export function textRecipes(context) {
201
226
  const described = describedText(context, endMatter.body, [article.key]);
202
227
  descriptions.push(...described.recipes);
203
228
  cards.push(...described.cards);
204
- narrationText = described.spoken;
229
+ // A sample says only the opening of the described narration too.
230
+ narrationText =
231
+ config.sample === undefined || described.spoken === null
232
+ ? described.spoken
233
+ : sampleNarrationText(described.spoken);
205
234
  }
206
235
  }
207
236
  const glossary = glossaryOf(context, endMatter);
@@ -210,11 +239,20 @@ export function textRecipes(context) {
210
239
  const choice = config[category];
211
240
  if (choice === undefined || config.sources.audio !== "generate")
212
241
  continue;
213
- const prompt = renderedPrompt(context, category);
242
+ const filled = promptOf(keyed, category);
243
+ const prompt = filled.text;
244
+ const waiting = filled.waits.length > 0;
214
245
  const key = `entry:${category}:text`;
215
246
  const input = choice.mode === "text"
216
- ? { kind: "local", version: 1, operation: "entry-text", values: prompt }
217
- : pictureText === null
247
+ ? waiting
248
+ ? {
249
+ kind: "deferred",
250
+ version: 1,
251
+ operation: key,
252
+ template: [prompt, ...filled.waits.map((one) => one.fingerprint)],
253
+ }
254
+ : { kind: "local", version: 1, operation: "entry-text", values: prompt }
255
+ : pictureText === null || waiting
218
256
  ? {
219
257
  kind: "deferred",
220
258
  version: 1,
@@ -222,36 +260,43 @@ export function textRecipes(context) {
222
260
  template: [
223
261
  llmInputFingerprint(context, withLanguage(segmentMessages(prompt, config, ""), config.language)),
224
262
  article.fingerprint,
263
+ ...filled.waits.map((one) => one.fingerprint),
225
264
  ],
226
265
  }
227
266
  : llmInput(context, withLanguage(segmentMessages(prompt, config, pictureText), config.language));
228
- const value = recipe(context, key, "article", input, choice.mode === "llm" ? [article.key] : []);
267
+ const value = recipe(context, key, "article", input, choice.mode === "llm" ? [...new Set([article.key, ...filled.dependsOn])] : filled.dependsOn);
229
268
  recipes.push(value);
230
269
  entries[category] = {
231
270
  recipe: value,
232
- text: choice.mode === "text" ? prompt : matchingText(context, value, "text"),
271
+ text: choice.mode === "text" ? (waiting ? null : prompt) : matchingText(context, value, "text"),
233
272
  };
234
273
  }
235
274
  if (config.sources.thumbnail === "prompt_by_llm") {
236
- const prompt = renderedPrompt(context, "thumbnailPrompt");
275
+ const filled = promptOf(keyed, "thumbnailPrompt");
237
276
  const messages = thumbnailMessages({
238
- instruction: prompt,
277
+ // The AI writes the picture itself, so a `Slopify_` scene keyword has nothing to fill.
278
+ instruction: withoutSlopifyScene(filled.text),
239
279
  title: subjectOf(config),
240
280
  values: config.values,
241
281
  format: config.format,
242
282
  article: pictureText ?? "",
243
283
  });
244
- recipes.push(recipe(context, "thumbnail:prompt", "thumbnail", pictureText === null
284
+ recipes.push(recipe(context, "thumbnail:prompt", "thumbnail", pictureText === null || filled.waits.length > 0
245
285
  ? {
246
286
  kind: "deferred",
247
287
  version: 1,
248
288
  operation: "thumbnail-prompt",
249
- template: [llmInputFingerprint(context, messages), article.fingerprint],
289
+ template: [
290
+ llmInputFingerprint(context, messages),
291
+ article.fingerprint,
292
+ ...filled.waits.map((one) => one.fingerprint),
293
+ ],
250
294
  }
251
- : llmInput(context, messages), [article.key]));
295
+ : llmInput(context, messages), [...new Set([article.key, ...filled.dependsOn])]));
252
296
  }
253
297
  return {
254
298
  recipes,
299
+ keywords,
255
300
  articleText,
256
301
  pictureText,
257
302
  narrationText,
@@ -276,12 +321,6 @@ export function matchingText(context, value, field) {
276
321
  return null;
277
322
  return selectedText(context, value.key, field);
278
323
  }
279
- function articleMarkdownOf(context) {
280
- const { config, content, resolved } = context;
281
- return config.sources.article !== "generate" || content.articleEdited === true
282
- ? (content.articleMarkdown ?? config.provided.article ?? "")
283
- : resolved.articleMarkdown;
284
- }
285
324
  function glossaryOf(context, endMatter) {
286
325
  if (!usesPronunciationGlossary(context.config))
287
326
  return { ok: true, entries: [] };
@@ -301,3 +340,8 @@ function withShared(own, config) {
301
340
  return own;
302
341
  return { ...own, entries: withSharedGlossary(own.entries, config.sharedGlossary) };
303
342
  }
343
+ function withSampleScope(prompt, sample) {
344
+ return sample === undefined || sample.fullScript
345
+ ? prompt
346
+ : `${prompt.trimEnd()}\n\n${sampleArticleInstruction}`;
347
+ }
@@ -1,10 +1,15 @@
1
1
  import { narrationPreparationFields, shortsFields, usesNarrationPreparation, usesReference, usesShorts, usesYoutubeDescription, videoEditFields, youtubeDescriptionFields, } from "../admission/rules.js";
2
2
  import { detectSlots, filledKeywords, render } from "../admission/substitute.js";
3
3
  import { imagesPerVideoMax } from "../images/scale.js";
4
+ import { keywordUsesOf, placedProblems, textEntryProblems, usesFacts, } from "../keywords/rules.js";
4
5
  import { loudnessFields } from "../loudness/model.js";
5
6
  import { pauseFields } from "../narration/pauses-model.js";
6
- import { reviewFields } from "../reviews/rules.js";
7
+ import { reviewPromptKey } from "../reviews/model.js";
8
+ import { activeReviewStages, reviewFields } from "../reviews/rules.js";
9
+ import { socialPlatforms } from "../social/model.js";
10
+ import { usesPostPrompt } from "../social/rules.js";
7
11
  import { usesAnimation } from "../video/edit-settings.js";
12
+ import { usesScriptPrompt } from "../voices/model.js";
8
13
  export function validateRecipeInputs(config, content) {
9
14
  const fields = [
10
15
  ...narrationPreparationFields(config),
@@ -20,9 +25,13 @@ export function validateRecipeInputs(config, content) {
20
25
  config.sources.thumbnail === "prompt_by_llm" ||
21
26
  usesNarrationPreparation(config) ||
22
27
  usesYoutubeDescription(config) ||
28
+ // A post written by a post prompt.
29
+ usesPostPrompt(config) ||
23
30
  usesShorts(config) ||
24
31
  (config.sources.audio === "generate" &&
25
- (config.intro?.mode === "llm" || config.outro?.mode === "llm"));
32
+ (config.intro?.mode === "llm" || config.outro?.mode === "llm")) ||
33
+ // The keyword digest and the scene keywords are written by the text model too.
34
+ Object.keys(keywordUsesOf(keywordBodies(config, content))).some((cost) => cost === "digest" || cost === "scenes");
26
35
  if (llm && (!config.llm?.provider.trim() || !config.llm.model.trim()))
27
36
  fields.push({ field: "llm", message: "Pick a text provider and model." });
28
37
  if (config.sources.audio === "generate") {
@@ -58,10 +67,60 @@ export function validateRecipeInputs(config, content) {
58
67
  field: `chunking.${chunking?.mode}`,
59
68
  message: "Use a whole number from 1 to 1,000,000.",
60
69
  });
70
+ if (usesReference(config) &&
71
+ config.reference?.source === "provide" &&
72
+ content.provided.reference === undefined)
73
+ fields.push({
74
+ field: "content.provided.reference",
75
+ message: "Upload the establishing image, choose From a prompt instead, or set Establishing image to Off.",
76
+ });
77
+ if (config.sources.images !== "off")
78
+ for (const key of content.imageOrder) {
79
+ const image = content.imageDefinitions[key];
80
+ if (image?.source === "provide" && image.assetId === null)
81
+ fields.push({
82
+ field: `content.imageDefinitions.${key}.assetId`,
83
+ message: "Choose an uploaded image.",
84
+ });
85
+ }
86
+ const prompts = checkedPrompts(config, content);
87
+ for (const prompt of prompts) {
88
+ if (!(prompt.raw === null ? prompt.literal : render(prompt.raw, config.values))?.trim())
89
+ fields.push({ field: prompt.field, message: "Enter a prompt or text." });
90
+ const text = prompt.raw ?? prompt.literal ?? "";
91
+ if (config.keywordFacts === undefined && usesFacts([text]))
92
+ fields.push({
93
+ field: prompt.field,
94
+ message: "This prompt uses a keyword Slopify fills from the channel and its videos, but this project has no keyword facts yet. Use Refresh keyword facts in Edit project → Prompts, then save again.",
95
+ });
96
+ if (prompt.raw === null)
97
+ continue;
98
+ const slots = detectSlots(prompt.raw);
99
+ if (slots.errors.length > 0)
100
+ fields.push({ field: prompt.field, message: "Fix the keyword placeholders in this prompt." });
101
+ for (const key of slots.names)
102
+ if (!filledKeywords.includes(key) &&
103
+ (!Object.hasOwn(config.values, key) || !config.values[key]?.trim()))
104
+ fields.push({ field: `values.${key}`, message: "Enter a value for this keyword." });
105
+ }
106
+ fields.push(...placedProblems(keywordBodies(config, content), config.values));
107
+ for (const category of ["intro", "outro"]) {
108
+ const raw = content.promptTemplates[category] ?? config.rendered[category];
109
+ if (config.sources.audio === "generate" && config[category]?.mode === "text" && raw != null)
110
+ fields.push(...textEntryProblems(raw, category).map((problem) => ({
111
+ field: `rendered.${category}`,
112
+ message: problem.message,
113
+ })));
114
+ }
115
+ return fields;
116
+ }
117
+ // The prompts a version runs, each with the field a refusal marks and where it runs.
118
+ function checkedPrompts(config, content) {
61
119
  const prompts = [];
62
120
  if (usesNarrationPreparation(config))
63
121
  prompts.push({
64
122
  field: "rendered.narration",
123
+ place: "narration",
65
124
  raw: content.promptTemplates.narration ?? null,
66
125
  literal: config.rendered.narration,
67
126
  });
@@ -71,6 +130,7 @@ export function validateRecipeInputs(config, content) {
71
130
  content.promptTemplates.description != null)
72
131
  prompts.push({
73
132
  field: "rendered.description",
133
+ place: "description",
74
134
  raw: content.promptTemplates.description,
75
135
  literal: config.rendered.description,
76
136
  });
@@ -82,6 +142,7 @@ export function validateRecipeInputs(config, content) {
82
142
  if (usesShorts(config) && name?.trim() && content.promptTemplates[key] != null)
83
143
  prompts.push({
84
144
  field: `rendered.${key}`,
145
+ place: key === "shorts" ? "shorts" : "image",
85
146
  raw: content.promptTemplates[key] ?? null,
86
147
  literal: config.rendered[key],
87
148
  });
@@ -89,6 +150,7 @@ export function validateRecipeInputs(config, content) {
89
150
  (config.sources.research === "generate" && config.sources.article !== "provide"))
90
151
  prompts.push({
91
152
  field: "rendered.article",
153
+ place: usesScriptPrompt(config) ? "script" : "article",
92
154
  raw: content.promptTemplates.article ?? null,
93
155
  literal: config.rendered.article,
94
156
  });
@@ -96,12 +158,26 @@ export function validateRecipeInputs(config, content) {
96
158
  if (config.sources.audio === "generate" && config[category] !== undefined)
97
159
  prompts.push({
98
160
  field: `rendered.${category}`,
161
+ place: category,
99
162
  raw: content.promptTemplates[category] ?? null,
100
163
  literal: config.rendered[category],
101
164
  });
165
+ // Each site's post prompt, only when one is picked: none picked uses the built-in one.
166
+ for (const platform of socialPlatforms) {
167
+ const choice = config.social?.[platform];
168
+ const key = `social.${platform}`;
169
+ if (choice?.text === "prompt" && choice.prompt?.trim() && content.promptTemplates[key] != null)
170
+ prompts.push({
171
+ field: `rendered.${key}`,
172
+ place: "post",
173
+ raw: content.promptTemplates[key] ?? null,
174
+ literal: config.rendered[key],
175
+ });
176
+ }
102
177
  if (config.sources.thumbnail === "from_prompt" || config.sources.thumbnail === "prompt_by_llm")
103
178
  prompts.push({
104
179
  field: "rendered.thumbnailPrompt",
180
+ place: "thumbnail",
105
181
  raw: content.promptTemplates.thumbnailPrompt ?? null,
106
182
  literal: config.rendered.thumbnailPrompt,
107
183
  });
@@ -109,43 +185,37 @@ export function validateRecipeInputs(config, content) {
109
185
  if (usesReference(config) && config.reference?.source === "prompt")
110
186
  prompts.push({
111
187
  field: "rendered.referencePrompt",
188
+ place: "image",
112
189
  raw: content.promptTemplates.referencePrompt ?? null,
113
190
  literal: config.rendered.referencePrompt,
114
191
  });
115
- if (usesReference(config) &&
116
- config.reference?.source === "provide" &&
117
- content.provided.reference === undefined)
118
- fields.push({
119
- field: "content.provided.reference",
120
- message: "Upload the establishing image, choose From a prompt instead, or set Establishing image to Off.",
121
- });
122
192
  if (config.sources.images !== "off")
123
193
  for (const key of content.imageOrder) {
124
194
  const image = content.imageDefinitions[key];
125
195
  if (image?.source === "generate")
126
196
  prompts.push({
127
197
  field: `content.imageDefinitions.${key}.prompt`,
198
+ place: "image",
128
199
  raw: image.templateKey == null ? null : (content.promptTemplates[image.templateKey] ?? null),
129
200
  literal: image.prompt ?? undefined,
130
201
  });
131
- else if (image?.source === "provide" && image.assetId === null)
132
- fields.push({
133
- field: `content.imageDefinitions.${key}.assetId`,
134
- message: "Choose an uploaded image.",
135
- });
136
202
  }
137
- for (const prompt of prompts) {
138
- if (!(prompt.raw === null ? prompt.literal : render(prompt.raw, config.values))?.trim())
139
- fields.push({ field: prompt.field, message: "Enter a prompt or text." });
140
- if (prompt.raw === null)
141
- continue;
142
- const slots = detectSlots(prompt.raw);
143
- if (slots.errors.length > 0)
144
- fields.push({ field: prompt.field, message: "Fix the keyword placeholders in this prompt." });
145
- for (const key of slots.names)
146
- if (!filledKeywords.includes(key) &&
147
- (!Object.hasOwn(config.values, key) || !config.values[key]?.trim()))
148
- fields.push({ field: `values.${key}`, message: "Enter a value for this keyword." });
149
- }
150
- return fields;
203
+ return prompts;
204
+ }
205
+ // The bodies a version runs with their places, for the keyword checks: its prompts and its
206
+ // reviews' prompts.
207
+ export function keywordBodies(config, content) {
208
+ return [
209
+ ...checkedPrompts(config, content).map((prompt) => ({
210
+ field: prompt.field,
211
+ place: prompt.place,
212
+ body: prompt.raw ?? prompt.literal ?? "",
213
+ })),
214
+ ...activeReviewStages(config).flatMap((stage) => {
215
+ const raw = content.promptTemplates[reviewPromptKey(stage)];
216
+ return raw == null || !config.reviews?.stages[stage]?.prompt?.trim()
217
+ ? []
218
+ : [{ field: `rendered.${reviewPromptKey(stage)}`, place: "review", body: raw }];
219
+ }),
220
+ ];
151
221
  }
@@ -1,3 +1,4 @@
1
+ import { internalError } from "../../kernel/errors.js";
1
2
  import { thumbnailAspect } from "../../kernel/pipeline.js";
2
3
  import { subjectOf, thumbnailCountOf, thumbnailKey } from "../admission/model.js";
3
4
  import { usesShortMode } from "../admission/short-mode.js";
@@ -7,6 +8,7 @@ import { thumbnailOverrideOf } from "../revisions/model.js";
7
8
  import { usesAmbientBed } from "../video/ambient-bed.js";
8
9
  import { lookWait, withLooks } from "./recipe-appearance.js";
9
10
  import { castFor } from "./recipe-cast.js";
11
+ import { fillPrompt } from "./recipe-keywords.js";
10
12
  import { noMaster } from "./recipe-loudness.js";
11
13
  import { recipe, resourceIdentity, } from "./recipe-model.js";
12
14
  import { castField, imageChoice } from "./recipe-reference.js";
@@ -23,30 +25,36 @@ master = noMaster,
23
25
  // Each image's own scene, for the images whose prompt asks for one (`recipe-scenes.ts`).
24
26
  scenes,
25
27
  // How the subject and characters look, for prompts with `{{Appearance}}`.
26
- appearance) {
28
+ appearance,
29
+ // The keywords Slopify fills in an image prompt (`recipe-keywords.ts`).
30
+ fill = unfilled) {
27
31
  const recipes = [];
28
32
  const imageKeys = config.sources.images === "off" ? [] : content.imageOrder;
29
33
  for (const key of imageKeys) {
30
34
  const image = content.imageDefinitions[key];
31
35
  if (image === undefined)
32
- throw new Error("Slopify hit an internal error (one of the project's images has no saved settings). Try again; if it happens again, use Download diagnostics in Settings and report it.");
36
+ throw new Error(internalError("one of the project's images has no saved settings"));
33
37
  const raw = image.templateKey === undefined || image.templateKey === null
34
38
  ? undefined
35
39
  : content.promptTemplates[image.templateKey];
36
40
  const at = scenes?.keys.indexOf(key) ?? -1;
37
41
  if (scenes !== undefined && at !== -1 && image.source === "generate") {
38
- recipes.push(sceneImage(config, content, key, raw ?? image.prompt ?? "", scenes, at, reference, appearance));
42
+ recipes.push(sceneImage(config, content, key, raw ?? image.prompt ?? "", scenes, at, reference, appearance, fill));
39
43
  continue;
40
44
  }
41
45
  // With Scenes from the article off, a prompt's `{{Scene}}` line is left out.
42
- const written = raw === undefined || raw === null
46
+ const base = raw === undefined || raw === null
43
47
  ? image.prompt === null || image.prompt === undefined
44
48
  ? image.prompt
45
49
  : withoutScene(image.prompt)
46
50
  : render(withoutScene(raw), config.values);
47
- const prompt = written === null || written === undefined || image.source !== "generate"
51
+ const keyed = base === null || base === undefined || image.source !== "generate" ? undefined : fill(base);
52
+ const written = keyed?.text ?? base;
53
+ const prompt = written === null || written === undefined || keyed === undefined
48
54
  ? written
49
- : withLooks(written, appearance);
55
+ : keyed.waits.length > 0
56
+ ? null
57
+ : withLooks(written, appearance);
50
58
  // Waiting for the looks: the same wait an image with a scene has, on the lookup instead.
51
59
  if (prompt === null && written !== null && written !== undefined) {
52
60
  const wait = lookWait(written, appearance);
@@ -62,8 +70,13 @@ appearance) {
62
70
  config.format,
63
71
  ...(config.images?.thinking === undefined ? [] : [config.images.thinking]),
64
72
  ...(reference === undefined ? [] : [reference.input.fingerprint]),
73
+ ...(keyed?.waits ?? []).map((one) => one.fingerprint),
65
74
  ],
66
- }, [...wait.dependsOn, ...(reference === undefined ? [] : [reference.key])]));
75
+ }, [
76
+ ...wait.dependsOn,
77
+ ...(reference === undefined ? [] : [reference.key]),
78
+ ...(keyed?.waits ?? []).map((one) => one.key),
79
+ ]));
67
80
  continue;
68
81
  }
69
82
  recipes.push(recipe({ content }, `image:${key}`, "images", image.source === "provide"
@@ -76,7 +89,10 @@ appearance) {
76
89
  prompt: prompt ?? "",
77
90
  ...(reference === undefined ? {} : { reference: reference.input }),
78
91
  ...castField(castFor(config, prompt)),
79
- }, image.source === "provide" || reference === undefined ? [] : [reference.key], { unresolved: image.source === "provide" ? image.assetId === null : !prompt?.trim() }));
92
+ }, [
93
+ ...(image.source === "provide" || reference === undefined ? [] : [reference.key]),
94
+ ...(keyed?.dependsOn ?? []),
95
+ ], { unresolved: image.source === "provide" ? image.assetId === null : !prompt?.trim() }));
80
96
  }
81
97
  if (config.sources.video !== "off") {
82
98
  const short = usesShortMode(config);
@@ -152,11 +168,12 @@ appearance) {
152
168
  // An image drawn from its own scene. Until the scenes are written it waits as a deferred
153
169
  // request that names the scenes step and the image's place among them, so writing them turns
154
170
  // it into its image request (materialization), and writing them again draws it again.
155
- function sceneImage(config, content, key, body, scenes, at, reference, appearance) {
171
+ function sceneImage(config, content, key, body, scenes, at, reference, appearance, fill) {
156
172
  const scene = scenes.scenes?.[at];
157
- const prompt = scene === undefined
173
+ const keyed = fill(render(body, config.values));
174
+ const prompt = scene === undefined || keyed.waits.length > 0
158
175
  ? undefined
159
- : (withLooks(withScene(render(body, config.values), scene), appearance, scene) ?? undefined);
176
+ : (withLooks(withScene(keyed.text, scene, { legacy: scenes.legacy, ...scenes.details?.[at] }), appearance, scene) ?? undefined);
160
177
  const wait = lookWait(body, appearance);
161
178
  return recipe({ content }, `image:${key}`, "images", prompt === undefined
162
179
  ? {
@@ -172,6 +189,7 @@ function sceneImage(config, content, key, body, scenes, at, reference, appearanc
172
189
  ...(config.images?.thinking === undefined ? [] : [config.images.thinking]),
173
190
  ...(reference === undefined ? [] : [reference.input.fingerprint]),
174
191
  ...wait.template,
192
+ ...keyed.waits.map((one) => one.fingerprint),
175
193
  ],
176
194
  }
177
195
  : {
@@ -182,7 +200,15 @@ function sceneImage(config, content, key, body, scenes, at, reference, appearanc
182
200
  prompt,
183
201
  ...(reference === undefined ? {} : { reference: reference.input }),
184
202
  ...castField(castFor(config, prompt)),
185
- }, [scenes.recipe.key, ...wait.dependsOn, ...(reference === undefined ? [] : [reference.key])]);
203
+ }, [
204
+ scenes.recipe.key,
205
+ ...wait.dependsOn,
206
+ ...(reference === undefined ? [] : [reference.key]),
207
+ ...keyed.dependsOn,
208
+ ]);
209
+ }
210
+ function unfilled(text) {
211
+ return { text, waits: [], dependsOn: [] };
186
212
  }
187
213
  // What the ambient bed adds to the render's fingerprint: its settings and, for the user's own
188
214
  // file, the project asset it plays. Undefined without a bed.
@@ -219,9 +245,17 @@ appearance) {
219
245
  }, [], { unresolved: content.provided.thumbnail === undefined }),
220
246
  ];
221
247
  const promptRecipe = textRecipes.find((value) => value.key === "thumbnail:prompt");
222
- const written = config.sources.thumbnail === "prompt_by_llm" && promptRecipe !== undefined
223
- ? matchingText(context, promptRecipe, "prompt")
224
- : renderedPrompt(context, "thumbnailPrompt");
248
+ // A prompt the AI wrote is sent as it wrote it; a Library prompt has its keywords filled.
249
+ const keyed = config.sources.thumbnail === "prompt_by_llm" && promptRecipe !== undefined
250
+ ? undefined
251
+ : fillPrompt(context, renderedPrompt(context, "thumbnailPrompt"), "thumbnail");
252
+ const written = keyed === undefined
253
+ ? promptRecipe === undefined
254
+ ? null
255
+ : matchingText(context, promptRecipe, "prompt")
256
+ : keyed.waits.length > 0
257
+ ? null
258
+ : keyed.text;
225
259
  const sceneFor = (variant) => scenes?.scenes?.[variant - 1];
226
260
  const withScenes = scenes !== undefined;
227
261
  const variants = Array.from({ length: thumbnailCountOf(config) }, (_, index) => index + 1);
@@ -239,7 +273,10 @@ appearance) {
239
273
  : withScenes
240
274
  ? scene === undefined
241
275
  ? null
242
- : withScene(written, scene)
276
+ : withScene(written, scene, {
277
+ legacy: scenes.legacy,
278
+ ...scenes.details?.[variant - 1],
279
+ })
243
280
  : withoutScene(written);
244
281
  // A thumbnail stands for the subject, so it always has the subject's look.
245
282
  const prompt = sceneFilled === null
@@ -263,6 +300,7 @@ appearance) {
263
300
  ...(variant === 1 ? [] : [["thumbnail-variant", variant]]),
264
301
  ...(withScenes ? [["thumbnail-scene", scenes.recipe.fingerprint]] : []),
265
302
  ...wait.template,
303
+ ...(keyed?.waits ?? []).map((one) => one.fingerprint),
266
304
  ],
267
305
  }
268
306
  : {
@@ -279,6 +317,7 @@ appearance) {
279
317
  ...(reference === undefined ? [] : [reference.key]),
280
318
  ...(withScenes ? [scenes.recipe.key] : []),
281
319
  ...wait.dependsOn,
320
+ ...(keyed?.dependsOn ?? []),
282
321
  ]);
283
322
  });
284
323
  }
@@ -285,7 +285,9 @@ export function priceRecipe(work, value, config) {
285
285
  (input.operation === "image-scene" ||
286
286
  input.operation === "image-scenes" ||
287
287
  input.operation === "thumbnail-scenes" ||
288
- input.operation === "image-appearance")) {
288
+ input.operation === "image-appearance" ||
289
+ input.operation === "keyword-digest" ||
290
+ input.operation === "social-post")) {
289
291
  const choice = config === undefined ? undefined : recipeProviderChoice(value, config);
290
292
  if (choice !== undefined && input.operation === "image-scene")
291
293
  return { kind: "image", stage: work.key, provider: choice.provider, model: choice.model };
@@ -296,7 +298,13 @@ export function priceRecipe(work, value, config) {
296
298
  provider: choice.provider,
297
299
  model: choice.model,
298
300
  inputCharacters: 90000,
299
- outputCharacters: input.operation === "image-appearance" ? 6000 : 4000,
301
+ outputCharacters: input.operation === "image-appearance"
302
+ ? 6000
303
+ : input.operation === "keyword-digest"
304
+ ? 2000
305
+ : input.operation === "social-post"
306
+ ? 600
307
+ : 4000,
300
308
  detail: "The article is read when the step runs; its length is estimated. Retries are excluded.",
301
309
  };
302
310
  }
@@ -320,7 +328,11 @@ export function priceRecipe(work, value, config) {
320
328
  model: input.model,
321
329
  inputCharacters: input.messages.reduce((sum, message) => sum + message.content.length, 0) +
322
330
  (input.documents ?? []).reduce((sum, document) => sum + document.content.length, 0),
323
- outputCharacters: work.key === "article:body" ? 9000 : input.describe !== undefined ? 600 : 2400,
331
+ outputCharacters: work.key === "article:body"
332
+ ? 9000
333
+ : input.describe !== undefined || input.social !== undefined
334
+ ? 600
335
+ : 2400,
324
336
  detail: "Actual saved request input; output length is estimated. Retries and tools are excluded.",
325
337
  };
326
338
  if (input.kind === "tts")