@oh-story/dsh 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/README.md +30 -15
  2. package/lib/client.js +93 -16
  3. package/lib/client.js.map +2 -2
  4. package/lib/drama/manifest.json +165 -69
  5. package/lib/drama/skills/short-drama/SKILL.md +5 -1
  6. package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +126 -9
  7. package/lib/drama/skills/short-drama/scripts/project_tool.py +11 -0
  8. package/lib/drama/skills/short-drama-assets/references/character-and-look.md +18 -0
  9. package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +10 -1
  10. package/lib/drama/skills/short-drama-edit/SKILL.md +148 -0
  11. package/lib/drama/skills/short-drama-edit/agents/openai.yaml +4 -0
  12. package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +60 -0
  13. package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +15 -0
  14. package/lib/drama/skills/short-drama-edit/assets/remotion/remotion.config.ts +6 -0
  15. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +26 -0
  16. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +88 -0
  17. package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +46 -0
  18. package/lib/drama/skills/short-drama-edit/assets/remotion/src/index.ts +4 -0
  19. package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +32 -0
  20. package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +13 -0
  21. package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +43 -0
  22. package/lib/drama/skills/short-drama-edit/references/cut-craft.md +81 -0
  23. package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +58 -0
  24. package/lib/drama/skills/short-drama-edit/references/generated-footage.md +96 -0
  25. package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +113 -0
  26. package/lib/drama/skills/short-drama-edit/references/stage-contract.md +55 -0
  27. package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1041 -0
  28. package/lib/drama/skills/short-drama-edit/scripts/selftest.py +456 -0
  29. package/lib/drama/skills/short-drama-image-prompts/assets/image-prompts.md +5 -0
  30. package/lib/drama/skills/short-drama-image-prompts/assets/lookdev-prompts.md +3 -0
  31. package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +2 -2
  32. package/lib/drama/skills/short-drama-novel-analyze/references/chapter-extraction.md +4 -1
  33. package/lib/drama/skills/short-drama-produce/SKILL.md +24 -3
  34. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +28 -0
  35. package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +24 -5
  36. package/lib/drama/skills/short-drama-produce/references/providers/seedance.md +7 -0
  37. package/lib/drama/skills/short-drama-produce/scripts/fixture_adapter.py +18 -1
  38. package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +181 -2
  39. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +57 -2
  40. package/lib/drama/skills/short-drama-review/SKILL.md +3 -0
  41. package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +20 -0
  42. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +36 -0
  43. package/lib/drama/skills/short-drama-storyboard/SKILL.md +7 -0
  44. package/lib/drama/skills/short-drama-storyboard/assets/keyframe-prompts.md +4 -0
  45. package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +39 -0
  46. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +2 -0
  47. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +7 -1
  48. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +101 -6
  49. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +7 -1
  50. package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +35 -0
  51. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +3 -1
  52. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +13 -3
  53. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +35 -4
  54. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +1 -1
  55. package/lib/drama/skills/short-drama-write/SKILL.md +13 -2
  56. package/lib/drama/skills/short-drama-write/references/screenplay-format.md +6 -0
  57. package/lib/drama/skills/short-drama-write/references/stage-contract.md +1 -0
  58. package/lib/index.js +4 -2
  59. package/lib/index.js.map +2 -2
  60. package/lib/novel-to-game/VERSION +1 -1
  61. package/lib/novel-to-game/manifest.json +28 -28
  62. package/lib/novel-to-game/skills/game-art-direction/SKILL.md +6 -27
  63. package/lib/novel-to-game/skills/game-build/SKILL.md +12 -29
  64. package/lib/novel-to-game/skills/game-concept/SKILL.md +10 -26
  65. package/lib/novel-to-game/skills/game-qa/SKILL.md +5 -15
  66. package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +1 -1
  67. package/lib/novel-to-game/skills/game-world-design/SKILL.md +19 -28
  68. package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +1 -2
  69. package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +6 -9
  70. package/lib/novel-to-game/skills/novel-to-game/SKILL.md +6 -11
  71. package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +0 -4
  72. package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +1 -3
  73. package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +3 -5
  74. package/lib/oh-story/manifest.json +292 -172
  75. package/lib/oh-story/roles/character-designer.md +1 -1
  76. package/lib/oh-story/roles/narrative-writer.md +56 -242
  77. package/lib/oh-story/roles/story-architect.md +1 -1
  78. package/lib/oh-story/skills/story/SKILL.md +1 -1
  79. package/lib/oh-story/skills/story/VERSION +1 -1
  80. package/lib/oh-story/skills/story/references/author-memory.md +10 -8
  81. package/lib/oh-story/skills/story-deslop/SKILL.md +20 -182
  82. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +19 -47
  83. package/lib/oh-story/skills/story-deslop/references/author-memory.md +10 -8
  84. package/lib/oh-story/skills/story-deslop/references/banned-words.md +3 -1
  85. package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +154 -0
  86. package/lib/oh-story/skills/story-deslop/references/style-resolution.md +27 -0
  87. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +6 -1
  88. package/lib/oh-story/skills/story-deslop/scripts/check-degeneration.js +39 -20
  89. package/lib/oh-story/skills/story-deslop/scripts/normalize-punctuation.js +10 -5
  90. package/lib/oh-story/skills/story-deslop/scripts/style-whitelist.js +39 -0
  91. package/lib/oh-story/skills/story-import/SKILL.md +2 -2
  92. package/lib/oh-story/skills/story-import/references/format-and-structure.md +2 -2
  93. package/lib/oh-story/skills/story-import/references/tracking-initialization.md +31 -0
  94. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +2 -29
  95. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +176 -12
  96. package/lib/oh-story/skills/story-long-analyze/SKILL.md +1 -1
  97. package/lib/oh-story/skills/story-long-write/SKILL.md +20 -193
  98. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +19 -47
  99. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +62 -7
  100. package/lib/oh-story/skills/story-long-write/references/author-memory.md +10 -8
  101. package/lib/oh-story/skills/story-long-write/references/banned-words.md +3 -1
  102. package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +20 -0
  103. package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +6 -4
  104. package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +2 -2
  105. package/lib/oh-story/skills/story-long-write/references/long-format.md +2 -0
  106. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +2 -0
  107. package/lib/oh-story/skills/story-long-write/references/project-files.md +108 -0
  108. package/lib/oh-story/skills/story-long-write/references/reference-index.md +87 -0
  109. package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -1
  110. package/lib/oh-story/skills/story-long-write/references/style-craft.md +9 -29
  111. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  112. package/lib/oh-story/skills/story-long-write/references/style-resolution.md +27 -0
  113. package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +31 -0
  114. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +2 -29
  115. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +51 -52
  116. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +8 -7
  117. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +36 -15
  118. package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +22 -0
  119. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +69 -2
  120. package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +410 -0
  121. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +6 -1
  122. package/lib/oh-story/skills/story-long-write/scripts/check-degeneration.js +39 -20
  123. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +176 -12
  124. package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +90 -23
  125. package/lib/oh-story/skills/story-long-write/scripts/normalize-punctuation.js +10 -5
  126. package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +283 -0
  127. package/lib/oh-story/skills/story-long-write/scripts/style-whitelist.js +39 -0
  128. package/lib/oh-story/skills/story-review/SKILL.md +13 -3
  129. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +19 -47
  130. package/lib/oh-story/skills/story-review/references/author-memory.md +10 -8
  131. package/lib/oh-story/skills/story-review/references/banned-words.md +3 -1
  132. package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +6 -4
  133. package/lib/oh-story/skills/story-review/references/review-quality.md +2 -2
  134. package/lib/oh-story/skills/story-review/references/style-resolution.md +27 -0
  135. package/lib/oh-story/skills/story-review/references/tracking-initialization.md +31 -0
  136. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +2 -29
  137. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +6 -1
  138. package/lib/oh-story/skills/story-review/scripts/check-degeneration.js +39 -20
  139. package/lib/oh-story/skills/story-review/scripts/normalize-punctuation.js +10 -5
  140. package/lib/oh-story/skills/story-review/scripts/style-whitelist.js +39 -0
  141. package/lib/oh-story/skills/story-setup/SKILL.md +17 -10
  142. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +19 -47
  143. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +3 -1
  144. package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +154 -0
  145. package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +6 -4
  146. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +2 -2
  147. package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +1 -1
  148. package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -1
  149. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  150. package/lib/oh-story/skills/story-setup/references/agent-references/style-resolution.md +27 -0
  151. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +69 -2
  152. package/lib/oh-story/skills/story-setup/references/diagnostics.md +38 -0
  153. package/lib/oh-story/skills/story-short-analyze/references/analysis-writing-techniques.md +1 -1
  154. package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +2 -2
  155. package/lib/oh-story/skills/story-short-write/SKILL.md +18 -186
  156. package/lib/oh-story/skills/story-short-write/references/author-memory.md +10 -8
  157. package/lib/oh-story/skills/story-short-write/references/banned-words.md +3 -1
  158. package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +60 -0
  159. package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +6 -4
  160. package/lib/oh-story/skills/story-short-write/references/genre-styles//344/270/226/346/203/205/346/211/223/350/204/270.md +2 -2
  161. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +1 -1
  162. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/244/215/344/273/207/346/211/223/350/204/270.md +3 -3
  163. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/256/205/346/226/227/345/256/253/346/226/227.md +3 -3
  164. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/200/273/350/243/201/350/261/252/351/227/250.md +3 -3
  165. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +5 -5
  166. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/260/221/344/277/227/346/200/252/350/260/210.md +4 -4
  167. package/lib/oh-story/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +5 -5
  168. package/lib/oh-story/skills/story-short-write/references/genre-styles//350/277/275/345/246/273/347/201/253/350/221/254/345/234/272.md +3 -3
  169. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +1 -1
  170. package/lib/oh-story/skills/story-short-write/references/genre-writing-techniques.md +1 -1
  171. package/lib/oh-story/skills/story-short-write/references/reference-index.md +53 -0
  172. package/lib/oh-story/skills/story-short-write/references/short-craft.md +12 -14
  173. package/lib/oh-story/skills/story-short-write/references/short-deslop.md +10 -10
  174. package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -1
  175. package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -2
  176. package/lib/oh-story/skills/story-short-write/references/style-resolution.md +27 -0
  177. package/lib/oh-story/skills/story-short-write/references/workflow-design.md +77 -0
  178. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +5 -5
  179. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +6 -1
  180. package/lib/oh-story/skills/story-short-write/scripts/check-degeneration.js +39 -20
  181. package/lib/oh-story/skills/story-short-write/scripts/check-delivery-contract.js +34 -5
  182. package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +90 -23
  183. package/lib/oh-story/skills/story-short-write/scripts/normalize-punctuation.js +10 -5
  184. package/lib/oh-story/skills/story-short-write/scripts/style-whitelist.js +39 -0
  185. package/lib/types/client/drama-production-view.d.ts.map +1 -1
  186. package/lib/types/client/index.d.ts.map +1 -1
  187. package/lib/types/client/production-prompts.d.ts.map +1 -1
  188. package/lib/types/client/production-runtime.d.ts +17 -0
  189. package/lib/types/client/production-runtime.d.ts.map +1 -1
  190. package/lib/types/skill-provider.d.ts.map +1 -1
  191. package/lib/video-recap/CHANGELOG.md +31 -1
  192. package/lib/video-recap/manifest.json +162 -162
  193. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +4 -9
  194. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +47 -60
  195. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +77 -115
  196. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +41 -40
  197. package/lib/video-recap/skills/video-assemble/scripts/audio_automation.py +22 -32
  198. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +102 -192
  199. package/lib/video-recap/skills/video-assemble/scripts/jianying_builders.py +14 -35
  200. package/lib/video-recap/skills/video-assemble/scripts/jianying_model.py +14 -12
  201. package/lib/video-recap/skills/video-assemble/scripts/jianying_optional.py +8 -9
  202. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +3 -14
  203. package/lib/video-recap/skills/video-assemble/scripts/jianying_timeline_contract.py +23 -12
  204. package/lib/video-recap/skills/video-assemble/scripts/jianying_tracks.py +1 -4
  205. package/lib/video-recap/skills/video-assemble/scripts/jianying_writer.py +24 -25
  206. package/lib/video-recap/skills/video-assemble/scripts/lib.py +31 -85
  207. package/lib/video-recap/skills/video-assemble/scripts/media.py +100 -144
  208. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +101 -195
  209. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +10 -21
  210. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +90 -169
  211. package/lib/video-recap/skills/video-assemble/scripts/subtitle_core.py +69 -122
  212. package/lib/video-recap/skills/video-assemble/scripts/subtitle_render.py +10 -17
  213. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +33 -48
  214. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +14 -20
  215. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +135 -213
  216. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +49 -57
  217. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +38 -74
  218. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +46 -95
  219. package/lib/video-recap/skills/video-cut/scripts/lib.py +23 -25
  220. package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +70 -173
  221. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +28 -53
  222. package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +162 -322
  223. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +6 -7
  224. package/lib/video-recap/skills/video-recap/scripts/doctor.py +104 -124
  225. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +255 -412
  226. package/lib/video-recap/skills/video-recap/scripts/lib.py +30 -52
  227. package/lib/video-recap/skills/video-recap/scripts/materials.py +97 -120
  228. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_client.py +7 -12
  229. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_evidence.py +46 -97
  230. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_observations.py +18 -36
  231. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_payload.py +29 -47
  232. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_report.py +43 -76
  233. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_runner.py +2 -1
  234. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +2 -1
  235. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +2 -2
  236. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +129 -145
  237. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +27 -20
  238. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +20 -31
  239. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +31 -106
  240. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +43 -113
  241. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +196 -377
  242. package/lib/video-recap/skills/video-script/scripts/agent_brief.py +1 -1
  243. package/lib/video-recap/skills/video-script/scripts/agent_text.py +63 -172
  244. package/lib/video-recap/skills/video-script/scripts/brief_context.py +80 -135
  245. package/lib/video-recap/skills/video-script/scripts/brief_inputs.py +125 -167
  246. package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +93 -208
  247. package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +20 -46
  248. package/lib/video-recap/skills/video-script/scripts/lib.py +30 -26
  249. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +145 -140
  250. package/lib/video-recap/skills/video-script/scripts/review_response.py +12 -42
  251. package/lib/video-recap/skills/video-script/scripts/review_runner.py +0 -6
  252. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +39 -100
  253. package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +33 -124
  254. package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +1 -1
  255. package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +63 -172
  256. package/lib/video-recap/skills/video-understanding/scripts/asr.py +1 -2
  257. package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +80 -135
  258. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +125 -167
  259. package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +93 -208
  260. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +1 -1
  261. package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +20 -46
  262. package/lib/video-recap/skills/video-understanding/scripts/lib.py +31 -26
  263. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +145 -140
  264. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +39 -100
  265. package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +33 -124
  266. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +1 -4
  267. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +7 -13
  268. package/lib/video-recap/skills/video-voiceover/scripts/fish_audio.py +12 -30
  269. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +98 -131
  270. package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +17 -28
  271. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +151 -240
  272. package/package.json +41 -41
@@ -114,36 +114,15 @@ def merge_review_findings(chunks):
114
114
 
115
115
 
116
116
  def _bundle_fingerprint(bundle):
117
- try:
118
- return stable_hash(
119
- {
120
- "schema_version": bundle.get("schema_version"),
121
- "clock": bundle.get("clock"),
122
- "coverage": bundle.get("coverage"),
123
- "items": bundle.get("items"),
124
- "context_items": bundle.get("context_items"),
125
- }
126
- )
127
- except (TypeError, ValueError, RecursionError) as exc:
128
- if isinstance(bundle, dict):
129
- bundle.setdefault("metadata", {})["evidence_bundle_fingerprint_warning"] = (
130
- f"evidence bundle fingerprint unavailable: {type(exc).__name__}"
131
- )
132
- return ""
133
-
134
-
135
- def _bundle_fingerprint_warning(bundle):
136
- if not isinstance(bundle, dict):
137
- return "evidence bundle fingerprint unavailable: invalid bundle"
138
- warning = (bundle.get("metadata") or {}).get("evidence_bundle_fingerprint_warning")
139
- if warning:
140
- return warning
141
- return ""
142
-
143
-
144
- def _append_warning_once(target, warning):
145
- if warning and warning not in target:
146
- target.append(warning)
117
+ return stable_hash(
118
+ {
119
+ "schema_version": bundle.get("schema_version"),
120
+ "clock": bundle.get("clock"),
121
+ "coverage": bundle.get("coverage"),
122
+ "items": bundle.get("items"),
123
+ "context_items": bundle.get("context_items"),
124
+ }
125
+ )
147
126
 
148
127
 
149
128
  def _bundle_prompt_size(bundle):
@@ -163,13 +142,9 @@ def _chunk_evidence_bundle(bundle, *, max_items=80, max_chars=12000):
163
142
  one = dict(bundle)
164
143
  one["chunk_index"] = 0
165
144
  one["chunk_count"] = 1
166
- fp = _bundle_fingerprint(bundle)
167
- fp_warning = _bundle_fingerprint_warning(bundle)
168
- one.setdefault("metadata", {})["evidence_bundle_fingerprint"] = fp
169
- if fp_warning:
170
- one["metadata"]["evidence_bundle_fingerprint_warning"] = fp_warning
171
- one.setdefault("warnings", list(bundle.get("warnings") or []))
172
- _append_warning_once(one["warnings"], fp_warning)
145
+ one.setdefault("metadata", {})["evidence_bundle_fingerprint"] = (
146
+ _bundle_fingerprint(bundle)
147
+ )
173
148
  return [one]
174
149
  ranges = bundle.get("coverage", {}).get("selected_ranges") or []
175
150
  chunks = []
@@ -210,15 +185,10 @@ def _chunk_evidence_bundle(bundle, *, max_items=80, max_chars=12000):
210
185
  chunks = [chunk]
211
186
  count = len(chunks)
212
187
  fp = _bundle_fingerprint(bundle)
213
- fp_warning = _bundle_fingerprint_warning(bundle)
214
188
  for chunk in chunks:
215
189
  chunk["chunk_count"] = count
216
190
  chunk.setdefault("metadata", {})["chunked_review"] = count > 1
217
191
  chunk["metadata"]["evidence_bundle_fingerprint"] = fp
218
- if fp_warning:
219
- chunk["metadata"]["evidence_bundle_fingerprint_warning"] = fp_warning
220
- chunk.setdefault("warnings", list(bundle.get("warnings") or []))
221
- _append_warning_once(chunk["warnings"], fp_warning)
222
192
  return chunks
223
193
 
224
194
 
@@ -17,9 +17,7 @@ from review_grounding import (
17
17
  remap_grounding_to_output_timeline,
18
18
  )
19
19
  from review_response import (
20
- _append_warning_once,
21
20
  _bundle_fingerprint,
22
- _bundle_fingerprint_warning,
23
21
  _chunk_evidence_bundle,
24
22
  _load_review_research_context,
25
23
  _merge_chunk_reviews,
@@ -65,10 +63,6 @@ def review_narration(work_dir, *, timeline="source", strict_evidence=False):
65
63
  warnings=warnings,
66
64
  )
67
65
  bundle_fp = _bundle_fingerprint(bundle)
68
- fp_warning = _bundle_fingerprint_warning(bundle)
69
- if fp_warning:
70
- _append_warning_once(warnings, fp_warning)
71
- _append_warning_once(bundle.setdefault("warnings", []), fp_warning)
72
66
  chunk_reviews = []
73
67
  chunks = _chunk_evidence_bundle(bundle)
74
68
  for chunk in chunks:
@@ -1,7 +1,6 @@
1
1
  """Load measured source-speech evidence and classify narration ownership."""
2
2
 
3
3
  import json
4
- import math
5
4
  from pathlib import Path
6
5
 
7
6
  from lib import CONFIG, stable_hash
@@ -17,36 +16,16 @@ def _empty_evidence(mode):
17
16
 
18
17
 
19
18
  def _read_json(path):
20
- try:
21
- return json.loads(Path(path).read_text(encoding="utf-8"))
22
- except (OSError, json.JSONDecodeError):
23
- return None
24
-
25
-
26
- def _timed_rows(payload, key):
27
- rows = payload.get(key, []) if isinstance(payload, dict) else []
28
- out = []
29
- for row in rows if isinstance(rows, list) else []:
30
- if not isinstance(row, dict):
31
- continue
32
- try:
33
- start, end = float(row.get("start")), float(row.get("end"))
34
- except (TypeError, ValueError):
35
- continue
36
- if math.isfinite(start) and math.isfinite(end) and end > start:
37
- out.append({**row, "start": start, "end": end})
38
- return out
19
+ """JSON artifact, or None when the optional file was never written."""
20
+ return json.loads(path.read_text(encoding="utf-8")) if path.exists() else None
39
21
 
40
22
 
41
23
  def _output_payload_is_current(payload, work_dir):
42
- if not isinstance(payload, dict):
43
- return False
44
- if payload.get("schema_version") != 2 or payload.get("timeline") != "cut_output":
45
- return False
46
24
  plan = _read_json(Path(work_dir) / "clip_plan_validated.json")
47
- return bool(
48
- isinstance(plan, dict)
49
- and payload.get("clip_plan_fingerprint") == stable_hash(plan)
25
+ return (
26
+ payload is not None
27
+ and plan is not None
28
+ and payload["clip_plan_fingerprint"] == stable_hash(plan)
50
29
  )
51
30
 
52
31
 
@@ -55,55 +34,31 @@ def load_source_sentence_evidence(work_dir, mode="full"):
55
34
  if work_dir is None:
56
35
  return _empty_evidence(mode)
57
36
  work_dir = Path(work_dir)
58
- output_mode = mode in {"cut", "cut_output"}
59
- path = work_dir / (
60
- "speech_boundary_anchors_output.json"
61
- if output_mode
62
- else "speech_boundary_anchors.json"
63
- )
64
- payload = _read_json(path)
65
- if mode == "cut_output" and not _output_payload_is_current(payload, work_dir):
66
- return _empty_evidence(mode)
67
- if not isinstance(payload, dict):
68
- payload = {}
69
-
70
- speech_spans = _timed_rows(payload, "speech_spans")
71
- quiet_windows = _timed_rows(payload, "quiet_windows")
72
- if not output_mode:
73
- for name in ("asr_result.json", "asr_clean.json"):
74
- raw = _read_json(work_dir / name)
75
- if isinstance(raw, list):
76
- candidate = {"speech_spans": raw}
77
- elif isinstance(raw, dict):
78
- candidate = {"speech_spans": raw.get("segments", [])}
79
- else:
80
- continue
81
- speech_spans = _timed_rows(candidate, "speech_spans")
82
- if speech_spans:
83
- break
84
- raw_quiet = _read_json(work_dir / "silence_periods.json")
85
- candidate = {
86
- "quiet_windows": [
87
- row
88
- for row in raw_quiet
89
- if isinstance(row, dict) and not bool(row.get("has_speech", False))
90
- ]
91
- } if isinstance(raw_quiet, list) else {}
92
- quiet_windows = _timed_rows(candidate, "quiet_windows")
93
-
94
- anchors = []
95
- for anchor in payload.get("sentence_anchors", []):
96
- if not isinstance(anchor, dict) or anchor.get("confidence") not in {
97
- "high",
98
- "medium",
99
- }:
100
- continue
101
- try:
102
- when = float(anchor.get("time"))
103
- except (TypeError, ValueError):
104
- continue
105
- if math.isfinite(when) and when >= 0:
106
- anchors.append({**anchor, "time": round(when, 3)})
37
+ if mode == "full":
38
+ payload = _read_json(work_dir / "speech_boundary_anchors.json") or {
39
+ "sentence_anchors": []
40
+ }
41
+ speech_spans = [
42
+ row for row in _read_json(work_dir / "asr_result.json") or [] if row["text"]
43
+ ]
44
+ quiet_windows = [
45
+ row
46
+ for row in _read_json(work_dir / "silence_periods.json") or []
47
+ if not row["has_speech"]
48
+ ]
49
+ else:
50
+ payload = _read_json(work_dir / "speech_boundary_anchors_output.json")
51
+ if mode == "cut_output" and not _output_payload_is_current(payload, work_dir):
52
+ return _empty_evidence(mode)
53
+ if payload is None:
54
+ payload = {"sentence_anchors": [], "speech_spans": [], "quiet_windows": []}
55
+ speech_spans = payload["speech_spans"]
56
+ quiet_windows = payload["quiet_windows"]
57
+ anchors = [
58
+ anchor
59
+ for anchor in payload["sentence_anchors"]
60
+ if anchor["confidence"] in {"high", "medium"}
61
+ ]
107
62
  return {
108
63
  "anchors": sorted(anchors, key=lambda item: item["time"]),
109
64
  "speech_spans": speech_spans,
@@ -147,16 +102,9 @@ def _speech_overlap_excluding_quiet(start, end, speech, quiet):
147
102
 
148
103
  def segment_overlaps_source_speech(seg, evidence):
149
104
  """Classify aggregate mix ownership across the complete narration interval."""
150
- try:
151
- start, end = float(seg.get("start")), float(seg.get("end"))
152
- except (AttributeError, TypeError, ValueError):
153
- return bool(getattr(seg, "get", lambda *_: True)("overlaps_speech", True))
154
- duration = max(0.0, end - start)
105
+ start, end = seg["start"], seg["end"]
155
106
  quiet = evidence["quiet_windows"]
156
- quiet_min = max(
157
- 0.3,
158
- duration * float(CONFIG.get("quiet_overlap_min_ratio", 0.8) or 0.8),
159
- )
107
+ quiet_min = max(0.3, (end - start) * CONFIG["quiet_overlap_min_ratio"])
160
108
  speech = evidence["speech_spans"]
161
109
  if speech:
162
110
  return _speech_overlap_excluding_quiet(start, end, speech, quiet) > 0.05
@@ -167,12 +115,8 @@ def segment_overlaps_source_speech(seg, evidence):
167
115
  return bool(seg.get("overlaps_speech", True))
168
116
 
169
117
 
170
- def entry_overlaps_source_speech(seg, evidence, tolerance=0.05):
118
+ def entry_overlaps_source_speech(start, evidence, *, authored_overlap=True, tolerance=0.05):
171
119
  """Classify the entry instant; later quiet time cannot erase an unsafe start."""
172
- try:
173
- start = float(seg.get("start"))
174
- except (AttributeError, TypeError, ValueError):
175
- return True
176
120
  if any(
177
121
  row["start"] - tolerance <= start <= row["end"] + tolerance
178
122
  for row in evidence["quiet_windows"]
@@ -187,18 +131,13 @@ def entry_overlaps_source_speech(seg, evidence, tolerance=0.05):
187
131
  return False
188
132
  if evidence["anchors"] or evidence["require_measured"]:
189
133
  return True
190
- return bool(seg.get("overlaps_speech", True))
134
+ return bool(authored_overlap)
191
135
 
192
136
 
193
137
  def measure_narration_speech_ownership(narration, work_dir, mode="full"):
194
138
  """Return narration copies with aggregate ownership derived from evidence."""
195
139
  evidence = load_source_sentence_evidence(work_dir, mode=mode)
196
- measured = []
197
- for seg in narration if isinstance(narration, list) else []:
198
- if not isinstance(seg, dict):
199
- measured.append(seg)
200
- continue
201
- item = dict(seg)
202
- item["overlaps_speech"] = segment_overlaps_source_speech(item, evidence)
203
- measured.append(item)
204
- return measured
140
+ return [
141
+ {**seg, "overlaps_speech": segment_overlaps_source_speech(seg, evidence)}
142
+ for seg in narration
143
+ ]
@@ -1,88 +1,49 @@
1
1
  """Fuse scenes, ASR, and quiet windows for narration planning."""
2
2
 
3
- import importlib.util
4
-
5
-
6
- from pathlib import Path
7
-
8
3
  from lib import CONFIG
9
-
10
-
11
4
  from agent_text import _overlap_seconds, _recommended_char_budget
12
5
  from narration_lint import _validate_narration_budget
13
6
 
14
- try:
15
- from deslop_qc import analyze_deslop_qc
16
- except ModuleNotFoundError:
17
- _deslop_qc_path = Path(__file__).with_name("deslop_qc.py")
18
- _deslop_qc_spec = importlib.util.spec_from_file_location(
19
- "deslop_qc", _deslop_qc_path
20
- )
21
- if _deslop_qc_spec is None or _deslop_qc_spec.loader is None:
22
- raise
23
- _deslop_qc_module = importlib.util.module_from_spec(_deslop_qc_spec)
24
- _deslop_qc_spec.loader.exec_module(_deslop_qc_module)
25
- analyze_deslop_qc = _deslop_qc_module.analyze_deslop_qc
7
+
8
+ def _quiet_windows(silence_periods):
9
+ return [qp for qp in silence_periods if not qp["has_speech"]]
26
10
 
27
11
 
28
12
  def _align_narration_to_quiet(narration, scenes_analysis, silence_periods):
29
13
  """Recompute overlaps_speech from real quiet windows; keep the agent's timing.
30
14
 
31
15
  The dense continuous-bed design places narration ON the pictured beat over a
32
- ducked original bed, so we no longer relocate segments into silence gaps. The
33
- old shift moved voiceover up to 3s off the moment it was written for and could
34
- silently blank a squeezed segment; both fought the design intent. We now only
35
- correct the overlaps_speech flag that the ducking stage consumes, leaving the
36
- agent's start/end (and text) intact.
37
- """
38
- if not silence_periods:
39
- for n in narration:
40
- n["overlaps_speech"] = True
41
- return _validate_narration_budget(narration, scenes_analysis)
16
+ ducked original bed, so segments are never relocated into silence gaps. Only
17
+ the overlaps_speech flag that the ducking stage consumes is corrected, leaving
18
+ the agent's start/end (and text) intact.
42
19
 
43
- quiet_windows = [
44
- qp for qp in silence_periods if _silence_window_is_usable_quiet(qp)
45
- ]
46
- quiet_ratio_min = float(CONFIG.get("quiet_overlap_min_ratio", 0.8) or 0.8)
47
-
48
- # Run budget/dedup FIRST, then set the flag on the final (possibly merged) spans,
49
- # so a dedup-merged beat's overlaps_speech reflects its extended timing, not its
50
- # original shorter span (which the ducking stage in assemble.py would mis-duck).
20
+ Budget/dedup runs FIRST so a dedup-merged beat's overlaps_speech reflects its
21
+ extended timing, not its original shorter span.
22
+ """
51
23
  aligned = _validate_narration_budget(narration, scenes_analysis)
24
+ quiet_windows = _quiet_windows(silence_periods)
25
+ quiet_ratio_min = CONFIG["quiet_overlap_min_ratio"]
52
26
  for n in aligned:
53
- try:
54
- seg_start = float(n["start"])
55
- seg_end = float(n["end"])
56
- except (KeyError, TypeError, ValueError):
57
- n["overlaps_speech"] = True
58
- continue
59
- seg_dur = max(0.0, seg_end - seg_start)
60
- quiet_overlap = _quiet_overlap_seconds(seg_start, seg_end, quiet_windows)
27
+ seg_dur = n["end"] - n["start"]
28
+ quiet_overlap = sum(
29
+ _overlap_seconds(n["start"], n["end"], qw["start"], qw["end"])
30
+ for qw in quiet_windows
31
+ )
61
32
  n["overlaps_speech"] = quiet_overlap < max(0.3, seg_dur * quiet_ratio_min)
62
-
63
33
  return aligned
64
34
 
65
35
 
66
36
  def _scene_asr_lines(asr_result, scene):
67
37
  lines = []
68
- for seg in asr_result or []:
69
- try:
70
- start = float(seg.get("start", 0))
71
- end = float(seg.get("end", start))
72
- except (TypeError, ValueError):
73
- continue
74
- if scene["start"] < end and scene["end"] > start:
75
- text = str(seg.get("text", "")).strip()
76
- if text:
77
- lines.append(f" [{start:.1f}-{end:.1f}] {text}")
38
+ for seg in asr_result:
39
+ if scene["start"] < seg["end"] and scene["end"] > seg["start"] and seg["text"]:
40
+ lines.append(f" [{seg['start']:.1f}-{seg['end']:.1f}] {seg['text']}")
78
41
  return lines
79
42
 
80
43
 
81
44
  def _quiet_windows_for_scene(silence_periods, scene):
82
45
  windows = []
83
- for qp in silence_periods or []:
84
- if not _silence_window_is_usable_quiet(qp):
85
- continue
46
+ for qp in _quiet_windows(silence_periods):
86
47
  if qp["start"] < scene["end"] and qp["end"] > scene["start"]:
87
48
  start = max(qp["start"], scene["start"])
88
49
  end = min(qp["end"], scene["end"])
@@ -91,87 +52,35 @@ def _quiet_windows_for_scene(silence_periods, scene):
91
52
  return windows
92
53
 
93
54
 
94
- def _silence_window_is_usable_quiet(qp):
95
- if not isinstance(qp, dict):
96
- return False
97
- reason = str(qp.get("has_speech_reason", "")).lower()
98
- granularity = str(qp.get("asr_granularity", "")).lower()
99
- if reason in {
100
- "coarse_asr_overlap_ignored",
101
- "asr_overlap_low_confidence_quiet",
102
- "coarse_asr_no_overlap",
103
- }:
104
- return True
105
- if granularity == "coarse_grid" and qp.get("has_speech") is True:
106
- return True
107
- return not qp.get("has_speech", False)
108
-
109
-
110
- def _quiet_overlap_seconds(start, end, quiet_windows):
111
- overlap_seconds = 0.0
112
- for qw in quiet_windows:
113
- try:
114
- if isinstance(qw, dict):
115
- q_start = float(qw.get("start", 0))
116
- q_end = float(qw.get("end", q_start))
117
- else:
118
- q_start, q_end = qw
119
- q_start = float(q_start)
120
- q_end = float(q_end)
121
- except (TypeError, ValueError):
122
- continue
123
- overlap_seconds += _overlap_seconds(start, end, q_start, q_end)
124
- return overlap_seconds
125
-
126
-
127
55
  def _build_timeline_fusion(scenes, asr_segments, silence_periods):
128
56
  """Fuse VLM scenes, ASR dialogue and quiet narration slots on one timeline."""
129
57
  fusion = []
130
- quiet_windows = [
131
- w for w in silence_periods or [] if _silence_window_is_usable_quiet(w)
132
- ]
133
- for scene in scenes or []:
134
- try:
135
- start = float(scene.get("start", 0))
136
- end = float(scene.get("end", start))
137
- except (TypeError, ValueError):
138
- continue
58
+ quiet_windows = _quiet_windows(silence_periods)
59
+ for scene in scenes:
60
+ start, end = scene["start"], scene["end"]
139
61
  dialogue_segments = []
140
62
  dialogue_overlap = 0.0
141
- for seg in asr_segments or []:
142
- if not isinstance(seg, dict):
143
- continue
144
- try:
145
- seg_start = float(seg.get("start", 0))
146
- seg_end = float(seg.get("end", seg_start))
147
- except (TypeError, ValueError):
148
- continue
149
- overlap = _overlap_seconds(start, end, seg_start, seg_end)
63
+ for seg in asr_segments:
64
+ overlap = _overlap_seconds(start, end, seg["start"], seg["end"])
150
65
  if overlap <= 0:
151
66
  continue
152
- text = str(seg.get("text", "")).strip()
153
67
  dialogue_overlap += overlap
154
68
  dialogue_segments.append(
155
69
  {
156
- "start": round(seg_start, 2),
157
- "end": round(seg_end, 2),
70
+ "start": round(seg["start"], 2),
71
+ "end": round(seg["end"], 2),
158
72
  "overlap_seconds": round(overlap, 2),
159
- "text": text,
73
+ "text": seg["text"],
160
74
  }
161
75
  )
162
76
 
163
77
  narration_slots = []
164
78
  for window in quiet_windows:
165
- try:
166
- w_start = float(window.get("start", 0))
167
- w_end = float(window.get("end", w_start))
168
- except (TypeError, ValueError):
169
- continue
170
- overlap = _overlap_seconds(start, end, w_start, w_end)
79
+ overlap = _overlap_seconds(start, end, window["start"], window["end"])
171
80
  if overlap <= 0:
172
81
  continue
173
- slot_start = max(start, w_start)
174
- slot_end = min(end, w_end)
82
+ slot_start = max(start, window["start"])
83
+ slot_end = min(end, window["end"])
175
84
  narration_slots.append(
176
85
  {
177
86
  "start": round(slot_start, 2),
@@ -183,9 +92,9 @@ def _build_timeline_fusion(scenes, asr_segments, silence_periods):
183
92
 
184
93
  fusion.append(
185
94
  {
186
- "scene_id": scene.get("scene_id"),
95
+ "scene_id": scene["scene_id"],
187
96
  "time_range": [round(start, 2), round(end, 2)],
188
- "visual_description": scene.get("description", ""),
97
+ "visual_description": scene["description"],
189
98
  "depth_analysis": scene.get("depth_analysis", ""),
190
99
  "frame_facts": scene.get("frame_facts", {}),
191
100
  "dialogue_segments": dialogue_segments,
@@ -220,7 +220,7 @@ def build_agent_brief(
220
220
  lines.extend(_format_timeline_fusion_for_brief(timeline_fusion))
221
221
  lines.extend(_format_sentence_entry_anchors_for_brief(work_dir, edit_mode))
222
222
 
223
- overview_text = (mimo_overview.get("content") or "").strip()
223
+ overview_text = (mimo_overview or {}).get("content", "").strip()
224
224
  if overview_text:
225
225
  lines.extend(
226
226
  [