@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.
- package/dist/adapters/account-limits.js +30 -0
- package/dist/adapters/alignment/cache.js +3 -5
- package/dist/adapters/alignment/ctc.js +1 -2
- package/dist/adapters/alignment/lock.js +4 -3
- package/dist/adapters/alignment/multilingual.js +0 -3
- package/dist/adapters/alignment/prefetch.js +2 -1
- package/dist/adapters/alignment/worker.js +2 -1
- package/dist/adapters/explain.js +0 -3
- package/dist/adapters/ffmpeg.js +19 -1
- package/dist/adapters/host-cli/index.js +19 -3
- package/dist/adapters/http-failure.js +53 -0
- package/dist/adapters/image/codex.js +36 -4
- package/dist/adapters/image/fal.js +17 -50
- package/dist/adapters/image/google.js +31 -55
- package/dist/adapters/image/openai.js +20 -47
- package/dist/adapters/image/reference.js +36 -0
- package/dist/adapters/image/replicate.js +18 -47
- package/dist/adapters/interest/search-interest.js +113 -0
- package/dist/adapters/key-probes.js +53 -2
- package/dist/adapters/llm/antigravity.js +4 -0
- package/dist/adapters/llm/assistant-cli.js +388 -0
- package/dist/adapters/llm/child-session.js +83 -0
- package/dist/adapters/llm/claude-code-models.js +51 -98
- package/dist/adapters/llm/claude-code.js +13 -2
- package/dist/adapters/llm/codex-limits.js +27 -68
- package/dist/adapters/llm/codex-server-models.js +61 -109
- package/dist/adapters/llm/codex.js +12 -1
- package/dist/adapters/llm/openrouter.js +10 -47
- package/dist/adapters/llm/run-cli.js +6 -22
- package/dist/adapters/llm/sse-lines.js +5 -1
- package/dist/adapters/model-access.js +37 -0
- package/dist/adapters/tts/cartesia.js +15 -46
- package/dist/adapters/tts/elevenlabs.js +20 -52
- package/dist/adapters/tts/gemini.js +19 -40
- package/dist/adapters/tts/inworld-async.js +4 -1
- package/dist/adapters/tts/inworld.js +3 -0
- package/dist/adapters/tts/openai.js +20 -50
- package/dist/assets/models-lifecycle.yaml +6 -0
- package/dist/assistant-wiring.js +60 -0
- package/dist/autopilot-timers.js +59 -0
- package/dist/catalog/lifecycle.js +52 -0
- package/dist/catalog/propose.js +204 -0
- package/dist/catalog/registry.js +3 -1
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/store.js +39 -0
- package/dist/edge/autostart/native.js +3 -5
- package/dist/edge/autostart/prompt.js +2 -1
- package/dist/edge/autostart/service.js +3 -4
- package/dist/edge/cli-args.js +3 -4
- package/dist/edge/cli.js +2 -1
- package/dist/edge/docker-install/apply.js +11 -11
- package/dist/edge/docker-install/run.js +2 -1
- package/dist/edge/docker-install/state.js +4 -3
- package/dist/edge/docker-install/tree.js +2 -4
- package/dist/edge/docker-install/volume.js +3 -2
- package/dist/edge/docker.js +5 -4
- package/dist/edge/events/hub.js +5 -5
- package/dist/edge/http/actions.js +4 -25
- package/dist/edge/http/actor.js +5 -3
- package/dist/edge/http/app.js +85 -10
- package/dist/edge/http/assistant.js +127 -0
- package/dist/edge/http/audio-preview.js +4 -11
- package/dist/edge/http/auditions.js +1 -1
- package/dist/edge/http/autostart.js +5 -5
- package/dist/edge/http/backups.js +1 -3
- package/dist/edge/http/channel-memory.js +7 -20
- package/dist/edge/http/channels.js +13 -26
- package/dist/edge/http/checkpoints.js +33 -17
- package/dist/edge/http/design-templates.js +167 -0
- package/dist/edge/http/designs.js +178 -0
- package/dist/edge/http/diagnostics.js +5 -1
- package/dist/edge/http/document-themes.js +2 -11
- package/dist/edge/http/draft-files.js +1 -3
- package/dist/edge/http/draft-problem.js +4 -9
- package/dist/edge/http/drafts.js +3 -3
- package/dist/edge/http/entries.js +6 -9
- package/dist/edge/http/extension-auth.js +27 -0
- package/dist/edge/http/external-reviews.js +12 -11
- package/dist/edge/http/files.js +5 -11
- package/dist/edge/http/folder-location.js +1 -4
- package/dist/edge/http/fonts.js +0 -7
- package/dist/edge/http/home.js +38 -13
- package/dist/edge/http/host-cli.js +18 -8
- package/dist/edge/http/ids.js +8 -0
- package/dist/edge/http/keywords.js +35 -0
- package/dist/edge/http/model-checks.js +132 -0
- package/dist/edge/http/narration-peaks.js +5 -13
- package/dist/edge/http/onboarding.js +1 -14
- package/dist/edge/http/open-folder.js +5 -10
- package/dist/edge/http/outputs.js +4 -12
- package/dist/edge/http/patch-notes.js +4 -10
- package/dist/edge/http/planning.js +2 -6
- package/dist/edge/http/problem.js +2 -4
- package/dist/edge/http/project-create.js +1 -0
- package/dist/edge/http/project-templates.js +29 -17
- package/dist/edge/http/projects.js +2 -12
- package/dist/edge/http/prompts.js +7 -14
- package/dist/edge/http/pronunciations.js +1 -2
- package/dist/edge/http/providers.js +39 -24
- package/dist/edge/http/refusal.js +29 -0
- package/dist/edge/http/remote-access.js +40 -0
- package/dist/edge/http/remote-guard.js +71 -0
- package/dist/edge/http/request-guard.js +91 -0
- package/dist/edge/http/reviews.js +11 -24
- package/dist/edge/http/revision-files.js +5 -10
- package/dist/edge/http/revisions.js +10 -15
- package/dist/edge/http/run-cost.js +1 -2
- package/dist/edge/http/same-page.js +80 -0
- package/dist/edge/http/schedules.js +59 -32
- package/dist/edge/http/settings.js +22 -13
- package/dist/edge/http/social.js +282 -0
- package/dist/edge/http/staging.js +2 -4
- package/dist/edge/http/storage-files.js +5 -7
- package/dist/edge/http/storage.js +23 -23
- package/dist/edge/http/studio.js +23 -50
- package/dist/edge/http/style-preview.js +1 -4
- package/dist/edge/http/subtitles.js +3 -17
- package/dist/edge/http/template-studio.js +433 -0
- package/dist/edge/http/trash.js +10 -11
- package/dist/edge/http/tutorial.js +1 -2
- package/dist/edge/http/tutorials.js +5 -11
- package/dist/edge/http/update.js +7 -13
- package/dist/edge/http/waveform.js +3 -6
- package/dist/edge/http/youtube-edits.js +2 -3
- package/dist/edge/mcp/assistant.js +279 -0
- package/dist/edge/mcp/attribution.js +8 -2
- package/dist/edge/mcp/call-route.js +11 -2
- package/dist/edge/mcp/excluded-routes.js +25 -0
- package/dist/edge/mcp/http.js +11 -2
- package/dist/edge/mcp/instructions.js +5 -3
- package/dist/edge/mcp/library-tools.js +1 -0
- package/dist/edge/mcp/route-tool.js +35 -15
- package/dist/edge/mcp/server.js +13 -8
- package/dist/edge/mcp/tools-channels.js +2 -0
- package/dist/edge/mcp/tools-designs.js +165 -0
- package/dist/edge/mcp/tools-library.js +14 -2
- package/dist/edge/mcp/tools-planning.js +23 -4
- package/dist/edge/mcp/tools-projects.js +28 -1
- package/dist/edge/mcp/tools-runs.js +14 -1
- package/dist/edge/mcp/tools-schedules.js +28 -4
- package/dist/edge/mcp/tools-setup.js +61 -3
- package/dist/edge/mcp/tools-social.js +46 -0
- package/dist/edge/mcp/tools-storage.js +8 -0
- package/dist/edge/mcp/tools-template-studio.js +166 -0
- package/dist/edge/mcp/tools.js +6 -0
- package/dist/edge/remote-access/cli.js +71 -0
- package/dist/edge/remote-access/model.js +32 -0
- package/dist/edge/remote-access/rules.js +207 -0
- package/dist/edge/remote-access/service.js +203 -0
- package/dist/edge/remote-access/store.js +44 -0
- package/dist/edge/remote-access/views.js +43 -0
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/host-cli/install.js +2 -2
- package/dist/host-cli/open-folder.js +2 -2
- package/dist/host-cli/paths.js +2 -4
- package/dist/host-cli/server.js +4 -4
- package/dist/host-cli/service.js +3 -3
- package/dist/host-cli/status.js +7 -7
- package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
- package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
- package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
- package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
- package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
- package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
- package/dist/kernel/db/migrations/0070-designs.sql +53 -0
- package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
- package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
- package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
- package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
- package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
- package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
- package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
- package/dist/kernel/db/tx.js +2 -4
- package/dist/kernel/errors.js +27 -1
- package/dist/kernel/json-answer.js +15 -0
- package/dist/kernel/lock.js +4 -9
- package/dist/kernel/paths.js +8 -0
- package/dist/kernel/ports/assistant-cli.js +4 -0
- package/dist/kernel/ports/host-cli.js +3 -0
- package/dist/kernel/ports/interest.js +4 -0
- package/dist/kernel/ports/languages.js +0 -3
- package/dist/kernel/ports/model.js +2 -0
- package/dist/kernel/runner/attempt.js +3 -1
- package/dist/kernel/runner/audition.js +3 -1
- package/dist/kernel/runner/checkpoint-authority.js +2 -1
- package/dist/kernel/runner/index.js +8 -10
- package/dist/kernel/runner/model-access.js +40 -0
- package/dist/kernel/runner/model-check.js +153 -0
- package/dist/kernel/runner/providers.js +15 -10
- package/dist/kernel/runner/standalone.js +9 -5
- package/dist/main.js +334 -19
- package/dist/model-checks.js +166 -0
- package/dist/patch-notes/4.0.0.md +51 -0
- package/dist/patch-notes/index.json +6 -0
- package/dist/slices/admission/heads.js +2 -1
- package/dist/slices/admission/repo.js +2 -1
- package/dist/slices/admission/rules.js +43 -8
- package/dist/slices/admission/schema.js +8 -0
- package/dist/slices/admission/stage-writes.js +0 -3
- package/dist/slices/admission/start.js +48 -31
- package/dist/slices/admission/substitute.js +7 -4
- package/dist/slices/agents/attention-posts.js +31 -0
- package/dist/slices/agents/attention-templates.js +33 -0
- package/dist/slices/agents/attention.js +10 -1
- package/dist/slices/agents/connection.js +2 -6
- package/dist/slices/agents/model.js +6 -3
- package/dist/slices/agents/repo.js +15 -7
- package/dist/slices/article/continuation.js +0 -63
- package/dist/slices/article/plain.js +3 -2
- package/dist/slices/article/segments.js +2 -62
- package/dist/slices/assistant/access.js +1 -0
- package/dist/slices/assistant/asks.js +80 -0
- package/dist/slices/assistant/cost.js +54 -0
- package/dist/slices/assistant/gate.js +222 -0
- package/dist/slices/assistant/model.js +79 -0
- package/dist/slices/assistant/names.js +23 -0
- package/dist/slices/assistant/prompt.js +74 -0
- package/dist/slices/assistant/repo.js +74 -0
- package/dist/slices/assistant/tokens.js +18 -0
- package/dist/slices/assistant/turn.js +342 -0
- package/dist/slices/autopilot/buffer.js +76 -0
- package/dist/slices/autopilot/model.js +14 -0
- package/dist/slices/autopilot/notify.js +27 -0
- package/dist/slices/autopilot/repo.js +34 -0
- package/dist/slices/autopilot/signals.js +194 -0
- package/dist/slices/autopilot/warnings.js +85 -0
- package/dist/slices/autopilot/weekly.js +100 -0
- package/dist/slices/backups/files.js +2 -6
- package/dist/slices/backups/repo.js +3 -15
- package/dist/slices/backups/service.js +4 -3
- package/dist/slices/channels/cast-images.js +2 -1
- package/dist/slices/channels/model.js +0 -1
- package/dist/slices/channels/rebrand.js +0 -3
- package/dist/slices/channels/repo.js +4 -0
- package/dist/slices/channels/schema.js +6 -0
- package/dist/slices/channels/service.js +10 -0
- package/dist/slices/channels/videos.js +2 -14
- package/dist/slices/checkpoints/index.js +0 -1
- package/dist/slices/control/providers.js +0 -26
- package/dist/slices/designs/artwork.js +66 -0
- package/dist/slices/designs/budget.js +23 -0
- package/dist/slices/designs/calls.js +124 -0
- package/dist/slices/designs/check-context.js +38 -0
- package/dist/slices/designs/check-text.js +43 -0
- package/dist/slices/designs/fields.js +88 -0
- package/dist/slices/designs/frames.js +34 -0
- package/dist/slices/designs/gate.js +128 -0
- package/dist/slices/designs/layout.js +293 -0
- package/dist/slices/designs/model.js +35 -0
- package/dist/slices/designs/pdf-job.js +23 -0
- package/dist/slices/designs/pdf-pages.js +30 -0
- package/dist/slices/designs/pdf-runner.js +68 -0
- package/dist/slices/designs/pdf-worker.js +24 -0
- package/dist/slices/designs/picture-size.js +79 -0
- package/dist/slices/designs/pictures.js +55 -0
- package/dist/slices/designs/quote.js +24 -0
- package/dist/slices/designs/render-args.js +36 -0
- package/dist/slices/designs/render-input.js +59 -0
- package/dist/slices/designs/render.js +215 -0
- package/dist/slices/designs/repo.js +101 -0
- package/dist/slices/designs/requests.js +80 -0
- package/dist/slices/designs/runner.js +239 -0
- package/dist/slices/designs/sample-repo.js +91 -0
- package/dist/slices/designs/sample.js +222 -0
- package/dist/slices/designs/schema.js +87 -0
- package/dist/slices/designs/sections.js +44 -0
- package/dist/slices/designs/service.js +233 -0
- package/dist/slices/designs/steps-repo.js +102 -0
- package/dist/slices/designs/studio-kind.js +39 -0
- package/dist/slices/designs/template-checks.js +44 -0
- package/dist/slices/designs/template-make.js +118 -0
- package/dist/slices/designs/templates.js +218 -0
- package/dist/slices/document/fonts.js +2 -1
- package/dist/slices/episodes/summarize.js +5 -1
- package/dist/slices/estimate/index.js +45 -7
- package/dist/slices/fonts/catalog.js +3 -2
- package/dist/slices/fonts/discovery.js +3 -2
- package/dist/slices/fonts/files.js +2 -6
- package/dist/slices/fonts/index.js +0 -1
- package/dist/slices/fonts/sfnt.js +45 -10
- package/dist/slices/images/appearance.js +6 -15
- package/dist/slices/images/scenes.js +100 -27
- package/dist/slices/keywords/digest.js +84 -0
- package/dist/slices/keywords/facts.js +92 -0
- package/dist/slices/keywords/fill.js +80 -0
- package/dist/slices/keywords/model.js +75 -0
- package/dist/slices/keywords/rules.js +187 -0
- package/dist/slices/keywords/schema.js +28 -0
- package/dist/slices/keywords/specs.js +260 -0
- package/dist/slices/keywords/values.js +74 -0
- package/dist/slices/library/lint.js +29 -12
- package/dist/slices/library/model.js +2 -0
- package/dist/slices/library/photorealistic.js +4 -12
- package/dist/slices/library/rename.js +9 -7
- package/dist/slices/library/save.js +14 -10
- package/dist/slices/library/slots.js +37 -11
- package/dist/slices/library/transfer.js +2 -1
- package/dist/slices/library/used-by.js +25 -10
- package/dist/slices/loudness/loudnorm.js +51 -11
- package/dist/slices/model-checks/cost.js +61 -0
- package/dist/slices/model-checks/listing.js +124 -0
- package/dist/slices/model-checks/model.js +20 -0
- package/dist/slices/model-checks/record.js +32 -0
- package/dist/slices/model-checks/repo.js +120 -0
- package/dist/slices/model-checks/rules.js +112 -0
- package/dist/slices/model-checks/run.js +143 -0
- package/dist/slices/model-checks/service.js +198 -0
- package/dist/slices/model-upkeep/switch.js +70 -25
- package/dist/slices/model-upkeep/usage.js +26 -11
- package/dist/slices/narration/breaths.js +2 -1
- package/dist/slices/narration/clean-pieces.js +2 -1
- package/dist/slices/narration/concat.js +2 -1
- package/dist/slices/narration/peaks.js +3 -4
- package/dist/slices/narration/pronunciation-chunks.js +4 -3
- package/dist/slices/narration/pronunciation.js +3 -2
- package/dist/slices/narration/steering.js +4 -3
- package/dist/slices/notifications/notifier.js +4 -3
- package/dist/slices/notifications/rules.js +5 -2
- package/dist/slices/notifications/settings.js +4 -12
- package/dist/slices/notifications/subject.js +2 -1
- package/dist/slices/onboarding/copy.js +2 -1
- package/dist/slices/onboarding/first-run.js +3 -2
- package/dist/slices/onboarding/full-video.js +3 -2
- package/dist/slices/onboarding/install.js +4 -3
- package/dist/slices/onboarding/model.js +0 -16
- package/dist/slices/onboarding/packs.js +22 -22
- package/dist/slices/onboarding/quick-short.js +2 -1
- package/dist/slices/onboarding/sample.js +0 -1
- package/dist/slices/onboarding/state.js +2 -11
- package/dist/slices/onboarding/template.js +2 -1
- package/dist/slices/patch-notes/library.js +3 -2
- package/dist/slices/play-drafts/convert.js +4 -0
- package/dist/slices/play-drafts/review-inputs.js +8 -2
- package/dist/slices/play-drafts/review.js +5 -4
- package/dist/slices/play-drafts/schema.js +4 -0
- package/dist/slices/project-templates/from-project.js +9 -2
- package/dist/slices/project-templates/repo.js +6 -1
- package/dist/slices/project-templates/setup.js +8 -0
- package/dist/slices/project-templates/stand-ins.js +22 -0
- package/dist/slices/rebuild/activity.js +2 -0
- package/dist/slices/rebuild/admission-repo.js +3 -2
- package/dist/slices/rebuild/narration-history.js +4 -5
- package/dist/slices/rebuild/narration-retry.js +2 -1
- package/dist/slices/rebuild/preview-details.js +27 -9
- package/dist/slices/rebuild/preview-plan.js +3 -8
- package/dist/slices/rebuild/recipe-build.js +8 -3
- package/dist/slices/rebuild/recipe-describe.js +3 -3
- package/dist/slices/rebuild/recipe-digest.js +58 -0
- package/dist/slices/rebuild/recipe-input-schema.js +10 -0
- package/dist/slices/rebuild/recipe-keywords.js +71 -0
- package/dist/slices/rebuild/recipe-legacy.js +5 -1
- package/dist/slices/rebuild/recipe-model.js +4 -0
- package/dist/slices/rebuild/recipe-narration-text.js +2 -1
- package/dist/slices/rebuild/recipe-preparation.js +3 -3
- package/dist/slices/rebuild/recipe-reference.js +8 -4
- package/dist/slices/rebuild/recipe-reviews.js +27 -7
- package/dist/slices/rebuild/recipe-save.js +6 -3
- package/dist/slices/rebuild/recipe-scenes.js +40 -11
- package/dist/slices/rebuild/recipe-shorts.js +8 -8
- package/dist/slices/rebuild/recipe-social.js +46 -0
- package/dist/slices/rebuild/recipe-text.js +64 -20
- package/dist/slices/rebuild/recipe-validation.js +98 -28
- package/dist/slices/rebuild/recipe-visual.js +55 -16
- package/dist/slices/rebuild/recipe-work.js +15 -3
- package/dist/slices/rebuild/recipe-youtube.js +4 -3
- package/dist/slices/rebuild/recovery-selection.js +3 -1
- package/dist/slices/rebuild/recovery.js +3 -2
- package/dist/slices/rebuild/repo.js +4 -3
- package/dist/slices/rebuild/review-promise.js +25 -0
- package/dist/slices/rebuild/review-redo.js +2 -1
- package/dist/slices/rebuild/runtime-admission.js +4 -3
- package/dist/slices/rebuild/runtime-agent-review.js +11 -5
- package/dist/slices/rebuild/runtime-animate.js +9 -8
- package/dist/slices/rebuild/runtime-article.js +3 -2
- package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
- package/dist/slices/rebuild/runtime-document.js +8 -11
- package/dist/slices/rebuild/runtime-export-bed.js +6 -11
- package/dist/slices/rebuild/runtime-export-edit.js +6 -15
- package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
- package/dist/slices/rebuild/runtime-export-short.js +8 -4
- package/dist/slices/rebuild/runtime-export.js +34 -12
- package/dist/slices/rebuild/runtime-figure-card.js +2 -1
- package/dist/slices/rebuild/runtime-image.js +4 -9
- package/dist/slices/rebuild/runtime-instructions.js +2 -0
- package/dist/slices/rebuild/runtime-keywords.js +59 -0
- package/dist/slices/rebuild/runtime-lines.js +2 -11
- package/dist/slices/rebuild/runtime-local.js +14 -20
- package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
- package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
- package/dist/slices/rebuild/runtime-narration-text.js +10 -15
- package/dist/slices/rebuild/runtime-pictures.js +2 -4
- package/dist/slices/rebuild/runtime-piece-label.js +10 -6
- package/dist/slices/rebuild/runtime-plan.js +31 -5
- package/dist/slices/rebuild/runtime-provider.js +60 -19
- package/dist/slices/rebuild/runtime-publication.js +10 -9
- package/dist/slices/rebuild/runtime-review.js +33 -28
- package/dist/slices/rebuild/runtime-run.js +4 -1
- package/dist/slices/rebuild/runtime-shorts.js +25 -45
- package/dist/slices/rebuild/runtime-store.js +3 -8
- package/dist/slices/rebuild/runtime-subtitles.js +16 -14
- package/dist/slices/rebuild/runtime-voices.js +8 -15
- package/dist/slices/rebuild/runtime-youtube.js +11 -29
- package/dist/slices/rebuild/transition-repo.js +6 -5
- package/dist/slices/rebuild/work-records.js +6 -0
- package/dist/slices/reruns/cascade.js +1 -2
- package/dist/slices/reruns/index.js +6 -9
- package/dist/slices/reviews/external-model.js +3 -0
- package/dist/slices/reviews/external-repo.js +2 -1
- package/dist/slices/reviews/external-rows.js +2 -3
- package/dist/slices/reviews/model.js +26 -8
- package/dist/slices/reviews/repo.js +2 -1
- package/dist/slices/reviews/rules.js +1 -0
- package/dist/slices/reviews/view.js +5 -2
- package/dist/slices/revisions/downloads.js +4 -5
- package/dist/slices/revisions/mutation-assets.js +7 -4
- package/dist/slices/revisions/mutation-cues.js +2 -5
- package/dist/slices/revisions/mutations.js +0 -1
- package/dist/slices/revisions/repo.js +3 -1
- package/dist/slices/revisions/schema.js +1 -0
- package/dist/slices/revisions/view.js +20 -0
- package/dist/slices/run-cost/limits.js +5 -2
- package/dist/slices/run-cost/meter.js +12 -5
- package/dist/slices/run-cost/panel.js +8 -74
- package/dist/slices/run-cost/plan-readings.js +127 -0
- package/dist/slices/run-cost/plan-use.js +169 -0
- package/dist/slices/run-cost/pricing.js +3 -3
- package/dist/slices/run-cost/week.js +32 -4
- package/dist/slices/schedules/agenda.js +11 -2
- package/dist/slices/schedules/by-date.js +42 -0
- package/dist/slices/schedules/calendar.js +5 -0
- package/dist/slices/schedules/design-agenda.js +36 -0
- package/dist/slices/schedules/design-check.js +41 -0
- package/dist/slices/schedules/design-run.js +126 -0
- package/dist/slices/schedules/next-run.js +62 -0
- package/dist/slices/schedules/prepare.js +18 -2
- package/dist/slices/schedules/repo.js +24 -16
- package/dist/slices/schedules/rescore.js +35 -0
- package/dist/slices/schedules/scheduler.js +13 -3
- package/dist/slices/schedules/schema.js +54 -3
- package/dist/slices/schedules/score.js +107 -0
- package/dist/slices/schedules/service.js +57 -24
- package/dist/slices/schedules/topic-list.js +15 -0
- package/dist/slices/schedules/topic-scores.js +104 -0
- package/dist/slices/schedules/topics.js +48 -36
- package/dist/slices/settings/cli-paths.js +4 -6
- package/dist/slices/settings/first-run.js +2 -11
- package/dist/slices/settings/model-reach.js +3 -17
- package/dist/slices/settings/model.js +10 -0
- package/dist/slices/settings/playback.js +33 -22
- package/dist/slices/settings/probe-read.js +16 -0
- package/dist/slices/settings/repo.js +33 -0
- package/dist/slices/settings/tutorial.js +2 -2
- package/dist/slices/settings/voices.js +4 -12
- package/dist/slices/shorts/pick.js +2 -15
- package/dist/slices/shorts/prompts.js +2 -13
- package/dist/slices/shorts/render.js +3 -0
- package/dist/slices/social/channel.js +44 -0
- package/dist/slices/social/defaults.js +17 -0
- package/dist/slices/social/due.js +36 -0
- package/dist/slices/social/extension.js +18 -0
- package/dist/slices/social/fit.js +115 -0
- package/dist/slices/social/limits.js +186 -0
- package/dist/slices/social/links.js +21 -0
- package/dist/slices/social/media.js +131 -0
- package/dist/slices/social/model.js +75 -0
- package/dist/slices/social/notice.js +19 -0
- package/dist/slices/social/pack.js +176 -0
- package/dist/slices/social/post-prompt.js +55 -0
- package/dist/slices/social/queue.js +31 -0
- package/dist/slices/social/repo.js +133 -0
- package/dist/slices/social/reshape.js +97 -0
- package/dist/slices/social/rules.js +5 -0
- package/dist/slices/social/schema.js +70 -0
- package/dist/slices/social/serve.js +51 -0
- package/dist/slices/social/service.js +128 -0
- package/dist/slices/social/settings.js +21 -0
- package/dist/slices/social/sources.js +114 -0
- package/dist/slices/social/state.js +110 -0
- package/dist/slices/social/text.js +149 -0
- package/dist/slices/storage/arrange.js +3 -1
- package/dist/slices/storage/assets.js +8 -10
- package/dist/slices/storage/backup-designs.js +74 -0
- package/dist/slices/storage/backup-export.js +8 -3
- package/dist/slices/storage/backup-format.js +10 -0
- package/dist/slices/storage/backup-import.js +20 -2
- package/dist/slices/storage/delete-project.js +6 -5
- package/dist/slices/storage/files-location.js +6 -8
- package/dist/slices/storage/layout.js +13 -2
- package/dist/slices/storage/model.js +3 -0
- package/dist/slices/storage/portable.js +16 -11
- package/dist/slices/storage/schema.js +2 -0
- package/dist/slices/storage/staging-refs.js +2 -1
- package/dist/slices/storage/staging.js +6 -9
- package/dist/slices/storage/trim.js +10 -3
- package/dist/slices/studio/calendar.js +4 -1
- package/dist/slices/studio/pick.js +2 -11
- package/dist/slices/studio/plan.js +6 -15
- package/dist/slices/studio/queue.js +3 -14
- package/dist/slices/studio/releases.js +26 -0
- package/dist/slices/studio/settings.js +14 -51
- package/dist/slices/studio/videos.js +12 -0
- package/dist/slices/style-preview/render.js +34 -13
- package/dist/slices/style-preview/schema.js +13 -0
- package/dist/slices/style-preview/service.js +2 -1
- package/dist/slices/style-preview/settings.js +33 -5
- package/dist/slices/telemetry/collector-client.js +2 -4
- package/dist/slices/telemetry/flush.js +2 -4
- package/dist/slices/telemetry/model.js +0 -7
- package/dist/slices/telemetry/record.js +10 -3
- package/dist/slices/template-studio/anatomy-view.js +118 -0
- package/dist/slices/template-studio/anatomy.js +73 -0
- package/dist/slices/template-studio/bakeoff-make.js +225 -0
- package/dist/slices/template-studio/bakeoff-repo.js +83 -0
- package/dist/slices/template-studio/bakeoff.js +333 -0
- package/dist/slices/template-studio/budget.js +103 -0
- package/dist/slices/template-studio/checks.js +273 -0
- package/dist/slices/template-studio/document-sample.js +56 -0
- package/dist/slices/template-studio/edit.js +15 -0
- package/dist/slices/template-studio/for-all.js +161 -0
- package/dist/slices/template-studio/free-video.js +31 -0
- package/dist/slices/template-studio/free.js +176 -0
- package/dist/slices/template-studio/gate.js +40 -0
- package/dist/slices/template-studio/hidden.js +20 -0
- package/dist/slices/template-studio/jobs.js +79 -0
- package/dist/slices/template-studio/loudness.js +111 -0
- package/dist/slices/template-studio/make.js +40 -0
- package/dist/slices/template-studio/model.js +54 -0
- package/dist/slices/template-studio/pack-import.js +253 -0
- package/dist/slices/template-studio/pack-samples.js +60 -0
- package/dist/slices/template-studio/pack.js +205 -0
- package/dist/slices/template-studio/paid-view.js +66 -0
- package/dist/slices/template-studio/paid.js +348 -0
- package/dist/slices/template-studio/quote.js +148 -0
- package/dist/slices/template-studio/ready.js +104 -0
- package/dist/slices/template-studio/repo.js +134 -0
- package/dist/slices/template-studio/reviews-path.js +58 -0
- package/dist/slices/template-studio/runner.js +84 -0
- package/dist/slices/template-studio/save.js +236 -0
- package/dist/slices/template-studio/say-it-as.js +248 -0
- package/dist/slices/template-studio/schedule-gate.js +34 -0
- package/dist/slices/template-studio/schedule-price.js +30 -0
- package/dist/slices/template-studio/schedule-sample.js +87 -0
- package/dist/slices/template-studio/schema.js +218 -0
- package/dist/slices/template-studio/scope.js +124 -0
- package/dist/slices/template-studio/sections.js +176 -0
- package/dist/slices/template-studio/service.js +173 -0
- package/dist/slices/template-studio/status.js +174 -0
- package/dist/slices/template-studio/topic.js +223 -0
- package/dist/slices/trash/designs.js +76 -0
- package/dist/slices/trash/model.js +13 -4
- package/dist/slices/trash/service.js +52 -2
- package/dist/slices/tutorials/library.js +3 -2
- package/dist/slices/video/ass-text.js +17 -0
- package/dist/slices/video/audio-export.js +2 -64
- package/dist/slices/video/encoding.js +160 -0
- package/dist/slices/video/ffmpeg.js +23 -21
- package/dist/slices/video/figure-card.js +1 -15
- package/dist/slices/video/slideshow.js +43 -18
- package/dist/slices/voices/palette.js +0 -3
- package/dist/slices/youtube/answer.js +2 -15
- package/dist/slices/youtube/edits-repo.js +2 -11
- package/dist/tutorials/Agents-MCP.md +2 -1
- package/dist/tutorials/Assistant.md +42 -0
- package/dist/tutorials/Channels.md +2 -0
- package/dist/tutorials/Home-and-Projects.md +19 -0
- package/dist/tutorials/Install.md +1 -0
- package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
- package/dist/tutorials/Models.md +33 -1
- package/dist/tutorials/Notifications.md +6 -0
- package/dist/tutorials/Play-Images.md +4 -0
- package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
- package/dist/tutorials/Prompts.md +10 -6
- package/dist/tutorials/Remote-Access.md +63 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
- package/dist/tutorials/Schedules.md +21 -1
- package/dist/tutorials/Settings-Reference.md +50 -8
- package/dist/tutorials/Template-Studio.md +125 -0
- package/dist/tutorials/Templates.md +3 -0
- package/dist/tutorials/_Sidebar.md +5 -0
- package/dist/updater/work-in-progress.js +42 -12
- package/dist/web/assets/index-CkAI_2E7.css +1 -0
- package/dist/web/assets/index-DcD0cdfo.js +334 -0
- package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +5 -1
- package/dist/slices/article/provided-entries.js +0 -66
- package/dist/slices/article/run.js +0 -145
- package/dist/slices/images/run.js +0 -234
- package/dist/slices/narration/legacy-plan.js +0 -58
- package/dist/slices/narration/run.js +0 -270
- package/dist/slices/rebuild/runtime-actions.js +0 -90
- package/dist/slices/research/run.js +0 -224
- package/dist/slices/subtitles/prepare.js +0 -194
- package/dist/slices/subtitles/transcript.js +0 -34
- package/dist/slices/thumbnail/run.js +0 -219
- package/dist/slices/video/audio-inputs.js +0 -33
- package/dist/slices/video/reuse-audio.js +0 -62
- package/dist/slices/video/run.js +0 -78
- package/dist/slices/video/write-export.js +0 -158
- package/dist/slices/video/write-subtitles.js +0 -59
- package/dist/web/assets/index-BadsoORk.css +0 -1
- package/dist/web/assets/index-D8LqNsCl.js +0 -326
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname } from "node:path";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { piecesOf, setPiece } from "../../kernel/runner/piece-repo.js";
|
|
5
|
-
import { entryModes } from "../admission/model.js";
|
|
6
|
-
import { projectById, stagesOf } from "../admission/repo.js";
|
|
7
|
-
import { setStageProgress } from "../admission/stage-writes.js";
|
|
8
|
-
import { entryCategories } from "../library/model.js";
|
|
9
|
-
import { outputFileName, outputPath } from "../storage/layout.js";
|
|
10
|
-
import { insertOutput, outputsOf } from "../storage/repo.js";
|
|
11
|
-
import { probeDurationMs } from "../video/ffmpeg.js";
|
|
12
|
-
import { chunkNarration, defaultChunking } from "./chunk.js";
|
|
13
|
-
import { joinNarration } from "./concat.js";
|
|
14
|
-
import { planLegacyNarration } from "./legacy-plan.js";
|
|
15
|
-
import { observeNarration } from "./live.js";
|
|
16
|
-
// An empty narration source fails the stage immediately with no retries. Thrown from the
|
|
17
|
-
// slice rather than from a provider call, so the attempt wrapper never sees it.
|
|
18
|
-
export const nothingToNarrate = "There is nothing to narrate because the article is empty. Add article text in Edit project → Article, then Try again.";
|
|
19
|
-
// What a chunk piece carries between runs: the text that was sent, and the file it came
|
|
20
|
-
// back as once the provider answered. That is the whole of the resume.
|
|
21
|
-
const chunkPayload = z.object({ text: z.string(), file: z.string().optional() });
|
|
22
|
-
// What the article stage left on its `segment` pieces.
|
|
23
|
-
const segmentPayload = z.object({
|
|
24
|
-
category: z.enum(entryCategories),
|
|
25
|
-
name: z.string(),
|
|
26
|
-
mode: z.enum(entryModes),
|
|
27
|
-
text: z.string(),
|
|
28
|
-
});
|
|
29
|
-
// The chunk audio sits in a folder of its own under the project. It is not an output: the
|
|
30
|
-
// concatenated body plus the picked intro and outro are the project's only audio outputs,
|
|
31
|
-
// so the pieces name these files and the boot reconcile keeps them by that name
|
|
32
|
-
// (`slices/storage/reconcile.ts`).
|
|
33
|
-
const chunkDir = "audio-chunks";
|
|
34
|
-
export async function runNarration(deps, context, providers) {
|
|
35
|
-
const { projectId } = context.stage;
|
|
36
|
-
deps.audioPreviews?.clear(projectId);
|
|
37
|
-
const project = projectById(deps.db, projectId);
|
|
38
|
-
if (project === undefined) {
|
|
39
|
-
throw new Error("This project no longer exists; it may have been deleted while it was running.");
|
|
40
|
-
}
|
|
41
|
-
const choice = project.config.audio;
|
|
42
|
-
if (choice === undefined) {
|
|
43
|
-
// Admission refuses a run whose audio is Generate without a provider and a voice, so
|
|
44
|
-
// reaching here is a bug in admission rather than something the user did.
|
|
45
|
-
throw new Error("No voice is set for the narration. Choose a voice in Edit project → Providers, then Try again.");
|
|
46
|
-
}
|
|
47
|
-
// Read once, before anything is written: the segment steps below ask it what a previous
|
|
48
|
-
// run already stored, and the body step is the only thing that adds to it.
|
|
49
|
-
const outputs = outputsOf(deps.db, projectId);
|
|
50
|
-
// The end matter is already out of this text: the article stage cut it into
|
|
51
|
-
// files of its own, so nothing here can narrate a sources list.
|
|
52
|
-
const chunking = project.config.chunking ?? defaultChunking;
|
|
53
|
-
const texts = chunkNarration(narrationSource(deps, outputs, projectId), chunking);
|
|
54
|
-
if (texts.length === 0) {
|
|
55
|
-
throw new Error(nothingToNarrate);
|
|
56
|
-
}
|
|
57
|
-
const planned = planLegacyNarration(deps, context.stage.id, texts, deps.maxCharacters?.(choice.provider, choice.model) ?? 1000000, (piece) => finished(deps, projectId, piece) !== undefined);
|
|
58
|
-
const result = await speakChunks(deps, context, providers, choice, planned);
|
|
59
|
-
if (!result.ok)
|
|
60
|
-
return "held";
|
|
61
|
-
await storeBody(deps, context, choice, result.value, outputs);
|
|
62
|
-
if ((await speakSegments(deps, context, providers, choice, outputs)) === "held")
|
|
63
|
-
return "held";
|
|
64
|
-
deps.log.write("info", "narration.done", {
|
|
65
|
-
projectId,
|
|
66
|
-
stage: "audio",
|
|
67
|
-
detail: `${String(texts.length)} chunks in ${chunking.mode} mode`,
|
|
68
|
-
});
|
|
69
|
-
return "done";
|
|
70
|
-
}
|
|
71
|
-
// The narration source: the plain-text article, written by the article stage or pasted in
|
|
72
|
-
// by a user whose article stage was set to Provide.
|
|
73
|
-
function narrationSource(deps, outputs, projectId) {
|
|
74
|
-
const article = outputs.find((output) => output.role === "article_txt");
|
|
75
|
-
if (article === undefined) {
|
|
76
|
-
throw new Error(nothingToNarrate);
|
|
77
|
-
}
|
|
78
|
-
return readFileSync(outputPath(deps.paths, projectId, article.path), "utf8");
|
|
79
|
-
}
|
|
80
|
-
// Chunks are scheduled through the shared provider queue. A chunk a previous run finished is not spoken
|
|
81
|
-
// again, and one that fails takes down the whole stage while its siblings finish and keep their
|
|
82
|
-
// audio for the next resume.
|
|
83
|
-
async function speakChunks(deps, context, providers, choice, pieces) {
|
|
84
|
-
const { projectId } = context.stage;
|
|
85
|
-
const total = pieces.length;
|
|
86
|
-
let done = pieces.filter((piece) => finished(deps, projectId, piece)).length;
|
|
87
|
-
report(deps, context, done, total);
|
|
88
|
-
const outcomes = await Promise.all(pieces.map(async (piece) => {
|
|
89
|
-
const carried = chunkOf(piece);
|
|
90
|
-
const kept = finished(deps, projectId, piece);
|
|
91
|
-
if (kept !== undefined) {
|
|
92
|
-
return { ok: true, file: kept };
|
|
93
|
-
}
|
|
94
|
-
const file = `${chunkDir}/${piece.id}.mp3`;
|
|
95
|
-
setPiece(deps.db, piece.id, "running", piece.payload);
|
|
96
|
-
try {
|
|
97
|
-
const spoken = await providers.forPiece(piece.id).tts({
|
|
98
|
-
provider: choice.provider,
|
|
99
|
-
model: choice.model,
|
|
100
|
-
voiceId: choice.voice,
|
|
101
|
-
text: carried.text,
|
|
102
|
-
}, observeNarration(deps.audioPreviews, projectId, piece.id, total === 1 ? "Body" : `Body part ${piece.idx} of ${total}`));
|
|
103
|
-
if (!spoken.ok) {
|
|
104
|
-
setPiece(deps.db, piece.id, "pending", piece.payload);
|
|
105
|
-
return spoken;
|
|
106
|
-
}
|
|
107
|
-
write(deps, projectId, file, spoken.value.bytes);
|
|
108
|
-
// The file is on disk before the row says so: a crash between the two leaves a
|
|
109
|
-
// file the reconcile collects, where the other order would leave a `done` chunk
|
|
110
|
-
// whose audio never existed and a concat that fails on every retry.
|
|
111
|
-
setPiece(deps.db, piece.id, "done", JSON.stringify({ text: carried.text, file }));
|
|
112
|
-
done += 1;
|
|
113
|
-
report(deps, context, done, total);
|
|
114
|
-
return { ok: true, file };
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
// A cancel is not this chunk failing: an aborted call counts as nothing, and the
|
|
118
|
-
// resume runs a `pending` chunk exactly as a failed one.
|
|
119
|
-
setPiece(deps.db, piece.id, context.signal.aborted ? "pending" : "failed", piece.payload);
|
|
120
|
-
return { ok: false, error };
|
|
121
|
-
}
|
|
122
|
-
}));
|
|
123
|
-
const files = [];
|
|
124
|
-
for (const outcome of outcomes) {
|
|
125
|
-
if (!outcome.ok) {
|
|
126
|
-
if ("reason" in outcome)
|
|
127
|
-
return outcome;
|
|
128
|
-
throw outcome.error;
|
|
129
|
-
}
|
|
130
|
-
files.push(outcome.file);
|
|
131
|
-
}
|
|
132
|
-
return { ok: true, value: files };
|
|
133
|
-
}
|
|
134
|
-
// One body file, in chunk order, with its measured duration on the row.
|
|
135
|
-
async function storeBody(deps, context, choice, files, outputs) {
|
|
136
|
-
const { projectId } = context.stage;
|
|
137
|
-
if (outputs.some((output) => output.role === "audio_body")) {
|
|
138
|
-
// A retry that failed on a segment last time: the body is already joined and measured,
|
|
139
|
-
// and joining it again would insert a second row for the same file.
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
const name = outputFileName("audio_body", 1, ".mp3", "audio");
|
|
143
|
-
const durationMs = await joinNarration({ bin: deps.ffmpeg, log: deps.log }, {
|
|
144
|
-
files: files.map((file) => outputPath(deps.paths, projectId, file)),
|
|
145
|
-
listPath: outputPath(deps.paths, projectId, `${chunkDir}/concat.txt`),
|
|
146
|
-
output: outputPath(deps.paths, projectId, name),
|
|
147
|
-
signal: context.signal,
|
|
148
|
-
});
|
|
149
|
-
// A cancel protects an output already stored, not one about to be: one landing between
|
|
150
|
-
// ffmpeg exiting and the row below leaves nothing recorded.
|
|
151
|
-
context.signal.throwIfAborted();
|
|
152
|
-
store(deps, projectId, "audio_body", name, durationMs, choice);
|
|
153
|
-
counted(deps, "body", durationMs, choice);
|
|
154
|
-
}
|
|
155
|
-
// Each picked segment's text is one TTS request with the same provider and voice, stored as
|
|
156
|
-
// its own audio file with its duration; body chunking does not apply to them. The text is
|
|
157
|
-
// whatever the article stage wrote onto the segment piece: an LLM-mode entry's answer, or a
|
|
158
|
-
// text-mode entry's rendered body, spoken verbatim.
|
|
159
|
-
async function speakSegments(deps, context, providers, choice, outputs) {
|
|
160
|
-
const { projectId } = context.stage;
|
|
161
|
-
for (const piece of segmentPieces(deps, projectId)) {
|
|
162
|
-
const segment = segmentOf(piece);
|
|
163
|
-
const role = segment.category === "intro" ? "audio_intro" : "audio_outro";
|
|
164
|
-
if (outputs.some((output) => output.role === role)) {
|
|
165
|
-
// Spoken by a previous run of this stage; the resume keeps it.
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
const text = segment.text.trim();
|
|
169
|
-
if (text === "") {
|
|
170
|
-
// The same rule, applied to a segment: there is nothing to say, and silently
|
|
171
|
-
// dropping an intro the user picked would lose it without telling anyone.
|
|
172
|
-
throw new Error(`The ${segment.category} is empty, so there is nothing to narrate for it. Write the ${segment.category} or remove it in Edit project, then Try again.`);
|
|
173
|
-
}
|
|
174
|
-
const spoken = await providers.forPiece(piece.id).tts({
|
|
175
|
-
provider: choice.provider,
|
|
176
|
-
model: choice.model,
|
|
177
|
-
voiceId: choice.voice,
|
|
178
|
-
text,
|
|
179
|
-
}, observeNarration(deps.audioPreviews, projectId, piece.id, segment.category === "intro" ? "Intro" : "Outro"));
|
|
180
|
-
if (!spoken.ok)
|
|
181
|
-
return "held";
|
|
182
|
-
const name = outputFileName(role, 1, ".mp3", "audio");
|
|
183
|
-
write(deps, projectId, name, spoken.value.bytes);
|
|
184
|
-
// Measured the same way the body is, because the render adds all three and the gaps to
|
|
185
|
-
// get the length of the video.
|
|
186
|
-
const durationMs = await probeDurationMs(deps.ffmpeg, outputPath(deps.paths, projectId, name), context.signal, deps.log);
|
|
187
|
-
context.signal.throwIfAborted();
|
|
188
|
-
store(deps, projectId, role, name, durationMs, choice);
|
|
189
|
-
counted(deps, segment.category, durationMs, choice);
|
|
190
|
-
}
|
|
191
|
-
return "done";
|
|
192
|
-
}
|
|
193
|
-
// The segment pieces belong to the *article* stage, which is where they are written, so
|
|
194
|
-
// they are read under its id and never under this stage's own. They stay there because the
|
|
195
|
-
// re-run rules depend on it: a Re-run audio, and the audio re-run an article edit cascades
|
|
196
|
-
// into, both clear the audio stage's pieces, and only the article stage ever writes an
|
|
197
|
-
// entry text - so an intro kept on this stage's row would be thrown away by the first
|
|
198
|
-
// re-narration and never written again.
|
|
199
|
-
function segmentPieces(deps, projectId) {
|
|
200
|
-
const article = stagesOf(deps.db, projectId).find((stage) => stage.kind === "article");
|
|
201
|
-
if (article === undefined) {
|
|
202
|
-
// Admission writes all six stage rows with the project, so a project without an article
|
|
203
|
-
// stage is a bug rather than a run the user configured.
|
|
204
|
-
throw new Error("Slopify hit an internal error (the project has no article stage). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
205
|
-
}
|
|
206
|
-
return piecesOf(deps.db, article.id, "segment");
|
|
207
|
-
}
|
|
208
|
-
// Audio seconds come from the measured duration per segment, never from the text's length.
|
|
209
|
-
function counted(deps, segment, durationMs, choice) {
|
|
210
|
-
deps.count("stage.completed", {
|
|
211
|
-
stage: "audio",
|
|
212
|
-
segment,
|
|
213
|
-
provider: choice.provider,
|
|
214
|
-
model: choice.model,
|
|
215
|
-
audioSeconds: durationMs / 1000,
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
// K of N chunks narrated, written as well as emitted, so a page opened mid-stage reads the
|
|
219
|
-
// count off the row rather than waiting for the next chunk.
|
|
220
|
-
function report(deps, context, done, total) {
|
|
221
|
-
setStageProgress(deps.db, context.stage.id, done, total);
|
|
222
|
-
context.emit({
|
|
223
|
-
type: "stage.progress",
|
|
224
|
-
projectId: context.stage.projectId,
|
|
225
|
-
stage: "audio",
|
|
226
|
-
current: done,
|
|
227
|
-
total,
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
// A chunk counts as finished only when its audio is still on disk: the row and the file
|
|
231
|
-
// are written one after the other, and the boot reconcile can remove a file whose row
|
|
232
|
-
// survived. Answering "done" for a missing file would fail the concat instead.
|
|
233
|
-
function finished(deps, projectId, piece) {
|
|
234
|
-
const file = chunkOf(piece).file;
|
|
235
|
-
if (file === undefined) {
|
|
236
|
-
return undefined;
|
|
237
|
-
}
|
|
238
|
-
return existsSync(outputPath(deps.paths, projectId, file)) ? file : undefined;
|
|
239
|
-
}
|
|
240
|
-
function write(deps, projectId, path, bytes) {
|
|
241
|
-
const target = outputPath(deps.paths, projectId, path);
|
|
242
|
-
mkdirSync(dirname(target), { recursive: true, mode: 0o700 });
|
|
243
|
-
writeFileSync(target, bytes, { mode: 0o600 });
|
|
244
|
-
}
|
|
245
|
-
function store(deps, projectId, role, path, durationMs, choice) {
|
|
246
|
-
insertOutput(deps.db, {
|
|
247
|
-
id: deps.ids.next(),
|
|
248
|
-
projectId,
|
|
249
|
-
stageKind: "audio",
|
|
250
|
-
role,
|
|
251
|
-
path,
|
|
252
|
-
originalFilename: null,
|
|
253
|
-
bytes: statSync(outputPath(deps.paths, projectId, path)).size,
|
|
254
|
-
durationMs,
|
|
255
|
-
meta: { provider: choice.provider, model: choice.model, voice: choice.voice },
|
|
256
|
-
createdAt: deps.clock.now().toISOString(),
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
function chunkOf(piece) {
|
|
260
|
-
if (piece.payload === null) {
|
|
261
|
-
throw new Error("Slopify hit an internal error (a saved narration chunk is empty). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
262
|
-
}
|
|
263
|
-
return chunkPayload.parse(JSON.parse(piece.payload));
|
|
264
|
-
}
|
|
265
|
-
function segmentOf(piece) {
|
|
266
|
-
if (piece.payload === null) {
|
|
267
|
-
throw new Error("Slopify hit an internal error (a saved intro or outro is empty). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
268
|
-
}
|
|
269
|
-
return segmentPayload.parse(JSON.parse(piece.payload));
|
|
270
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { saveRevision } from "../revisions/mutations.js";
|
|
2
|
-
import { currentRevisionId } from "../revisions/repo.js";
|
|
3
|
-
import { getRevisionView } from "../revisions/view.js";
|
|
4
|
-
import { projectStandings } from "./runtime-store.js";
|
|
5
|
-
export async function revisionAction(deps, projectId, action) {
|
|
6
|
-
const revisionId = currentRevisionId(deps.db, projectId);
|
|
7
|
-
const view = revisionId === undefined ? undefined : getRevisionView(deps, projectId, revisionId);
|
|
8
|
-
if (view === undefined)
|
|
9
|
-
return { ok: false, reason: "no-project" };
|
|
10
|
-
if (action.kind === "retry")
|
|
11
|
-
return { ok: false, reason: "rebuild-required" };
|
|
12
|
-
const config = view.revision.config;
|
|
13
|
-
const content = view.revision.content;
|
|
14
|
-
const image = action.kind === "delete-image" || action.kind === "regenerate-image"
|
|
15
|
-
? view.outputs.find((row) => row.selected &&
|
|
16
|
-
row.output.id === action.outputId &&
|
|
17
|
-
(row.output.role === "image" ||
|
|
18
|
-
// One of the generated thumbnails is made again on its own, the others kept.
|
|
19
|
-
(action.kind === "regenerate-image" &&
|
|
20
|
-
row.output.role === "thumbnail" &&
|
|
21
|
-
(config.sources.thumbnail === "from_prompt" ||
|
|
22
|
-
config.sources.thumbnail === "prompt_by_llm")) ||
|
|
23
|
-
// A generated establishing image is made again like any image; it is never deleted
|
|
24
|
-
// on its own (Edit project sets it Off), and an uploaded one has nothing to redo.
|
|
25
|
-
(action.kind === "regenerate-image" &&
|
|
26
|
-
row.output.role === "reference" &&
|
|
27
|
-
config.reference?.source === "prompt")))
|
|
28
|
-
: undefined;
|
|
29
|
-
if ((action.kind === "delete-image" || action.kind === "regenerate-image") && image === undefined)
|
|
30
|
-
return { ok: false, reason: "unknown-image" };
|
|
31
|
-
const imageKey = image?.workKey.slice(6);
|
|
32
|
-
if (action.kind === "article" && action.markdown.trim() === "")
|
|
33
|
-
return { ok: false, reason: "empty-article" };
|
|
34
|
-
const regenerate = action.kind === "rerun"
|
|
35
|
-
? Object.keys(view.revision.fingerprints).filter((key) => keyStage(key) === action.stage)
|
|
36
|
-
: action.kind === "regenerate-image" && image !== undefined
|
|
37
|
-
? [image.workKey]
|
|
38
|
-
: undefined;
|
|
39
|
-
const nextContent = action.kind === "article"
|
|
40
|
-
? { ...content, articleMarkdown: action.markdown, articleEdited: true }
|
|
41
|
-
: action.kind === "delete-image"
|
|
42
|
-
? {
|
|
43
|
-
...content,
|
|
44
|
-
imageOrder: content.imageOrder.filter((key) => key !== imageKey),
|
|
45
|
-
imageDefinitions: Object.fromEntries(Object.entries(content.imageDefinitions).filter(([key]) => key !== imageKey)),
|
|
46
|
-
}
|
|
47
|
-
: content;
|
|
48
|
-
const nextConfig = action.kind === "delete-image" && nextContent.imageOrder.length === 0
|
|
49
|
-
? {
|
|
50
|
-
...config,
|
|
51
|
-
sources: { ...config.sources, images: "off", video: "off" },
|
|
52
|
-
...(config.subtitles?.mode === "burn-in"
|
|
53
|
-
? { subtitles: { ...config.subtitles, mode: "files" } }
|
|
54
|
-
: {}),
|
|
55
|
-
}
|
|
56
|
-
: config;
|
|
57
|
-
const saved = await saveRevision(deps, {
|
|
58
|
-
projectId,
|
|
59
|
-
baseRevisionId: view.revision.id,
|
|
60
|
-
idempotencyKey: deps.ids.next(),
|
|
61
|
-
edit: {
|
|
62
|
-
config: nextConfig,
|
|
63
|
-
content: nextContent,
|
|
64
|
-
...(regenerate === undefined ? {} : { regenerate }),
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
if (!saved.ok)
|
|
68
|
-
return { ok: false, reason: "not-rerunnable" };
|
|
69
|
-
projectStandings(deps, projectId);
|
|
70
|
-
return {
|
|
71
|
-
ok: true,
|
|
72
|
-
redone: [],
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
function keyStage(key) {
|
|
76
|
-
const prefix = key.split(":")[0];
|
|
77
|
-
if (prefix === "entry" || prefix === "article")
|
|
78
|
-
return "article";
|
|
79
|
-
if (prefix === "image" || prefix === "reference")
|
|
80
|
-
return "images";
|
|
81
|
-
if (prefix === "audio")
|
|
82
|
-
return "audio";
|
|
83
|
-
if (prefix === "thumbnail")
|
|
84
|
-
return "thumbnail";
|
|
85
|
-
if (prefix === "research")
|
|
86
|
-
return "research";
|
|
87
|
-
if (prefix === "document")
|
|
88
|
-
return "document";
|
|
89
|
-
return "video";
|
|
90
|
-
}
|
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { transact } from "../../kernel/db/tx.js";
|
|
3
|
-
import { isProviderError } from "../../kernel/ports/model.js";
|
|
4
|
-
import { insertPiece, piecesOf, setPiece } from "../../kernel/runner/piece-repo.js";
|
|
5
|
-
import { projectById } from "../admission/repo.js";
|
|
6
|
-
import { setStageProgress } from "../admission/stage-writes.js";
|
|
7
|
-
import { storeText } from "../storage/staging.js";
|
|
8
|
-
import { noTokens, plusUsage } from "../telemetry/model.js";
|
|
9
|
-
import { researchDocuments } from "./documents.js";
|
|
10
|
-
import { chaptersFrom, plannerMessages, subAgentMessages } from "./planner.js";
|
|
11
|
-
import { sourcedAnswer, synthesisMessages } from "./synthesis.js";
|
|
12
|
-
// A model that cannot ground on the web fails the stage immediately, with no fallback to
|
|
13
|
-
// what the model already knows.
|
|
14
|
-
export const webResearchUnsupported = "The chosen AI model can't search the web, which research needs. Choose a model with web search in Edit project → Providers, or turn research off, then Try again.";
|
|
15
|
-
// What a chapter piece carries between runs: its title, and its notes once a sub-agent
|
|
16
|
-
// has answered. That is the whole of the resume.
|
|
17
|
-
const chapterPayload = z.object({ title: z.string(), notes: z.string().optional() });
|
|
18
|
-
export async function runResearch(deps, context, providers) {
|
|
19
|
-
const { projectId } = context.stage;
|
|
20
|
-
const project = projectById(deps.db, projectId);
|
|
21
|
-
if (project === undefined) {
|
|
22
|
-
throw new Error("This project no longer exists; it may have been deleted while it was running.");
|
|
23
|
-
}
|
|
24
|
-
const choice = project.config.llm;
|
|
25
|
-
const articlePrompt = project.config.rendered.article;
|
|
26
|
-
if (choice === undefined || articlePrompt === undefined) {
|
|
27
|
-
// Admission refuses a run whose research is Generate without both, so
|
|
28
|
-
// reaching here is a bug in admission rather than something the user did.
|
|
29
|
-
throw new Error("No AI model or article prompt is set for research. Choose a model in Edit project → Providers and check Edit project → Prompts, then Try again.");
|
|
30
|
-
}
|
|
31
|
-
const brief = { articlePrompt, values: project.config.values };
|
|
32
|
-
try {
|
|
33
|
-
return await research(deps, context, providers, choice, brief);
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
// The adapter says the model cannot ground on the web and the wrapper has already
|
|
37
|
-
// made that terminal; this is where it becomes the sentence the user reads.
|
|
38
|
-
if (isProviderError(error) && error.fault.kind === "unsupported") {
|
|
39
|
-
throw new Error(webResearchUnsupported);
|
|
40
|
-
}
|
|
41
|
-
throw error;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
async function research(deps, context, providers, choice, brief) {
|
|
45
|
-
// Tokens are counted per stage, so the planner, every sub-agent and the synthesis add
|
|
46
|
-
// into one total. A call that was aborted or failed never answers and adds nothing; a
|
|
47
|
-
// provider that reports no usage adds zero, never an estimate.
|
|
48
|
-
let tokens = noTokens;
|
|
49
|
-
const add = (usage) => {
|
|
50
|
-
tokens = plusUsage(tokens, usage);
|
|
51
|
-
};
|
|
52
|
-
const planned = await plan(deps, context, providers, choice, brief, add);
|
|
53
|
-
if (!planned.ok)
|
|
54
|
-
return "held";
|
|
55
|
-
const researched = await researchChapters(deps, context, providers, choice, brief, planned.value, add);
|
|
56
|
-
if (!researched.ok)
|
|
57
|
-
return "held";
|
|
58
|
-
const findings = researched.value;
|
|
59
|
-
const answer = await providers.llm({
|
|
60
|
-
provider: choice.provider,
|
|
61
|
-
model: choice.model,
|
|
62
|
-
...(choice.thinking === undefined ? {} : { thinking: choice.thinking }),
|
|
63
|
-
messages: synthesisMessages(brief, findings),
|
|
64
|
-
documents: researchDocuments(findings),
|
|
65
|
-
previewLabel: "Writing research notes",
|
|
66
|
-
check: (given) => sourcedAnswer("the research summary", given.text),
|
|
67
|
-
});
|
|
68
|
-
if (!answer.ok)
|
|
69
|
-
return "held";
|
|
70
|
-
add(answer.value.usage);
|
|
71
|
-
const { projectId } = context.stage;
|
|
72
|
-
// The notes and every instruction sent are stored on the project. Each sub-agent's own output
|
|
73
|
-
// stays on its chapter row, which is where the resume reads it.
|
|
74
|
-
storeText(deps, {
|
|
75
|
-
projectId,
|
|
76
|
-
stageKind: "research",
|
|
77
|
-
role: "notes",
|
|
78
|
-
text: answer.value.text.trim(),
|
|
79
|
-
});
|
|
80
|
-
storeText(deps, {
|
|
81
|
-
projectId,
|
|
82
|
-
stageKind: "research",
|
|
83
|
-
role: "instructions",
|
|
84
|
-
text: instructionsText(brief, findings),
|
|
85
|
-
});
|
|
86
|
-
deps.count("stage.completed", {
|
|
87
|
-
stage: "research",
|
|
88
|
-
provider: choice.provider,
|
|
89
|
-
model: choice.model,
|
|
90
|
-
...(choice.thinking === undefined ? {} : { thinking: choice.thinking }),
|
|
91
|
-
...tokens,
|
|
92
|
-
});
|
|
93
|
-
deps.log.write("info", "research.done", {
|
|
94
|
-
projectId,
|
|
95
|
-
stage: "research",
|
|
96
|
-
detail: `${String(findings.length)} chapters researched`,
|
|
97
|
-
});
|
|
98
|
-
return "done";
|
|
99
|
-
}
|
|
100
|
-
// The chapter list, planned once and kept. A retry after a failure finds the rows the
|
|
101
|
-
// first run wrote and does not ask again.
|
|
102
|
-
async function plan(deps, context, providers, choice, brief, add) {
|
|
103
|
-
const existing = piecesOf(deps.db, context.stage.id, "chapter");
|
|
104
|
-
if (existing.length > 0) {
|
|
105
|
-
return { ok: true, value: existing };
|
|
106
|
-
}
|
|
107
|
-
const answer = await providers.llm({
|
|
108
|
-
provider: choice.provider,
|
|
109
|
-
model: choice.model,
|
|
110
|
-
...(choice.thinking === undefined ? {} : { thinking: choice.thinking }),
|
|
111
|
-
messages: plannerMessages(brief),
|
|
112
|
-
previewLabel: "Planning research",
|
|
113
|
-
// An empty answer, or one with no chapter in it, is a failed attempt.
|
|
114
|
-
check: (given) => chaptersFrom(given.text).length === 0
|
|
115
|
-
? "The AI model didn't return any research topics. Try again; if it keeps happening, choose a different model in Edit project → Providers."
|
|
116
|
-
: undefined,
|
|
117
|
-
});
|
|
118
|
-
if (!answer.ok)
|
|
119
|
-
return answer;
|
|
120
|
-
add(answer.value.usage);
|
|
121
|
-
// No cap on the count. The list is the prompt's own section guide as often as
|
|
122
|
-
// not, and capping it would silently drop a section the article asks for.
|
|
123
|
-
const planned = chaptersFrom(answer.value.text).map((title, index) => ({
|
|
124
|
-
id: deps.ids.next(),
|
|
125
|
-
stageId: context.stage.id,
|
|
126
|
-
kind: "chapter",
|
|
127
|
-
idx: index + 1,
|
|
128
|
-
state: "pending",
|
|
129
|
-
payload: JSON.stringify({ title }),
|
|
130
|
-
}));
|
|
131
|
-
transact(deps.db, () => {
|
|
132
|
-
for (const piece of planned) {
|
|
133
|
-
insertPiece(deps.db, piece);
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
return { ok: true, value: planned };
|
|
137
|
-
}
|
|
138
|
-
// One sub-agent per chapter, all in parallel. A chapter a previous run finished is not
|
|
139
|
-
// researched again, and one that fails takes down the whole stage while its siblings finish and
|
|
140
|
-
// keep their output for the next resume.
|
|
141
|
-
async function researchChapters(deps, context, providers, choice, brief, chapters, add) {
|
|
142
|
-
const outline = chapters.map((piece) => payloadOf(piece).title);
|
|
143
|
-
const total = chapters.length;
|
|
144
|
-
let done = chapters.filter((piece) => payloadOf(piece).notes !== undefined).length;
|
|
145
|
-
report(deps, context, done, total);
|
|
146
|
-
const outcomes = await Promise.all(chapters.map(async (piece) => {
|
|
147
|
-
const kept = payloadOf(piece);
|
|
148
|
-
if (kept.notes !== undefined) {
|
|
149
|
-
return { ok: true, finding: { title: kept.title, notes: kept.notes } };
|
|
150
|
-
}
|
|
151
|
-
setPiece(deps.db, piece.id, "running", piece.payload);
|
|
152
|
-
try {
|
|
153
|
-
const answer = await providers.forPiece(piece.id).llm({
|
|
154
|
-
provider: choice.provider,
|
|
155
|
-
model: choice.model,
|
|
156
|
-
...(choice.thinking === undefined ? {} : { thinking: choice.thinking }),
|
|
157
|
-
messages: subAgentMessages(brief, kept.title, outline),
|
|
158
|
-
previewLabel: kept.title,
|
|
159
|
-
// Grounding is asked for explicitly, so a model without it says so
|
|
160
|
-
// instead of answering from what it already knows.
|
|
161
|
-
webSearch: true,
|
|
162
|
-
check: (given) => sourcedAnswer(`the research topic "${kept.title}"`, given.text),
|
|
163
|
-
});
|
|
164
|
-
if (!answer.ok) {
|
|
165
|
-
setPiece(deps.db, piece.id, "pending", piece.payload);
|
|
166
|
-
return answer;
|
|
167
|
-
}
|
|
168
|
-
add(answer.value.usage);
|
|
169
|
-
const notes = answer.value.text.trim();
|
|
170
|
-
setPiece(deps.db, piece.id, "done", JSON.stringify({ title: kept.title, notes }));
|
|
171
|
-
done += 1;
|
|
172
|
-
report(deps, context, done, total);
|
|
173
|
-
return { ok: true, finding: { title: kept.title, notes } };
|
|
174
|
-
}
|
|
175
|
-
catch (error) {
|
|
176
|
-
// A cancel is not this chapter failing: an aborted call counts as nothing, and
|
|
177
|
-
// the resume runs a `pending` chapter exactly as a failed one.
|
|
178
|
-
setPiece(deps.db, piece.id, context.signal.aborted ? "pending" : "failed", piece.payload);
|
|
179
|
-
return { ok: false, error };
|
|
180
|
-
}
|
|
181
|
-
}));
|
|
182
|
-
const findings = [];
|
|
183
|
-
for (const outcome of outcomes) {
|
|
184
|
-
if (!outcome.ok) {
|
|
185
|
-
if ("reason" in outcome)
|
|
186
|
-
return outcome;
|
|
187
|
-
throw outcome.error;
|
|
188
|
-
}
|
|
189
|
-
findings.push(outcome.finding);
|
|
190
|
-
}
|
|
191
|
-
return { ok: true, value: findings };
|
|
192
|
-
}
|
|
193
|
-
// K of N chapters researched, written as well as emitted, so a page opened mid-stage reads the
|
|
194
|
-
// count off the row rather than waiting for the next chapter.
|
|
195
|
-
function report(deps, context, done, total) {
|
|
196
|
-
setStageProgress(deps.db, context.stage.id, done, total);
|
|
197
|
-
context.emit({
|
|
198
|
-
type: "stage.progress",
|
|
199
|
-
projectId: context.stage.projectId,
|
|
200
|
-
stage: "research",
|
|
201
|
-
current: done,
|
|
202
|
-
total,
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
// Every instruction sent is stored on the project. All three are pure functions of
|
|
206
|
-
// the brief and the findings, so a resumed run reproduces the ones it did not send.
|
|
207
|
-
function instructionsText(brief, findings) {
|
|
208
|
-
const outline = findings.map((finding) => finding.title);
|
|
209
|
-
const parts = [section("Planner", plannerMessages(brief))];
|
|
210
|
-
for (const title of outline) {
|
|
211
|
-
parts.push(section(`Sub-agent: ${title}`, subAgentMessages(brief, title, outline)));
|
|
212
|
-
}
|
|
213
|
-
parts.push(section("Synthesis", synthesisMessages(brief, findings)));
|
|
214
|
-
return `${parts.join("\n\n")}\n`;
|
|
215
|
-
}
|
|
216
|
-
function section(label, messages) {
|
|
217
|
-
return `=== ${label} ===\n\n${messages.map((message) => message.content).join("\n\n")}`;
|
|
218
|
-
}
|
|
219
|
-
function payloadOf(piece) {
|
|
220
|
-
if (piece.payload === null) {
|
|
221
|
-
throw new Error("Slopify hit an internal error (a saved research topic is empty). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
222
|
-
}
|
|
223
|
-
return chapterPayload.parse(JSON.parse(piece.payload));
|
|
224
|
-
}
|