@oh-story/dsh 0.1.8 → 0.1.10

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 (360) hide show
  1. package/README.md +14 -14
  2. package/lib/client.js +237 -84
  3. package/lib/client.js.map +3 -3
  4. package/lib/drama/manifest.json +183 -77
  5. package/lib/drama/skills/short-drama/SKILL.md +5 -1
  6. package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +140 -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 +28 -0
  9. package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +8 -2
  10. package/lib/drama/skills/short-drama-assets/references/voice-direction.md +28 -0
  11. package/lib/drama/skills/short-drama-edit/SKILL.md +146 -0
  12. package/lib/drama/skills/short-drama-edit/agents/openai.yaml +4 -0
  13. package/lib/drama/skills/short-drama-edit/assets/remotion/README.md +60 -0
  14. package/lib/drama/skills/short-drama-edit/assets/remotion/package.json +15 -0
  15. package/lib/drama/skills/short-drama-edit/assets/remotion/remotion.config.ts +6 -0
  16. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Root.tsx +26 -0
  17. package/lib/drama/skills/short-drama-edit/assets/remotion/src/Subtitles.tsx +88 -0
  18. package/lib/drama/skills/short-drama-edit/assets/remotion/src/font.ts +46 -0
  19. package/lib/drama/skills/short-drama-edit/assets/remotion/src/index.ts +4 -0
  20. package/lib/drama/skills/short-drama-edit/assets/remotion/src/schema.ts +32 -0
  21. package/lib/drama/skills/short-drama-edit/assets/remotion/tsconfig.json +13 -0
  22. package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +43 -0
  23. package/lib/drama/skills/short-drama-edit/references/cut-craft.md +38 -0
  24. package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +29 -0
  25. package/lib/drama/skills/short-drama-edit/references/generated-footage.md +54 -0
  26. package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +60 -0
  27. package/lib/drama/skills/short-drama-edit/references/stage-contract.md +51 -0
  28. package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1156 -0
  29. package/lib/drama/skills/short-drama-edit/scripts/selftest.py +520 -0
  30. package/lib/drama/skills/short-drama-image-prompts/SKILL.md +2 -0
  31. package/lib/drama/skills/short-drama-image-prompts/assets/image-prompts.md +5 -0
  32. package/lib/drama/skills/short-drama-image-prompts/assets/lookdev-prompts.md +3 -0
  33. package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +11 -0
  34. package/lib/drama/skills/short-drama-novel-analyze/references/aggregation-and-entities.md +2 -2
  35. package/lib/drama/skills/short-drama-novel-analyze/references/chapter-extraction.md +4 -1
  36. package/lib/drama/skills/short-drama-produce/SKILL.md +29 -4
  37. package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +22 -0
  38. package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +13 -12
  39. package/lib/drama/skills/short-drama-produce/references/providers/minimax-speech.md +41 -0
  40. package/lib/drama/skills/short-drama-produce/references/providers/seedance.md +7 -0
  41. package/lib/drama/skills/short-drama-produce/scripts/fixture_adapter.py +18 -1
  42. package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +181 -2
  43. package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +199 -3
  44. package/lib/drama/skills/short-drama-review/SKILL.md +2 -0
  45. package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +7 -0
  46. package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +10 -0
  47. package/lib/drama/skills/short-drama-storyboard/SKILL.md +12 -1
  48. package/lib/drama/skills/short-drama-storyboard/assets/keyframe-prompts.md +4 -0
  49. package/lib/drama/skills/short-drama-storyboard/references/lighting-craft.md +39 -0
  50. package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +78 -2
  51. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +2 -0
  52. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +12 -5
  53. package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +17 -0
  54. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +76 -7
  55. package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +5 -2
  56. package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +21 -0
  57. package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +6 -17
  58. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +12 -3
  59. package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +61 -5
  60. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +3 -3
  61. package/lib/drama/skills/short-drama-write/SKILL.md +11 -2
  62. package/lib/drama/skills/short-drama-write/references/screenplay-format.md +6 -0
  63. package/lib/drama/skills/short-drama-write/references/stage-contract.md +1 -0
  64. package/lib/index.js +290 -90
  65. package/lib/index.js.map +4 -4
  66. package/lib/novel-to-game/README.md +299 -59
  67. package/lib/novel-to-game/README_ZH.md +299 -56
  68. package/lib/novel-to-game/VERSION +1 -1
  69. package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +3 -1
  70. package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +11 -3
  71. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +397 -8
  72. package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +0 -31
  73. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +71 -73
  74. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +78 -89
  75. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +37 -14
  76. package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +5 -5
  77. package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +2 -1
  78. package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +11 -16
  79. package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +11 -11
  80. package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +23 -0
  81. package/lib/novel-to-game/manifest.json +77 -87
  82. package/lib/novel-to-game/skills/game-art-direction/SKILL.md +9 -35
  83. package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +27 -106
  84. package/lib/novel-to-game/skills/game-build/SKILL.md +17 -35
  85. package/lib/novel-to-game/skills/game-build/references/blender-asset-workflow.md +24 -0
  86. package/lib/novel-to-game/skills/game-build/references/build-brief-contract.md +45 -82
  87. package/lib/novel-to-game/skills/game-build/references/generative-media-pipeline.md +14 -45
  88. package/lib/novel-to-game/skills/game-build/references/playable-model-contract.md +30 -65
  89. package/lib/novel-to-game/skills/game-build/references/tts-production-contract.md +13 -27
  90. package/lib/novel-to-game/skills/game-concept/SKILL.md +16 -34
  91. package/lib/novel-to-game/skills/game-concept/references/concept-method.md +28 -75
  92. package/lib/novel-to-game/skills/game-qa/SKILL.md +9 -20
  93. package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +10 -6
  94. package/lib/novel-to-game/skills/game-world-design/SKILL.md +19 -32
  95. package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +23 -72
  96. package/lib/novel-to-game/skills/game-world-design/references/numeric-design-method.md +14 -48
  97. package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +36 -103
  98. package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +9 -18
  99. package/lib/novel-to-game/skills/novel-game-analyze/references/gameability-protocol.md +4 -4
  100. package/lib/novel-to-game/skills/novel-to-game/SKILL.md +10 -27
  101. package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +5 -6
  102. package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +6 -11
  103. package/lib/oh-story/manifest.json +419 -254
  104. package/lib/oh-story/roles/chapter-extractor.md +177 -237
  105. package/lib/oh-story/roles/character-designer.md +1 -1
  106. package/lib/oh-story/roles/consistency-checker.md +4 -4
  107. package/lib/oh-story/roles/narrative-writer.md +56 -243
  108. package/lib/oh-story/roles/story-architect.md +6 -6
  109. package/lib/oh-story/roles/story-explorer.md +11 -11
  110. package/lib/oh-story/skills/story/SKILL.md +13 -7
  111. package/lib/oh-story/skills/story/VERSION +1 -1
  112. package/lib/oh-story/skills/story/references/author-memory.md +61 -29
  113. package/lib/oh-story/skills/story/scripts/author_memory_commit.py +934 -170
  114. package/lib/oh-story/skills/story-cover/SKILL.md +10 -1
  115. package/lib/oh-story/skills/story-deslop/SKILL.md +54 -229
  116. package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +29 -56
  117. package/lib/oh-story/skills/story-deslop/references/author-memory.md +61 -29
  118. package/lib/oh-story/skills/story-deslop/references/banned-words.md +3 -1
  119. package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +154 -0
  120. package/lib/oh-story/skills/story-deslop/references/style-resolution.md +27 -0
  121. package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +934 -170
  122. package/lib/oh-story/skills/story-deslop/scripts/check-ai-patterns.js +6 -1
  123. package/lib/oh-story/skills/story-deslop/scripts/check-degeneration.js +39 -20
  124. package/lib/oh-story/skills/story-deslop/scripts/normalize-punctuation.js +10 -5
  125. package/lib/oh-story/skills/story-deslop/scripts/style-whitelist.js +39 -0
  126. package/lib/oh-story/skills/story-import/SKILL.md +78 -82
  127. package/lib/oh-story/skills/story-import/references/character-state-reverse.md +1 -1
  128. package/lib/oh-story/skills/story-import/references/format-and-structure.md +2 -2
  129. package/lib/oh-story/skills/story-import/references/state-tracking.md +4 -69
  130. package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +10 -8
  131. package/lib/oh-story/skills/story-import/references/tracking-initialization.md +31 -0
  132. package/lib/oh-story/skills/story-import/references/tracking-transaction.md +9 -34
  133. package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +197 -15
  134. package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +210 -2
  135. package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +3 -0
  136. package/lib/oh-story/skills/story-long-analyze/SKILL.md +91 -258
  137. package/lib/oh-story/skills/story-long-analyze/references/author-facing.md +245 -0
  138. package/lib/oh-story/skills/story-long-analyze/references/deconstruction-notes.md +2 -2
  139. package/lib/oh-story/skills/story-long-analyze/references/inspiration-library.md +105 -0
  140. package/lib/oh-story/skills/story-long-analyze/references/material-decomposition.md +113 -105
  141. package/lib/oh-story/skills/story-long-analyze/references/output-templates.md +169 -125
  142. package/lib/oh-story/skills/story-long-analyze/references/pipeline-ops.md +172 -64
  143. package/lib/oh-story/skills/story-long-analyze/references/semantic-acceptance-fixtures.md +84 -0
  144. package/lib/oh-story/skills/story-long-analyze/references/style-profile-generator.md +42 -132
  145. package/lib/oh-story/skills/story-long-analyze/references/style-profile-protocol.md +5 -5
  146. package/lib/oh-story/skills/story-long-analyze/scripts/build_chapter_index.py +466 -0
  147. package/lib/oh-story/skills/story-long-analyze/scripts/inspect_existing_assets.py +702 -0
  148. package/lib/oh-story/skills/story-long-analyze/scripts/inspiration_index.py +864 -0
  149. package/lib/oh-story/skills/story-long-analyze/scripts/manage_analysis_run.py +1106 -0
  150. package/lib/oh-story/skills/story-long-analyze/scripts/render_relation_chart.py +425 -0
  151. package/lib/oh-story/skills/story-long-scan/SKILL.md +4 -1
  152. package/lib/oh-story/skills/story-long-write/SKILL.md +52 -216
  153. package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +29 -56
  154. package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +72 -12
  155. package/lib/oh-story/skills/story-long-write/references/author-memory.md +61 -29
  156. package/lib/oh-story/skills/story-long-write/references/banned-words.md +3 -1
  157. package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +20 -0
  158. package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +26 -0
  159. package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +10 -4
  160. package/lib/oh-story/skills/story-long-write/references/emotional-methods.md +2 -2
  161. package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +6 -15
  162. package/lib/oh-story/skills/story-long-write/references/long-format.md +2 -0
  163. package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +2 -0
  164. package/lib/oh-story/skills/story-long-write/references/project-files.md +110 -0
  165. package/lib/oh-story/skills/story-long-write/references/reader-contract-and-progression.md +12 -4
  166. package/lib/oh-story/skills/story-long-write/references/reference-index.md +86 -0
  167. package/lib/oh-story/skills/story-long-write/references/style-combat-face.md +1 -1
  168. package/lib/oh-story/skills/story-long-write/references/style-craft.md +9 -29
  169. package/lib/oh-story/skills/story-long-write/references/style-genre-modules.md +1 -1
  170. package/lib/oh-story/skills/story-long-write/references/style-resolution.md +27 -0
  171. package/lib/oh-story/skills/story-long-write/references/tracking-initialization.md +31 -0
  172. package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +9 -34
  173. package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +88 -118
  174. package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +55 -119
  175. package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +15 -11
  176. package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +73 -54
  177. package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +22 -0
  178. package/lib/oh-story/skills/story-long-write/references/writing-craft.md +63 -4
  179. package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +934 -170
  180. package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +531 -0
  181. package/lib/oh-story/skills/story-long-write/scripts/check-ai-patterns.js +6 -1
  182. package/lib/oh-story/skills/story-long-write/scripts/check-degeneration.js +39 -20
  183. package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +197 -15
  184. package/lib/oh-story/skills/story-long-write/scripts/check-outline-copy.js +90 -23
  185. package/lib/oh-story/skills/story-long-write/scripts/normalize-punctuation.js +10 -5
  186. package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +285 -0
  187. package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +58 -0
  188. package/lib/oh-story/skills/story-long-write/scripts/style-whitelist.js +39 -0
  189. package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +210 -2
  190. package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +3 -0
  191. package/lib/oh-story/skills/story-review/SKILL.md +80 -87
  192. package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +29 -56
  193. package/lib/oh-story/skills/story-review/references/author-memory.md +61 -29
  194. package/lib/oh-story/skills/story-review/references/banned-words.md +3 -1
  195. package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +10 -4
  196. package/lib/oh-story/skills/story-review/references/review-quality.md +6 -12
  197. package/lib/oh-story/skills/story-review/references/style-resolution.md +27 -0
  198. package/lib/oh-story/skills/story-review/references/tracking-initialization.md +31 -0
  199. package/lib/oh-story/skills/story-review/references/tracking-transaction.md +9 -34
  200. package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +934 -170
  201. package/lib/oh-story/skills/story-review/scripts/check-ai-patterns.js +6 -1
  202. package/lib/oh-story/skills/story-review/scripts/check-degeneration.js +39 -20
  203. package/lib/oh-story/skills/story-review/scripts/normalize-punctuation.js +10 -5
  204. package/lib/oh-story/skills/story-review/scripts/style-whitelist.js +39 -0
  205. package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +210 -2
  206. package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +3 -0
  207. package/lib/oh-story/skills/story-setup/SKILL.md +58 -43
  208. package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +29 -56
  209. package/lib/oh-story/skills/story-setup/references/agent-references/banned-words.md +3 -1
  210. package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +154 -0
  211. package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +10 -4
  212. package/lib/oh-story/skills/story-setup/references/agent-references/format-and-structure.md +2 -2
  213. package/lib/oh-story/skills/story-setup/references/agent-references/long-emotional-methods.md +2 -2
  214. package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +1 -1
  215. package/lib/oh-story/skills/story-setup/references/agent-references/short-genre-formulas.md +1 -1
  216. package/lib/oh-story/skills/story-setup/references/agent-references/style-combat-face.md +1 -1
  217. package/lib/oh-story/skills/story-setup/references/agent-references/style-genre-modules.md +1 -1
  218. package/lib/oh-story/skills/story-setup/references/agent-references/style-resolution.md +27 -0
  219. package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +63 -4
  220. package/lib/oh-story/skills/story-setup/references/diagnostics.md +38 -0
  221. package/lib/oh-story/skills/story-short-analyze/SKILL.md +22 -3
  222. package/lib/oh-story/skills/story-short-analyze/references/analysis-writing-techniques.md +1 -1
  223. package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +5 -1
  224. package/lib/oh-story/skills/story-short-analyze/references/source-story-quality.md +2 -2
  225. package/lib/oh-story/skills/story-short-scan/SKILL.md +5 -1
  226. package/lib/oh-story/skills/story-short-write/SKILL.md +15 -324
  227. package/lib/oh-story/skills/story-short-write/references/author-memory.md +61 -29
  228. package/lib/oh-story/skills/story-short-write/references/banned-words.md +3 -1
  229. package/lib/oh-story/skills/story-short-write/references/benchmark-recall.md +60 -0
  230. package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +10 -4
  231. 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
  232. package/lib/oh-story/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +1 -1
  233. 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
  234. 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
  235. 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
  236. package/lib/oh-story/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +5 -5
  237. 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
  238. package/lib/oh-story/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +5 -5
  239. 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
  240. package/lib/oh-story/skills/story-short-write/references/genre-writing-formulas.md +1 -1
  241. package/lib/oh-story/skills/story-short-write/references/genre-writing-techniques.md +1 -1
  242. package/lib/oh-story/skills/story-short-write/references/reference-index.md +55 -0
  243. package/lib/oh-story/skills/story-short-write/references/short-craft.md +12 -14
  244. package/lib/oh-story/skills/story-short-write/references/short-deslop.md +10 -10
  245. package/lib/oh-story/skills/story-short-write/references/short-format.md +3 -1
  246. package/lib/oh-story/skills/story-short-write/references/short-prose-quality.md +2 -2
  247. package/lib/oh-story/skills/story-short-write/references/style-resolution.md +27 -0
  248. package/lib/oh-story/skills/story-short-write/references/workflow-design.md +78 -0
  249. package/lib/oh-story/skills/story-short-write/references/workflow-draft.md +126 -0
  250. package/lib/oh-story/skills/story-short-write/references/workflow-revision.md +32 -0
  251. package/lib/oh-story/skills/story-short-write/references/writing-workflow.md +5 -5
  252. package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +934 -170
  253. package/lib/oh-story/skills/story-short-write/scripts/check-ai-patterns.js +6 -1
  254. package/lib/oh-story/skills/story-short-write/scripts/check-degeneration.js +39 -20
  255. package/lib/oh-story/skills/story-short-write/scripts/check-delivery-contract.js +34 -5
  256. package/lib/oh-story/skills/story-short-write/scripts/check-outline-copy.js +90 -23
  257. package/lib/oh-story/skills/story-short-write/scripts/normalize-punctuation.js +10 -5
  258. package/lib/oh-story/skills/story-short-write/scripts/style-whitelist.js +39 -0
  259. package/lib/types/client/drama-production-view.d.ts.map +1 -1
  260. package/lib/types/client/file-activity.d.ts +19 -1
  261. package/lib/types/client/file-activity.d.ts.map +1 -1
  262. package/lib/types/client/index.d.ts.map +1 -1
  263. package/lib/types/client/production-prompts.d.ts +7 -0
  264. package/lib/types/client/production-prompts.d.ts.map +1 -1
  265. package/lib/types/client/production-runtime.d.ts +33 -0
  266. package/lib/types/client/production-runtime.d.ts.map +1 -1
  267. package/lib/types/drama-adapters.d.ts +5 -3
  268. package/lib/types/drama-adapters.d.ts.map +1 -1
  269. package/lib/types/native-hooks.d.ts +52 -0
  270. package/lib/types/native-hooks.d.ts.map +1 -1
  271. package/lib/types/role-identity.d.ts +17 -0
  272. package/lib/types/role-identity.d.ts.map +1 -0
  273. package/lib/types/role-tool.d.ts.map +1 -1
  274. package/lib/types/skill-provider.d.ts.map +1 -1
  275. package/lib/video-recap/CHANGELOG.md +31 -1
  276. package/lib/video-recap/manifest.json +162 -162
  277. package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +4 -9
  278. package/lib/video-recap/skills/video-assemble/scripts/assemble.py +47 -60
  279. package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +77 -115
  280. package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +41 -40
  281. package/lib/video-recap/skills/video-assemble/scripts/audio_automation.py +22 -32
  282. package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +102 -192
  283. package/lib/video-recap/skills/video-assemble/scripts/jianying_builders.py +14 -35
  284. package/lib/video-recap/skills/video-assemble/scripts/jianying_model.py +14 -12
  285. package/lib/video-recap/skills/video-assemble/scripts/jianying_optional.py +8 -9
  286. package/lib/video-recap/skills/video-assemble/scripts/jianying_schema.py +3 -14
  287. package/lib/video-recap/skills/video-assemble/scripts/jianying_timeline_contract.py +23 -12
  288. package/lib/video-recap/skills/video-assemble/scripts/jianying_tracks.py +1 -4
  289. package/lib/video-recap/skills/video-assemble/scripts/jianying_writer.py +24 -25
  290. package/lib/video-recap/skills/video-assemble/scripts/lib.py +31 -85
  291. package/lib/video-recap/skills/video-assemble/scripts/media.py +100 -144
  292. package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +101 -195
  293. package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +10 -21
  294. package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +90 -169
  295. package/lib/video-recap/skills/video-assemble/scripts/subtitle_core.py +69 -122
  296. package/lib/video-recap/skills/video-assemble/scripts/subtitle_render.py +10 -17
  297. package/lib/video-recap/skills/video-assemble/scripts/timeline.py +33 -48
  298. package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +14 -20
  299. package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +135 -213
  300. package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +49 -57
  301. package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +38 -74
  302. package/lib/video-recap/skills/video-cut/scripts/cut_render.py +46 -95
  303. package/lib/video-recap/skills/video-cut/scripts/lib.py +23 -25
  304. package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +70 -173
  305. package/lib/video-recap/skills/video-cut/scripts/narration_mapping.py +28 -53
  306. package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +162 -322
  307. package/lib/video-recap/skills/video-recap/references/timeline-and-jianying.md +6 -7
  308. package/lib/video-recap/skills/video-recap/scripts/doctor.py +104 -124
  309. package/lib/video-recap/skills/video-recap/scripts/final_qc.py +255 -412
  310. package/lib/video-recap/skills/video-recap/scripts/lib.py +30 -52
  311. package/lib/video-recap/skills/video-recap/scripts/materials.py +97 -120
  312. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_client.py +7 -12
  313. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_evidence.py +46 -97
  314. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_observations.py +18 -36
  315. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_payload.py +29 -47
  316. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_report.py +43 -76
  317. package/lib/video-recap/skills/video-recap/scripts/mimo_qc_runner.py +2 -1
  318. package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +2 -1
  319. package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +2 -2
  320. package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +129 -145
  321. package/lib/video-recap/skills/video-recap/scripts/recap_review.py +27 -20
  322. package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +20 -31
  323. package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +31 -106
  324. package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +43 -113
  325. package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +196 -377
  326. package/lib/video-recap/skills/video-script/scripts/agent_brief.py +1 -1
  327. package/lib/video-recap/skills/video-script/scripts/agent_text.py +63 -172
  328. package/lib/video-recap/skills/video-script/scripts/brief_context.py +80 -135
  329. package/lib/video-recap/skills/video-script/scripts/brief_inputs.py +125 -167
  330. package/lib/video-recap/skills/video-script/scripts/brief_timeline.py +93 -208
  331. package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +20 -46
  332. package/lib/video-recap/skills/video-script/scripts/lib.py +30 -26
  333. package/lib/video-recap/skills/video-script/scripts/narration_lint.py +145 -140
  334. package/lib/video-recap/skills/video-script/scripts/review_response.py +12 -42
  335. package/lib/video-recap/skills/video-script/scripts/review_runner.py +0 -6
  336. package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +39 -100
  337. package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +33 -124
  338. package/lib/video-recap/skills/video-understanding/scripts/agent_brief.py +1 -1
  339. package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +63 -172
  340. package/lib/video-recap/skills/video-understanding/scripts/asr.py +1 -2
  341. package/lib/video-recap/skills/video-understanding/scripts/brief_context.py +80 -135
  342. package/lib/video-recap/skills/video-understanding/scripts/brief_inputs.py +125 -167
  343. package/lib/video-recap/skills/video-understanding/scripts/brief_timeline.py +93 -208
  344. package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +1 -1
  345. package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +20 -46
  346. package/lib/video-recap/skills/video-understanding/scripts/lib.py +31 -26
  347. package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +145 -140
  348. package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +39 -100
  349. package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +33 -124
  350. package/lib/video-recap/skills/video-understanding/scripts/vlm.py +1 -4
  351. package/lib/video-recap/skills/video-voiceover/scripts/dub.py +7 -13
  352. package/lib/video-recap/skills/video-voiceover/scripts/fish_audio.py +12 -30
  353. package/lib/video-recap/skills/video-voiceover/scripts/lib.py +98 -131
  354. package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +17 -28
  355. package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +151 -240
  356. package/package.json +45 -45
  357. package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +0 -59
  358. package/lib/novel-to-game/skills/game-world-design/references/game-writing-craft.md +0 -53
  359. package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +0 -42
  360. package/lib/oh-story/skills/story-long-write/references/state-tracking.md +0 -108
@@ -181,9 +181,9 @@ const HEROINE_BOUNDARY_FLAGS = Object.freeze({
181
181
  // 顺序重演「越界/修复」,才能同时支持修复后重开与后来再次越界重新关门。
182
182
  function routeBoundaryBreachActive(state, heroineId) {
183
183
  const contract = HEROINE_BOUNDARY_FLAGS[heroineId];
184
- if (!contract || !state?.flags?.[contract.broken]) return false;
184
+ if (!contract || !state.flags[contract.broken]) return false;
185
185
  let active = null;
186
- for (const entry of state.history ?? []) {
186
+ for (const entry of state.history) {
187
187
  if (entry.type !== 'visit_choice' || entry.heroine !== heroineId) continue;
188
188
  const flags = routeChoiceById(heroineId, entry.choice)?.effects?.flags ?? [];
189
189
  if (flags.includes(contract.broken)) active = true;
@@ -246,7 +246,7 @@ function costLockedText(choice) {
246
246
 
247
247
  // 安抚实价:妒过四十两起步价之后,每多一点妒加一两。
248
248
  export function appeaseCost(state, heroineId) {
249
- return APPEASE_BASE + Math.max(0, (state.relations[heroineId]?.du ?? 0) - APPEASE_DU_FLOOR);
249
+ return APPEASE_BASE + Math.max(0, state.relations[heroineId].du - APPEASE_DU_FLOOR);
250
250
  }
251
251
 
252
252
  const makeRel = () => ({ qing: 8, yu: 6, du: 0, ignored: 0, reasons: [] });
@@ -263,7 +263,7 @@ export function bondKey(left, right) {
263
263
 
264
264
  export function bondValue(state, left, right) {
265
265
  const key = bondKey(left, right);
266
- return key ? state.bonds?.[key] ?? 0 : 0;
266
+ return key ? state.bonds[key] : 0;
267
267
  }
268
268
 
269
269
  export function bondTier(value) {
@@ -277,7 +277,7 @@ export function bondTier(value) {
277
277
  export function bondStatus(state) {
278
278
  return PAIR_IDS.map((id) => {
279
279
  const [left, right] = id.split('|');
280
- const value = state.bonds?.[id] ?? 0;
280
+ const value = state.bonds[id];
281
281
  return { id, left, right, value, tier: bondTier(value) };
282
282
  });
283
283
  }
@@ -335,8 +335,8 @@ function routeChoiceLane(heroineId, choiceId) {
335
335
  }
336
336
 
337
337
  function dominantRouteLane(stance) {
338
- if ((stance?.covenant ?? 0) > (stance?.private ?? 0)) return 'covenant';
339
- if ((stance?.private ?? 0) > (stance?.covenant ?? 0)) return 'private';
338
+ if (stance.covenant > stance.private) return 'covenant';
339
+ if (stance.private > stance.covenant) return 'private';
340
340
  return null;
341
341
  }
342
342
 
@@ -367,11 +367,11 @@ function routeBondChanges(heroineId, lane) {
367
367
  }
368
368
 
369
369
  export function routeStance(state, heroineId) {
370
- const row = state.routeStances?.[heroineId] ?? { covenant: 0, private: 0 };
370
+ const row = state.routeStances[heroineId];
371
371
  return {
372
372
  covenant: row.covenant,
373
373
  private: row.private,
374
- tone: row.covenant === row.private ? '两面都在看' : row.covenant > row.private ? '更信共同承担' : '更信私下情分',
374
+ tone: row.covenant === row.private ? '人前人后的话都记着' : row.covenant > row.private ? '更记得你当众做过的事' : '更记得门内只对她说的话',
375
375
  };
376
376
  }
377
377
 
@@ -381,7 +381,7 @@ const ROUTE_BRANCH_MEMORY = Object.freeze({
381
381
  private: '她记得你总把最难看的账先带进她门内;这一次会追问信任是否又让她独自遮丑。',
382
382
  }),
383
383
  pan_jinlian: Object.freeze({
384
- covenant: '她已经把锋利磨成人人可用的追问程序;这一次要看你是否也肯受同一套真话约束。',
384
+ covenant: '她已经让别人也能拿原话来问。这一次,她要看你自己被问时会不会改口。',
385
385
  private: '她记得那些只在角门内成立的偏话;这一次会逼你分清亲近、撒谎与可停止的追问。',
386
386
  }),
387
387
  li_pinger: Object.freeze({
@@ -400,7 +400,7 @@ const ROUTE_BRANCH_MEMORY = Object.freeze({
400
400
 
401
401
  export function routeBranchContext(state, heroineId) {
402
402
  const stepIndex = routeStep(state, heroineId);
403
- const stance = state.routeStances?.[heroineId] ?? { covenant: 0, private: 0 };
403
+ const stance = state.routeStances[heroineId];
404
404
  const lane = dominantRouteLane(stance);
405
405
  if (!lane || !ROUTE_BRANCHES[heroineId]?.[stepIndex]?.[lane]) return null;
406
406
  const lead = lane === 'covenant' ? stance.covenant : stance.private;
@@ -419,7 +419,7 @@ export function routeBranchContext(state, heroineId) {
419
419
  function changeBond(state, left, right, delta) {
420
420
  const key = bondKey(left, right);
421
421
  if (!key || !delta) return;
422
- state.bonds[key] = clamp((state.bonds[key] ?? 0) + delta, -100, 100);
422
+ state.bonds[key] = clamp(state.bonds[key] + delta, -100, 100);
423
423
  }
424
424
 
425
425
  function dayNetworkChanges(day, actionId, actor) {
@@ -559,8 +559,8 @@ export function dayPressureRule(state, day = state.day) {
559
559
  export function dayFavorSolution(state, day = state.day) {
560
560
  const row = DAY_FAVOR_SOLUTIONS[day - 1];
561
561
  if (!row) return null;
562
- const stance = state.routeStances?.[row.heroine] ?? { covenant: 0, private: 0 };
563
- const qing = state.relations?.[row.heroine]?.qing ?? 0;
562
+ const stance = state.routeStances[row.heroine];
563
+ const qing = state.relations[row.heroine].qing;
564
564
  const bond = bondValue(state, row.heroine, row.observer);
565
565
  const accord = heroineAccordReady(state, row.heroine);
566
566
  const leverageReady = accord
@@ -613,11 +613,11 @@ function resolvePressure(state, actionId, joint = false, planned = null) {
613
613
  : { house: 4, exposure: -8 };
614
614
  const momentum = pressureMomentum(state);
615
615
  if (momentum.resolved >= 2) {
616
- reward.house = (reward.house ?? 0) + 2;
616
+ reward.house += 2;
617
617
  reward.power = 1;
618
618
  }
619
619
  if (momentum.missed >= 2) {
620
- reward.house = (reward.house ?? 0) + 6;
620
+ reward.house += 6;
621
621
  reward.strain = -4;
622
622
  }
623
623
  changeResources(state, reward);
@@ -1042,7 +1042,7 @@ function addFlag(state, id) {
1042
1042
  state.flags[id] = true;
1043
1043
  const heroine = OVERRIDE_FLAG_TO_HEROINE[id];
1044
1044
  if (heroine) {
1045
- state.publicOverrides[heroine] = (state.publicOverrides[heroine] ?? 0) + 1;
1045
+ state.publicOverrides[heroine] += 1;
1046
1046
  evaluateBreak(state, heroine);
1047
1047
  }
1048
1048
  }
@@ -1068,9 +1068,9 @@ export function evaluateHouseBreak(state) {
1068
1068
  record(state, 'route_break', { heroine: null, cause: 'house', house: state.resources.house });
1069
1069
  }
1070
1070
 
1071
- // 路线是否在冷却中。旧档缺这两个字段时按「未冷却」回退,不影响读档。
1071
+ // 路线是否在冷却中。
1072
1072
  export function routeCooling(state, heroineId) {
1073
- return (state.routeReopensOn?.[heroineId] ?? 0) > state.day;
1073
+ return state.routeReopensOn[heroineId] > state.day;
1074
1074
  }
1075
1075
 
1076
1076
  function changeRel(state, heroineId, delta = {}, reason = '') {
@@ -1093,7 +1093,7 @@ function setIgnored(state, heroineId, value) {
1093
1093
  }
1094
1094
 
1095
1095
  function changeHousehold(state, effect, reason = '') {
1096
- if (!effect?.id || !state.household?.[effect.id]) return;
1096
+ if (!effect?.id || !state.household[effect.id]) return;
1097
1097
  const row = state.household[effect.id];
1098
1098
  row.regard = clamp(row.regard + (effect.regard ?? 0), -100, 100);
1099
1099
  if (reason && effect.regard) {
@@ -1104,7 +1104,7 @@ function changeHousehold(state, effect, reason = '') {
1104
1104
 
1105
1105
  function changeResources(state, effects = {}) {
1106
1106
  applyResourceDelta(state.resources, effects);
1107
- if (effects.house && state.routeReopensOn) evaluateHouseBreak(state);
1107
+ if (effects.house) evaluateHouseBreak(state);
1108
1108
  }
1109
1109
 
1110
1110
  function applyResourceDelta(r, effects = {}) {
@@ -1999,7 +1999,7 @@ function fivePriceSupport(basis, heroine) {
1999
1999
  const candidates = HEROINE_IDS
2000
2000
  .filter((id) => id !== heroine)
2001
2001
  .map((id) => {
2002
- const trust = basis.bonds[bondKey(heroine, id)] ?? 0;
2002
+ const trust = basis.bonds[bondKey(heroine, id)];
2003
2003
  const witnessed = basis.history.some((entry) => (
2004
2004
  entry.type === 'pair_interlude'
2005
2005
  && entry.pair?.includes(heroine)
@@ -2013,7 +2013,7 @@ function fivePriceSupport(basis, heroine) {
2013
2013
  const strongest = candidates[0] ?? null;
2014
2014
  const supported = !!strongest;
2015
2015
  const ally = strongest?.heroine ?? null;
2016
- return { supported, ally, trust: ally ? basis.bonds[bondKey(heroine, ally)] ?? 0 : null };
2016
+ return { supported, ally, trust: ally ? basis.bonds[bondKey(heroine, ally)] : null };
2017
2017
  }
2018
2018
 
2019
2019
  function fivePriceBreakSource(basis, breakIndex, heroine) {
@@ -2050,7 +2050,7 @@ function fivePriceBreakStatus(basis, heroine) {
2050
2050
  for (let index = 0; index < basis.history.length; index += 1) {
2051
2051
  const entry = basis.history[index];
2052
2052
  if (entry.type !== 'route_break' || (entry.heroine !== heroine && entry.heroine !== null)) continue;
2053
- if (entry.heroine === heroine && (basis.overrides[heroine] ?? 0) < BREAK_OVERRIDE_LIMIT) continue;
2053
+ if (entry.heroine === heroine && basis.overrides[heroine] < BREAK_OVERRIDE_LIMIT) continue;
2054
2054
  breakIndex = index;
2055
2055
  }
2056
2056
  if (breakIndex < 0) return { entry:null, index:-1, source:null, repair:null, unresolved:false };
@@ -2112,7 +2112,7 @@ function fivePriceReplyReasons(heroine, { accord, stance, support, arrangement,
2112
2112
  function expectedFivePriceReply(state, heroine) {
2113
2113
  const basis = fivePriceBasis(state);
2114
2114
  const accord = basis.accords.has(FIVE_PRICE_ACCORD[heroine]);
2115
- const stance = basis.stances[heroine] ?? { covenant:0, private:0 };
2115
+ const stance = basis.stances[heroine];
2116
2116
  const support = fivePriceSupport(basis, heroine);
2117
2117
  const broken = fivePriceUnresolvedBreak(basis, heroine);
2118
2118
  const arrangement = latestIntimacyArrangement({ history:basis.history }, heroine);
@@ -2247,10 +2247,10 @@ export function currentFivePrivatePrices(state) {
2247
2247
  || JSON.stringify(pending.coalition) !== JSON.stringify(fivePriceCoalition(pending.protocol, pending.right, pending.replies))) return null;
2248
2248
  const names = pending.coalition.members.map((id) => HEROINES[id].short).join('、');
2249
2249
  const result = pending.coalition.kind === 'full'
2250
- ? { title:'五封不同的答复仍能互相作证', body:'没有人被要求同声,五个人却都保留了足够的互证条件。明日可以继续谈五院共同出证,但每页仍须本人放行。' }
2250
+ ? { title:'五封信说得不一样,却能对上同一件事', body:'月娘没有把五封信誊成一句。她只把日期、纸脚和各人亲手经过的那一段排在一起;明日若要拿出去,仍要一封一封问主人。' }
2251
2251
  : pending.coalition.kind === 'limited'
2252
- ? { title:'有限互证候选成形,今夜尚无人预签同席', body:`${names}愿意成为明日互证候选;其余院门没有被判作失败者,也不会被强拉进共同授权。外账只能写“候选间互证、非候选者自持”,不能提前把候选叫作盟员。` }
2253
- : { title:'五封各归原主,只保下一条权利底线', body:'今夜没有形成可共同授权的联盟。明日仍须结外账,但不能用付银、见官或分册按钮把这道裂口洗成五院圆满。' };
2252
+ ? { title:`${names}肯互借一页,其余的信仍各自收好`, body:`${names}只答应明日在彼此看过的那几页上落名。其余人没有被说成不肯帮忙,也没有人替她们把信抽走。` }
2253
+ : { title:'五封信都回到了主人手里', body:'月娘没有强行把信订成一册。明日的外账仍要面对,但今夜没有谁愿意把自己的名字、原话和钥匙一并交给别人。' };
2254
2254
  const jiaoerEcho = fivePriceJiaoerEcho(state, 'resolution');
2255
2255
  const dayPreparation = fivePriceDayPreparation(state, 'resolution');
2256
2256
  return {
@@ -2324,7 +2324,7 @@ export function advanceFivePrivatePrices(state) {
2324
2324
  export function jointActionOptions(state) {
2325
2325
  const completed = completedJointActions(state);
2326
2326
  return JOINT_ACTIONS.map((choice) => {
2327
- const missing = choice.requires.filter((key) => !state.accords?.[key]);
2327
+ const missing = choice.requires.filter((key) => !state.accords[key]);
2328
2328
  const used = completed.has(choice.id);
2329
2329
  const unaffordable = cannotAfford(state, choice);
2330
2330
  const pairTrust = choice.participants?.length === 2 ? bondValue(state, choice.participants[0], choice.participants[1]) : 0;
@@ -2418,10 +2418,10 @@ function portablePrecedentBasis(history) {
2418
2418
  function portablePrecedentReplyFromBasis(basis, event, joint, heroine) {
2419
2419
  const pair = basis.history.find((entry) => entry.type === 'pair_interlude'
2420
2420
  && entry.pair?.includes(joint.participants[0]) && entry.pair?.includes(joint.participants[1])) ?? null;
2421
- const stance = basis.stances[heroine] ?? { covenant:0, private:0 };
2421
+ const stance = basis.stances[heroine];
2422
2422
  const lane = stance.covenant === stance.private ? 'tie' : stance.covenant > stance.private ? 'covenant' : 'private';
2423
2423
  const broken = fivePriceUnresolvedBreak(basis, heroine);
2424
- const trust = basis.bonds[bondKey(joint.participants[0], joint.participants[1])] ?? 0;
2424
+ const trust = basis.bonds[bondKey(joint.participants[0], joint.participants[1])];
2425
2425
  const accordKey = Object.values(ACCORD_META).find((row) => row.heroine === heroine)?.key ?? null;
2426
2426
  const accord = accordKey ? basis.accords.has(accordKey) : false;
2427
2427
  let outcome = 'narrow';
@@ -4309,10 +4309,10 @@ export function publicEvidenceOptions(state) {
4309
4309
  ...item,
4310
4310
  strength,
4311
4311
  meta: strength === 2
4312
- ? (item.id === 'cross_words' ? '收口成立 · 让改口撞上前证' : '强承接 · 能托住下一步')
4312
+ ? (item.id === 'cross_words' ? '正好收住前两样 · 改口有物可对' : '接得上 · 下一样还能往下查')
4313
4313
  : strength === 1
4314
- ? (item.id === 'grain_measure' ? '事实迟到 · 仍有效但无法回填前缝' : '旁证补强 · 不能代替最后拆口')
4315
- : '承接悬空 · 对方可立即反问',
4314
+ ? (item.id === 'grain_measure' ? '米斗来迟了 · 短米仍真,前面的空处却补不回' : '能添一笔 · 还不能回答前面的反问')
4315
+ : '前后接不上 · 堂下会当场追问',
4316
4316
  };
4317
4317
  });
4318
4318
  }
@@ -4327,24 +4327,24 @@ export function choosePublicEvidence(state, evidenceId) {
4327
4327
  return {
4328
4328
  ok:true,
4329
4329
  text:evidence.resistance,
4330
- announcement:`第${state.publicEvidence.selected.length}步,${evidence.label}已落案。反问:${evidence.resistance}`,
4330
+ announcement:`第${state.publicEvidence.selected.length}样,${evidence.label}已经摆上堂。${evidence.resistance}`,
4331
4331
  };
4332
4332
  }
4333
4333
  const evaluation = publicEvidenceEvaluationForState(state, state.publicEvidence.selected);
4334
- if (!evaluation) return { ok:false, error:'三步证链没有接成可核的次序。' };
4334
+ if (!evaluation) return { ok:false, error:'这三样还没有按能查下去的次序摆好。' };
4335
4335
  state.publicEvidence.result = evaluation.id;
4336
4336
  state.log.push(evaluation.outcome.body);
4337
4337
  return {
4338
4338
  ok:true,
4339
4339
  text:evaluation.outcome.body,
4340
- announcement:`第3步,${evidence.label}已落案。判词:${evaluation.outcome.label}。${evaluation.outcome.title}。承接力 ${evaluation.score}/6。${evaluation.outcome.body}`,
4340
+ announcement:`第三样,${evidence.label}已经摆上堂。结果:${evaluation.outcome.label}——${evaluation.outcome.title}。${evaluation.outcome.body}`,
4341
4341
  };
4342
4342
  }
4343
4343
 
4344
4344
  export function completePublicEvidence(state) {
4345
4345
  const current = currentPublicEvidence(state);
4346
- if (!current?.resolved || !current.result) return { ok:false, error:'先把三份证物依次递完,再进入主签裁决。' };
4347
- applyEffects(state, current.result.effects, null, `堂前公审:${current.result.label}`);
4346
+ if (!current?.resolved || !current.result) return { ok:false, error:'先把三样东西依次递完,再决定由谁在收案文书上落款。' };
4347
+ applyEffects(state, current.result.effects, null, `堂前对账:${current.result.label}`);
4348
4348
  record(state, 'public_evidence_chain', {
4349
4349
  event:PUBLIC_EVIDENCE_CHAIN.id,
4350
4350
  chain:[...current.selected],
@@ -4511,7 +4511,7 @@ function publicEvidenceFutureEcho(state) {
4511
4511
  if (evidence.id === 'rebuttable') return `第十五日的案卷既有承接空白,也保留韩道国先于证物被押的时辰。复案必须同时补证并区分押前、押后口供;主签不能把一段新证据扩成对旧预断的追认。${coda}`;
4512
4512
  return `第十五日三份证物没有接成链,韩道国又先被押成共同答案。堂外已经把这场公审讲成五院围住一个伙计逼供;今日若不同时清证物次序与错误归罪,缺口会继续被一名替罪者吞掉。${coda}`;
4513
4513
  }
4514
- if (evidence.id === 'complete') return `第十五日那条证链仍能逐段复查:雪娥的米斗定事实,账页或门簿接经手,最后一份证物才拆口供。韩道国、玳安与应伯爵此后只能各自质疑一段,不能再用一套圆话抹掉全部。${coda}`;
4514
+ if (evidence.id === 'complete') return `第十五日三样东西仍可重新摆一遍:先称雪娥的米,中间以账页或门簿追经手,最后才拿改口来对。韩道国、玳安与应伯爵可以各问其中一段,却不能再用一句圆话把短米、中间那只手和最后的改口一并抹掉。${coda}`;
4515
4515
  if (evidence.id === 'rebuttable') return `第十五日的案卷仍有一处承接空白。韩道国正要求重验,应伯爵也把“待复核”讲成五院自相矛盾;今日结外账时,必须决定由谁补证而不是再靠主签压过去。${coda}`;
4516
4516
  return `第十五日三份真证没有接成一条链。玳安未认空白门路,韩道国与应伯爵已经把公审改讲成围人逼供;今日若仍只清银不清经手,这个版本会比原件传得更远。${coda}`;
4517
4517
  }
@@ -4989,11 +4989,11 @@ export function resolveFinalReckoningAftermath(state, choiceId) {
4989
4989
  }
4990
4990
 
4991
4991
  export function accordStatus(state) {
4992
- return Object.values(ACCORD_META).map((row) => ({ ...row, complete: !!state.accords?.[row.key] }));
4992
+ return Object.values(ACCORD_META).map((row) => ({ ...row, complete: state.accords[row.key] }));
4993
4993
  }
4994
4994
 
4995
4995
  export function coalitionProofStatus(state) {
4996
- return Object.values(COALITION_PROOF_META).map((row) => ({ ...row, complete: !!state.flags?.[row.flag] }));
4996
+ return Object.values(COALITION_PROOF_META).map((row) => ({ ...row, complete: !!state.flags[row.flag] }));
4997
4997
  }
4998
4998
 
4999
4999
  export function publicPromisesReady(state) {
@@ -5033,11 +5033,11 @@ function personalFinaleDepartureBasis(history) {
5033
5033
  }
5034
5034
 
5035
5035
  function personalFinaleDepartureOutcome(basis, heroine, procedure) {
5036
- const stance = basis.stances[heroine] ?? { covenant:0, private:0 };
5036
+ const stance = basis.stances[heroine];
5037
5037
  const lane = stance.covenant === stance.private ? 'tie' : stance.covenant > stance.private ? 'covenant' : 'private';
5038
5038
  const accord = basis.accords.has(FIVE_PRICE_ACCORD[heroine]);
5039
5039
  const visits = basis.history.filter((entry) => entry.type === 'visit_choice' && entry.heroine === heroine).length;
5040
- const arrangements = basis.arrangements[heroine] ?? [];
5040
+ const arrangements = basis.arrangements[heroine];
5041
5041
  const close = visits >= 6 || arrangements.length > 0;
5042
5042
  const broken = fivePriceUnresolvedBreak(basis, heroine);
5043
5043
  const match = procedure?.lane === lane && lane !== 'tie';
@@ -5253,24 +5253,24 @@ export function personalFinaleRouteReason(history, heroineId, procedure) {
5253
5253
  });
5254
5254
  const covenantRows = rows.filter((row) => row.lane === 'covenant');
5255
5255
  const privateRows = rows.filter((row) => row.lane === 'private');
5256
- let historyText = '她此前没有可核的人物路线选择,因此没有一条隐藏倾向可以替她预先答应善后。';
5256
+ let historyText = '你们之前没有做成一件足以回答今日的事。她不会因为气氛到了,就替空白点头。';
5257
5257
  if (covenantRows.length > privateRows.length) {
5258
5258
  const latest = covenantRows.at(-1);
5259
- historyText = `她的路线账留下共同承担${covenantRows.length}次、本人私门${privateRows.length}次;最近支撑共同规则的是第${latest.day}日“${latest.label}”:${latest.text}这项真选择,而非汇总分数,构成她今天核约的同向依据。`;
5259
+ historyText = `你更常把她的话带到人前。最近一次是第${latest.day}日的“${latest.label}”:${latest.text}她今日记得的是这件真事,不是你们累计了多少次。`;
5260
5260
  } else if (privateRows.length > covenantRows.length) {
5261
5261
  const latest = privateRows.at(-1);
5262
- historyText = `她的路线账留下共同承担${covenantRows.length}次、本人私门${privateRows.length}次;最近支撑本人私门的是第${latest.day}日“${latest.label}”:${latest.text}这项真选择,而非汇总分数,构成她今天核约的同向依据。`;
5262
+ historyText = `你更常把她的话留在门内。最近一次是第${latest.day}日的“${latest.label}”:${latest.text}她今日要从这件真事往下谈,不让一句“我们亲近”替代回答。`;
5263
5263
  } else if (rows.length) {
5264
5264
  const covenant = covenantRows.at(-1);
5265
5265
  const privateChoice = privateRows.at(-1);
5266
- historyText = `她的路线账在共同承担与本人私门各有${covenantRows.length}次:最近一项共同规则是第${covenant.day}日“${covenant.label}”:${covenant.text}最近一项本人私门是第${privateChoice.day}日“${privateChoice.label}”:${privateChoice.text}两项都仍在场,没有一边能被系统写成默认答案。`;
5266
+ historyText = `你们既有人前做过的事,也有只在门内说过的话。最近的两件是第${covenant.day}日“${covenant.label}”:${covenant.text}以及第${privateChoice.day}日“${privateChoice.label}”:${privateChoice.text}她不打算为了今夜好看,就把其中一件当成没发生。`;
5267
5267
  }
5268
- return `${historyText}本次真实提案是“${procedure?.label ?? '未知善后'}”:${procedure?.summary ?? ''}其直接触及${procedure?.focus ?? '本人权利'}。`;
5268
+ return `${historyText}现在摆在她面前的是“${procedure?.label ?? '还没说清的安排'}”:${procedure?.summary ?? ''}它会直接动到${procedure?.focus ?? '她自己手里的东西'}。`;
5269
5269
  }
5270
5270
 
5271
5271
  export function personalFinaleSelectedProcedureReason(procedure, heroineId) {
5272
5272
  if (!procedure?.id || !HEROINE_IDS.includes(heroineId) || !PERSONAL_FINALE_DEPARTURES[procedure.id]) return '';
5273
- return `你与${HEROINES[heroineId].short}在第三答落下的真实程序是“${procedure.label}”:${procedure.summary}它只处分${procedure.focus}。她可以确认自己怎样陪你执行、怎样不替你遮掩,却不能替另外四院接下、改写或拒绝;因此这项共同决定必须在四个人逐一亲口作答以后才生效。`;
5273
+ return `你和${HEROINES[heroineId].short}最后说定的是“${procedure.label}”:${procedure.summary}这件事只动到${procedure.focus}。她能答应自己怎样做,也能当面不答应;另外四个人仍要一个个开口,她不替任何人点头。`;
5274
5274
  }
5275
5275
 
5276
5276
  export function personalFinaleSelectedReasons(history, heroineId, beatIndex, selectedChoice) {
@@ -5301,7 +5301,7 @@ export function personalFinaleSelectedReasons(history, heroineId, beatIndex, sel
5301
5301
  }
5302
5302
 
5303
5303
  function personalFinaleDepartureReasons(basis, heroine, procedure, outcome) {
5304
- const arrangements = basis.arrangements[heroine] ?? [];
5304
+ const arrangements = basis.arrangements[heroine];
5305
5305
  const relationText = arrangements.length
5306
5306
  ? personalFinaleIntimacyReason(basis.history, heroine, procedure)
5307
5307
  : personalFinaleRelationshipReason(basis.history, heroine);
@@ -5632,7 +5632,7 @@ function sharedNightAccordEntry(state, heroineId) {
5632
5632
  const accordRow = [...state.history].reverse().find((entry) => entry.type === 'accord_term' && entry.heroine === heroineId && entry.choice === accord?.id) ?? null;
5633
5633
  const proof = COALITION_PROOF_META[heroineId];
5634
5634
  const day20 = allianceDay20Protection(state, heroineId, settlement.right);
5635
- if (!reply || !protocol || !right || !offer || !outcome || !finalChoice || !aftermathChoice || aftermathRow.approach !== finalRow.choice || !accordRow || !proof || !state.flags?.[proof.flag]) return null;
5635
+ if (!reply || !protocol || !right || !offer || !outcome || !finalChoice || !aftermathChoice || aftermathRow.approach !== finalRow.choice || !accordRow || !proof || !state.flags[proof.flag]) return null;
5636
5636
  const boundary = PERSONAL_FINALE_BOUNDARY_OBJECTS[heroineId];
5637
5637
  const protectionLabel = SHARED_NIGHT_PROTECTION_LABELS[day20.day20Protection];
5638
5638
  const protectionText = sharedNightProtectionText(settlement.right, day20.day20Protection);
@@ -6199,7 +6199,7 @@ export function sharedNightStatus(state) {
6199
6199
  visible: state.day === MAX_DAY && state.phase === 'choose_visit',
6200
6200
  ready: !reason,
6201
6201
  reason,
6202
- complete: ACCORD_KEYS.filter((key) => state.accords?.[key]).length,
6202
+ complete: ACCORD_KEYS.filter((key) => state.accords[key]).length,
6203
6203
  total: ACCORD_KEYS.length,
6204
6204
  jointComplete: Math.min(jointComplete, JOINT_ACTION_TARGET),
6205
6205
  jointTotal: JOINT_ACTION_TARGET,
@@ -6317,11 +6317,11 @@ function sharedFinaleRouteReason(history, heroineId) {
6317
6317
  const lane = routeChoiceLane(heroineId, entry.choice);
6318
6318
  return choice && lane ? [{ day:entry.day, choice, lane }] : [];
6319
6319
  });
6320
- if (!rows.length) return `${HEROINES[heroineId].short}此前没有一项可核的人物路线选择;重读五约或并肩等天亮都不能从好感数值补出她的长期相处方式。`;
6320
+ if (!rows.length) return `${HEROINES[heroineId].short}之前没有和你做成一件足以带到今夜的事。重念一遍约定,也不能替她编出早已愿意的答案。`;
6321
6321
  const covenant = rows.filter((row) => row.lane === 'covenant');
6322
6322
  const privateRows = rows.filter((row) => row.lane === 'private');
6323
6323
  const latest = rows.at(-1);
6324
- return `她在二十日里留下共同承担${covenant.length}次、本人私门${privateRows.length}次;最近一项真实选择是第${latest.day}日“${latest.choice.label}”(${latest.choice.hint}):${latest.choice.text}五院共守只能继续核验这段具体来往,不能用终夜风格覆盖它。`;
6324
+ return `她最近记得的是第${latest.day}日“${latest.choice.label}”(${latest.choice.hint}):${latest.choice.text}今夜的灯再暖,也只能从这件真事往下谈,不能把她以前的点头或拒绝一并抹掉。`;
6325
6325
  }
6326
6326
 
6327
6327
  function sharedAfterglowChoiceReason(choice, heroineId, beatIndex) {
@@ -6741,19 +6741,19 @@ function finishSharedNight(state) {
6741
6741
  // 个人弧线的拍号 = 你第几次进她的门(结算成功才计次)。
6742
6742
  // 取代旧的 state.day - 1:按日历天索引会让轮换玩家撞上「好选项被锁、只能选伤害项」。
6743
6743
  function routeStep(state, heroineId) {
6744
- return state.visits?.[heroineId] ?? 0;
6744
+ return state.visits[heroineId];
6745
6745
  }
6746
6746
 
6747
6747
  export function routeComplete(state, heroineId) {
6748
6748
  const routeDone = routeStep(state, heroineId) >= (ROUTE_CHOICES[heroineId]?.length ?? 0);
6749
6749
  const accord = ACCORD_CHOICES[heroineId];
6750
- return routeDone && (!accord || !!state.accords?.[accord.effects.accord]);
6750
+ return routeDone && (!accord || state.accords[accord.effects.accord]);
6751
6751
  }
6752
6752
 
6753
6753
  export function visitChoices(state, heroineId) {
6754
- const rows = routeRowsFor(heroineId, routeStep(state, heroineId), state.routeStances?.[heroineId]);
6754
+ const rows = routeRowsFor(heroineId, routeStep(state, heroineId), state.routeStances[heroineId]);
6755
6755
  const accord = ACCORD_CHOICES[heroineId];
6756
- const choices = accord && !state.accords?.[accord.effects.accord] ? [...rows, accord] : rows;
6756
+ const choices = accord && !state.accords[accord.effects.accord] ? [...rows, accord] : rows;
6757
6757
  return choices.map((choice) => {
6758
6758
  const conditionLocked = !!choice.condition && !hasToken(state, choice.condition);
6759
6759
  const unaffordable = cannotAfford(state, choice);
@@ -7127,7 +7127,7 @@ export function chooseVisit(state, choiceId) {
7127
7127
  }
7128
7128
  for (const row of routeBondChanges(heroine, lane)) changeBond(state, heroine, row.other, row.delta);
7129
7129
  }
7130
- state.visits[heroine] = (state.visits[heroine] ?? 0) + 1;
7130
+ state.visits[heroine] += 1;
7131
7131
  record(state, 'visit_choice', { heroine, choice: choiceId, lane, public: !!PUBLIC_EVENTS[state.day] });
7132
7132
  }
7133
7133
  state.log.push(choice.text);
@@ -7191,7 +7191,7 @@ export function visitForecast(state, heroineId) {
7191
7191
  choice.effects?.accord ? '院约' : routeChoiceLane(heroineId, choice.id) === 'covenant' ? '共担' : '私情'
7192
7192
  )))];
7193
7193
  const obligation = activeObligations(state).find((row) => row.heroine === heroineId && row.type !== 'cooldown') ?? null;
7194
- const reopenDay = state.routeReopensOn?.[heroineId] ?? 0;
7194
+ const reopenDay = state.routeReopensOn[heroineId];
7195
7195
  const branch = routeBranchContext(state, heroineId);
7196
7196
  return {
7197
7197
  mostJealous,
@@ -8798,7 +8798,7 @@ export function activeObligations(state) {
8798
8798
  }
8799
8799
 
8800
8800
  for (const heroine of HEROINE_IDS) {
8801
- const reopenDay = state.routeReopensOn?.[heroine] ?? 0;
8801
+ const reopenDay = state.routeReopensOn[heroine];
8802
8802
  if (reopenDay <= state.day) continue;
8803
8803
  rows.push({
8804
8804
  id:`cooldown:${heroine}`, type:'cooldown', status:'locked',
@@ -9286,7 +9286,7 @@ function morningSettlementNarrowAuthorized(state, heroine) {
9286
9286
  && entry.day === state.morningSettlement.sourceDay
9287
9287
  && entry.action === state.morningSettlement.sourceId
9288
9288
  && entry.actor === heroine);
9289
- return (!!accord && !!state.accords?.[accord] || sourceIsNamed)
9289
+ return (!!accord && state.accords[accord] || sourceIsNamed)
9290
9290
  && !fivePriceUnresolvedBreak(morningSettlementBasis(state), heroine);
9291
9291
  }
9292
9292
 
@@ -10034,7 +10034,7 @@ function combinations(rows, size, start = 0, prefix = [], result = []) {
10034
10034
 
10035
10035
  function heroineAccordReady(state, heroineId) {
10036
10036
  const key = Object.values(ACCORD_META).find((row) => row.heroine === heroineId)?.key;
10037
- return !!key && !!state.accords?.[key];
10037
+ return !!key && !!state.accords[key];
10038
10038
  }
10039
10039
 
10040
10040
  function allianceEndingText(members, style = null) {
@@ -10053,18 +10053,18 @@ function allianceEndingText(members, style = null) {
10053
10053
  : style === '明账共恋'
10054
10054
  ? '去处与偏爱仍围着你发生,却不再靠猜;每一次靠近、改口与独宿都必须当着联盟成员说清。'
10055
10055
  : style === '有限共居'
10056
- ? '她们保留各自院门,只把确实愿意共同承担的部分放到桌心;亲近没有被伪装成吞并。'
10056
+ ? '她们仍各自收着院门钥匙,只把亲口答应的东西放到桌中。坐得近,不等于从此什么都归在一起。'
10057
10057
  : '';
10058
10058
  if (exact) return `${exact}${styleText ? ` ${styleText}` : ''}`;
10059
10059
  const names = members.map((id) => HEROINES[id].name).join('、');
10060
- return `${names}把能共同承担的事写成一份有限同盟。未入盟的院门仍保留自己的账与去路,你也不能拿这份亲近冒充五院圆满。${styleText ? ` ${styleText}` : ''}`;
10060
+ return `${names}只把彼此亲手经过、亲口答应的事写在同一页。没有坐进来的人仍收着自己的账、钥匙和去处;你不能因为今夜亲近,就说五处院门都已点头。${styleText ? ` ${styleText}` : ''}`;
10061
10061
  }
10062
10062
 
10063
10063
  function heroineEpilogueVariant(state, endingId, allianceMembers, exclusiveHeroine, heroineId) {
10064
10064
  if (endingId === 'balanced') return 'balanced';
10065
10065
  if (endingId === 'alliance') return allianceMembers?.includes(heroineId) ? 'alliance' : 'outside';
10066
10066
  if (endingId === 'exclusive') return exclusiveHeroine === heroineId ? 'exclusive' : 'outside';
10067
- const relation = state.relations?.[heroineId] ?? { qing: 0, du: 100 };
10067
+ const relation = state.relations[heroineId];
10068
10068
  return relation.qing >= 35 && relation.du < 65 ? 'personal' : 'outside';
10069
10069
  }
10070
10070
 
@@ -10083,7 +10083,7 @@ function endingEpilogues(state, endingId, allianceMembers, exclusiveHeroine) {
10083
10083
  const morningSettlements = recordedMorningSettlements(state);
10084
10084
  return HEROINE_IDS.map((heroine) => {
10085
10085
  const stance = routeStance(state, heroine);
10086
- const relation = state.relations?.[heroine] ?? { qing: 0, yu: 0, du: 0 };
10086
+ const relation = state.relations[heroine];
10087
10087
  const variant = heroineEpilogueVariant(state, endingId, allianceMembers, exclusiveHeroine, heroine);
10088
10088
  const base = EPILOGUES[heroine][variant];
10089
10089
  const habit = nightRelationshipPattern(state, heroine);
@@ -10118,11 +10118,11 @@ function endingEpilogues(state, endingId, allianceMembers, exclusiveHeroine) {
10118
10118
  : '';
10119
10119
  const firstOpeningText = firstOpening?.epilogueTexts?.[heroine] ?? '';
10120
10120
  const routeHistoryNote = stance.covenant > stance.private
10121
- ? `二十日里,你有 ${stance.covenant} 次把她的要求带到共同规则中,另有 ${stance.private} 次只留在门内。这封笺记住了你更偏向共同承担。`
10121
+ ? `二十日里,你更常把她说过的话带到人前,让别人也能看见你有没有照做。这封笺记的是那些真正发生过的事。`
10122
10122
  : stance.private > stance.covenant
10123
- ? `二十日里,你有 ${stance.private} 次选择私下相护,只有 ${stance.covenant} 次愿意让众人共同追问。这份亲近也留下了旁院要回看的后果。`
10124
- : stance.covenant
10125
- ? `共同承担与私下情分各有 ${stance.covenant} 次。她没有替你把两种选择算成同一件事。`
10123
+ ? `二十日里,你更常把话留在她的门内。那些亲近是真的,门外等过、猜过和来问过的人也是真的。`
10124
+ : stance.covenant
10125
+ ? `你既在人前接过她的话,也和她关过门。她记得这是两种不同的事,不打算替你混成一句“都很好”。`
10126
10126
  : '你没有真正走进她的路线;结局仍会交代她怎样收回自己的名字、钥匙与去处。';
10127
10127
  const habitNote = habit.mode
10128
10128
  ? `已走的 ${habit.chapters}/4 章夜谈更偏向“${habit.label}”:明说 ${habit.counts.honest}、由她定界 ${habit.counts.listen}、门内私情 ${habit.counts.private}。`
@@ -10298,7 +10298,7 @@ export function determineEnding(state) {
10298
10298
  state.day === MAX_DAY
10299
10299
  && recordedFivePriceSettlement(state)?.coalition.kind === 'full'
10300
10300
  && state.flags.harem_coalition
10301
- && ACCORD_KEYS.every((key) => state.accords?.[key])
10301
+ && ACCORD_KEYS.every((key) => state.accords[key])
10302
10302
  && state.unlocked.includes('inner_court_accord')
10303
10303
  && state.unlocked.includes('inner_court_afterglow')
10304
10304
  && state.sharedAfterglowChoices.length === SHARED_AFTERGLOW_BEATS.length
@@ -10717,7 +10717,7 @@ export function startFateCoda(state) {
10717
10717
 
10718
10718
  function fateCodaInstitutionEcho(state) {
10719
10719
  const accordNames = Object.values(ACCORD_META)
10720
- .filter((row) => state.accords?.[row.key])
10720
+ .filter((row) => state.accords[row.key])
10721
10721
  .map((row) => ({
10722
10722
  order: '月娘留下的正堂定账',
10723
10723
  truth: '金莲留下的去处真话',
@@ -10726,10 +10726,10 @@ function fateCodaInstitutionEcho(state) {
10726
10726
  hearth: '雪娥留下的停灶与工账',
10727
10727
  })[row.key]);
10728
10728
  const accordCount = accordNames.length;
10729
- const covenant = Object.values(state.routeStances ?? {}).reduce((sum, row) => sum + (row?.covenant ?? 0), 0);
10730
- const privateCount = Object.values(state.routeStances ?? {}).reduce((sum, row) => sum + (row?.private ?? 0), 0);
10731
- const publicProof = ['harem_coalition', ...Object.keys(state.flags ?? {}).filter((key) => key.includes('public') || key.includes('balance'))]
10732
- .some((key) => state.flags?.[key]);
10729
+ const covenant = Object.values(state.routeStances).reduce((sum, row) => sum + row.covenant, 0);
10730
+ const privateCount = Object.values(state.routeStances).reduce((sum, row) => sum + row.private, 0);
10731
+ const publicProof = ['harem_coalition', ...Object.keys(state.flags).filter((key) => key.includes('public') || key.includes('balance'))]
10732
+ .some((key) => state.flags[key]);
10733
10733
  const accordEcho = accordCount
10734
10734
  ? `二十日里真正立过的${accordNames.join('、')}没有救回死者,却让后来的人仍能据此拒绝被并账。`
10735
10735
  : '二十日里没有一条院约真正落稳;离散来时,每个人只能凭自己还握得住的物件作证。';
@@ -13064,14 +13064,3 @@ export function snapshot(state) {
13064
13064
  return structuredClone(state);
13065
13065
  }
13066
13066
 
13067
- export const APPROVED_ADULT_IDS = Object.freeze([
13068
- 'wu_yueniang', 'pan_jinlian', 'li_pinger', 'meng_yulou', 'sun_xuee',
13069
- ]);
13070
-
13071
- export function sceneIsAdultSafe(scene) {
13072
- const approved = new Set(APPROVED_ADULT_IDS);
13073
- return !!scene
13074
- && Array.isArray(scene.participants)
13075
- && scene.participants.length > 0
13076
- && scene.participants.every((id) => approved.has(id) && HEROINES[id]?.adult === true);
13077
- }