videobook 0.0.0-stage → 0.2.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/CHANGELOG.md +54 -0
- package/LICENSE +21 -0
- package/README.md +195 -2
- package/bin/videobook.js +7 -0
- package/dist/acestep/client.js +203 -0
- package/dist/acestep/config.js +62 -0
- package/dist/acestep/errors.js +8 -0
- package/dist/acestep/process.js +175 -0
- package/dist/alive-captions/analyze.js +117 -0
- package/dist/alive-captions/audio.js +179 -0
- package/dist/alive-captions/frames.js +59 -0
- package/dist/alive-captions/model.js +37 -0
- package/dist/alive-captions/schema.js +49 -0
- package/dist/alive-captions/track.js +9 -0
- package/dist/api-key.js +33 -0
- package/dist/app-state.js +56 -0
- package/dist/app.js +257 -0
- package/dist/archive-b2.js +332 -0
- package/dist/archive-store.js +169 -0
- package/dist/artifact-label.js +36 -0
- package/dist/artifact-refs.js +49 -0
- package/dist/asset-tag-extraction.js +169 -0
- package/dist/asset-tags.js +228 -0
- package/dist/asset-usage.js +23 -0
- package/dist/audio-cue.js +71 -0
- package/dist/audio-encoding.js +32 -0
- package/dist/audio-sync-request.js +1 -0
- package/dist/audio-sync.js +223 -0
- package/dist/audio-trim-range.js +31 -0
- package/dist/book-context.js +31 -0
- package/dist/book-manager.js +374 -0
- package/dist/catalog-gc.js +26 -0
- package/dist/chain-executor.js +815 -0
- package/dist/chain-generation.js +115 -0
- package/dist/chain-plan.js +746 -0
- package/dist/chain-step-review.js +60 -0
- package/dist/character-name.js +25 -0
- package/dist/chat-generation-routing.js +73 -0
- package/dist/chat-generation-settings.js +78 -0
- package/dist/chat-models.js +44 -0
- package/dist/chat-prompt-rewrite.js +67 -0
- package/dist/chat-router.js +628 -0
- package/dist/chat-view-actions.js +49 -0
- package/dist/cli/args.js +242 -0
- package/dist/cli/config-command.js +103 -0
- package/dist/cli/doctor-command.js +289 -0
- package/dist/cli/entry.js +2 -0
- package/dist/cli/key-report.js +16 -0
- package/dist/cli/main.js +104 -0
- package/dist/cli/mcp-command.js +88 -0
- package/dist/cli/mcp-relay.js +51 -0
- package/dist/cli/open-browser.js +14 -0
- package/dist/cli/run-command.js +231 -0
- package/dist/cli/serve-command.js +62 -0
- package/dist/cli/setup-command.js +60 -0
- package/dist/cli/stdio-guard.js +8 -0
- package/dist/cold-open-model.js +7 -0
- package/dist/cold-open.js +79 -0
- package/dist/command-reference.js +95 -0
- package/dist/content-store.js +74 -0
- package/dist/cost.js +8 -0
- package/dist/crop-geometry.js +23 -0
- package/dist/dancing-workflow.js +447 -0
- package/dist/data-home.js +44 -0
- package/dist/diagnostics.js +109 -0
- package/dist/dialog-quality.js +34 -0
- package/dist/downloads-dest.js +56 -0
- package/dist/editing-moments.js +194 -0
- package/dist/elevenlabs-audio-extension.js +66 -0
- package/dist/engine.js +54 -0
- package/dist/env-file.js +91 -0
- package/dist/env-secrets.js +148 -0
- package/dist/ffmpeg-binary.js +12 -0
- package/dist/ffmpeg.js +425 -0
- package/dist/ffprobe-binary.js +77 -0
- package/dist/forget-asset.js +216 -0
- package/dist/frame-utils.js +27 -0
- package/dist/generation-credit-error.js +11 -0
- package/dist/generation-input-mentions.js +117 -0
- package/dist/generation-input-policy.js +68 -0
- package/dist/generation-reference-mentions.js +97 -0
- package/dist/goal-characters.js +49 -0
- package/dist/goal-plan-v3.js +273 -0
- package/dist/goal-plan.js +220 -0
- package/dist/goal-planner-prompt.js +28 -0
- package/dist/goal-prompt-budget.js +4 -0
- package/dist/goal-reference-images.js +25 -0
- package/dist/goal-state.js +261 -0
- package/dist/goal-stills.js +153 -0
- package/dist/goal-store.js +144 -0
- package/dist/goal-styles.js +144 -0
- package/dist/goal-v3-runtime.js +157 -0
- package/dist/guides/character_schema_guide.md +70 -0
- package/dist/guides/gpt_image_2_5_prompting_guide.md +21 -0
- package/dist/guides/grok_imagine_prompting_guide.md +72 -0
- package/dist/guides/image_reproduction_guide.md +84 -0
- package/dist/guides/kling_3_0_prompting_guide.md +136 -0
- package/dist/guides/nano_banana_pro_prompting_guide.md +28 -0
- package/dist/guides/seedance_2_prompting_guide.md +172 -0
- package/dist/http-access.js +49 -0
- package/dist/http-byte-range.js +31 -0
- package/dist/hydration.js +56 -0
- package/dist/image-compress.js +77 -0
- package/dist/image-edit-default.js +1 -0
- package/dist/image-edit-models.js +41 -0
- package/dist/image-edit-settings.js +11 -0
- package/dist/image-region-edit.js +32 -0
- package/dist/image-subject-map.js +523 -0
- package/dist/image.js +46 -0
- package/dist/index.js +12 -0
- package/dist/job-deadline.js +60 -0
- package/dist/jobs/_alibaba-upload.js +30 -0
- package/dist/jobs/_audio-chunks.js +31 -0
- package/dist/jobs/_download-into-asset.js +15 -0
- package/dist/jobs/_fal-shared.js +22 -0
- package/dist/jobs/_fal-upload.js +35 -0
- package/dist/jobs/_image-edit-adapter.js +213 -0
- package/dist/jobs/_kie-poll.js +119 -0
- package/dist/jobs/_kie-upload.js +32 -0
- package/dist/jobs/_lease.js +163 -0
- package/dist/jobs/_persist-output.js +19 -0
- package/dist/jobs/_storage-retry.js +21 -0
- package/dist/jobs/_transcribe.js +245 -0
- package/dist/jobs/_translate-transcript.js +270 -0
- package/dist/jobs/analyze-alive-captions.js +10 -0
- package/dist/jobs/analyze-editing-moments.js +104 -0
- package/dist/jobs/analyze-jokes.js +143 -0
- package/dist/jobs/analyze-keyword-placement.js +155 -0
- package/dist/jobs/analyze-music.js +14 -0
- package/dist/jobs/analyze-video.js +282 -0
- package/dist/jobs/apply-cuts.js +83 -0
- package/dist/jobs/apply-sfx.js +107 -0
- package/dist/jobs/archive-clip.js +458 -0
- package/dist/jobs/assemble-goal.js +98 -0
- package/dist/jobs/audio-boost.js +71 -0
- package/dist/jobs/audio-mix.js +161 -0
- package/dist/jobs/backfill-asset-tags.js +144 -0
- package/dist/jobs/burn-watermark.js +316 -0
- package/dist/jobs/change-video-speed.js +176 -0
- package/dist/jobs/clean-video-audio.js +207 -0
- package/dist/jobs/cold-open.js +166 -0
- package/dist/jobs/combine-goal.js +108 -0
- package/dist/jobs/complete-provider-task.js +185 -0
- package/dist/jobs/copy-library-asset.js +33 -0
- package/dist/jobs/cost-estimate-assessment.js +113 -0
- package/dist/jobs/crop-image.js +47 -0
- package/dist/jobs/crop-video.js +71 -0
- package/dist/jobs/describe-image.js +181 -0
- package/dist/jobs/detect-scenes.js +33 -0
- package/dist/jobs/doctor-fix.js +100 -0
- package/dist/jobs/download-video.js +427 -0
- package/dist/jobs/enqueue-search-index.js +24 -0
- package/dist/jobs/even-split.js +122 -0
- package/dist/jobs/export-sequence.js +365 -0
- package/dist/jobs/extend-audio.js +229 -0
- package/dist/jobs/extract-audio.js +72 -0
- package/dist/jobs/extract-frame.js +58 -0
- package/dist/jobs/extract-notebook-audio.js +82 -0
- package/dist/jobs/extract-notebook-frame.js +93 -0
- package/dist/jobs/extract-video-clip.js +8 -0
- package/dist/jobs/extract-video-poster.js +55 -0
- package/dist/jobs/extract-waveform.js +25 -0
- package/dist/jobs/finish-goal.js +255 -0
- package/dist/jobs/forget-asset.js +11 -0
- package/dist/jobs/generate-acestep-cover.js +219 -0
- package/dist/jobs/generate-media.js +70 -0
- package/dist/jobs/image-subjects.js +1059 -0
- package/dist/jobs/index.js +18 -0
- package/dist/jobs/inject-first-frame.js +175 -0
- package/dist/jobs/isolate-vocals.js +69 -0
- package/dist/jobs/kie-paygate.js +177 -0
- package/dist/jobs/label-music-sections.js +5 -0
- package/dist/jobs/osg-placement.js +135 -0
- package/dist/jobs/pad-video.js +72 -0
- package/dist/jobs/plan-goal.js +97 -0
- package/dist/jobs/post-record.js +21 -0
- package/dist/jobs/post-to-x.js +21 -0
- package/dist/jobs/prepare-search-reference.js +190 -0
- package/dist/jobs/prepare-temporal-search.js +16 -0
- package/dist/jobs/process-audio.js +67 -0
- package/dist/jobs/process-image.js +58 -0
- package/dist/jobs/process-upload.js +166 -0
- package/dist/jobs/process-video.js +438 -0
- package/dist/jobs/register-all.js +161 -0
- package/dist/jobs/relayout-notebook.js +78 -0
- package/dist/jobs/remove-sound.js +299 -0
- package/dist/jobs/render-project.js +380 -0
- package/dist/jobs/render-qr-outro.js +138 -0
- package/dist/jobs/render-timeline.js +283 -0
- package/dist/jobs/repair-search-indexes.js +42 -0
- package/dist/jobs/replace-video-audio.js +64 -0
- package/dist/jobs/restore-archive-clip.js +53 -0
- package/dist/jobs/reverse-video.js +53 -0
- package/dist/jobs/rewrite-script.js +173 -0
- package/dist/jobs/run-notebook.js +3335 -0
- package/dist/jobs/semantic-index.js +797 -0
- package/dist/jobs/smooth-scene-concat.js +257 -0
- package/dist/jobs/splice-video.js +280 -0
- package/dist/jobs/split-video.js +127 -0
- package/dist/jobs/suggest-cuts.js +149 -0
- package/dist/jobs/suggest-sfx.js +135 -0
- package/dist/jobs/sync-video-to-audio.js +329 -0
- package/dist/jobs/tag-asset.js +266 -0
- package/dist/jobs/task-failure.js +16 -0
- package/dist/jobs/timewarp-video.js +152 -0
- package/dist/jobs/transcribe-video-audio.js +117 -0
- package/dist/jobs/transcribe.js +35 -0
- package/dist/jobs/translate-transcript.js +55 -0
- package/dist/jobs/trim-audio.js +133 -0
- package/dist/jobs/trim-video.js +291 -0
- package/dist/jobs/xai-paygate.js +26 -0
- package/dist/keyword-placement-cache.js +74 -0
- package/dist/lazy-render.js +142 -0
- package/dist/library-browse.js +78 -0
- package/dist/lightning-defaults.js +206 -0
- package/dist/load-env.js +12 -0
- package/dist/local-search-models.js +206 -0
- package/dist/local-upload.js +318 -0
- package/dist/logger.js +69 -0
- package/dist/media/adapters/alibaba.js +20 -0
- package/dist/media/adapters/elevenlabs.js +58 -0
- package/dist/media/adapters/fal.js +45 -0
- package/dist/media/adapters/index.js +26 -0
- package/dist/media/adapters/kie.js +120 -0
- package/dist/media/adapters/pending.js +21 -0
- package/dist/media/adapters/types.js +5 -0
- package/dist/media/adapters/xai.js +18 -0
- package/dist/media/catalog.js +236 -0
- package/dist/media/complete.js +191 -0
- package/dist/media/cost.js +45 -0
- package/dist/media/descriptor.js +157 -0
- package/dist/media/endpoints.js +11 -0
- package/dist/media/frame-options.js +18 -0
- package/dist/media/insert-video-timing.js +17 -0
- package/dist/media/json-schema.js +75 -0
- package/dist/media/legacy-task-types.js +129 -0
- package/dist/media/listing.js +24 -0
- package/dist/media/model-id.js +14 -0
- package/dist/media/preview.js +86 -0
- package/dist/media/prompt-dialects.js +65 -0
- package/dist/media/ref-fields.js +106 -0
- package/dist/media/refs.js +131 -0
- package/dist/media/request.js +188 -0
- package/dist/media/types.js +6 -0
- package/dist/media/wire.js +161 -0
- package/dist/media-library.js +535 -0
- package/dist/media-output-path.js +33 -0
- package/dist/multyculty-workflow.js +757 -0
- package/dist/notebook-action-catalog.js +608 -0
- package/dist/notebook-cell-outputs.js +726 -0
- package/dist/notebook-credit-retry.js +45 -0
- package/dist/notebook-fixtures.js +8 -0
- package/dist/notebook-generation-contract.js +77 -0
- package/dist/notebook-generation-controls.js +145 -0
- package/dist/notebook-goal-layout.js +40 -0
- package/dist/notebook-graph.js +37 -0
- package/dist/notebook-image-edit.js +84 -0
- package/dist/notebook-initial-generation-prompt.js +222 -0
- package/dist/notebook-live-analysis.js +941 -0
- package/dist/notebook-media-ops.js +579 -0
- package/dist/notebook-mutations.js +20 -0
- package/dist/notebook-placement.js +41 -0
- package/dist/notebook-provider-failure.js +87 -0
- package/dist/notebook-published-post.js +26 -0
- package/dist/notebook-reference-resolver.js +227 -0
- package/dist/notebook-relayout.js +270 -0
- package/dist/notebook-run-plan.js +519 -0
- package/dist/notebook-shots.js +162 -0
- package/dist/notebook-workflow.js +856 -0
- package/dist/one-password.js +200 -0
- package/dist/openai-intelligence.js +99 -0
- package/dist/original-media-file.js +12 -0
- package/dist/osg-assets.js +36 -0
- package/dist/osg-placement-cache.js +64 -0
- package/dist/osg-settings.js +27 -0
- package/dist/paid-tools.js +22 -0
- package/dist/platform-guidance.js +26 -0
- package/dist/posts/dropbox-auth.js +91 -0
- package/dist/posts/dropbox-upload.js +132 -0
- package/dist/posts/icloud-drive.js +134 -0
- package/dist/posts/icloud-status.js +64 -0
- package/dist/posts/post-contract.js +2 -0
- package/dist/posts/post-copy-format.js +43 -0
- package/dist/posts/post-copy.js +193 -0
- package/dist/posts/post-folder.js +18 -0
- package/dist/posts/post-metrics.js +119 -0
- package/dist/posts/post-store.js +340 -0
- package/dist/posts/x-post-target.js +43 -0
- package/dist/preview-payload.js +90 -0
- package/dist/prompt-surfaces.js +22 -0
- package/dist/provider-prompt-transform.js +77 -0
- package/dist/provider-status-link.js +21 -0
- package/dist/provider-task-record.js +78 -0
- package/dist/provider-task-status.js +47 -0
- package/dist/provider-usage.js +294 -0
- package/dist/queue.js +810 -0
- package/dist/reference-video-duration.js +17 -0
- package/dist/remotion/BlurFillVideo.js +51 -0
- package/dist/remotion/KeywordGraphicsOverlay.js +68 -0
- package/dist/remotion/OsgOverlay.js +30 -0
- package/dist/remotion/QrOutroComposition.js +47 -0
- package/dist/remotion/RenderComposition.js +11 -0
- package/dist/remotion/browser.js +59 -0
- package/dist/remotion/constants.js +35 -0
- package/dist/remotion/index.js +66 -0
- package/dist/remotion/keyword-graphics.js +151 -0
- package/dist/remotion/keyword-placement.js +182 -0
- package/dist/remotion/osg-spec.js +198 -0
- package/dist/remotion/public/qr-outro-impact.wav +0 -0
- package/dist/remotion/qr-outro-audio.js +1 -0
- package/dist/remotion/qr-outro-spec.js +211 -0
- package/dist/remotion/shared-bundle.js +37 -0
- package/dist/remotion/subtitles/AliveCaptions.js +83 -0
- package/dist/remotion/subtitles/Highlight.js +58 -0
- package/dist/remotion/subtitles/Impact.js +67 -0
- package/dist/remotion/subtitles/SubtitleOverlay.js +9 -0
- package/dist/remotion/subtitles/alive-layout.js +321 -0
- package/dist/remotion/subtitles/alive-types.js +17 -0
- package/dist/remotion/subtitles/chunker.js +68 -0
- package/dist/remotion/subtitles/segments.js +42 -0
- package/dist/remotion/subtitles/styles.js +4 -0
- package/dist/remotion/subtitles/types.js +1 -0
- package/dist/remotion/util.js +18 -0
- package/dist/remotion-cancellation.js +15 -0
- package/dist/rename-helpers.js +204 -0
- package/dist/rhythm/analyze.js +128 -0
- package/dist/rhythm/contract.js +14 -0
- package/dist/rhythm/draft-model.js +19 -0
- package/dist/rhythm/energy.js +133 -0
- package/dist/rhythm/fft.js +44 -0
- package/dist/rhythm/model.js +71 -0
- package/dist/rhythm/music-analysis.js +124 -0
- package/dist/rhythm/music-draft.js +261 -0
- package/dist/rhythm/onset.js +101 -0
- package/dist/rhythm/run-worker.js +51 -0
- package/dist/rhythm/score.js +47 -0
- package/dist/rhythm/section-labels.js +168 -0
- package/dist/rhythm/sequence-rhythm.js +67 -0
- package/dist/rhythm/synthesize.js +103 -0
- package/dist/rhythm/timing.js +98 -0
- package/dist/rhythm/track.js +320 -0
- package/dist/rhythm/visual-fit.js +4 -0
- package/dist/rhythm/worker.js +25 -0
- package/dist/scene-detect.js +200 -0
- package/dist/scene-transition.js +12 -0
- package/dist/scheduled-posts.js +200 -0
- package/dist/search-index-coverage.js +13 -0
- package/dist/search-index-media.js +71 -0
- package/dist/search-reference-store.js +60 -0
- package/dist/search-scoring.js +17 -0
- package/dist/serve.js +125 -0
- package/dist/server-lock.js +143 -0
- package/dist/server.js +42 -0
- package/dist/srt.js +16 -0
- package/dist/tag-backfill-state.js +121 -0
- package/dist/tag-settings.js +30 -0
- package/dist/tag-vocabulary.js +237 -0
- package/dist/test-queue-cleanup.js +1 -0
- package/dist/time-parse.js +50 -0
- package/dist/timeline-helpers.js +554 -0
- package/dist/timewarp.js +87 -0
- package/dist/tools/_image-inputs.js +9 -0
- package/dist/tools/_media-refs.js +84 -0
- package/dist/tools/_shared.js +59 -0
- package/dist/tools/_suggest-shared.js +279 -0
- package/dist/tools/add-timeline-audio.js +73 -0
- package/dist/tools/alive-captions.js +157 -0
- package/dist/tools/analyze-jokes.js +56 -0
- package/dist/tools/analyze-video.js +66 -0
- package/dist/tools/animate-keywords.js +217 -0
- package/dist/tools/api-status.js +42 -0
- package/dist/tools/application-commands.js +34 -0
- package/dist/tools/apply-cuts.js +112 -0
- package/dist/tools/apply-sfx.js +152 -0
- package/dist/tools/apply-watermark.js +670 -0
- package/dist/tools/archive-clip.js +74 -0
- package/dist/tools/asset-history.js +61 -0
- package/dist/tools/asset-manifest.js +24 -0
- package/dist/tools/asset-tags.js +151 -0
- package/dist/tools/assign-slot.js +97 -0
- package/dist/tools/audio-boost.js +107 -0
- package/dist/tools/audio-mix.js +121 -0
- package/dist/tools/book-usage.js +16 -0
- package/dist/tools/chain.js +136 -0
- package/dist/tools/change-video-speed.js +105 -0
- package/dist/tools/chat-history.js +5 -0
- package/dist/tools/chat.js +848 -0
- package/dist/tools/clean-video-audio.js +97 -0
- package/dist/tools/clear-failed-tasks.js +66 -0
- package/dist/tools/cold-open.js +114 -0
- package/dist/tools/command-plan.js +1680 -0
- package/dist/tools/commands.js +1666 -0
- package/dist/tools/concat-images.js +236 -0
- package/dist/tools/connect-service.js +55 -0
- package/dist/tools/copy-file-to-clipboard.js +58 -0
- package/dist/tools/create-character.js +114 -0
- package/dist/tools/create-qart.js +253 -0
- package/dist/tools/create-qr-outro.js +149 -0
- package/dist/tools/create-qrbtf.js +215 -0
- package/dist/tools/create-watermark.js +97 -0
- package/dist/tools/crop-asset.js +18 -0
- package/dist/tools/crop-image.js +120 -0
- package/dist/tools/crop-video.js +97 -0
- package/dist/tools/delete-asset.js +104 -0
- package/dist/tools/describe-image.js +69 -0
- package/dist/tools/detect-scenes.js +44 -0
- package/dist/tools/doctor.js +126 -0
- package/dist/tools/download-video.js +114 -0
- package/dist/tools/duplicate-asset.js +184 -0
- package/dist/tools/edit-timeline.js +59 -0
- package/dist/tools/editing-moments.js +105 -0
- package/dist/tools/estimate-cost.js +171 -0
- package/dist/tools/even-split.js +295 -0
- package/dist/tools/export-asset-file.js +22 -0
- package/dist/tools/export-asset.js +140 -0
- package/dist/tools/export-sequence.js +250 -0
- package/dist/tools/export-to-downloads.js +54 -0
- package/dist/tools/export-transcript-srt.js +79 -0
- package/dist/tools/extend-audio.js +100 -0
- package/dist/tools/extract-audio.js +174 -0
- package/dist/tools/extract-frame.js +108 -0
- package/dist/tools/extract-notebook-audio.js +173 -0
- package/dist/tools/extract-notebook-frame.js +160 -0
- package/dist/tools/extract-video-clip.js +101 -0
- package/dist/tools/generate-media.js +157 -0
- package/dist/tools/generation-payload.js +48 -0
- package/dist/tools/get-timeline.js +12 -0
- package/dist/tools/get-watermark-config.js +38 -0
- package/dist/tools/goal.js +644 -0
- package/dist/tools/image-subjects.js +711 -0
- package/dist/tools/index.js +323 -0
- package/dist/tools/inject-first-frame.js +154 -0
- package/dist/tools/invert-image.js +70 -0
- package/dist/tools/isolate-vocals.js +66 -0
- package/dist/tools/list-assets.js +98 -0
- package/dist/tools/list-cell-generations.js +225 -0
- package/dist/tools/list-elevenlabs-voices.js +163 -0
- package/dist/tools/log-wrapper.js +29 -0
- package/dist/tools/make-cover-song.js +95 -0
- package/dist/tools/mark-asset-seen.js +23 -0
- package/dist/tools/media-library.js +245 -0
- package/dist/tools/move-timeline-audio.js +35 -0
- package/dist/tools/music-analysis.js +60 -0
- package/dist/tools/music-draft.js +29 -0
- package/dist/tools/pad-video.js +87 -0
- package/dist/tools/post-record.js +83 -0
- package/dist/tools/post.js +168 -0
- package/dist/tools/prompt-history.js +164 -0
- package/dist/tools/provider-key-status.js +248 -0
- package/dist/tools/qrbtf-render.js +849 -0
- package/dist/tools/read-asset-metadata.js +26 -0
- package/dist/tools/read-file-at-commit.js +29 -0
- package/dist/tools/read-transcript.js +41 -0
- package/dist/tools/reconcile-project-work.js +1452 -0
- package/dist/tools/recover-asset.js +28 -0
- package/dist/tools/remove-sound.js +72 -0
- package/dist/tools/remove-timeline-audio.js +34 -0
- package/dist/tools/remove-timeline-slot.js +40 -0
- package/dist/tools/rename-all.js +434 -0
- package/dist/tools/rename-asset.js +34 -0
- package/dist/tools/render-settings.js +47 -0
- package/dist/tools/render-timeline.js +120 -0
- package/dist/tools/render.js +384 -0
- package/dist/tools/reorder-character-images.js +45 -0
- package/dist/tools/replace-video-audio.js +133 -0
- package/dist/tools/restore-archive-clips.js +209 -0
- package/dist/tools/restore-asset-version.js +37 -0
- package/dist/tools/retry-generation.js +54 -0
- package/dist/tools/reverse-video.js +74 -0
- package/dist/tools/rewrite-prompt.js +388 -0
- package/dist/tools/rewrite-script.js +67 -0
- package/dist/tools/schema-from-apicity.js +66 -0
- package/dist/tools/search-archive.js +150 -0
- package/dist/tools/semantic-search.js +1214 -0
- package/dist/tools/sequence-rhythm.js +43 -0
- package/dist/tools/set-character-metadata.js +84 -0
- package/dist/tools/set-env-secret.js +40 -0
- package/dist/tools/set-osg.js +143 -0
- package/dist/tools/show-folder.js +37 -0
- package/dist/tools/smooth-scene-concat.js +122 -0
- package/dist/tools/splice-video.js +149 -0
- package/dist/tools/split-video.js +202 -0
- package/dist/tools/suggest-cuts.js +45 -0
- package/dist/tools/suggest-sfx.js +43 -0
- package/dist/tools/sync-video-to-audio.js +115 -0
- package/dist/tools/tag-backfill.js +177 -0
- package/dist/tools/timewarp-video.js +85 -0
- package/dist/tools/tool-def.js +1 -0
- package/dist/tools/tool-json-schema.js +116 -0
- package/dist/tools/transcribe.js +90 -0
- package/dist/tools/translate-subtitles.js +59 -0
- package/dist/tools/trim-audio.js +139 -0
- package/dist/tools/trim-video.js +168 -0
- package/dist/tools/update-script.js +25 -0
- package/dist/tools/update-timeline-audio.js +40 -0
- package/dist/tools/upload-file.js +41 -0
- package/dist/tools/verify-x-mentions.js +141 -0
- package/dist/tools/video-editing-command-contract.js +291 -0
- package/dist/tools/video-source-reference.js +30 -0
- package/dist/tools/video-time-input.js +18 -0
- package/dist/tools/videobook-domain.js +2021 -0
- package/dist/tools/wait-for-asset-ready.js +143 -0
- package/dist/tools/workspace-actions.js +54 -0
- package/dist/transcript-projection.js +202 -0
- package/dist/transcript-srt.js +33 -0
- package/dist/upload.js +76 -0
- package/dist/usage-providers.js +25 -0
- package/dist/video-analysis-projection.js +46 -0
- package/dist/video-poster.js +33 -0
- package/dist/video-proxy.js +99 -0
- package/dist/vision-regions.js +111 -0
- package/dist/voice-actions.js +90 -0
- package/dist/voice-audio.js +55 -0
- package/dist/voice-config.js +122 -0
- package/dist/voice-continuation.js +54 -0
- package/dist/voice-contract.js +1 -0
- package/dist/voice-http.js +145 -0
- package/dist/voice-reply-progress.js +55 -0
- package/dist/voice-session.js +992 -0
- package/dist/watermark-badge.js +259 -0
- package/dist/watermark-config.js +111 -0
- package/dist/watermark-handle-position.js +55 -0
- package/dist/watermark-platform.js +15 -0
- package/dist/watermark-timing.js +16 -0
- package/dist/workspace-action-catalog.js +315 -0
- package/dist/workspace-action-contract.js +21 -0
- package/dist/workspace-action-groups.js +98 -0
- package/dist/workspace-action-surfaces.js +7 -0
- package/dist/x/account.js +88 -0
- package/dist/x/authenticated-handle.js +30 -0
- package/dist/x/oauth-flow.js +191 -0
- package/dist/x/post-contract.js +1 -0
- package/dist/x/post-metadata.js +44 -0
- package/dist/x/post-to-x.js +456 -0
- package/dist/x/published-post-cell.js +90 -0
- package/dist/x/x-tokens.js +69 -0
- package/dist/ytdlp-binary.js +65 -0
- package/dist/zod-slots.js +334 -0
- package/package.json +84 -4
- package/ui/assets/alive-captions-BWb5nLKj.css +1 -0
- package/ui/assets/alive-captions-DPEr1nKM.js +1 -0
- package/ui/assets/confirm-bodies-D8ouHXKy.js +1 -0
- package/ui/assets/confirm-bodies-DrMhaXC6.css +1 -0
- package/ui/assets/fullscreen-B7nw4YbE.js +1 -0
- package/ui/assets/goal-ui-DK-xTCK0.js +2 -0
- package/ui/assets/goal-ui-DXdUM2KF.css +1 -0
- package/ui/assets/index-DzPAnCUj.css +1 -0
- package/ui/assets/index-kS1905s5.js +37 -0
- package/ui/assets/inspector-MlzQbTVo.js +1 -0
- package/ui/assets/jobs-ui-CoinOWx0.js +1 -0
- package/ui/assets/music-video-Cbe80kmQ.js +1 -0
- package/ui/assets/music-video-DPaiKKEq.css +1 -0
- package/ui/assets/ui-Oirr9VOf.css +1 -0
- package/ui/assets/ui-xo_Y1yQg.js +2 -0
- package/ui/assets/vendor-D2rfiZ5j.css +1 -0
- package/ui/assets/vendor-DfTCWgKm.js +19 -0
- package/ui/assets/vendor-flow-BXbeWxW7.js +7 -0
- package/ui/assets/vendor-flow-BnuhLJ6X.css +1 -0
- package/ui/assets/vendor-icons-CGqhCIXV.js +1 -0
- package/ui/assets/vendor-mcp-BTlEJcHV.js +67 -0
- package/ui/assets/vendor-react-Dj7D1fD4.js +9 -0
- package/ui/assets/vendor-remotion-CH1HXrlc.js +95 -0
- package/ui/assets/videobook-Qlp9HQFu.js +20 -0
- package/ui/favicon.svg +8 -0
- package/ui/index.html +126 -0
|
@@ -0,0 +1,848 @@
|
|
|
1
|
+
import { v7 as uuidv7 } from "uuid";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { applyBackgroundAudioRoute, planBackgroundAudioFromQuery, applyBytedanceNamedModelRoute, applyStackedChainRoute, applyVideoJoinRoute, AUTO_APPLY_TOOL_NAMES, buildChatRoutingPrompt, buildChatRoutingUserMessage, buildRoutableTools, effectForTool, getRoutableTool, NESTED_TIMESTAMP_PARAMS, TIMESTAMP_PARAMS, } from "../chat-router.js";
|
|
4
|
+
import { ensureChainStepIds, planStackedChainFromQuery, preflightChainPlan, } from "../chain-plan.js";
|
|
5
|
+
import { withoutUnrequestedSettings } from "../chat-generation-settings.js";
|
|
6
|
+
import { resolveChatViewAction } from "../chat-view-actions.js";
|
|
7
|
+
import { rewriteChatGenerationPrompt } from "../chat-prompt-rewrite.js";
|
|
8
|
+
import { parseTime } from "../time-parse.js";
|
|
9
|
+
import { withLogging } from "./log-wrapper.js";
|
|
10
|
+
import { applyGenerationRoutingDefaults } from "../chat-generation-routing.js";
|
|
11
|
+
import { getApiKey } from "../api-key.js";
|
|
12
|
+
import { SCRIPT_FILENAME } from "../jobs/describe-image.js";
|
|
13
|
+
import { timelineSlotArtifactIds } from "../timeline-helpers.js";
|
|
14
|
+
import { expandSlotRefs, parseArtifactIdTags, resolveArtifactRefs, } from "../artifact-refs.js";
|
|
15
|
+
import { buildIntelligenceResponses, firstResponseText, intelligenceRequest, } from "../openai-intelligence.js";
|
|
16
|
+
import { chatIntelligenceRequestOptions, chatModelSchema, } from "../chat-models.js";
|
|
17
|
+
import { notebookGridAddress, } from "videobook-engine/notebook-grid";
|
|
18
|
+
import { NOTEBOOK_GRID_ADDRESS_SOURCE, replaceNotebookMentions, } from "videobook-engine/notebook-mentions";
|
|
19
|
+
import { notebookMediaAddresses, resolveNotebookPromptReferences, tagNotebookGridAddresses, } from "../notebook-reference-resolver.js";
|
|
20
|
+
const chatActionPlanSchema = z.object({
|
|
21
|
+
kind: z.literal("chat-action-plan"),
|
|
22
|
+
planId: z.string().uuid(),
|
|
23
|
+
bookId: z.string().uuid(),
|
|
24
|
+
prompt: z.string().min(1),
|
|
25
|
+
routedTool: z.string().min(1),
|
|
26
|
+
tool: z.string().min(1),
|
|
27
|
+
params: z.record(z.string(), z.unknown()),
|
|
28
|
+
message: z.string(),
|
|
29
|
+
summary: z.string().min(1),
|
|
30
|
+
effect: z.enum(["mutation", "generation", "view"]),
|
|
31
|
+
confirmation: z.object({
|
|
32
|
+
required: z.boolean(),
|
|
33
|
+
reason: z.string().min(1),
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
const CHAT_ROUTING_MAX_OUTPUT_TOKENS = 2048;
|
|
37
|
+
const CHAT_ROUTING_TIMEOUT_MS = 45_000;
|
|
38
|
+
const NOTEBOOK_DESTINATION_SOURCE = NOTEBOOK_GRID_ADDRESS_SOURCE;
|
|
39
|
+
function duplicateDestinationReference(prompt) {
|
|
40
|
+
if (!/\b(?:copy|duplicate)\b/iu.test(prompt))
|
|
41
|
+
return null;
|
|
42
|
+
const explicit = new RegExp(`\\b(?:at|in|into|to)\\s+@(${NOTEBOOK_DESTINATION_SOURCE})(?![\\w-])`, "iu").exec(prompt);
|
|
43
|
+
if (explicit?.[1])
|
|
44
|
+
return explicit[1].toLowerCase();
|
|
45
|
+
const mentions = [...prompt.matchAll(new RegExp(`@(${NOTEBOOK_DESTINATION_SOURCE})(?![\\w-])`, "giu"))];
|
|
46
|
+
return mentions.length > 1
|
|
47
|
+
? mentions.at(-1)?.[1]?.toLowerCase() ?? null
|
|
48
|
+
: null;
|
|
49
|
+
}
|
|
50
|
+
function buildChatNotebookContext(engine, notebook) {
|
|
51
|
+
if (!notebook)
|
|
52
|
+
return null;
|
|
53
|
+
return {
|
|
54
|
+
id: notebook.id,
|
|
55
|
+
name: notebook.name,
|
|
56
|
+
cells: notebook.cells.map((cell) => {
|
|
57
|
+
const artifact = cell.outputArtifactId
|
|
58
|
+
? engine.artifacts.get(cell.outputArtifactId)
|
|
59
|
+
: null;
|
|
60
|
+
return {
|
|
61
|
+
address: `@${notebookGridAddress(cell.slot)}`,
|
|
62
|
+
cellId: cell.id,
|
|
63
|
+
...(cell.label === undefined ? {} : { label: cell.label }),
|
|
64
|
+
type: cell.type,
|
|
65
|
+
outputAsset: artifact?.ok ? `@${artifact.value.artifactId}` : null,
|
|
66
|
+
};
|
|
67
|
+
}),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function attachNotebookReferences(assetContext, notebookContext) {
|
|
71
|
+
if (!notebookContext)
|
|
72
|
+
return assetContext;
|
|
73
|
+
return assetContext.map((asset) => {
|
|
74
|
+
const assetId = typeof asset.asset_id === "string" ? asset.asset_id : "";
|
|
75
|
+
const references = notebookContext.cells
|
|
76
|
+
.filter((cell) => cell.outputAsset === `@${assetId}`)
|
|
77
|
+
.map((cell) => cell.address);
|
|
78
|
+
return references.length > 0
|
|
79
|
+
? { ...asset, notebook_references: references }
|
|
80
|
+
: asset;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function formatHistoryForPrompt(lines) {
|
|
84
|
+
if (lines.length === 0)
|
|
85
|
+
return "";
|
|
86
|
+
const formatted = lines.map((line) => line.role === "user"
|
|
87
|
+
? `User: ${line.text ?? ""}`
|
|
88
|
+
: `Assistant: ${line.tool ? `[${line.tool}] ` : ""}${line.message ?? ""}`);
|
|
89
|
+
return `Recent conversation:\n${formatted.join("\n")}`;
|
|
90
|
+
}
|
|
91
|
+
function isMediaContextObject(value) {
|
|
92
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
93
|
+
}
|
|
94
|
+
function stripAssetToken(value) {
|
|
95
|
+
return value.replace(/^@/u, "").trim();
|
|
96
|
+
}
|
|
97
|
+
function stringAssetIds(value) {
|
|
98
|
+
if (!Array.isArray(value))
|
|
99
|
+
return [];
|
|
100
|
+
return value.flatMap((entry) => typeof entry === "string" && entry.trim()
|
|
101
|
+
? [stripAssetToken(entry)]
|
|
102
|
+
: []);
|
|
103
|
+
}
|
|
104
|
+
function overlapNumber(value) {
|
|
105
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0)
|
|
106
|
+
return value;
|
|
107
|
+
if (typeof value === "string" && value.trim()) {
|
|
108
|
+
const parsed = Number(value);
|
|
109
|
+
if (Number.isFinite(parsed) && parsed >= 0)
|
|
110
|
+
return parsed;
|
|
111
|
+
}
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
export function normalizeChatRoutedParams(tool, source) {
|
|
115
|
+
const params = { ...source };
|
|
116
|
+
if (tool === "plan_chat_command") {
|
|
117
|
+
const context = params.media_context;
|
|
118
|
+
if (Array.isArray(context)) {
|
|
119
|
+
if (context.length === 1 && isMediaContextObject(context[0])) {
|
|
120
|
+
params.media_context = context[0];
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
delete params.media_context;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return params;
|
|
127
|
+
}
|
|
128
|
+
if (tool === "chain") {
|
|
129
|
+
if ("steps" in params)
|
|
130
|
+
params.steps = ensureChainStepIds(params.steps);
|
|
131
|
+
return params;
|
|
132
|
+
}
|
|
133
|
+
if (tool !== "splice_video")
|
|
134
|
+
return params;
|
|
135
|
+
const assetIds = stringAssetIds(params.asset_ids);
|
|
136
|
+
if (typeof params.first_asset_id !== "string"
|
|
137
|
+
&& assetIds.length >= 2) {
|
|
138
|
+
params.first_asset_id = assetIds[0];
|
|
139
|
+
params.second_asset_id = assetIds[1];
|
|
140
|
+
if (assetIds.length > 2)
|
|
141
|
+
params.additional_asset_ids = assetIds.slice(2);
|
|
142
|
+
}
|
|
143
|
+
if (typeof params.first_asset_id === "string") {
|
|
144
|
+
params.first_asset_id = stripAssetToken(params.first_asset_id);
|
|
145
|
+
}
|
|
146
|
+
if (typeof params.second_asset_id === "string") {
|
|
147
|
+
params.second_asset_id = stripAssetToken(params.second_asset_id);
|
|
148
|
+
}
|
|
149
|
+
const additional = stringAssetIds(params.additional_asset_ids);
|
|
150
|
+
if (additional.length > 0)
|
|
151
|
+
params.additional_asset_ids = additional;
|
|
152
|
+
const overlap = overlapNumber(params.overlap);
|
|
153
|
+
if (overlap !== undefined)
|
|
154
|
+
params.overlap = overlap;
|
|
155
|
+
else if (params.overlap === undefined)
|
|
156
|
+
params.overlap = 0;
|
|
157
|
+
delete params.asset_ids;
|
|
158
|
+
delete params.media_context;
|
|
159
|
+
return params;
|
|
160
|
+
}
|
|
161
|
+
export function normalizeChatTimestampParams(tool, source) {
|
|
162
|
+
const params = { ...source };
|
|
163
|
+
const timestampParams = TIMESTAMP_PARAMS[tool];
|
|
164
|
+
if (timestampParams) {
|
|
165
|
+
for (const paramName of timestampParams) {
|
|
166
|
+
if (!(paramName in params))
|
|
167
|
+
continue;
|
|
168
|
+
try {
|
|
169
|
+
params[paramName] = parseTime(params[paramName]);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const nestedTimestampParams = NESTED_TIMESTAMP_PARAMS[tool];
|
|
177
|
+
if (!nestedTimestampParams)
|
|
178
|
+
return params;
|
|
179
|
+
for (const [collectionName, fieldNames] of Object.entries(nestedTimestampParams)) {
|
|
180
|
+
const collection = params[collectionName];
|
|
181
|
+
if (!Array.isArray(collection))
|
|
182
|
+
continue;
|
|
183
|
+
params[collectionName] = collection.map((item) => {
|
|
184
|
+
if (typeof item !== "object" || item === null || Array.isArray(item))
|
|
185
|
+
return item;
|
|
186
|
+
const normalized = { ...item };
|
|
187
|
+
for (const fieldName of fieldNames) {
|
|
188
|
+
if (!(fieldName in normalized))
|
|
189
|
+
continue;
|
|
190
|
+
try {
|
|
191
|
+
normalized[fieldName] = parseTime(normalized[fieldName]);
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return normalized;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return params;
|
|
201
|
+
}
|
|
202
|
+
/** Close open strings / braces / brackets so truncated Gemini JSON can parse. */
|
|
203
|
+
function closeTruncatedJson(text) {
|
|
204
|
+
let inString = false;
|
|
205
|
+
let escape = false;
|
|
206
|
+
const stack = [];
|
|
207
|
+
for (const ch of text) {
|
|
208
|
+
if (inString) {
|
|
209
|
+
if (escape) {
|
|
210
|
+
escape = false;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (ch === "\\") {
|
|
214
|
+
escape = true;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (ch === "\"")
|
|
218
|
+
inString = false;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (ch === "\"") {
|
|
222
|
+
inString = true;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (ch === "{")
|
|
226
|
+
stack.push("}");
|
|
227
|
+
else if (ch === "[")
|
|
228
|
+
stack.push("]");
|
|
229
|
+
else if (ch === "}" || ch === "]") {
|
|
230
|
+
if (stack.length > 0 && stack[stack.length - 1] === ch)
|
|
231
|
+
stack.pop();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
let out = text;
|
|
235
|
+
if (inString)
|
|
236
|
+
out += "\"";
|
|
237
|
+
// Drop a trailing incomplete property fragment: , "key or , "key": value-less
|
|
238
|
+
out = out.replace(/,\s*"[^"]*"\s*:\s*$/u, "");
|
|
239
|
+
out = out.replace(/,\s*"[^"]*$/u, "");
|
|
240
|
+
out = out.replace(/,\s*$/u, "");
|
|
241
|
+
// Re-scan stack after stripping incomplete tails
|
|
242
|
+
inString = false;
|
|
243
|
+
escape = false;
|
|
244
|
+
stack.length = 0;
|
|
245
|
+
for (const ch of out) {
|
|
246
|
+
if (inString) {
|
|
247
|
+
if (escape) {
|
|
248
|
+
escape = false;
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (ch === "\\") {
|
|
252
|
+
escape = true;
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (ch === "\"")
|
|
256
|
+
inString = false;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (ch === "\"") {
|
|
260
|
+
inString = true;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (ch === "{")
|
|
264
|
+
stack.push("}");
|
|
265
|
+
else if (ch === "[")
|
|
266
|
+
stack.push("]");
|
|
267
|
+
else if (ch === "}" || ch === "]") {
|
|
268
|
+
if (stack.length > 0 && stack[stack.length - 1] === ch)
|
|
269
|
+
stack.pop();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (inString)
|
|
273
|
+
out += "\"";
|
|
274
|
+
while (stack.length > 0)
|
|
275
|
+
out += stack.pop();
|
|
276
|
+
return out;
|
|
277
|
+
}
|
|
278
|
+
/** Best-effort repairs for common Gemini JSON glitches before JSON.parse. */
|
|
279
|
+
export function repairChatRoutingJson(text) {
|
|
280
|
+
let out = text.trim();
|
|
281
|
+
// Strip trailing commas before } or ]
|
|
282
|
+
out = out.replace(/,(\s*[}\]])/g, "$1");
|
|
283
|
+
// Quote bare http(s) URLs used as property values
|
|
284
|
+
out = out.replace(/(:\s*)(https?:\/\/[^\s,"}\]]+)/g, (_m, prefix, url) => `${prefix}${JSON.stringify(url)}`);
|
|
285
|
+
// Quote bare @slug tokens used as property values
|
|
286
|
+
out = out.replace(/(:\s*)(@[\w-]+)/g, (_m, prefix, slug) => `${prefix}${JSON.stringify(slug)}`);
|
|
287
|
+
// Close truncated objects (missing final } / message field) — the live
|
|
288
|
+
// failure mode for create_watermark_card routing under Kie load.
|
|
289
|
+
out = closeTruncatedJson(out);
|
|
290
|
+
return out;
|
|
291
|
+
}
|
|
292
|
+
export function parseChatDecision(outputText) {
|
|
293
|
+
const attempts = [outputText, repairChatRoutingJson(outputText)];
|
|
294
|
+
let lastError;
|
|
295
|
+
for (const candidate of attempts) {
|
|
296
|
+
try {
|
|
297
|
+
const parsed = JSON.parse(candidate);
|
|
298
|
+
if (parsed && typeof parsed === "object" && typeof parsed.tool === "string") {
|
|
299
|
+
return {
|
|
300
|
+
tool: parsed.tool,
|
|
301
|
+
params: (parsed.params && typeof parsed.params === "object"
|
|
302
|
+
? parsed.params
|
|
303
|
+
: {}),
|
|
304
|
+
message: typeof parsed.message === "string" ? parsed.message : "",
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
lastError = new Error("Routing decision missing tool field");
|
|
308
|
+
}
|
|
309
|
+
catch (e) {
|
|
310
|
+
lastError = e;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const msg = lastError instanceof Error ? lastError.message : String(lastError);
|
|
314
|
+
const snippet = outputText.length > 200 ? `${outputText.slice(0, 200)}…` : outputText;
|
|
315
|
+
throw new Error(`${msg} (raw: ${JSON.stringify(snippet)})`);
|
|
316
|
+
}
|
|
317
|
+
export async function enrichAssetContext(engine, resolvedAssets) {
|
|
318
|
+
const rows = [];
|
|
319
|
+
for (const a of resolvedAssets) {
|
|
320
|
+
const row = {
|
|
321
|
+
tag: a.tag,
|
|
322
|
+
type: a.artifactType,
|
|
323
|
+
asset_id: a.artifactId,
|
|
324
|
+
has_file: a.filePath !== null,
|
|
325
|
+
};
|
|
326
|
+
const metaResult = await engine.metadata.artifacts.read(a.artifactId, "original");
|
|
327
|
+
if (metaResult.ok) {
|
|
328
|
+
const sourceUrl = typeof metaResult.value.source_url === "string"
|
|
329
|
+
? metaResult.value.source_url
|
|
330
|
+
: typeof metaResult.value.webpage_url === "string"
|
|
331
|
+
? metaResult.value.webpage_url
|
|
332
|
+
: null;
|
|
333
|
+
if (sourceUrl)
|
|
334
|
+
row.source_url = sourceUrl;
|
|
335
|
+
}
|
|
336
|
+
rows.push(row);
|
|
337
|
+
}
|
|
338
|
+
return rows;
|
|
339
|
+
}
|
|
340
|
+
export async function loadScriptContentsForChat(engine, resolvedAssets) {
|
|
341
|
+
const scriptContents = [];
|
|
342
|
+
for (const asset of resolvedAssets) {
|
|
343
|
+
if (asset.artifactType !== "script" || !asset.filePath)
|
|
344
|
+
continue;
|
|
345
|
+
const readResult = await engine.files.read(asset.artifactId, SCRIPT_FILENAME);
|
|
346
|
+
if (!readResult.ok)
|
|
347
|
+
continue;
|
|
348
|
+
const content = typeof readResult.value === "string"
|
|
349
|
+
? readResult.value
|
|
350
|
+
: new TextDecoder().decode(readResult.value);
|
|
351
|
+
scriptContents.push({ tag: asset.tag, content });
|
|
352
|
+
}
|
|
353
|
+
return scriptContents;
|
|
354
|
+
}
|
|
355
|
+
function appendChatHistory(engine, input) {
|
|
356
|
+
void engine.logs
|
|
357
|
+
.append("chat", { role: "user", text: input.prompt, ts: Date.now() })
|
|
358
|
+
.catch(() => { });
|
|
359
|
+
void engine.logs
|
|
360
|
+
.append("chat", {
|
|
361
|
+
role: "assistant",
|
|
362
|
+
tool: input.tool,
|
|
363
|
+
params: input.params,
|
|
364
|
+
message: input.message,
|
|
365
|
+
error: input.failed,
|
|
366
|
+
ts: Date.now(),
|
|
367
|
+
})
|
|
368
|
+
.catch(() => { });
|
|
369
|
+
}
|
|
370
|
+
function autoApplyReason(tool, chainPausesPerStep = false) {
|
|
371
|
+
return tool === "duplicate_asset"
|
|
372
|
+
? "Duplication is reversible and only adds a copied asset and cell"
|
|
373
|
+
: tool === "extract_audio"
|
|
374
|
+
? "Audio extraction is free, local, and only adds derived media"
|
|
375
|
+
: tool === "chain"
|
|
376
|
+
? chainPausesPerStep
|
|
377
|
+
? "Every step that spends money stops for its own review before it runs"
|
|
378
|
+
: "This chain only reads or auto-applies existing local actions"
|
|
379
|
+
: "Frame extraction is free and only adds a new image cell";
|
|
380
|
+
}
|
|
381
|
+
async function applyChatActionPlan(engine, bookId, plan) {
|
|
382
|
+
if (plan.bookId !== bookId) {
|
|
383
|
+
return error("The chat action belongs to a different book");
|
|
384
|
+
}
|
|
385
|
+
if (plan.tool === "chat")
|
|
386
|
+
return error("Chat cannot route to itself");
|
|
387
|
+
if (plan.effect === "view")
|
|
388
|
+
return error("View actions open in the app and have nothing to apply");
|
|
389
|
+
const { getAllToolDefs } = await import("./index.js");
|
|
390
|
+
const targetDef = getAllToolDefs(engine).find((definition) => definition.name === plan.tool);
|
|
391
|
+
if (!targetDef)
|
|
392
|
+
return error(`Tool handler not found: ${plan.tool}`);
|
|
393
|
+
const params = { ...plan.params, book_id: bookId };
|
|
394
|
+
if (plan.tool === "chain" && "steps" in params) {
|
|
395
|
+
params.steps = ensureChainStepIds(params.steps);
|
|
396
|
+
}
|
|
397
|
+
const validated = z.object(targetDef.schema).safeParse(params);
|
|
398
|
+
if (!validated.success) {
|
|
399
|
+
return error(`Invalid parameters for ${plan.routedTool}: ${z.prettifyError(validated.error)}`);
|
|
400
|
+
}
|
|
401
|
+
const wrappedDef = withLogging(targetDef, engine);
|
|
402
|
+
let result;
|
|
403
|
+
try {
|
|
404
|
+
result = await wrappedDef.handler(validated.data);
|
|
405
|
+
}
|
|
406
|
+
catch (cause) {
|
|
407
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
408
|
+
result = error(`${plan.routedTool} failed: ${message}`);
|
|
409
|
+
}
|
|
410
|
+
appendChatHistory(engine, {
|
|
411
|
+
prompt: plan.prompt,
|
|
412
|
+
tool: plan.routedTool,
|
|
413
|
+
params: plan.params,
|
|
414
|
+
message: plan.message,
|
|
415
|
+
failed: Boolean(result.isError),
|
|
416
|
+
});
|
|
417
|
+
return result;
|
|
418
|
+
}
|
|
419
|
+
export { chatActionPlanSchema, duplicateDestinationReference };
|
|
420
|
+
export function chatDef(engine) {
|
|
421
|
+
return {
|
|
422
|
+
name: "chat",
|
|
423
|
+
description: "Route one composer request through the app's chat model: runs reads and returns reviewable plans for edits and generation. Agents should call the specific tools directly instead.",
|
|
424
|
+
schema: {
|
|
425
|
+
prompt: z.string().min(1).optional().describe("Natural language query with optional @asset tags"),
|
|
426
|
+
book_id: z.string().uuid().describe("Book UUID"),
|
|
427
|
+
notebook_id: z.string().uuid().optional().describe("Open notebook UUID for resolving grid addresses"),
|
|
428
|
+
model: chatModelSchema.optional().describe("Chat routing model"),
|
|
429
|
+
action_plan: chatActionPlanSchema.optional().describe("Previously previewed action to apply"),
|
|
430
|
+
},
|
|
431
|
+
handler: (args) => handleChatRequest(engine, args),
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
export function prepareVoiceChatAction(engine, input, decision) {
|
|
435
|
+
return handleChatRequest(engine, input, decision);
|
|
436
|
+
}
|
|
437
|
+
async function handleChatRequest(engine, args, proposedDecision) {
|
|
438
|
+
const { prompt, book_id: bookId, notebook_id: notebookId, model, action_plan: actionPlan } = args;
|
|
439
|
+
if (actionPlan)
|
|
440
|
+
return await applyChatActionPlan(engine, bookId, actionPlan);
|
|
441
|
+
if (!prompt)
|
|
442
|
+
return error("A chat prompt is required");
|
|
443
|
+
const chatRequest = chatIntelligenceRequestOptions(model);
|
|
444
|
+
const book = engine.book.get();
|
|
445
|
+
let notebookDocument = null;
|
|
446
|
+
try {
|
|
447
|
+
if (notebookId) {
|
|
448
|
+
const notebook = engine.notebooks.read(notebookId);
|
|
449
|
+
if (!notebook.ok)
|
|
450
|
+
throw new Error(notebook.error.message);
|
|
451
|
+
notebookDocument = notebook.value;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
catch (cause) {
|
|
455
|
+
return error(cause instanceof Error ? cause.message : String(cause));
|
|
456
|
+
}
|
|
457
|
+
const notebookContext = buildChatNotebookContext(engine, notebookDocument);
|
|
458
|
+
const notebookAddresses = notebookDocument
|
|
459
|
+
? notebookMediaAddresses(notebookDocument)
|
|
460
|
+
: new Set();
|
|
461
|
+
// 0. Tag bare grid addresses ("the image at A1") so the steps below see the
|
|
462
|
+
// reference the speaker meant instead of prose.
|
|
463
|
+
const taggedPrompt = tagNotebookGridAddresses(prompt, notebookAddresses);
|
|
464
|
+
// 1. Expand @s01 slot refs → the artifact ids they occupy
|
|
465
|
+
const expandedPrompt = expandSlotRefs(taggedPrompt, timelineSlotArtifactIds(engine));
|
|
466
|
+
const stackedPlan = planStackedChainFromQuery(expandedPrompt);
|
|
467
|
+
if (stackedPlan) {
|
|
468
|
+
const { getAllToolDefs } = await import("./index.js");
|
|
469
|
+
const allDefs = getAllToolDefs(engine);
|
|
470
|
+
const targetDef = allDefs.find((definition) => definition.name === "chain");
|
|
471
|
+
if (!targetDef)
|
|
472
|
+
return error("Tool handler not found: chain");
|
|
473
|
+
const preflight = await preflightChainPlan({
|
|
474
|
+
action: "run",
|
|
475
|
+
version: 1,
|
|
476
|
+
book_id: bookId,
|
|
477
|
+
...(notebookId ? { notebook_id: notebookId } : {}),
|
|
478
|
+
steps: stackedPlan.steps,
|
|
479
|
+
}, {
|
|
480
|
+
engine,
|
|
481
|
+
bookId,
|
|
482
|
+
notebookId,
|
|
483
|
+
toolSchemas: new Map(allDefs.map((definition) => [definition.name, definition.schema])),
|
|
484
|
+
autoApplyTools: AUTO_APPLY_TOOL_NAMES,
|
|
485
|
+
effectForTool: (name) => effectForTool(name, /^(?:generate|extend|upscale)_/u.test(name) ? "generation" : "action"),
|
|
486
|
+
});
|
|
487
|
+
if (!preflight.ok)
|
|
488
|
+
return error(preflight.error);
|
|
489
|
+
const params = {
|
|
490
|
+
action: "run",
|
|
491
|
+
version: 1,
|
|
492
|
+
book_id: bookId,
|
|
493
|
+
...(notebookId ? { notebook_id: notebookId } : {}),
|
|
494
|
+
steps: stackedPlan.steps,
|
|
495
|
+
};
|
|
496
|
+
const validated = z.object(targetDef.schema).safeParse(params);
|
|
497
|
+
if (!validated.success) {
|
|
498
|
+
return error(`Invalid parameters for chain: ${z.prettifyError(validated.error)}`);
|
|
499
|
+
}
|
|
500
|
+
const effect = preflight.plan.effect === "generation"
|
|
501
|
+
? "generation"
|
|
502
|
+
: "mutation";
|
|
503
|
+
const autoApply = preflight.plan.autoApply;
|
|
504
|
+
return json({
|
|
505
|
+
kind: "chat-action-plan",
|
|
506
|
+
planId: uuidv7(),
|
|
507
|
+
bookId,
|
|
508
|
+
prompt: taggedPrompt,
|
|
509
|
+
routedTool: "chain",
|
|
510
|
+
tool: "chain",
|
|
511
|
+
params: validated.data,
|
|
512
|
+
message: stackedPlan.message,
|
|
513
|
+
summary: stackedPlan.message,
|
|
514
|
+
effect,
|
|
515
|
+
confirmation: {
|
|
516
|
+
required: !autoApply,
|
|
517
|
+
reason: autoApply
|
|
518
|
+
? "This chain only reads or auto-applies existing local actions"
|
|
519
|
+
: "This request changes the current book",
|
|
520
|
+
},
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
const notebookResolution = notebookDocument
|
|
524
|
+
? resolveNotebookPromptReferences({
|
|
525
|
+
engine: engine,
|
|
526
|
+
document: notebookDocument,
|
|
527
|
+
prompt: expandedPrompt,
|
|
528
|
+
requireMedia: false,
|
|
529
|
+
})
|
|
530
|
+
: null;
|
|
531
|
+
const duplicateDestination = duplicateDestinationReference(expandedPrompt);
|
|
532
|
+
const resolutionPrompt = notebookResolution && duplicateDestination
|
|
533
|
+
? replaceNotebookMentions(expandedPrompt, notebookResolution.resolved
|
|
534
|
+
.filter((item) => item.reference !== duplicateDestination)
|
|
535
|
+
.map((item) => ({
|
|
536
|
+
reference: item.reference,
|
|
537
|
+
replacement: `@${item.artifactId}`,
|
|
538
|
+
})))
|
|
539
|
+
: notebookResolution?.prompt ?? expandedPrompt;
|
|
540
|
+
const unresolvedNotebookReferences = notebookResolution?.unresolved
|
|
541
|
+
.filter((issue) => issue.reference !== duplicateDestination)
|
|
542
|
+
?? [];
|
|
543
|
+
if (unresolvedNotebookReferences.length > 0) {
|
|
544
|
+
return error(`Asset not found: ${unresolvedNotebookReferences
|
|
545
|
+
.map((issue) => `${issue.mention} (${issue.code})`)
|
|
546
|
+
.join(", ")}`);
|
|
547
|
+
}
|
|
548
|
+
// 2. Resolve @<artifact-id> tags
|
|
549
|
+
const { resolved: resolvedAssets, unresolved } = await resolveArtifactRefs(engine, parseArtifactIdTags(resolutionPrompt));
|
|
550
|
+
if (unresolved.length > 0) {
|
|
551
|
+
return error(`Asset not found: ${unresolved.join(", ")}`);
|
|
552
|
+
}
|
|
553
|
+
// 3. Build asset context for the LLM (include source_url when known)
|
|
554
|
+
const assetContext = attachNotebookReferences(await enrichAssetContext(engine, resolvedAssets), notebookContext);
|
|
555
|
+
const imageFiles = resolvedAssets
|
|
556
|
+
.filter((asset) => asset.artifactType === "image")
|
|
557
|
+
.map((asset) => asset.filePath)
|
|
558
|
+
.filter((path) => path !== null);
|
|
559
|
+
const videoFiles = resolvedAssets
|
|
560
|
+
.filter((asset) => asset.artifactType === "video")
|
|
561
|
+
.map((asset) => asset.filePath)
|
|
562
|
+
.filter((path) => path !== null);
|
|
563
|
+
const scriptContents = await loadScriptContentsForChat(engine, resolvedAssets);
|
|
564
|
+
// 4. Read recent history for context
|
|
565
|
+
const historyLines = engine.logs.read("chat", {
|
|
566
|
+
limit: 10,
|
|
567
|
+
});
|
|
568
|
+
const historyContext = formatHistoryForPrompt(historyLines);
|
|
569
|
+
const initialDecision = proposedDecision ?? planBackgroundAudioFromQuery({
|
|
570
|
+
query: expandedPrompt, assetContext, notebookContext,
|
|
571
|
+
});
|
|
572
|
+
const apiKey = getApiKey("OPENAI_API_KEY");
|
|
573
|
+
if (!apiKey && !initialDecision) {
|
|
574
|
+
return error("OPENAI_API_KEY environment variable is not set");
|
|
575
|
+
}
|
|
576
|
+
const { getAllToolDefs } = await import("./index.js");
|
|
577
|
+
const allDefs = getAllToolDefs(engine);
|
|
578
|
+
const routableTools = buildRoutableTools(allDefs);
|
|
579
|
+
const systemPrompt = buildChatRoutingPrompt(book.name, routableTools, historyContext || undefined);
|
|
580
|
+
const userMessage = buildChatRoutingUserMessage({
|
|
581
|
+
query: expandedPrompt,
|
|
582
|
+
assetContext,
|
|
583
|
+
imageFiles,
|
|
584
|
+
videoFiles,
|
|
585
|
+
scriptContents,
|
|
586
|
+
notebookContext,
|
|
587
|
+
});
|
|
588
|
+
let decision;
|
|
589
|
+
if (initialDecision) {
|
|
590
|
+
decision = initialDecision;
|
|
591
|
+
}
|
|
592
|
+
else
|
|
593
|
+
try {
|
|
594
|
+
const responses = buildIntelligenceResponses(apiKey ?? undefined, {
|
|
595
|
+
timeout: CHAT_ROUTING_TIMEOUT_MS,
|
|
596
|
+
model: chatRequest.model,
|
|
597
|
+
reasoningEffort: chatRequest.reasoningEffort,
|
|
598
|
+
});
|
|
599
|
+
let lastError;
|
|
600
|
+
decision = { tool: "", params: {}, message: "" };
|
|
601
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
602
|
+
try {
|
|
603
|
+
const response = await responses(intelligenceRequest({
|
|
604
|
+
instructions: systemPrompt,
|
|
605
|
+
input: userMessage,
|
|
606
|
+
text: { format: { type: "json_object" } },
|
|
607
|
+
max_output_tokens: CHAT_ROUTING_MAX_OUTPUT_TOKENS,
|
|
608
|
+
}, chatRequest), AbortSignal.timeout(CHAT_ROUTING_TIMEOUT_MS));
|
|
609
|
+
const outputText = firstResponseText(response);
|
|
610
|
+
decision = parseChatDecision(outputText);
|
|
611
|
+
lastError = null;
|
|
612
|
+
break;
|
|
613
|
+
}
|
|
614
|
+
catch (e) {
|
|
615
|
+
lastError = e;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
if (lastError) {
|
|
619
|
+
const msg = lastError instanceof Error ? lastError.message : String(lastError);
|
|
620
|
+
return error(`Routing failed: ${msg}`);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
catch (e) {
|
|
624
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
625
|
+
return error(`Routing failed: ${msg}`);
|
|
626
|
+
}
|
|
627
|
+
if (!decision.tool) {
|
|
628
|
+
return error("Could not determine which tool to use. Please rephrase.");
|
|
629
|
+
}
|
|
630
|
+
const bytedanceRoute = applyBytedanceNamedModelRoute({
|
|
631
|
+
query: expandedPrompt,
|
|
632
|
+
tool: decision.tool,
|
|
633
|
+
params: decision.params ?? {},
|
|
634
|
+
message: decision.message,
|
|
635
|
+
assetContext,
|
|
636
|
+
});
|
|
637
|
+
const joinRoute = bytedanceRoute.substituted
|
|
638
|
+
? bytedanceRoute
|
|
639
|
+
: applyVideoJoinRoute({
|
|
640
|
+
query: expandedPrompt,
|
|
641
|
+
tool: bytedanceRoute.tool,
|
|
642
|
+
params: bytedanceRoute.params,
|
|
643
|
+
message: bytedanceRoute.message,
|
|
644
|
+
assetContext,
|
|
645
|
+
notebookContext,
|
|
646
|
+
});
|
|
647
|
+
const stackedRoute = joinRoute.substituted
|
|
648
|
+
? joinRoute
|
|
649
|
+
: applyStackedChainRoute({
|
|
650
|
+
query: expandedPrompt,
|
|
651
|
+
tool: joinRoute.tool,
|
|
652
|
+
params: joinRoute.params,
|
|
653
|
+
message: joinRoute.message,
|
|
654
|
+
assetContext,
|
|
655
|
+
notebookContext,
|
|
656
|
+
});
|
|
657
|
+
const audioRoute = stackedRoute.substituted
|
|
658
|
+
? stackedRoute
|
|
659
|
+
: applyBackgroundAudioRoute({
|
|
660
|
+
query: expandedPrompt,
|
|
661
|
+
tool: stackedRoute.tool,
|
|
662
|
+
params: stackedRoute.params,
|
|
663
|
+
message: stackedRoute.message,
|
|
664
|
+
assetContext,
|
|
665
|
+
notebookContext,
|
|
666
|
+
});
|
|
667
|
+
decision = {
|
|
668
|
+
tool: audioRoute.tool,
|
|
669
|
+
params: audioRoute.params,
|
|
670
|
+
// The voice path routes the spoken sentence here, so the sheet's title
|
|
671
|
+
// reads in the same vocabulary as the prompt beneath it.
|
|
672
|
+
message: tagNotebookGridAddresses(audioRoute.message, notebookAddresses),
|
|
673
|
+
};
|
|
674
|
+
// 6. Validate tool exists in routable registry
|
|
675
|
+
const routableTool = getRoutableTool(routableTools, decision.tool);
|
|
676
|
+
if (!routableTool) {
|
|
677
|
+
return error(`Unknown tool: ${decision.tool}. Please rephrase.`);
|
|
678
|
+
}
|
|
679
|
+
// A view opens in the app, so the plan carries the resolved target back
|
|
680
|
+
// instead of running a handler here.
|
|
681
|
+
if (routableTool.effect === "view") {
|
|
682
|
+
const view = resolveChatViewAction(engine, notebookDocument, routableTool.actualTool, decision.params ?? {});
|
|
683
|
+
if (!view.ok)
|
|
684
|
+
return error(view.message);
|
|
685
|
+
if (!proposedDecision)
|
|
686
|
+
appendChatHistory(engine, {
|
|
687
|
+
prompt: taggedPrompt,
|
|
688
|
+
tool: decision.tool,
|
|
689
|
+
params: view.params,
|
|
690
|
+
message: decision.message,
|
|
691
|
+
failed: false,
|
|
692
|
+
});
|
|
693
|
+
return json({
|
|
694
|
+
kind: "chat-action-plan",
|
|
695
|
+
planId: uuidv7(),
|
|
696
|
+
bookId,
|
|
697
|
+
prompt: taggedPrompt,
|
|
698
|
+
routedTool: decision.tool,
|
|
699
|
+
tool: routableTool.actualTool,
|
|
700
|
+
params: view.params,
|
|
701
|
+
message: decision.message,
|
|
702
|
+
summary: decision.message.trim() || "Show media fullscreen",
|
|
703
|
+
effect: "view",
|
|
704
|
+
confirmation: {
|
|
705
|
+
required: false,
|
|
706
|
+
reason: "Opening a view changes nothing in the book",
|
|
707
|
+
},
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
// 7. Apply fixups (spread to avoid mutating decision.params for logging)
|
|
711
|
+
const routedParams = normalizeChatRoutedParams(decision.tool, normalizeChatTimestampParams(decision.tool, decision.params ?? {}));
|
|
712
|
+
// The routing prompt asks the model to omit optional parameters so each tool
|
|
713
|
+
// can apply its defaults; enforce that for generation rather than trusting
|
|
714
|
+
// it, since an invented duration or a muted soundtrack costs a real
|
|
715
|
+
// generation. Reads the query, not the routed prompt, so only what the
|
|
716
|
+
// person actually said counts as asking.
|
|
717
|
+
const params = routableTool.category === "generation"
|
|
718
|
+
? withoutUnrequestedSettings({
|
|
719
|
+
query: expandedPrompt,
|
|
720
|
+
params: routedParams,
|
|
721
|
+
requiredParams: routableTool.requiredParams,
|
|
722
|
+
}).params
|
|
723
|
+
: routedParams;
|
|
724
|
+
// Inject book_id
|
|
725
|
+
params.book_id = bookId;
|
|
726
|
+
if (notebookId
|
|
727
|
+
&& routableTool.params.includes("notebook_id")
|
|
728
|
+
&& params.notebook_id === undefined) {
|
|
729
|
+
params.notebook_id = notebookId;
|
|
730
|
+
}
|
|
731
|
+
// create_watermark_card needs a URL; if the model omitted it, take source_url
|
|
732
|
+
// from a referenced asset (e.g. "QR from the url in @vid-og").
|
|
733
|
+
if (decision.tool === "create_watermark_card" && (params.url === undefined || params.url === "")) {
|
|
734
|
+
const fromAsset = assetContext.find((a) => typeof a.source_url === "string");
|
|
735
|
+
if (fromAsset)
|
|
736
|
+
params.url = fromAsset.source_url;
|
|
737
|
+
}
|
|
738
|
+
// Inject script content as prompt for generation tools
|
|
739
|
+
const scriptIsPrompt = scriptContents.length > 0
|
|
740
|
+
&& routableTool.category === "generation";
|
|
741
|
+
if (scriptIsPrompt) {
|
|
742
|
+
const combinedScript = scriptContents.map((s) => s.content).join("\n\n");
|
|
743
|
+
params.prompt = combinedScript;
|
|
744
|
+
}
|
|
745
|
+
// Generation routes: default the model, fold flat ref fields into refs, and
|
|
746
|
+
// default the ElevenLabs voice. Runs after the script injection above so
|
|
747
|
+
// script content reaches the prompt.
|
|
748
|
+
Object.assign(params, await applyGenerationRoutingDefaults(decision.tool, params));
|
|
749
|
+
// The voice model writes its own params, so a bare address can survive there
|
|
750
|
+
// even when the query carried none.
|
|
751
|
+
if (typeof params.prompt === "string") {
|
|
752
|
+
params.prompt = tagNotebookGridAddresses(params.prompt, notebookAddresses);
|
|
753
|
+
}
|
|
754
|
+
// Rewrite the routed prompt against the target model's guide, so the sheet
|
|
755
|
+
// reviews the prompt the provider will see rather than the sentence that
|
|
756
|
+
// picked the tool. A script the speaker attached is their own wording and
|
|
757
|
+
// stands as written. Runs last: the rewrite has to see resolved mentions to
|
|
758
|
+
// hand them back intact.
|
|
759
|
+
if (routableTool.category === "generation"
|
|
760
|
+
&& !scriptIsPrompt
|
|
761
|
+
&& typeof params.prompt === "string") {
|
|
762
|
+
params.prompt = await rewriteChatGenerationPrompt(engine, {
|
|
763
|
+
bookId,
|
|
764
|
+
model: typeof params.model === "string" ? params.model : "",
|
|
765
|
+
prompt: params.prompt,
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
// 8. Look up handler from tool defs
|
|
769
|
+
const targetDef = allDefs.find((d) => d.name === routableTool.actualTool);
|
|
770
|
+
if (!targetDef) {
|
|
771
|
+
return error(`Tool handler not found: ${routableTool.actualTool}`);
|
|
772
|
+
}
|
|
773
|
+
const validated = z.object(targetDef.schema).safeParse(params);
|
|
774
|
+
if (!validated.success) {
|
|
775
|
+
return error(`Invalid parameters for ${decision.tool}: ${z.prettifyError(validated.error)}`);
|
|
776
|
+
}
|
|
777
|
+
if (routableTool.effect !== "read") {
|
|
778
|
+
const effect = routableTool.effect;
|
|
779
|
+
const summary = decision.message.trim()
|
|
780
|
+
|| `${effect === "generation" ? "Generate" : "Apply"} with ${decision.tool}`;
|
|
781
|
+
let autoApply = routableTool.autoApply;
|
|
782
|
+
let chainPausesPerStep = false;
|
|
783
|
+
if (targetDef.name === "chain") {
|
|
784
|
+
const preflight = await preflightChainPlan({
|
|
785
|
+
action: "run",
|
|
786
|
+
version: 1,
|
|
787
|
+
...params,
|
|
788
|
+
book_id: bookId,
|
|
789
|
+
}, {
|
|
790
|
+
engine,
|
|
791
|
+
bookId,
|
|
792
|
+
notebookId,
|
|
793
|
+
toolSchemas: new Map(allDefs.map((definition) => [definition.name, definition.schema])),
|
|
794
|
+
autoApplyTools: AUTO_APPLY_TOOL_NAMES,
|
|
795
|
+
effectForTool: (name) => effectForTool(name, /^(?:generate|extend|upscale)_/u.test(name) ? "generation" : "action"),
|
|
796
|
+
});
|
|
797
|
+
if (!preflight.ok)
|
|
798
|
+
return error(preflight.error);
|
|
799
|
+
autoApply = preflight.plan.autoApply;
|
|
800
|
+
chainPausesPerStep = preflight.plan.steps.some((step) => step.requiresApproval);
|
|
801
|
+
}
|
|
802
|
+
return json({
|
|
803
|
+
kind: "chat-action-plan",
|
|
804
|
+
planId: uuidv7(),
|
|
805
|
+
bookId,
|
|
806
|
+
prompt: taggedPrompt,
|
|
807
|
+
routedTool: decision.tool,
|
|
808
|
+
tool: targetDef.name,
|
|
809
|
+
params: validated.data,
|
|
810
|
+
message: decision.message,
|
|
811
|
+
summary,
|
|
812
|
+
effect,
|
|
813
|
+
confirmation: {
|
|
814
|
+
required: !autoApply,
|
|
815
|
+
reason: autoApply
|
|
816
|
+
? autoApplyReason(targetDef.name, chainPausesPerStep)
|
|
817
|
+
: effect === "generation"
|
|
818
|
+
? "This request creates generated media and may incur provider spend"
|
|
819
|
+
: "This request changes the current book",
|
|
820
|
+
},
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
// 9. Execute (wrapped so the routed tool writes its own action log entry)
|
|
824
|
+
const wrappedDef = withLogging(targetDef, engine);
|
|
825
|
+
let handlerResult;
|
|
826
|
+
try {
|
|
827
|
+
handlerResult = await wrappedDef.handler(validated.data);
|
|
828
|
+
}
|
|
829
|
+
catch (cause) {
|
|
830
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
831
|
+
return error(`${decision.tool} failed: ${message}`);
|
|
832
|
+
}
|
|
833
|
+
if (!proposedDecision)
|
|
834
|
+
appendChatHistory(engine, {
|
|
835
|
+
prompt: taggedPrompt,
|
|
836
|
+
tool: decision.tool,
|
|
837
|
+
params: decision.params,
|
|
838
|
+
message: decision.message,
|
|
839
|
+
failed: Boolean(handlerResult.isError),
|
|
840
|
+
});
|
|
841
|
+
return handlerResult;
|
|
842
|
+
}
|
|
843
|
+
function error(text) {
|
|
844
|
+
return { isError: true, content: [{ type: "text", text }] };
|
|
845
|
+
}
|
|
846
|
+
function json(value) {
|
|
847
|
+
return { content: [{ type: "text", text: JSON.stringify(value) }] };
|
|
848
|
+
}
|