@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
@@ -2,18 +2,12 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
-
6
5
  import hashlib
7
-
8
6
  import json
9
-
10
-
11
7
  import os
12
-
13
-
14
8
  from pathlib import Path
15
-
16
- from typing import Any, Mapping, Sequence
9
+ from typing import Any
10
+ from collections.abc import Mapping, Sequence
17
11
 
18
12
  import qc_contract
19
13
  from mimo_qc_client import DEFAULT_CONFIG
@@ -62,13 +56,8 @@ def _fingerprint_value(value: Any) -> str:
62
56
  return hashlib.sha256(_stable_json(value).encode("utf-8")).hexdigest()
63
57
 
64
58
 
65
- def _redact(value: Any) -> Any:
66
- return qc_contract.redact_secrets(value)
67
-
68
-
69
59
  def _load_json(path: Path) -> Any:
70
- with path.open("r", encoding="utf-8") as handle:
71
- return _redact(json.load(handle))
60
+ return json.loads(path.read_text(encoding="utf-8"))
72
61
 
73
62
 
74
63
  def _read_text_sample(path: Path, *, max_chars: int = 4000) -> dict[str, Any]:
@@ -77,7 +66,7 @@ def _read_text_sample(path: Path, *, max_chars: int = 4000) -> dict[str, Any]:
77
66
  "kind": "text",
78
67
  "bytes": path.stat().st_size,
79
68
  "truncated": len(text) > max_chars,
80
- "sample": _redact(text[:max_chars]),
69
+ "sample": text[:max_chars],
81
70
  }
82
71
 
83
72
 
@@ -85,7 +74,6 @@ def _summarize(
85
74
  value: Any, *, max_items: int = 8, max_string: int = 700, depth: int = 0
86
75
  ) -> Any:
87
76
  """Keep request/report evidence bounded while retaining useful structure."""
88
- value = _redact(value)
89
77
  if depth >= 4:
90
78
  return {"type": type(value).__name__, "fingerprint": _fingerprint_value(value)}
91
79
  if isinstance(value, Mapping):
@@ -118,22 +106,17 @@ def _collect_file(work_dir: Path, name: str) -> dict[str, Any] | None:
118
106
  path = work_dir / name
119
107
  if not path.is_file():
120
108
  return None
121
- try:
122
- if path.suffix.lower() == ".json":
123
- summary = _summarize(_load_json(path))
124
- kind = "json"
125
- else:
126
- summary = _summarize(_read_text_sample(path))
127
- kind = "text"
128
- return {
129
- "path": name,
130
- "kind": kind,
131
- "bytes": path.stat().st_size,
132
- "fingerprint": qc_contract.artifact_fingerprint(path),
133
- "summary": summary,
134
- }
135
- except Exception as exc: # evidence collection is advisory too
136
- return {"path": name, "kind": "unreadable", "error": type(exc).__name__}
109
+ if path.suffix.lower() == ".json":
110
+ kind, summary = "json", _summarize(_load_json(path))
111
+ else:
112
+ kind, summary = "text", _summarize(_read_text_sample(path))
113
+ return {
114
+ "path": name,
115
+ "kind": kind,
116
+ "bytes": path.stat().st_size,
117
+ "fingerprint": qc_contract.artifact_fingerprint(path),
118
+ "summary": summary,
119
+ }
137
120
 
138
121
 
139
122
  def _first_existing(work_dir: Path, names: Sequence[str]) -> str | None:
@@ -145,32 +128,21 @@ def _collect_multi_source_asr(work_dir: Path) -> dict[str, Any]:
145
128
  manifest_path = work_dir / "multi_source_manifest.json"
146
129
  if not manifest_path.is_file():
147
130
  return {}
148
- try:
149
- manifest = _load_json(manifest_path)
150
- except Exception:
151
- return {}
152
- sources = manifest.get("sources") if isinstance(manifest, Mapping) else None
153
- if not isinstance(sources, list):
154
- return {}
131
+ root = work_dir.resolve(strict=False)
155
132
  collected = {}
156
- for source in sources:
157
- if not isinstance(source, Mapping):
158
- continue
159
- source_id = str(source.get("source_id") or "").strip()
160
- relative_dir = str(source.get("source_work_dir") or "").strip()
161
- if not source_id or not relative_dir:
162
- continue
163
- source_dir = (work_dir / relative_dir).resolve(strict=False)
164
- try:
165
- source_dir.relative_to(work_dir.resolve(strict=False))
166
- except ValueError:
133
+ for source in _load_json(manifest_path)["sources"]:
134
+ relative_dir = source["source_work_dir"]
135
+ source_dir = (root / relative_dir).resolve(strict=False)
136
+ if not source_dir.is_relative_to(root):
167
137
  continue
168
- relative_name = _first_existing(source_dir, _SOURCE_ASR_ARTIFACTS)
169
- if not relative_name:
170
- continue
171
- item = _collect_file(work_dir, str(Path(relative_dir) / relative_name))
172
- if item is not None:
173
- collected[source_id] = item
138
+ name = _first_existing(source_dir, _SOURCE_ASR_ARTIFACTS)
139
+ if name is not None:
140
+ evidence_path = (source_dir / name).resolve(strict=False)
141
+ if not evidence_path.is_relative_to(root):
142
+ continue
143
+ collected[source["source_id"]] = _collect_file(
144
+ root, str(evidence_path.relative_to(root))
145
+ )
174
146
  return collected
175
147
 
176
148
 
@@ -187,14 +159,7 @@ def _final_output_candidates(
187
159
  raw.append(final_output)
188
160
  manifest_path = work_dir / "assembly_manifest.json"
189
161
  if manifest_path.is_file():
190
- try:
191
- manifest = _load_json(manifest_path)
192
- if isinstance(manifest, Mapping):
193
- for key in ("final_output", "output", "output_path", "video_path"):
194
- if manifest.get(key):
195
- raw.append(str(manifest[key]))
196
- except Exception:
197
- pass
162
+ raw.append(_load_json(manifest_path)["final_output"])
198
163
  raw.extend(("output.mp4", "recap.mp4", "final.mp4"))
199
164
  seen: set[str] = set()
200
165
  result = []
@@ -221,7 +186,7 @@ def _final_output_metadata(
221
186
  }
222
187
  )
223
188
  outputs.append(item)
224
- return {"candidates": _redact(outputs)}
189
+ return {"candidates": outputs}
225
190
 
226
191
 
227
192
  def _existing_final_output(
@@ -279,41 +244,28 @@ def collect_evidence(
279
244
  if not evidence["source_asr"]:
280
245
  evidence["source_asr"] = _collect_multi_source_asr(root)
281
246
  evidence["fingerprint"] = _fingerprint_value(_cache_evidence(evidence))
282
- return _redact(evidence)
247
+ # The evidence goes into the MiMo request as well as the persisted report, so it is
248
+ # redacted here, before either.
249
+ return qc_contract.redact_secrets(evidence)
283
250
 
284
251
 
285
252
  def _cache_file_group(group: Mapping[str, Any]) -> dict[str, Any]:
286
253
  return {
287
- name: {
288
- key: item.get(key)
289
- for key in ("kind", "bytes", "fingerprint")
290
- if isinstance(item, Mapping) and item.get(key) is not None
291
- }
254
+ name: {key: item[key] for key in ("kind", "bytes", "fingerprint")}
292
255
  for name, item in sorted(group.items())
293
256
  }
294
257
 
295
258
 
296
259
  def _cache_evidence(evidence: Mapping[str, Any]) -> dict[str, Any]:
297
- final_items = []
298
- final_output = evidence.get("final_output")
299
- if isinstance(final_output, Mapping):
300
- for item in final_output.get("candidates", []):
301
- if isinstance(item, Mapping):
302
- final_items.append(
303
- {
304
- key: item.get(key)
305
- for key in ("exists", "bytes", "fingerprint")
306
- if key in item
307
- }
308
- )
309
260
  return {
310
- "artifacts": _cache_file_group(evidence.get("artifacts", {})),
311
- "source_asr": _cache_file_group(evidence.get("source_asr", {})),
312
- "generated_subtitles": _cache_file_group(
313
- evidence.get("generated_subtitles", {})
314
- ),
315
- "visual_metadata": _cache_file_group(evidence.get("visual_metadata", {})),
316
- "final_output": final_items,
261
+ "artifacts": _cache_file_group(evidence["artifacts"]),
262
+ "source_asr": _cache_file_group(evidence["source_asr"]),
263
+ "generated_subtitles": _cache_file_group(evidence["generated_subtitles"]),
264
+ "visual_metadata": _cache_file_group(evidence["visual_metadata"]),
265
+ "final_output": [
266
+ {key: item[key] for key in ("exists", "bytes", "fingerprint") if key in item}
267
+ for item in evidence["final_output"]["candidates"]
268
+ ],
317
269
  }
318
270
 
319
271
 
@@ -325,7 +277,7 @@ def _effective_config(config: Mapping[str, Any] | None = None) -> dict[str, Any]
325
277
  source["mimo_qc_model"] = (
326
278
  config.get("mimo_video_model")
327
279
  or config.get("mimo_model")
328
- or source.get("mimo_qc_model")
280
+ or source["mimo_qc_model"]
329
281
  )
330
282
  # MIMO_QC_MODEL is intentionally read at call time for embedded/CLI tests and
331
283
  # long-running agent processes whose environment may be adjusted between runs.
@@ -354,18 +306,15 @@ def safe_mimo_config(config: Mapping[str, Any] | None = None) -> dict[str, Any]:
354
306
  "mimo_media_resolution",
355
307
  "mimo_media_resolution_source",
356
308
  )
357
- safe = {key: source[key] for key in keep if key in source}
309
+ safe = {key: source[key] for key in keep}
358
310
  safe["model"] = (
359
- source.get("mimo_qc_model")
360
- or source.get("mimo_video_model")
361
- or source.get("mimo_model")
362
- or "mimo-v2.5"
311
+ source["mimo_qc_model"] or source["mimo_video_model"] or source["mimo_model"]
363
312
  )
364
313
  key_present = bool(
365
314
  source.get("mimo_video_api_key")
366
315
  or source.get("mimo_api_key")
367
316
  or source.get("api_key")
368
317
  )
369
- safe = _redact(safe)
318
+ safe = qc_contract.redact_secrets(safe)
370
319
  safe["key_present"] = key_present
371
320
  return safe
@@ -2,18 +2,13 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
-
6
5
  import json
7
-
8
-
9
6
  import re
10
-
11
-
12
- from typing import Any, Mapping
7
+ from typing import Any
8
+ from collections.abc import Mapping
13
9
 
14
10
  import qc_contract
15
-
16
- from mimo_qc_evidence import _fingerprint_value, _redact, _summarize, safe_mimo_config
11
+ from mimo_qc_evidence import _fingerprint_value, _summarize, safe_mimo_config
17
12
  from mimo_qc_payload import _strip_json_fence
18
13
  from mimo_qc_contract import (
19
14
  ARTIFACT_NAME,
@@ -24,11 +19,11 @@ from mimo_qc_contract import (
24
19
 
25
20
 
26
21
  def _extract_observations(model_output: Any) -> list[Mapping[str, Any]]:
27
- model_output = _redact(model_output)
22
+ """Bound whatever shape the model (or an offline fixture) returned to observation dicts."""
28
23
  if isinstance(model_output, str):
29
24
  try:
30
25
  model_output = json.loads(_strip_json_fence(model_output))
31
- except (TypeError, ValueError):
26
+ except ValueError:
32
27
  return [
33
28
  {
34
29
  "code": "freeform_observation",
@@ -47,10 +42,9 @@ def _extract_observations(model_output: Any) -> list[Mapping[str, Any]]:
47
42
  return [item for item in model_output[key] if isinstance(item, Mapping)][
48
43
  :MAX_OBSERVATIONS
49
44
  ]
50
- try:
45
+ if "choices" in model_output: # a raw chat-completion response saved as a fixture
51
46
  return _extract_observations(model_output["choices"][0]["message"]["content"])
52
- except (KeyError, IndexError, TypeError):
53
- return [model_output] if model_output else []
47
+ return [model_output] if model_output else []
54
48
 
55
49
 
56
50
  def _norm_choice(raw: Any, allowed: set[str], default: str) -> str:
@@ -59,15 +53,10 @@ def _norm_choice(raw: Any, allowed: set[str], default: str) -> str:
59
53
 
60
54
 
61
55
  def _caption_match_text(value: Any) -> str:
62
- return re.sub(r"[^0-9A-Za-z\u3400-\u9fff]+", "", str(value or "")).lower()
56
+ return re.sub(r"[^0-9A-Za-z㐀-鿿]+", "", str(value or "")).lower()
63
57
 
64
58
 
65
59
  def _generated_subtitle_corpus(payload: Mapping[str, Any]) -> str:
66
- group = (
67
- (payload.get("evidence") or {}).get("generated_subtitles")
68
- if isinstance(payload.get("evidence"), Mapping)
69
- else None
70
- )
71
60
  strings = []
72
61
 
73
62
  def visit(value: Any) -> None:
@@ -80,7 +69,7 @@ def _generated_subtitle_corpus(payload: Mapping[str, Any]) -> str:
80
69
  for child in value:
81
70
  visit(child)
82
71
 
83
- visit(group or {})
72
+ visit(payload["evidence"]["generated_subtitles"])
84
73
  return _caption_match_text(" ".join(strings))
85
74
 
86
75
 
@@ -117,21 +106,18 @@ def _misclassified_generated_caption(
117
106
  def normalize_observations(
118
107
  model_output: Any,
119
108
  *,
109
+ payload: Mapping[str, Any],
120
110
  stage: str = DEFAULT_STAGE,
121
- payload: Mapping[str, Any] | None = None,
122
111
  model_config: Mapping[str, Any] | None = None,
123
112
  ) -> list[dict[str, Any]]:
124
113
  """Normalize bounded model output into permanently non-blocking findings."""
125
- payload = _redact(payload or {})
126
114
  cfg = safe_mimo_config(model_config)
127
- model_used = str(cfg.get("model") or "mimo-qc-offline-fixture")
115
+ model_used = cfg["model"]
128
116
  findings = []
129
117
  for index, observation in enumerate(_extract_observations(model_output), start=1):
130
118
  if _misclassified_generated_caption(observation, payload, stage):
131
119
  continue
132
120
  obs = _summarize(observation, max_items=10, max_string=MAX_MESSAGE_CHARS)
133
- if not isinstance(obs, Mapping):
134
- continue
135
121
  category_hint = str(
136
122
  obs.get("category") or obs.get("type") or "semantic"
137
123
  ).lower()
@@ -156,18 +142,16 @@ def normalize_observations(
156
142
  "aesthetic" if category == "mimo_aesthetic" else "semantic",
157
143
  )
158
144
  raw_evidence = obs.get("evidence")
159
- supplied_evidence: Mapping[str, Any] = (
160
- raw_evidence if isinstance(raw_evidence, Mapping) else {}
161
- )
162
145
  evidence = {
163
- **dict(supplied_evidence),
146
+ **(raw_evidence if isinstance(raw_evidence, Mapping) else {}),
164
147
  "model": model_used,
165
148
  "config": cfg,
166
149
  "fingerprint": {
167
- "evidence": payload.get("evidence_fingerprint"),
150
+ "evidence": payload["evidence_fingerprint"],
168
151
  "observation": _fingerprint_value(observation),
169
152
  },
170
153
  }
154
+ location = obs.get("location")
171
155
  findings.append(
172
156
  qc_contract.build_finding(
173
157
  finding_id=str(
@@ -185,14 +169,12 @@ def normalize_observations(
185
169
  deterministic=False,
186
170
  blocking=False,
187
171
  source={"artifact": ARTIFACT_NAME, "adapter": "mimo_qc.py"},
188
- location=obs.get("location")
189
- if isinstance(obs.get("location"), Mapping)
190
- else {},
191
- evidence=_redact(evidence),
172
+ location=location if isinstance(location, Mapping) else {},
173
+ evidence=evidence,
192
174
  model_used=model_used,
193
175
  artifact_fingerprints={
194
- "payload": str(payload.get("payload_fingerprint") or "fixture"),
195
- "evidence": str(payload.get("evidence_fingerprint") or "fixture"),
176
+ "payload": payload["payload_fingerprint"],
177
+ "evidence": payload["evidence_fingerprint"],
196
178
  },
197
179
  next_action="human_review",
198
180
  decision_reason=message,
@@ -2,19 +2,12 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
-
6
5
  import json
6
+ from typing import Any
7
+ from collections.abc import Mapping, Sequence
7
8
 
8
- from typing import Any, Mapping, Sequence
9
-
10
-
11
- from mimo_qc_evidence import (
12
- _cache_evidence,
13
- _fingerprint_value,
14
- _redact,
15
- safe_mimo_config,
16
- )
17
- from mimo_qc_contract import ARTIFACT_NAME, DEFAULT_STAGE, MAX_FRAMES
9
+ from mimo_qc_evidence import _fingerprint_value, safe_mimo_config
10
+ from mimo_qc_contract import ARTIFACT_NAME, DEFAULT_STAGE
18
11
 
19
12
 
20
13
  def _semantic_evidence(evidence: Mapping[str, Any], *, stage: str) -> dict[str, Any]:
@@ -83,22 +76,16 @@ def _semantic_evidence(evidence: Mapping[str, Any], *, stage: str) -> dict[str,
83
76
  semantic["evidence_roles"].pop("post_render_frame_limits", None)
84
77
  semantic["evidence_roles"].pop("actual_audio_timing", None)
85
78
  else:
86
- final_output = semantic.get("final_output")
87
- candidates = (
88
- final_output.get("candidates", [])
89
- if isinstance(final_output, Mapping)
90
- else []
91
- )
92
79
  semantic["final_output"] = {
93
80
  "candidates": [
94
81
  dict(candidate)
95
- for candidate in candidates
96
- if isinstance(candidate, Mapping) and candidate.get("exists") is True
82
+ for candidate in semantic["final_output"]["candidates"]
83
+ if candidate["exists"]
97
84
  ]
98
85
  }
99
86
  # Artifact summaries are already bounded at collection time; summarizing them again
100
87
  # would wrap their ``items`` arrays in another summary layer and obscure the values.
101
- return _redact(semantic)
88
+ return semantic
102
89
 
103
90
 
104
91
  def build_payload(
@@ -144,11 +131,10 @@ def build_payload(
144
131
  '"semantic|aesthetic|sampled","evidence":{...}}]}。最多 12 条。'
145
132
  ),
146
133
  "evidence": _semantic_evidence(evidence, stage=stage),
147
- "evidence_fingerprint": evidence.get("fingerprint")
148
- or _fingerprint_value(_cache_evidence(evidence)),
134
+ "evidence_fingerprint": evidence["fingerprint"],
149
135
  }
150
136
  payload["payload_fingerprint"] = _fingerprint_value(payload)
151
- return _redact(payload)
137
+ return payload
152
138
 
153
139
 
154
140
  def _request_payload(
@@ -167,30 +153,24 @@ def _request_payload(
167
153
  ),
168
154
  }
169
155
  ]
170
- for index, sample in enumerate(list(frame_samples)[:MAX_FRAMES], start=1):
171
- data_url = sample.get("data_url") if isinstance(sample, Mapping) else None
172
- if isinstance(data_url, str) and data_url.startswith("data:image/jpeg;base64,"):
173
- timestamp = sample.get("timestamp")
174
- try:
175
- timestamp_label = f"{float(timestamp):.3f}s"
176
- except (TypeError, ValueError):
177
- timestamp_label = "unknown"
178
- content.append(
179
- {
180
- "type": "text",
181
- "text": (
182
- f"BEGIN QC_FRAME_{index}: final-output timestamp {timestamp_label}. "
183
- f"Judge only this image and do not transfer its content to another frame."
184
- ),
185
- }
186
- )
187
- content.append({"type": "image_url", "image_url": {"url": data_url}})
188
- content.append(
189
- {
190
- "type": "text",
191
- "text": f"END QC_FRAME_{index}: timestamp {timestamp_label}.",
192
- }
193
- )
156
+ for index, sample in enumerate(frame_samples, start=1):
157
+ timestamp_label = f"{sample['timestamp']:.3f}s"
158
+ content.append(
159
+ {
160
+ "type": "text",
161
+ "text": (
162
+ f"BEGIN QC_FRAME_{index}: final-output timestamp {timestamp_label}. "
163
+ f"Judge only this image and do not transfer its content to another frame."
164
+ ),
165
+ }
166
+ )
167
+ content.append({"type": "image_url", "image_url": {"url": sample["data_url"]}})
168
+ content.append(
169
+ {
170
+ "type": "text",
171
+ "text": f"END QC_FRAME_{index}: timestamp {timestamp_label}.",
172
+ }
173
+ )
194
174
  return {
195
175
  "model": payload["model"],
196
176
  "messages": [{"role": "user", "content": content}],
@@ -209,6 +189,8 @@ def _strip_json_fence(text: str) -> str:
209
189
 
210
190
 
211
191
  def _validated_live_output(response: Any) -> Any:
192
+ """The one shape check on the third-party model response: a list of observations, or an
193
+ object carrying `observations`/`findings`; anything else is a failed (fail-open) request."""
212
194
  try:
213
195
  content = response["choices"][0]["message"]["content"]
214
196
  except (KeyError, IndexError, TypeError):