@oh-story/dsh 0.1.4 → 0.1.6

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 (479) hide show
  1. package/README.md +56 -12
  2. package/lib/client.js +2707 -268
  3. package/lib/client.js.map +4 -4
  4. package/lib/drama/manifest.json +136 -101
  5. package/lib/drama/skills/short-drama/SKILL.md +61 -1
  6. package/lib/drama/skills/short-drama/references/contract-and-ownership.md +7 -4
  7. package/lib/drama/skills/short-drama/references/creator-documents.md +133 -8
  8. package/lib/drama/skills/short-drama/references/creator-workflow.md +38 -2
  9. package/lib/drama/skills/short-drama/references/knowhow-index.md +3 -0
  10. package/lib/drama/skills/short-drama/references/reference-roles.md +10 -7
  11. package/lib/drama/skills/short-drama/references/runtime-preflight.md +3 -0
  12. package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +1002 -0
  13. package/lib/drama/skills/short-drama/scripts/project_tool.py +485 -5
  14. package/lib/drama/skills/short-drama-assets/SKILL.md +13 -3
  15. package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +116 -0
  16. package/lib/drama/skills/short-drama-assets/references/identity-vs-variant.md +2 -1
  17. package/lib/drama/skills/short-drama-assets/references/prop-and-state.md +19 -0
  18. package/lib/drama/skills/short-drama-assets/references/stage-contract.md +2 -0
  19. package/lib/drama/skills/short-drama-assets/scripts/asset_check.py +1 -1
  20. package/lib/drama/skills/short-drama-image-prompts/SKILL.md +6 -2
  21. package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +4 -3
  22. package/lib/drama/skills/short-drama-image-prompts/references/lookdev-frame.md +1 -1
  23. package/lib/drama/skills/short-drama-image-prompts/references/review-and-fixtures.md +1 -1
  24. package/lib/drama/skills/short-drama-image-prompts/references/stage-contract.md +3 -1
  25. package/lib/drama/skills/short-drama-image-prompts/scripts/image_prompt_check.py +8 -8
  26. package/lib/drama/skills/short-drama-image-prompts/scripts/selftest.py +4 -2
  27. package/lib/drama/skills/short-drama-produce/SKILL.md +46 -10
  28. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +65 -8
  29. package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +85 -0
  30. package/lib/drama/skills/short-drama-produce/references/providers/seedance.md +27 -10
  31. package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +362 -3
  32. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +627 -40
  33. package/lib/drama/skills/short-drama-produce/scripts/selftest.py +5 -4
  34. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +12 -0
  35. package/lib/drama/skills/short-drama-review/scripts/review_check.py +1 -1
  36. package/lib/drama/skills/short-drama-storyboard/SKILL.md +76 -7
  37. package/lib/drama/skills/short-drama-storyboard/assets/shot-template.jsonl +1 -1
  38. package/lib/drama/skills/short-drama-storyboard/references/keyframe-craft.md +6 -2
  39. package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +19 -4
  40. package/lib/drama/skills/short-drama-storyboard/references/review-and-fixtures.md +5 -2
  41. package/lib/drama/skills/short-drama-storyboard/references/screenplay-to-keyframe-example.md +16 -0
  42. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +8 -3
  43. package/lib/drama/skills/short-drama-storyboard/scripts/storyboard_check.py +1 -1
  44. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +81 -13
  45. package/lib/drama/skills/short-drama-video-prompts/assets/motion-spec.jsonl.md +6 -1
  46. package/lib/drama/skills/short-drama-video-prompts/assets/video-prompts.md +17 -1
  47. package/lib/drama/skills/short-drama-video-prompts/references/delivery-profile.md +21 -1
  48. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +154 -0
  49. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +24 -6
  50. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +11 -7
  51. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +41 -0
  52. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +66 -0
  53. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +5 -2
  54. package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +115 -0
  55. package/lib/drama/skills/short-drama-video-prompts/scripts/container_check.py +1 -1
  56. package/lib/drama/skills/short-drama-video-prompts/scripts/music_spec_check.py +1 -1
  57. package/lib/drama/skills/short-drama-write/SKILL.md +3 -2
  58. package/lib/drama/skills/short-drama-write/scripts/duration_estimate.py +1 -1
  59. package/lib/drama/skills/short-drama-write/scripts/screenplay_index.py +2 -2
  60. package/lib/drama/skills/short-drama-write/scripts/voice_sheet_check.py +1 -1
  61. package/lib/index.js +865 -33
  62. package/lib/index.js.map +4 -4
  63. package/lib/novel-to-game/LICENSE +21 -0
  64. package/lib/novel-to-game/README.md +180 -0
  65. package/lib/novel-to-game/README_ZH.md +178 -0
  66. package/lib/novel-to-game/VERSION +1 -0
  67. package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +217 -0
  68. package/lib/novel-to-game/examples/jin-ping-mei/build/app/.vercelignore +3 -0
  69. package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +130 -0
  70. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/compound_new_guofeng.webp +0 -0
  71. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/fate_coda.webp +0 -0
  72. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/meng.webp +0 -0
  73. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/pan.webp +0 -0
  74. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/pinger.webp +0 -0
  75. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/xuee.webp +0 -0
  76. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/finale/yue.webp +0 -0
  77. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_accord_five.webp +0 -0
  78. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_afterglow_five.webp +0 -0
  79. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_alliance.webp +0 -0
  80. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day10.webp +0 -0
  81. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day15.webp +0 -0
  82. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day5.webp +0 -0
  83. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/title_new_guofeng.webp +0 -0
  84. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/pan_pinger.webp +0 -0
  85. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/pan_xuee.webp +0 -0
  86. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_meng.webp +0 -0
  87. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_pan.webp +0 -0
  88. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/joint/yue_pinger.webp +0 -0
  89. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/meng/explicit.webp +0 -0
  90. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/meng/prelude.webp +0 -0
  91. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/meng.webp +0 -0
  92. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/pan.webp +0 -0
  93. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/pinger.webp +0 -0
  94. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/xuee.webp +0 -0
  95. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/milestone/yue.webp +0 -0
  96. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pan/explicit.webp +0 -0
  97. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pan/prelude.webp +0 -0
  98. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pinger/explicit.webp +0 -0
  99. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/pinger/prelude.webp +0 -0
  100. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/xuee/explicit.webp +0 -0
  101. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/xuee/prelude.webp +0 -0
  102. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/yue/explicit.webp +0 -0
  103. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/yue/prelude.webp +0 -0
  104. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/meng/night.webp +0 -0
  105. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/pan/night.webp +0 -0
  106. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/pinger/night.webp +0 -0
  107. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/xuee/night.webp +0 -0
  108. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/heroine/yue/night.webp +0 -0
  109. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/household/li_jiaoer.webp +0 -0
  110. package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/scene/gate_collector.webp +0 -0
  111. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +1105 -0
  112. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +2539 -0
  113. package/lib/novel-to-game/examples/jin-ping-mei/build/app/index.html +18 -0
  114. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/assets.js +147 -0
  115. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/audio.js +332 -0
  116. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +7472 -0
  117. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +13077 -0
  118. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +3028 -0
  119. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +32 -0
  120. package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +10 -0
  121. package/lib/novel-to-game/examples/jin-ping-mei/build/app/vercel.json +23 -0
  122. package/lib/novel-to-game/examples/jin-ping-mei/build/art/generated-art.json +161 -0
  123. package/lib/novel-to-game/examples/jin-ping-mei/example.json +12 -0
  124. package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +31 -0
  125. package/lib/novel-to-game/examples/jin-ping-mei/qa/playable-model.json +99 -0
  126. package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +41 -0
  127. package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +129 -0
  128. package/lib/novel-to-game/manifest.json +508 -0
  129. package/lib/novel-to-game/skills/game-art-direction/SKILL.md +73 -0
  130. package/lib/novel-to-game/skills/game-art-direction/agents/openai.yaml +4 -0
  131. package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +119 -0
  132. package/lib/novel-to-game/skills/game-build/SKILL.md +84 -0
  133. package/lib/novel-to-game/skills/game-build/agents/openai.yaml +4 -0
  134. package/lib/novel-to-game/skills/game-build/references/build-brief-contract.md +109 -0
  135. package/lib/novel-to-game/skills/game-build/references/generative-media-pipeline.md +56 -0
  136. package/lib/novel-to-game/skills/game-build/references/playable-model-contract.md +81 -0
  137. package/lib/novel-to-game/skills/game-build/references/tts-production-contract.md +37 -0
  138. package/lib/novel-to-game/skills/game-concept/SKILL.md +72 -0
  139. package/lib/novel-to-game/skills/game-concept/agents/openai.yaml +4 -0
  140. package/lib/novel-to-game/skills/game-concept/references/concept-method.md +94 -0
  141. package/lib/novel-to-game/skills/game-qa/SKILL.md +50 -0
  142. package/lib/novel-to-game/skills/game-qa/agents/openai.yaml +4 -0
  143. package/lib/novel-to-game/skills/game-qa/references/qa-contract.md +66 -0
  144. package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +45 -0
  145. package/lib/novel-to-game/skills/game-world-design/SKILL.md +63 -0
  146. package/lib/novel-to-game/skills/game-world-design/agents/openai.yaml +4 -0
  147. package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +59 -0
  148. package/lib/novel-to-game/skills/game-world-design/references/game-writing-craft.md +53 -0
  149. package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +84 -0
  150. package/lib/novel-to-game/skills/game-world-design/references/numeric-design-method.md +56 -0
  151. package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +124 -0
  152. package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +77 -0
  153. package/lib/novel-to-game/skills/novel-game-analyze/agents/openai.yaml +4 -0
  154. package/lib/novel-to-game/skills/novel-game-analyze/references/gameability-protocol.md +65 -0
  155. package/lib/novel-to-game/skills/novel-to-game/SKILL.md +71 -0
  156. package/lib/novel-to-game/skills/novel-to-game/agents/openai.yaml +4 -0
  157. package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +42 -0
  158. package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +51 -0
  159. package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +65 -0
  160. package/lib/oh-story/manifest.json +415 -305
  161. package/lib/oh-story/roles/consistency-checker.md +1 -1
  162. package/lib/oh-story/roles/narrative-writer.md +32 -33
  163. package/lib/oh-story/roles/story-architect.md +16 -26
  164. package/lib/oh-story/skills/browser-cdp/SKILL.md +1 -1
  165. package/lib/oh-story/skills/story/SKILL.md +16 -9
  166. package/lib/oh-story/skills/story/VERSION +1 -1
  167. package/lib/oh-story/skills/story/references/author-memory.md +163 -0
  168. package/lib/oh-story/skills/story/scripts/author_memory_commit.py +871 -0
  169. package/lib/oh-story/skills/story-cover/SKILL.md +1 -1
  170. package/lib/oh-story/skills/story-deslop/SKILL.md +10 -5
  171. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +2 -1
  172. package/lib/oh-story/skills/story-deslop/references/author-memory.md +163 -0
  173. package/lib/oh-story/skills/story-deslop/references/banned-words.md +6 -4
  174. package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +871 -0
  175. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +64 -1
  176. package/lib/oh-story/skills/story-import/SKILL.md +19 -10
  177. package/lib/oh-story/skills/story-import/references/format-and-structure.md +9 -10
  178. package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +2 -2
  179. package/lib/oh-story/skills/story-import/references/structure-mapping-short.md +1 -1
  180. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +14 -6
  181. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +251 -0
  182. package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +98 -9
  183. package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +246 -0
  184. package/lib/oh-story/skills/story-long-analyze/SKILL.md +4 -4
  185. package/lib/oh-story/skills/story-long-scan/SKILL.md +1 -1
  186. package/lib/oh-story/skills/story-long-write/SKILL.md +35 -21
  187. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +2 -1
  188. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +7 -3
  189. package/lib/oh-story/skills/story-long-write/references/author-memory.md +163 -0
  190. package/lib/oh-story/skills/story-long-write/references/banned-words.md +6 -4
  191. package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +6 -6
  192. package/lib/oh-story/skills/story-long-write/references/genre-prose-cards.md +1 -1
  193. package/lib/oh-story/skills/story-long-write/references/genre-readers.md +1 -1
  194. package/lib/oh-story/skills/{story-setup/references/agent-references/hooks-chapter.md → story-long-write/references/long-chapter-hooks.md} +5 -5
  195. package/lib/oh-story/skills/story-long-write/references/{quality-checklist.md → long-chapter-quality.md} +2 -1
  196. package/lib/oh-story/skills/story-long-write/references/long-format.md +42 -0
  197. package/lib/oh-story/skills/{story-setup/references/agent-references/genre-catalog.md → story-long-write/references/long-genre-catalog.md} +11 -97
  198. package/lib/oh-story/skills/{story-setup/references/agent-references/genre-core-mechanics.md → story-long-write/references/long-genre-mechanics.md} +1 -1
  199. package/lib/oh-story/skills/{story-short-write/references/reversal-toolkit.md → story-long-write/references/long-reversal.md} +18 -32
  200. package/lib/oh-story/skills/story-long-write/references/{hooks-suspense.md → long-suspense.md} +6 -6
  201. package/lib/oh-story/skills/story-long-write/references/opening-design.md +1 -1
  202. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +1 -1
  203. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  204. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +14 -6
  205. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +32 -27
  206. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +4 -4
  207. package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +2 -2
  208. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +14 -14
  209. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +54 -54
  210. package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +871 -0
  211. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +64 -1
  212. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +251 -0
  213. package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +303 -0
  214. package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +98 -9
  215. package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +246 -0
  216. package/lib/oh-story/skills/story-review/SKILL.md +26 -17
  217. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +2 -1
  218. package/lib/oh-story/skills/story-review/references/author-memory.md +163 -0
  219. package/lib/oh-story/skills/story-review/references/banned-words.md +6 -4
  220. package/lib/oh-story/skills/story-review/references/{quality-checklist.md → review-quality.md} +22 -21
  221. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +14 -6
  222. package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +871 -0
  223. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +64 -1
  224. package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +98 -9
  225. package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +246 -0
  226. package/lib/oh-story/skills/story-setup/SKILL.md +74 -33
  227. package/lib/oh-story/skills/story-setup/references/agent-references/agent-quality.md +72 -0
  228. package/lib/oh-story/skills/story-setup/references/agent-references/agent-reference-profiles.md +55 -0
  229. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +2 -1
  230. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +6 -4
  231. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +9 -10
  232. package/lib/oh-story/skills/story-setup/references/agent-references/genre-prose-cards.md +1 -1
  233. package/lib/oh-story/skills/story-setup/references/agent-references/genre-readers.md +1 -1
  234. package/lib/oh-story/skills/{story-short-analyze/references/hooks-chapter.md → story-setup/references/agent-references/long-chapter-hooks.md} +5 -5
  235. package/lib/oh-story/skills/story-setup/references/agent-references/long-emotional-methods.md +209 -0
  236. package/lib/oh-story/skills/{story-long-write/references/genre-catalog.md → story-setup/references/agent-references/long-genre-catalog.md} +11 -97
  237. package/lib/oh-story/skills/{story-long-write/references/genre-core-mechanics.md → story-setup/references/agent-references/long-genre-mechanics.md} +1 -1
  238. package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +40 -0
  239. package/lib/oh-story/skills/story-setup/references/agent-references/{reversal-toolkit.md → long-reversal.md} +18 -32
  240. package/lib/oh-story/skills/{story-short-write/references/hooks-suspense.md → story-setup/references/agent-references/long-suspense.md} +6 -6
  241. package/lib/oh-story/skills/story-setup/references/agent-references/opening-design.md +1 -1
  242. package/lib/oh-story/skills/story-setup/references/agent-references/{emotional-methods.md → short-emotional-methods.md} +4 -4
  243. package/lib/oh-story/skills/{story-long-write/references/genre-writing-formulas.md → story-setup/references/agent-references/short-genre-formulas.md} +32 -44
  244. package/lib/oh-story/skills/story-setup/references/agent-references/short-quality.md +39 -0
  245. package/lib/oh-story/skills/{story-long-write/references/reversal-toolkit.md → story-setup/references/agent-references/short-reversal.md} +29 -23
  246. package/lib/oh-story/skills/story-setup/references/agent-references/{hooks-suspense.md → short-suspense.md} +31 -31
  247. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  248. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +55 -55
  249. package/lib/oh-story/skills/story-short-analyze/SKILL.md +28 -28
  250. package/lib/oh-story/skills/story-short-analyze/references/{character-basics.md → analysis-character-basics.md} +1 -1
  251. package/lib/oh-story/skills/story-short-analyze/references/{character-design-methods.md → analysis-character-design.md} +1 -1
  252. package/lib/oh-story/skills/story-short-analyze/references/{character-relations.md → analysis-character-relations.md} +1 -1
  253. package/lib/oh-story/skills/story-short-analyze/references/{hooks-paragraph.md → analysis-paragraph-hooks.md} +1 -1
  254. package/lib/oh-story/skills/story-short-analyze/references/{genre-readers.md → analysis-reader-profile.md} +2 -2
  255. package/lib/oh-story/skills/story-short-analyze/references/analysis-report-style.md +45 -0
  256. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-genres.md +65 -0
  257. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-hooks.md +77 -0
  258. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-mechanics.md +82 -0
  259. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-patterns.md +50 -0
  260. package/lib/oh-story/skills/story-short-analyze/references/analysis-short-suspense.md +98 -0
  261. package/lib/oh-story/skills/story-short-analyze/references/{genre-writing-techniques.md → analysis-writing-techniques.md} +21 -19
  262. package/lib/oh-story/skills/story-short-analyze/references/deconstruction-examples.md +1 -1
  263. package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +21 -28
  264. package/lib/oh-story/skills/story-short-analyze/references/output-contract.md +1 -1
  265. package/lib/oh-story/skills/story-short-analyze/references/output-templates.md +17 -17
  266. package/lib/oh-story/skills/story-short-analyze/references/{quality-checklist.md → source-story-quality.md} +28 -49
  267. package/lib/oh-story/skills/story-short-analyze/references/zhihu-style.md +2 -2
  268. package/lib/oh-story/skills/story-short-scan/SKILL.md +1 -1
  269. package/lib/oh-story/skills/story-short-write/SKILL.md +78 -55
  270. package/lib/oh-story/skills/story-short-write/references/author-memory.md +163 -0
  271. package/lib/oh-story/skills/story-short-write/references/banned-words.md +6 -4
  272. package/lib/oh-story/skills/story-short-write/references/cross-book-recall.md +3 -3
  273. package/lib/oh-story/skills/story-short-write/references/emotional-methods.md +4 -4
  274. package/lib/oh-story/skills/story-short-write/references/genre-styles//344/270/226/346/203/205/346/211/223/350/204/270.md +3 -3
  275. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +2 -2
  276. 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
  277. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/256/205/346/226/227/345/256/253/346/226/227.md +6 -6
  278. 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
  279. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +4 -4
  280. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/260/221/344/277/227/346/200/252/350/260/210.md +7 -7
  281. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/262/231/351/233/225/350/204/221/346/264/236.md +5 -5
  282. package/lib/oh-story/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +4 -4
  283. 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
  284. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +32 -44
  285. package/lib/oh-story/skills/story-short-write/references/genre-writing-techniques.md +20 -18
  286. package/lib/oh-story/skills/story-short-write/references/output-contract.md +1 -1
  287. package/lib/oh-story/skills/story-short-write/references/short-craft.md +18 -18
  288. package/lib/oh-story/skills/story-short-write/references/short-deslop.md +1 -1
  289. package/lib/oh-story/skills/story-short-write/references/short-format.md +38 -8
  290. package/lib/oh-story/skills/story-short-write/references/{quality-checklist.md → short-prose-quality.md} +22 -21
  291. package/lib/oh-story/skills/story-short-write/references/short-reversal.md +355 -0
  292. package/lib/oh-story/skills/{story-short-analyze/references/hooks-suspense.md → story-short-write/references/short-suspense.md} +31 -31
  293. package/lib/oh-story/skills/story-short-write/references/submission-craft.md +2 -2
  294. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +31 -22
  295. package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +871 -0
  296. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +64 -1
  297. package/lib/oh-story/skills/story-short-write/scripts/check-delivery-contract.js +212 -0
  298. package/lib/oh-story/skills/story-short-write/scripts/check-phase2-contract.js +363 -0
  299. package/lib/types/client/drama-production-view.d.ts +34 -0
  300. package/lib/types/client/drama-production-view.d.ts.map +1 -0
  301. package/lib/types/client/drama-production.d.ts +92 -0
  302. package/lib/types/client/drama-production.d.ts.map +1 -0
  303. package/lib/types/client/file-activity.d.ts +4 -2
  304. package/lib/types/client/file-activity.d.ts.map +1 -1
  305. package/lib/types/client/index.d.ts +1 -1
  306. package/lib/types/client/index.d.ts.map +1 -1
  307. package/lib/types/client/production-intents.d.ts +9 -0
  308. package/lib/types/client/production-intents.d.ts.map +1 -0
  309. package/lib/types/client/production-prompts.d.ts +9 -0
  310. package/lib/types/client/production-prompts.d.ts.map +1 -0
  311. package/lib/types/client/production-runtime.d.ts +58 -0
  312. package/lib/types/client/production-runtime.d.ts.map +1 -0
  313. package/lib/types/client/video-studio.d.ts +41 -0
  314. package/lib/types/client/video-studio.d.ts.map +1 -0
  315. package/lib/types/client/workbench-ui.d.ts +5 -0
  316. package/lib/types/client/workbench-ui.d.ts.map +1 -0
  317. package/lib/types/game-verification.d.ts +16 -0
  318. package/lib/types/game-verification.d.ts.map +1 -0
  319. package/lib/types/index.d.ts +3 -1
  320. package/lib/types/index.d.ts.map +1 -1
  321. package/lib/types/production-intent.d.ts +21 -0
  322. package/lib/types/production-intent.d.ts.map +1 -0
  323. package/lib/types/production-tool.d.ts +5 -0
  324. package/lib/types/production-tool.d.ts.map +1 -0
  325. package/lib/types/role-tool.d.ts.map +1 -1
  326. package/lib/types/skill-provider.d.ts +7 -0
  327. package/lib/types/skill-provider.d.ts.map +1 -1
  328. package/lib/types/video-project.d.ts +42 -0
  329. package/lib/types/video-project.d.ts.map +1 -0
  330. package/lib/types/workspace-request-trust.d.ts +6 -0
  331. package/lib/types/workspace-request-trust.d.ts.map +1 -1
  332. package/lib/types/workspace-route.d.ts +20 -0
  333. package/lib/types/workspace-route.d.ts.map +1 -1
  334. package/lib/video-recap/CHANGELOG.md +282 -0
  335. package/lib/video-recap/LICENSE +21 -0
  336. package/lib/video-recap/README.en.md +257 -0
  337. package/lib/video-recap/README.md +255 -0
  338. package/lib/video-recap/manifest.json +684 -0
  339. package/lib/video-recap/skills/video-assemble/SKILL.md +97 -0
  340. package/lib/video-recap/skills/video-assemble/references/jianying/LICENSE.duo-video +21 -0
  341. package/lib/video-recap/skills/video-assemble/references/jianying/SOURCE.md +15 -0
  342. package/lib/video-recap/skills/video-assemble/references/jianying/empty_draft_meta_info.json +69 -0
  343. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_segment.json +84 -0
  344. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_video_material.json +98 -0
  345. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_draft.json +204 -0
  346. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_material_video.json +83 -0
  347. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_meta_material_value.json +23 -0
  348. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_project_info.json +133 -0
  349. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_segment.json +70 -0
  350. package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_text_styles.json +30 -0
  351. package/lib/video-recap/skills/video-assemble/references/jianying/empty_yj_material_audio.json +41 -0
  352. package/lib/video-recap/skills/video-assemble/references/jianying/empty_yj_material_text.json +109 -0
  353. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +88 -0
  354. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +345 -0
  355. package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +34 -0
  356. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +305 -0
  357. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +94 -0
  358. package/lib/video-recap/skills/video-assemble/scripts/audio_automation.py +205 -0
  359. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +555 -0
  360. package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +261 -0
  361. package/lib/video-recap/skills/video-assemble/scripts/jianying_builders.py +767 -0
  362. package/lib/video-recap/skills/video-assemble/scripts/jianying_model.py +111 -0
  363. package/lib/video-recap/skills/video-assemble/scripts/jianying_optional.py +24 -0
  364. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +109 -0
  365. package/lib/video-recap/skills/video-assemble/scripts/jianying_templates.py +37 -0
  366. package/lib/video-recap/skills/video-assemble/scripts/jianying_timeline_contract.py +191 -0
  367. package/lib/video-recap/skills/video-assemble/scripts/jianying_tracks.py +75 -0
  368. package/lib/video-recap/skills/video-assemble/scripts/jianying_writer.py +394 -0
  369. package/lib/video-recap/skills/video-assemble/scripts/lib.py +239 -0
  370. package/lib/video-recap/skills/video-assemble/scripts/media.py +243 -0
  371. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +477 -0
  372. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +46 -0
  373. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +486 -0
  374. package/lib/video-recap/skills/video-assemble/scripts/subtitle_core.py +376 -0
  375. package/lib/video-recap/skills/video-assemble/scripts/subtitle_render.py +88 -0
  376. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +248 -0
  377. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +89 -0
  378. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +512 -0
  379. package/lib/video-recap/skills/video-cut/SKILL.md +90 -0
  380. package/lib/video-recap/skills/video-cut/scripts/cut.py +67 -0
  381. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +320 -0
  382. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +545 -0
  383. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +348 -0
  384. package/lib/video-recap/skills/video-cut/scripts/lib.py +77 -0
  385. package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +341 -0
  386. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +249 -0
  387. package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +745 -0
  388. package/lib/video-recap/skills/video-recap/SKILL.md +216 -0
  389. package/lib/video-recap/skills/video-recap/references/config-playbook.md +61 -0
  390. package/lib/video-recap/skills/video-recap/references/creative-editing-playbook.md +225 -0
  391. package/lib/video-recap/skills/video-recap/references/data-schema.md +534 -0
  392. package/lib/video-recap/skills/video-recap/references/research-guide.md +74 -0
  393. package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +23 -0
  394. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +251 -0
  395. package/lib/video-recap/skills/video-recap/scripts/doctor.py +483 -0
  396. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +737 -0
  397. package/lib/video-recap/skills/video-recap/scripts/lib.py +242 -0
  398. package/lib/video-recap/skills/video-recap/scripts/materials.py +457 -0
  399. package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +95 -0
  400. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_client.py +18 -0
  401. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_contract.py +9 -0
  402. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_evidence.py +371 -0
  403. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_observations.py +201 -0
  404. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_payload.py +227 -0
  405. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_report.py +362 -0
  406. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_runner.py +129 -0
  407. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +338 -0
  408. package/lib/video-recap/skills/video-recap/scripts/recap.py +9 -0
  409. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +140 -0
  410. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +550 -0
  411. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +77 -0
  412. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +654 -0
  413. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +335 -0
  414. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +217 -0
  415. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +795 -0
  416. package/lib/video-recap/skills/video-script/SKILL.md +248 -0
  417. package/lib/video-recap/skills/video-script/references/creative-editing-playbook.md +225 -0
  418. package/lib/video-recap/skills/video-script/references/research-guide.md +75 -0
  419. package/lib/video-recap/skills/video-script/scripts/agent_brief.py +399 -0
  420. package/lib/video-recap/skills/video-script/scripts/agent_text.py +410 -0
  421. package/lib/video-recap/skills/video-script/scripts/brief_context.py +403 -0
  422. package/lib/video-recap/skills/video-script/scripts/brief_inputs.py +332 -0
  423. package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +442 -0
  424. package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +237 -0
  425. package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +492 -0
  426. package/lib/video-recap/skills/video-script/scripts/lib.py +327 -0
  427. package/lib/video-recap/skills/video-script/scripts/narration.py +12 -0
  428. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +678 -0
  429. package/lib/video-recap/skills/video-script/scripts/review.py +42 -0
  430. package/lib/video-recap/skills/video-script/scripts/review_grounding.py +262 -0
  431. package/lib/video-recap/skills/video-script/scripts/review_response.py +761 -0
  432. package/lib/video-recap/skills/video-script/scripts/review_runner.py +200 -0
  433. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +204 -0
  434. package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +199 -0
  435. package/lib/video-recap/skills/video-script/scripts/validate.py +164 -0
  436. package/lib/video-recap/skills/video-understanding/SKILL.md +75 -0
  437. package/lib/video-recap/skills/video-understanding/references/data-schema.md +299 -0
  438. package/lib/video-recap/skills/video-understanding/references/prompt-templates.md +37 -0
  439. package/lib/video-recap/skills/video-understanding/references/research-guide.md +74 -0
  440. package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +399 -0
  441. package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +410 -0
  442. package/lib/video-recap/skills/video-understanding/scripts/asr.py +260 -0
  443. package/lib/video-recap/skills/video-understanding/scripts/brief.py +12 -0
  444. package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +403 -0
  445. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +332 -0
  446. package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +442 -0
  447. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +647 -0
  448. package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +237 -0
  449. package/lib/video-recap/skills/video-understanding/scripts/detect.py +513 -0
  450. package/lib/video-recap/skills/video-understanding/scripts/extract.py +62 -0
  451. package/lib/video-recap/skills/video-understanding/scripts/lib.py +470 -0
  452. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +678 -0
  453. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +204 -0
  454. package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +507 -0
  455. package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +199 -0
  456. package/lib/video-recap/skills/video-understanding/scripts/understand.py +9 -0
  457. package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +216 -0
  458. package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +327 -0
  459. package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +367 -0
  460. package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +162 -0
  461. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +705 -0
  462. package/lib/video-recap/skills/video-voiceover/SKILL.md +81 -0
  463. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +637 -0
  464. package/lib/video-recap/skills/video-voiceover/scripts/fish_audio.py +109 -0
  465. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +421 -0
  466. package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +113 -0
  467. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +782 -0
  468. package/package.json +55 -40
  469. package/lib/oh-story/skills/story-deslop/scripts/check-outline-copy.js +0 -241
  470. package/lib/oh-story/skills/story-long-write/references/format-and-structure.md +0 -169
  471. package/lib/oh-story/skills/story-setup/references/agent-references/genre-writing-formulas.md +0 -444
  472. package/lib/oh-story/skills/story-setup/references/agent-references/quality-checklist.md +0 -247
  473. package/lib/oh-story/skills/story-short-analyze/references/anti-ai-writing.md +0 -582
  474. package/lib/oh-story/skills/story-short-analyze/references/banned-words.md +0 -147
  475. package/lib/oh-story/skills/story-short-analyze/references/genre-catalog.md +0 -490
  476. package/lib/oh-story/skills/story-short-analyze/references/genre-core-mechanics.md +0 -425
  477. package/lib/oh-story/skills/story-short-analyze/references/genre-writing-formulas.md +0 -453
  478. /package/lib/oh-story/skills/{story-long-write/references/hooks-chapter.md → story-setup/references/agent-references/short-chapter-hooks.md} +0 -0
  479. /package/lib/oh-story/skills/{story-long-write/references/hooks-paragraph.md → story-setup/references/agent-references/short-paragraph-hooks.md} +0 -0
@@ -0,0 +1,227 @@
1
+ """Build bounded semantic and multimodal MiMo QC payloads."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ import json
7
+
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
18
+
19
+
20
+ def _semantic_evidence(evidence: Mapping[str, Any], *, stage: str) -> dict[str, Any]:
21
+ """Keep real artifact values visible to MiMo without sending irrelevant paths.
22
+
23
+ ``collect_evidence`` already bounds every artifact independently. The additional
24
+ summarization here therefore needs enough depth to retain the narration/ASR/TTS
25
+ scalars nested inside those summaries. A shallow second pass used to replace the
26
+ values with fingerprints, which made live QC invent missing-script and failed-TTS
27
+ findings from evidence it could no longer read.
28
+ """
29
+ semantic = dict(evidence)
30
+ semantic.pop("work_dir", None)
31
+ semantic.pop("fingerprint", None)
32
+ semantic["evidence_roles"] = {
33
+ "narration.json": (
34
+ "Planned recap voiceover on the OUTPUT timeline; judge its factual and temporal "
35
+ "fit rather than expecting verbatim source dialogue."
36
+ ),
37
+ "tts_meta.json": (
38
+ "Synthesis and placement metadata for narration.json; an empty failures list means "
39
+ "all requested narration segments synthesized successfully."
40
+ ),
41
+ "source_asr": (
42
+ "Transcript of the SOURCE media audio, not a transcript of generated TTS. "
43
+ "Wording differences from narration are expected; use this evidence for factual "
44
+ "support and original-dialogue timing, not verbatim equality."
45
+ ),
46
+ "generated_subtitles": (
47
+ "Recap subtitles derived from generated narration/TTS during assembly. They are not "
48
+ "source ASR and must never be used as independent factual support. Visible text that "
49
+ "matches one of these cues is the intended generated caption, including when it sits "
50
+ "on the black source-subtitle mask band."
51
+ ),
52
+ "visual_metadata": (
53
+ "Diagnostic storyboard/frame metadata. labels_burned refers only to diagnostic "
54
+ "timestamp labels and does not mean labels were burned into the final video."
55
+ ),
56
+ "final_output": (
57
+ "Only present for post_render and limited to candidates that actually exist."
58
+ ),
59
+ "post_render_frame_limits": (
60
+ "Sampled final-output frames are silent still images: they contain no audible audio. "
61
+ "Do not infer which audio track is playing from visible source captions. Narration/TTS "
62
+ "timings are segment-level, not word-level, so an internal phrase cannot be assigned to "
63
+ "an exact sampled-frame timestamp."
64
+ ),
65
+ "narration_and_subtitle_gaps": (
66
+ "Generated narration/subtitle gaps may be deliberate original-audio blocks. In those "
67
+ "gaps the source audio and its existing captions can intentionally return; do not call "
68
+ "the absence of generated subtitles a defect without evidence of accidental silence."
69
+ ),
70
+ "actual_audio_timing": (
71
+ "narration.json start/end values are authoring slots, not proof that speech fills the "
72
+ "whole slot. For post-render timing, assembly_manifest audio_segments "
73
+ "actual_place_start/actual_place_end are authoritative. A sampled frame after "
74
+ "actual_place_end is in an original-audio gap."
75
+ ),
76
+ }
77
+ if stage != "post_render":
78
+ semantic.pop("final_output", None)
79
+ # These files can be leftovers from an earlier assembly when narration is being revised.
80
+ # Pre-assemble QC must evaluate current narration/TTS, not stale rendered captions.
81
+ semantic.pop("generated_subtitles", None)
82
+ semantic["evidence_roles"].pop("generated_subtitles", None)
83
+ semantic["evidence_roles"].pop("post_render_frame_limits", None)
84
+ semantic["evidence_roles"].pop("actual_audio_timing", None)
85
+ 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
+ semantic["final_output"] = {
93
+ "candidates": [
94
+ dict(candidate)
95
+ for candidate in candidates
96
+ if isinstance(candidate, Mapping) and candidate.get("exists") is True
97
+ ]
98
+ }
99
+ # Artifact summaries are already bounded at collection time; summarizing them again
100
+ # would wrap their ``items`` arrays in another summary layer and obscure the values.
101
+ return _redact(semantic)
102
+
103
+
104
+ def build_payload(
105
+ evidence: Mapping[str, Any],
106
+ *,
107
+ stage: str = DEFAULT_STAGE,
108
+ config: Mapping[str, Any] | None = None,
109
+ ) -> dict[str, Any]:
110
+ """Build the report-safe semantic payload (never contains image base64)."""
111
+ cfg = safe_mimo_config(config)
112
+ payload = {
113
+ "stage": stage,
114
+ "artifact": ARTIFACT_NAME,
115
+ "model": cfg["model"],
116
+ "config": cfg,
117
+ "instructions": (
118
+ "你是 video-recap 的建议性质量审阅器。结合解说、剪辑计划、字幕/ASR、TTS、"
119
+ "组装元数据和抽样画面,指出语义或审美问题。只返回主观观察,不做自动修复,"
120
+ "只根据可见的实际字段判断,不得从文件字节数、指纹、截断或省略标记推断内容缺失;"
121
+ "source_asr 是源素材原声证据,不是生成后 TTS 的逐字转录;generated_subtitles 是本轮生成字幕,"
122
+ "二者角色不可混淆,解说改写与源台词不一致本身不是问题;"
123
+ "空 failures 表示没有失败,storyboard 的 labels_burned 仅表示诊断图上的时间标签,"
124
+ "不代表标签烧进最终视频;pre_assemble 阶段尚无 final_output 属于正常状态;"
125
+ "成片抽样画面是带时间戳的稀疏点样本,只能判断该具体时刻,不得用单帧否定整段内其他时刻的画面。"
126
+ "这些抽样帧是无声静帧,不能从画面中的源字幕推断当时实际播放哪条音轨;"
127
+ "源字幕遮罩策略为 off 时,保留的源字幕无需与解说逐字一致;解说/TTS 只有段级时间,"
128
+ "没有词级对齐,不得把句中某个短语强行对应到抽样帧的精确秒数。"
129
+ "生成解说和字幕之间的空档可能是刻意保留的原声块,此时原声和源字幕回归是正常设计,"
130
+ "没有意外静音证据时不得把生成字幕空档当成缺失。只输出可采取行动的疑似问题,"
131
+ "不要把正常、一致、相符或通过项作为 observation。不得提出阻断决定。"
132
+ "narration.json 的 start/end 是创作槽位,不表示旁白铺满整段;成片阶段必须以 "
133
+ "assembly_manifest.audio_segments 的 actual_place_start/actual_place_end 判断实际旁白。"
134
+ "抽样点超过 actual_place_end 时属于原声块;一个字幕时间段含多个分句时,静帧匹配其中任一分句都不算错位。"
135
+ "只有同一编号静帧中同时清晰可读两层不同字幕,才能报告字幕重叠;不同静帧分别出现解说字幕和源字幕不算重叠。"
136
+ "遮罩 opacity=1.0 表示源字幕像素已被不透明覆盖,不得臆测遮罩下仍有可见文字。"
137
+ "黑色遮罩带上与 generated_subtitles cue 一致的白字是本轮预期生成字幕,不是残留源字幕;"
138
+ "必须先逐字对照 generated_subtitles,再判断是否另有第二层源字幕。"
139
+ "每张抽样图都由同编号 BEGIN/END 文本包围,必须按编号和时间戳独立判断,"
140
+ "不得把一张图里的文字或人物归到另一张图。"
141
+ '返回 JSON:{"observations":[{"code":...,'
142
+ '"message":...,"category":"semantic|aesthetic",'
143
+ '"confidence":"low|medium|high","sample_policy":'
144
+ '"semantic|aesthetic|sampled","evidence":{...}}]}。最多 12 条。'
145
+ ),
146
+ "evidence": _semantic_evidence(evidence, stage=stage),
147
+ "evidence_fingerprint": evidence.get("fingerprint")
148
+ or _fingerprint_value(_cache_evidence(evidence)),
149
+ }
150
+ payload["payload_fingerprint"] = _fingerprint_value(payload)
151
+ return _redact(payload)
152
+
153
+
154
+ def _request_payload(
155
+ payload: Mapping[str, Any], frame_samples: Sequence[Mapping[str, Any]]
156
+ ) -> dict[str, Any]:
157
+ request_evidence = {
158
+ "stage": payload["stage"],
159
+ "instructions": payload["instructions"],
160
+ "evidence": payload["evidence"],
161
+ }
162
+ content: list[dict[str, Any]] = [
163
+ {
164
+ "type": "text",
165
+ "text": json.dumps(
166
+ request_evidence, ensure_ascii=False, separators=(",", ":")
167
+ ),
168
+ }
169
+ ]
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
+ )
194
+ return {
195
+ "model": payload["model"],
196
+ "messages": [{"role": "user", "content": content}],
197
+ "max_completion_tokens": 1600,
198
+ "thinking": {"type": "disabled"},
199
+ }
200
+
201
+
202
+ def _strip_json_fence(text: str) -> str:
203
+ value = text.strip()
204
+ if value.startswith("```") and value.endswith("```"):
205
+ lines = value.splitlines()
206
+ if len(lines) >= 3:
207
+ value = "\n".join(lines[1:-1]).strip()
208
+ return value
209
+
210
+
211
+ def _validated_live_output(response: Any) -> Any:
212
+ try:
213
+ content = response["choices"][0]["message"]["content"]
214
+ except (KeyError, IndexError, TypeError):
215
+ raise ValueError("malformed_response") from None
216
+ if isinstance(content, str):
217
+ try:
218
+ content = json.loads(_strip_json_fence(content))
219
+ except (TypeError, ValueError):
220
+ raise ValueError("malformed_json_content") from None
221
+ if isinstance(content, list):
222
+ return content
223
+ if isinstance(content, Mapping) and isinstance(content.get("observations"), list):
224
+ return content
225
+ if isinstance(content, Mapping) and isinstance(content.get("findings"), list):
226
+ return content
227
+ raise ValueError("malformed_observations")
@@ -0,0 +1,362 @@
1
+ """Sample frames and build aggregate MiMo QC reports."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ import base64
7
+
8
+ import hashlib
9
+
10
+ import json
11
+
12
+ import math
13
+
14
+ import os
15
+
16
+
17
+ import subprocess
18
+
19
+ import tempfile
20
+
21
+ from pathlib import Path
22
+
23
+ from typing import Any, Callable, Mapping, Sequence
24
+
25
+ import qc_contract
26
+
27
+ from mimo_qc_evidence import (
28
+ _cache_evidence,
29
+ _effective_config,
30
+ _existing_final_output,
31
+ _fingerprint_value,
32
+ _redact,
33
+ collect_evidence,
34
+ safe_mimo_config,
35
+ )
36
+ from mimo_qc_observations import normalize_observations
37
+ from mimo_qc_payload import _request_payload, _validated_live_output, build_payload
38
+ from mimo_qc_client import mimo_qc_api_call
39
+ from mimo_qc_contract import (
40
+ ARTIFACT_NAME,
41
+ DEFAULT_STAGE,
42
+ FRAME_SAMPLER_VERSION,
43
+ MAX_FRAME_DIMENSION,
44
+ MAX_FRAMES,
45
+ )
46
+
47
+ JudgeCallable = Callable[
48
+ [Mapping[str, Any]], Mapping[str, Any] | Sequence[Mapping[str, Any]]
49
+ ]
50
+
51
+ FrameSampler = Callable[..., Sequence[Mapping[str, Any]]]
52
+
53
+
54
+ def _probe_duration(path: Path) -> float:
55
+ result = subprocess.run(
56
+ [
57
+ "ffprobe",
58
+ "-v",
59
+ "error",
60
+ "-show_entries",
61
+ "format=duration",
62
+ "-of",
63
+ "json",
64
+ str(path),
65
+ ],
66
+ capture_output=True,
67
+ text=True,
68
+ timeout=20,
69
+ check=False,
70
+ )
71
+ if result.returncode != 0:
72
+ return 0.0
73
+ try:
74
+ return max(0.0, float(json.loads(result.stdout)["format"]["duration"]))
75
+ except (KeyError, TypeError, ValueError, json.JSONDecodeError):
76
+ return 0.0
77
+
78
+
79
+ def sample_video_frames(
80
+ video_path: str | Path,
81
+ *,
82
+ max_frames: int = MAX_FRAMES,
83
+ max_dimension: int = MAX_FRAME_DIMENSION,
84
+ ) -> list[dict[str, Any]]:
85
+ """Extract at most six bounded JPEGs; returned base64 is request-only."""
86
+ path = Path(video_path)
87
+ if not path.is_file() or max_frames <= 0:
88
+ return []
89
+ try:
90
+ duration = _probe_duration(path)
91
+ except (OSError, subprocess.SubprocessError):
92
+ return []
93
+ if duration <= 0:
94
+ return []
95
+ count = min(int(max_frames), max(1, int(math.ceil(duration / 15.0))))
96
+ samples = []
97
+ with tempfile.TemporaryDirectory(prefix="video-recap-mimo-qc-") as tmp:
98
+ for index in range(count):
99
+ timestamp = duration * (index + 0.5) / count
100
+ destination = Path(tmp) / f"frame-{index:02d}.jpg"
101
+ try:
102
+ result = subprocess.run(
103
+ [
104
+ "ffmpeg",
105
+ "-hide_banner",
106
+ "-loglevel",
107
+ "error",
108
+ "-y",
109
+ "-ss",
110
+ f"{timestamp:.3f}",
111
+ "-i",
112
+ str(path),
113
+ "-frames:v",
114
+ "1",
115
+ "-vf",
116
+ (
117
+ f"scale={int(max_dimension)}:{int(max_dimension)}:"
118
+ "force_original_aspect_ratio=decrease"
119
+ ),
120
+ "-q:v",
121
+ "4",
122
+ str(destination),
123
+ ],
124
+ capture_output=True,
125
+ timeout=30,
126
+ check=False,
127
+ )
128
+ except (OSError, subprocess.SubprocessError):
129
+ continue
130
+ if result.returncode != 0 or not destination.is_file():
131
+ continue
132
+ raw = destination.read_bytes()
133
+ samples.append(
134
+ {
135
+ "data_url": "data:image/jpeg;base64,"
136
+ + base64.b64encode(raw).decode("ascii"),
137
+ "sha256": hashlib.sha256(raw).hexdigest(),
138
+ "timestamp": round(timestamp, 3),
139
+ }
140
+ )
141
+ return samples[:MAX_FRAMES]
142
+
143
+
144
+ def _frame_metadata(samples: Sequence[Mapping[str, Any]]) -> dict[str, Any]:
145
+ return {
146
+ "count": min(len(samples), MAX_FRAMES),
147
+ "max_frames": MAX_FRAMES,
148
+ "max_dimension": MAX_FRAME_DIMENSION,
149
+ "sampler_version": FRAME_SAMPLER_VERSION,
150
+ "samples": [
151
+ {
152
+ "sha256": sample.get("sha256"),
153
+ "timestamp": sample.get("timestamp"),
154
+ }
155
+ for sample in list(samples)[:MAX_FRAMES]
156
+ ],
157
+ }
158
+
159
+
160
+ def _cache_input(
161
+ stage: str,
162
+ payload: Mapping[str, Any],
163
+ evidence: Mapping[str, Any],
164
+ frames: Mapping[str, Any],
165
+ ) -> dict[str, Any]:
166
+ return {
167
+ "stage": stage,
168
+ "model": payload.get("model"),
169
+ "payload_fingerprint": payload.get("payload_fingerprint"),
170
+ "evidence": _cache_evidence(evidence),
171
+ "frames": frames,
172
+ "contract": qc_contract.SCHEMA_VERSION,
173
+ }
174
+
175
+
176
+ def _stage_reports(path: Path) -> dict[str, dict[str, Any]]:
177
+ if not path.is_file():
178
+ return {}
179
+ try:
180
+ report = json.loads(path.read_text(encoding="utf-8"))
181
+ qc_contract.validate_report(report)
182
+ except (OSError, ValueError, TypeError, qc_contract.QCContractError):
183
+ return {}
184
+ stages = report.get("metadata", {}).get("stages")
185
+ if isinstance(stages, Mapping):
186
+ valid = {}
187
+ for stage, stage_report in stages.items():
188
+ try:
189
+ qc_contract.validate_report(stage_report)
190
+ except (TypeError, qc_contract.QCContractError):
191
+ continue
192
+ valid[str(stage)] = dict(stage_report)
193
+ return valid
194
+ return {str(report["stage"]): report}
195
+
196
+
197
+ def _error_name(exc: Exception) -> str:
198
+ text = str(_redact(str(exc))).strip()
199
+ return (text or type(exc).__name__)[:120]
200
+
201
+
202
+ def build_report(
203
+ work_dir: str | Path,
204
+ *,
205
+ stage: str = DEFAULT_STAGE,
206
+ fixture: Any | None = None,
207
+ dry_run: bool = False,
208
+ judge: JudgeCallable | None = None,
209
+ config: Mapping[str, Any] | None = None,
210
+ final_output: str | Path | None = None,
211
+ live: bool = False,
212
+ refresh: bool = False,
213
+ frame_sampler: FrameSampler | None = None,
214
+ existing: Mapping[str, Any] | None = None,
215
+ api_call: Callable[..., Any] | None = None,
216
+ ) -> dict[str, Any]:
217
+ """Build one validated stage report; all live failures remain successful QC."""
218
+ root = Path(work_dir)
219
+ evidence = collect_evidence(root, final_output=final_output)
220
+ payload = build_payload(evidence, stage=stage, config=config)
221
+ cfg = safe_mimo_config(config)
222
+ samples: Sequence[Mapping[str, Any]] = []
223
+ if stage == "post_render" and (fixture is not None or judge is not None or live):
224
+ output = _existing_final_output(root, final_output)
225
+ if output is not None:
226
+ sampler = frame_sampler or sample_video_frames
227
+ try:
228
+ samples = list(
229
+ sampler(
230
+ output, max_frames=MAX_FRAMES, max_dimension=MAX_FRAME_DIMENSION
231
+ )
232
+ )[:MAX_FRAMES]
233
+ except Exception:
234
+ samples = []
235
+ frame_meta = _frame_metadata(samples)
236
+ cache_input = _cache_input(stage, payload, evidence, frame_meta)
237
+ cache_key = _fingerprint_value(cache_input)
238
+
239
+ if (
240
+ not refresh
241
+ and existing
242
+ and existing.get("metadata", {}).get("cache_key") == cache_key
243
+ ):
244
+ cached = json.loads(json.dumps(existing))
245
+ cached["metadata"]["status"] = "cached"
246
+ cached["metadata"]["mode"] = "live_cache"
247
+ cached["metadata"]["request_count"] = 0
248
+ qc_contract.validate_report(cached)
249
+ return cached
250
+
251
+ status = "completed"
252
+ error = None
253
+ if fixture is not None:
254
+ model_output = fixture
255
+ mode = "fixture"
256
+ elif judge is not None and not dry_run:
257
+ mode = "injected_judge"
258
+ try:
259
+ model_output = judge(payload)
260
+ except Exception as exc:
261
+ model_output = {"observations": []}
262
+ status, error = "failed", _error_name(exc)
263
+ elif live and not dry_run:
264
+ mode = "live"
265
+ if not cfg["key_present"]:
266
+ model_output = {"observations": []}
267
+ status, error = "unavailable", "missing_key"
268
+ else:
269
+ try:
270
+ response = (api_call or mimo_qc_api_call)(
271
+ _request_payload(payload, samples),
272
+ config=_effective_config(config),
273
+ timeout=60,
274
+ )
275
+ model_output = _validated_live_output(response)
276
+ except Exception as exc:
277
+ model_output = {"observations": []}
278
+ status, error = "failed", _error_name(exc)
279
+ else:
280
+ model_output = {"observations": []}
281
+ mode = "dry_run"
282
+ status = "dry_run"
283
+
284
+ findings = normalize_observations(
285
+ model_output, stage=stage, payload=payload, model_config=config
286
+ )
287
+ metadata = {
288
+ "mode": mode,
289
+ "status": status,
290
+ "error": error,
291
+ "report_only": True,
292
+ "auto_repair": False,
293
+ "pipeline_blocking": False,
294
+ "deterministic": False,
295
+ "request_count": 1
296
+ if mode == "live" and status in {"completed", "failed"}
297
+ else 0,
298
+ "cache_key": cache_key,
299
+ "cache_input": cache_input,
300
+ "frame_samples": frame_meta,
301
+ "evidence": evidence,
302
+ "payload": payload,
303
+ "config": cfg,
304
+ }
305
+ report = qc_contract.build_report(
306
+ artifact=ARTIFACT_NAME,
307
+ stage=stage,
308
+ findings=findings,
309
+ metadata=metadata,
310
+ )
311
+ qc_contract.validate_report(report)
312
+ return _redact(report)
313
+
314
+
315
+ def _aggregate_reports(
316
+ stage_reports: Mapping[str, Mapping[str, Any]], current_stage: str
317
+ ) -> dict[str, Any]:
318
+ current = stage_reports[current_stage]
319
+ findings = [
320
+ finding
321
+ for stage_report in stage_reports.values()
322
+ for finding in stage_report.get("findings", [])
323
+ ]
324
+ metadata = dict(current.get("metadata", {}))
325
+ metadata["stages"] = {
326
+ stage: dict(report) for stage, report in stage_reports.items()
327
+ }
328
+ return qc_contract.build_report(
329
+ artifact=ARTIFACT_NAME,
330
+ stage=current_stage,
331
+ findings=findings,
332
+ metadata=metadata,
333
+ )
334
+
335
+
336
+ def write_report(
337
+ work_dir: str | Path, report: Mapping[str, Any], *, output: str | Path | None = None
338
+ ) -> tuple[Path, dict[str, Any]]:
339
+ """Atomically merge one stage into mimo_qc.json and return the aggregate."""
340
+ path = Path(output) if output else Path(work_dir) / ARTIFACT_NAME
341
+ path.parent.mkdir(parents=True, exist_ok=True)
342
+ safe_stage = _redact(dict(report))
343
+ qc_contract.validate_report(safe_stage)
344
+ stages = _stage_reports(path)
345
+ stages[safe_stage["stage"]] = safe_stage
346
+ aggregate = _aggregate_reports(stages, safe_stage["stage"])
347
+ qc_contract.validate_report(aggregate)
348
+ serialized = json.dumps(_redact(aggregate), ensure_ascii=False, indent=2) + "\n"
349
+ descriptor, temp_name = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
350
+ try:
351
+ with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
352
+ handle.write(serialized)
353
+ handle.flush()
354
+ os.fsync(handle.fileno())
355
+ os.replace(temp_name, path)
356
+ except Exception:
357
+ try:
358
+ os.unlink(temp_name)
359
+ except OSError:
360
+ pass
361
+ raise
362
+ return path, aggregate