@lingjingai/scriptctl 0.35.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. package/README.md +21 -5
  2. package/changes/0.36.0.md +22 -0
  3. package/dist/bin.js +0 -0
  4. package/dist/cli.d.ts +2 -1
  5. package/dist/cli.js +52 -128
  6. package/dist/cli.js.map +1 -1
  7. package/dist/common.d.ts +0 -16
  8. package/dist/common.js +2 -342
  9. package/dist/common.js.map +1 -1
  10. package/dist/domain/ingest/apply-plan.d.ts +23 -0
  11. package/dist/domain/ingest/apply-plan.js +210 -0
  12. package/dist/domain/ingest/apply-plan.js.map +1 -0
  13. package/dist/domain/ingest/assembler.d.ts +57 -0
  14. package/dist/domain/ingest/assembler.js +181 -0
  15. package/dist/domain/ingest/assembler.js.map +1 -0
  16. package/dist/domain/ingest/chunker.d.ts +9 -0
  17. package/dist/domain/ingest/chunker.js +38 -0
  18. package/dist/domain/ingest/chunker.js.map +1 -0
  19. package/dist/domain/ingest/evidence.d.ts +5 -0
  20. package/dist/domain/ingest/evidence.js +66 -0
  21. package/dist/domain/ingest/evidence.js.map +1 -0
  22. package/dist/domain/ingest/govern.d.ts +3 -0
  23. package/dist/domain/ingest/govern.js +127 -0
  24. package/dist/domain/ingest/govern.js.map +1 -0
  25. package/dist/domain/ingest/jsonl-parser.d.ts +6 -0
  26. package/dist/domain/ingest/jsonl-parser.js +187 -0
  27. package/dist/domain/ingest/jsonl-parser.js.map +1 -0
  28. package/dist/domain/ingest/markdown-parser.d.ts +6 -0
  29. package/dist/domain/ingest/markdown-parser.js +186 -0
  30. package/dist/domain/ingest/markdown-parser.js.map +1 -0
  31. package/dist/domain/ingest/records.d.ts +71 -0
  32. package/dist/domain/ingest/records.js +9 -0
  33. package/dist/domain/ingest/records.js.map +1 -0
  34. package/dist/domain/ingest/renumber.d.ts +14 -0
  35. package/dist/domain/ingest/renumber.js +130 -0
  36. package/dist/domain/ingest/renumber.js.map +1 -0
  37. package/dist/domain/ingest/script-v3.d.ts +94 -0
  38. package/dist/domain/ingest/script-v3.js +7 -0
  39. package/dist/domain/ingest/script-v3.js.map +1 -0
  40. package/dist/domain/ingest/state-reconcile.d.ts +11 -0
  41. package/dist/domain/ingest/state-reconcile.js +90 -0
  42. package/dist/domain/ingest/state-reconcile.js.map +1 -0
  43. package/dist/domain/ingest/validation.d.ts +11 -0
  44. package/dist/domain/ingest/validation.js +168 -0
  45. package/dist/domain/ingest/validation.js.map +1 -0
  46. package/dist/domain/ingest/video-aggregate.d.ts +23 -0
  47. package/dist/domain/ingest/video-aggregate.js +89 -0
  48. package/dist/domain/ingest/video-aggregate.js.map +1 -0
  49. package/dist/domain/ingest/video-apply.d.ts +37 -0
  50. package/dist/domain/ingest/video-apply.js +236 -0
  51. package/dist/domain/ingest/video-apply.js.map +1 -0
  52. package/dist/domain/ingest/video-md-parser.d.ts +2 -0
  53. package/dist/domain/ingest/video-md-parser.js +233 -0
  54. package/dist/domain/ingest/video-md-parser.js.map +1 -0
  55. package/dist/domain/ingest/video-records.d.ts +56 -0
  56. package/dist/domain/ingest/video-records.js +9 -0
  57. package/dist/domain/ingest/video-records.js.map +1 -0
  58. package/dist/domain/loom/index.d.ts +2 -1
  59. package/dist/domain/loom/index.js +12 -10
  60. package/dist/domain/loom/index.js.map +1 -1
  61. package/dist/domain/loom/types.d.ts +3 -2
  62. package/dist/domain/script/lookups.d.ts +3 -2
  63. package/dist/domain/script/lookups.js +25 -8
  64. package/dist/domain/script/lookups.js.map +1 -1
  65. package/dist/domain/script/patch/apply.js +2 -2
  66. package/dist/domain/script/patch/apply.js.map +1 -1
  67. package/dist/domain/script/patch/helpers.d.ts +3 -7
  68. package/dist/domain/script/patch/helpers.js +23 -96
  69. package/dist/domain/script/patch/helpers.js.map +1 -1
  70. package/dist/domain/script/patch/ops-action.d.ts +0 -2
  71. package/dist/domain/script/patch/ops-action.js +34 -100
  72. package/dist/domain/script/patch/ops-action.js.map +1 -1
  73. package/dist/domain/script/patch/ops-asset.d.ts +1 -0
  74. package/dist/domain/script/patch/ops-asset.js +102 -155
  75. package/dist/domain/script/patch/ops-asset.js.map +1 -1
  76. package/dist/domain/script/patch/ops-dialogue.d.ts +1 -4
  77. package/dist/domain/script/patch/ops-dialogue.js +43 -220
  78. package/dist/domain/script/patch/ops-dialogue.js.map +1 -1
  79. package/dist/domain/script/patch/ops-extend.js +8 -2
  80. package/dist/domain/script/patch/ops-extend.js.map +1 -1
  81. package/dist/domain/script/patch/ops-meta.js.map +1 -1
  82. package/dist/domain/script/patch/ops-scene.js +26 -39
  83. package/dist/domain/script/patch/ops-scene.js.map +1 -1
  84. package/dist/domain/script/patch/ops-state.d.ts +3 -3
  85. package/dist/domain/script/patch/ops-state.js +33 -49
  86. package/dist/domain/script/patch/ops-state.js.map +1 -1
  87. package/dist/domain/script/patch/registry.js +9 -13
  88. package/dist/domain/script/patch/registry.js.map +1 -1
  89. package/dist/domain/script/refs.js +23 -111
  90. package/dist/domain/script/refs.js.map +1 -1
  91. package/dist/domain/script/scene-refs.d.ts +3 -0
  92. package/dist/domain/script/scene-refs.js +35 -0
  93. package/dist/domain/script/scene-refs.js.map +1 -0
  94. package/dist/domain/script/schema.js +17 -21
  95. package/dist/domain/script/schema.js.map +1 -1
  96. package/dist/domain/script/text-util.d.ts +7 -0
  97. package/dist/domain/script/text-util.js +91 -0
  98. package/dist/domain/script/text-util.js.map +1 -0
  99. package/dist/help-text.js +117 -410
  100. package/dist/help-text.js.map +1 -1
  101. package/dist/infra/converters.js +1 -1
  102. package/dist/infra/converters.js.map +1 -1
  103. package/dist/infra/episode-title.d.ts +4 -0
  104. package/dist/infra/episode-title.js +129 -0
  105. package/dist/infra/episode-title.js.map +1 -0
  106. package/dist/infra/ingest/review-renderer.d.ts +1 -0
  107. package/dist/infra/ingest/review-renderer.js +431 -0
  108. package/dist/infra/ingest/review-renderer.js.map +1 -0
  109. package/dist/infra/ingest/source-resolver.d.ts +21 -0
  110. package/dist/infra/ingest/source-resolver.js +51 -0
  111. package/dist/infra/ingest/source-resolver.js.map +1 -0
  112. package/dist/infra/ingest/text-reader.d.ts +3 -0
  113. package/dist/infra/ingest/text-reader.js +27 -0
  114. package/dist/infra/ingest/text-reader.js.map +1 -0
  115. package/dist/infra/ingest/video-discovery.d.ts +8 -0
  116. package/dist/infra/ingest/video-discovery.js +60 -0
  117. package/dist/infra/ingest/video-discovery.js.map +1 -0
  118. package/dist/infra/ingest/workspace-store.d.ts +18 -0
  119. package/dist/infra/ingest/workspace-store.js +70 -0
  120. package/dist/infra/ingest/workspace-store.js.map +1 -0
  121. package/dist/infra/llm/anthropic-model.d.ts +11 -0
  122. package/dist/infra/llm/anthropic-model.js +109 -0
  123. package/dist/infra/llm/anthropic-model.js.map +1 -0
  124. package/dist/infra/llm/gemini-upload.d.ts +9 -0
  125. package/dist/infra/llm/gemini-upload.js +94 -0
  126. package/dist/infra/llm/gemini-upload.js.map +1 -0
  127. package/dist/infra/llm/gemini-video-model.d.ts +47 -0
  128. package/dist/infra/llm/gemini-video-model.js +167 -0
  129. package/dist/infra/llm/gemini-video-model.js.map +1 -0
  130. package/dist/infra/llm/openai-compatible-model.d.ts +13 -0
  131. package/dist/infra/llm/openai-compatible-model.js +143 -0
  132. package/dist/infra/llm/openai-compatible-model.js.map +1 -0
  133. package/dist/infra/llm/proxy.d.ts +1 -0
  134. package/dist/infra/llm/proxy.js +30 -0
  135. package/dist/infra/llm/proxy.js.map +1 -0
  136. package/dist/infra/llm/retry.d.ts +5 -0
  137. package/dist/infra/llm/retry.js +20 -0
  138. package/dist/infra/llm/retry.js.map +1 -0
  139. package/dist/llm/composition.d.ts +5 -0
  140. package/dist/llm/composition.js +5 -0
  141. package/dist/llm/composition.js.map +1 -0
  142. package/dist/llm/config.d.ts +26 -0
  143. package/dist/llm/config.js +201 -0
  144. package/dist/llm/config.js.map +1 -0
  145. package/dist/llm/registry.d.ts +44 -0
  146. package/dist/llm/registry.js +48 -0
  147. package/dist/llm/registry.js.map +1 -0
  148. package/dist/llm/router.d.ts +23 -0
  149. package/dist/llm/router.js +0 -0
  150. package/dist/llm/router.js.map +1 -0
  151. package/dist/llm/tasks/ingest/asset-curate.d.ts +13 -0
  152. package/dist/llm/tasks/ingest/asset-curate.js +45 -0
  153. package/dist/llm/tasks/ingest/asset-curate.js.map +1 -0
  154. package/dist/llm/tasks/ingest/asset-extract.d.ts +12 -0
  155. package/dist/llm/tasks/ingest/asset-extract.js +51 -0
  156. package/dist/llm/tasks/ingest/asset-extract.js.map +1 -0
  157. package/dist/llm/tasks/ingest/asset-govern.d.ts +22 -0
  158. package/dist/llm/tasks/ingest/asset-govern.js +143 -0
  159. package/dist/llm/tasks/ingest/asset-govern.js.map +1 -0
  160. package/dist/llm/tasks/ingest/mention-resolve.d.ts +9 -0
  161. package/dist/llm/tasks/ingest/mention-resolve.js +39 -0
  162. package/dist/llm/tasks/ingest/mention-resolve.js.map +1 -0
  163. package/dist/llm/tasks/ingest/plot-speaker.d.ts +11 -0
  164. package/dist/llm/tasks/ingest/plot-speaker.js +72 -0
  165. package/dist/llm/tasks/ingest/plot-speaker.js.map +1 -0
  166. package/dist/llm/tasks/ingest/roster-merge.d.ts +46 -0
  167. package/dist/llm/tasks/ingest/roster-merge.js +235 -0
  168. package/dist/llm/tasks/ingest/roster-merge.js.map +1 -0
  169. package/dist/llm/tasks/ingest/state-merge.d.ts +10 -0
  170. package/dist/llm/tasks/ingest/state-merge.js +84 -0
  171. package/dist/llm/tasks/ingest/state-merge.js.map +1 -0
  172. package/dist/llm/tasks/ingest/state-reconcile.d.ts +8 -0
  173. package/dist/llm/tasks/ingest/state-reconcile.js +44 -0
  174. package/dist/llm/tasks/ingest/state-reconcile.js.map +1 -0
  175. package/dist/llm/tasks/ingest/text-normalize.d.ts +10 -0
  176. package/dist/llm/tasks/ingest/text-normalize.js +89 -0
  177. package/dist/llm/tasks/ingest/text-normalize.js.map +1 -0
  178. package/dist/llm/tasks/ingest/video-correct.d.ts +30 -0
  179. package/dist/llm/tasks/ingest/video-correct.js +144 -0
  180. package/dist/llm/tasks/ingest/video-correct.js.map +1 -0
  181. package/dist/llm/tasks/ingest/video-transcribe.d.ts +4 -0
  182. package/dist/llm/tasks/ingest/video-transcribe.js +105 -0
  183. package/dist/llm/tasks/ingest/video-transcribe.js.map +1 -0
  184. package/dist/llm/tasks/schemas.d.ts +5 -0
  185. package/dist/llm/tasks/schemas.js +107 -0
  186. package/dist/llm/tasks/schemas.js.map +1 -0
  187. package/dist/llm/types.d.ts +97 -0
  188. package/dist/llm/types.js +13 -0
  189. package/dist/llm/types.js.map +1 -0
  190. package/dist/usecases/doctor.d.ts +1 -1
  191. package/dist/usecases/doctor.js +36 -9
  192. package/dist/usecases/doctor.js.map +1 -1
  193. package/dist/usecases/ingest/command.d.ts +5 -0
  194. package/dist/usecases/ingest/command.js +57 -0
  195. package/dist/usecases/ingest/command.js.map +1 -0
  196. package/dist/usecases/ingest/fanout.d.ts +10 -0
  197. package/dist/usecases/ingest/fanout.js +23 -0
  198. package/dist/usecases/ingest/fanout.js.map +1 -0
  199. package/dist/usecases/ingest/pass4-mention-resolve.d.ts +11 -0
  200. package/dist/usecases/ingest/pass4-mention-resolve.js +52 -0
  201. package/dist/usecases/ingest/pass4-mention-resolve.js.map +1 -0
  202. package/dist/usecases/ingest/pass5-extract.d.ts +7 -0
  203. package/dist/usecases/ingest/pass5-extract.js +50 -0
  204. package/dist/usecases/ingest/pass5-extract.js.map +1 -0
  205. package/dist/usecases/ingest/pass5-govern.d.ts +13 -0
  206. package/dist/usecases/ingest/pass5-govern.js +155 -0
  207. package/dist/usecases/ingest/pass5-govern.js.map +1 -0
  208. package/dist/usecases/ingest/pipeline.d.ts +28 -0
  209. package/dist/usecases/ingest/pipeline.js +144 -0
  210. package/dist/usecases/ingest/pipeline.js.map +1 -0
  211. package/dist/usecases/ingest/publish-command.d.ts +4 -0
  212. package/dist/usecases/ingest/publish-command.js +77 -0
  213. package/dist/usecases/ingest/publish-command.js.map +1 -0
  214. package/dist/usecases/ingest/status.d.ts +39 -0
  215. package/dist/usecases/ingest/status.js +155 -0
  216. package/dist/usecases/ingest/status.js.map +1 -0
  217. package/dist/usecases/ingest/video-pass3.d.ts +26 -0
  218. package/dist/usecases/ingest/video-pass3.js +0 -0
  219. package/dist/usecases/ingest/video-pass3.js.map +1 -0
  220. package/dist/usecases/ingest/video-pipeline.d.ts +32 -0
  221. package/dist/usecases/ingest/video-pipeline.js +202 -0
  222. package/dist/usecases/ingest/video-pipeline.js.map +1 -0
  223. package/dist/usecases/ingest/view-command.d.ts +4 -0
  224. package/dist/usecases/ingest/view-command.js +51 -0
  225. package/dist/usecases/ingest/view-command.js.map +1 -0
  226. package/dist/usecases/script/actions.js +2 -2
  227. package/dist/usecases/script/actions.js.map +1 -1
  228. package/dist/usecases/script/actor.js.map +1 -1
  229. package/dist/usecases/script/actors.js.map +1 -1
  230. package/dist/usecases/script/add-actor.js.map +1 -1
  231. package/dist/usecases/script/add-episode.js.map +1 -1
  232. package/dist/usecases/script/add-location.js.map +1 -1
  233. package/dist/usecases/script/add-prop.js.map +1 -1
  234. package/dist/usecases/script/alias.js.map +1 -1
  235. package/dist/usecases/script/assets.js.map +1 -1
  236. package/dist/usecases/script/context.js.map +1 -1
  237. package/dist/usecases/script/create.js +3 -6
  238. package/dist/usecases/script/create.js.map +1 -1
  239. package/dist/usecases/script/delete.js.map +1 -1
  240. package/dist/usecases/script/describe.js.map +1 -1
  241. package/dist/usecases/script/dialogue.js +6 -13
  242. package/dist/usecases/script/dialogue.js.map +1 -1
  243. package/dist/usecases/script/do.js +1 -1
  244. package/dist/usecases/script/do.js.map +1 -1
  245. package/dist/usecases/script/episodes.js.map +1 -1
  246. package/dist/usecases/script/{add-speaker.d.ts → importance.d.ts} +1 -1
  247. package/dist/usecases/script/importance.js +17 -0
  248. package/dist/usecases/script/importance.js.map +1 -0
  249. package/dist/usecases/script/insert.js.map +1 -1
  250. package/dist/usecases/script/issues.js.map +1 -1
  251. package/dist/usecases/script/lib.d.ts +8 -6
  252. package/dist/usecases/script/lib.js +81 -97
  253. package/dist/usecases/script/lib.js.map +1 -1
  254. package/dist/usecases/script/locations.js.map +1 -1
  255. package/dist/usecases/script/merge.js.map +1 -1
  256. package/dist/usecases/script/move.js.map +1 -1
  257. package/dist/usecases/script/props.js.map +1 -1
  258. package/dist/usecases/script/refs.js.map +1 -1
  259. package/dist/usecases/script/rename.js.map +1 -1
  260. package/dist/usecases/script/replace.js.map +1 -1
  261. package/dist/usecases/script/role.js.map +1 -1
  262. package/dist/usecases/script/scenes.js +8 -15
  263. package/dist/usecases/script/scenes.js.map +1 -1
  264. package/dist/usecases/script/script-patch.js +1 -1
  265. package/dist/usecases/script/script-patch.js.map +1 -1
  266. package/dist/usecases/script/script-validate.js +1 -1
  267. package/dist/usecases/script/script-validate.js.map +1 -1
  268. package/dist/usecases/script/session.d.ts +4 -4
  269. package/dist/usecases/script/session.js +57 -78
  270. package/dist/usecases/script/session.js.map +1 -1
  271. package/dist/usecases/script/split.js.map +1 -1
  272. package/dist/usecases/script/state-add.js.map +1 -1
  273. package/dist/usecases/script/state-delete.js.map +1 -1
  274. package/dist/usecases/script/state-rename.js.map +1 -1
  275. package/dist/usecases/script/summary.js.map +1 -1
  276. package/dist/usecases/script/synopsis-generate.d.ts +4 -5
  277. package/dist/usecases/script/synopsis-generate.js +30 -46
  278. package/dist/usecases/script/synopsis-generate.js.map +1 -1
  279. package/dist/usecases/script/transition.js.map +1 -1
  280. package/dist/usecases/script/type.js.map +1 -1
  281. package/dist/usecases/script/worldview.js.map +1 -1
  282. package/package.json +22 -17
  283. package/skills/scriptctl/SKILL.md +140 -120
  284. package/skills/scriptctl/references/atomic-write-workflow.md +12 -9
  285. package/skills/scriptctl/references/ingest-workflow.md +85 -0
  286. package/skills/scriptctl/references/state-reference-repair.md +26 -39
  287. package/dist/domain/direct/runner.d.ts +0 -25
  288. package/dist/domain/direct/runner.js +0 -88
  289. package/dist/domain/direct/runner.js.map +0 -1
  290. package/dist/domain/direct/stage.d.ts +0 -108
  291. package/dist/domain/direct/stage.js +0 -134
  292. package/dist/domain/direct/stage.js.map +0 -1
  293. package/dist/domain/direct/stages/asset-curation.d.ts +0 -2
  294. package/dist/domain/direct/stages/asset-curation.js +0 -10
  295. package/dist/domain/direct/stages/asset-curation.js.map +0 -1
  296. package/dist/domain/direct/stages/batch-extract.d.ts +0 -2
  297. package/dist/domain/direct/stages/batch-extract.js +0 -12
  298. package/dist/domain/direct/stages/batch-extract.js.map +0 -1
  299. package/dist/domain/direct/stages/batch-plan.d.ts +0 -2
  300. package/dist/domain/direct/stages/batch-plan.js +0 -9
  301. package/dist/domain/direct/stages/batch-plan.js.map +0 -1
  302. package/dist/domain/direct/stages/episode-merge.d.ts +0 -2
  303. package/dist/domain/direct/stages/episode-merge.js +0 -11
  304. package/dist/domain/direct/stages/episode-merge.js.map +0 -1
  305. package/dist/domain/direct/stages/episode-plan.d.ts +0 -2
  306. package/dist/domain/direct/stages/episode-plan.js +0 -9
  307. package/dist/domain/direct/stages/episode-plan.js.map +0 -1
  308. package/dist/domain/direct/stages/episode-synopsis.d.ts +0 -2
  309. package/dist/domain/direct/stages/episode-synopsis.js +0 -12
  310. package/dist/domain/direct/stages/episode-synopsis.js.map +0 -1
  311. package/dist/domain/direct/stages/episode-titles.d.ts +0 -2
  312. package/dist/domain/direct/stages/episode-titles.js +0 -12
  313. package/dist/domain/direct/stages/episode-titles.js.map +0 -1
  314. package/dist/domain/direct/stages/index.d.ts +0 -19
  315. package/dist/domain/direct/stages/index.js +0 -41
  316. package/dist/domain/direct/stages/index.js.map +0 -1
  317. package/dist/domain/direct/stages/metadata.d.ts +0 -2
  318. package/dist/domain/direct/stages/metadata.js +0 -11
  319. package/dist/domain/direct/stages/metadata.js.map +0 -1
  320. package/dist/domain/direct/stages/script-merge.d.ts +0 -2
  321. package/dist/domain/direct/stages/script-merge.js +0 -15
  322. package/dist/domain/direct/stages/script-merge.js.map +0 -1
  323. package/dist/domain/direct/stages/script-synopsis.d.ts +0 -2
  324. package/dist/domain/direct/stages/script-synopsis.js +0 -12
  325. package/dist/domain/direct/stages/script-synopsis.js.map +0 -1
  326. package/dist/domain/direct/stages/source-prepare.d.ts +0 -2
  327. package/dist/domain/direct/stages/source-prepare.js +0 -9
  328. package/dist/domain/direct/stages/source-prepare.js.map +0 -1
  329. package/dist/domain/direct/stages/state-binding.d.ts +0 -2
  330. package/dist/domain/direct/stages/state-binding.js +0 -9
  331. package/dist/domain/direct/stages/state-binding.js.map +0 -1
  332. package/dist/domain/direct/stages/state-curation.d.ts +0 -2
  333. package/dist/domain/direct/stages/state-curation.js +0 -10
  334. package/dist/domain/direct/stages/state-curation.js.map +0 -1
  335. package/dist/domain/direct/stages/validate.d.ts +0 -2
  336. package/dist/domain/direct/stages/validate.js +0 -10
  337. package/dist/domain/direct/stages/validate.js.map +0 -1
  338. package/dist/domain/direct-core.d.ts +0 -313
  339. package/dist/domain/direct-core.js +0 -8514
  340. package/dist/domain/direct-core.js.map +0 -1
  341. package/dist/domain/script/validate.d.ts +0 -22
  342. package/dist/domain/script/validate.js +0 -997
  343. package/dist/domain/script/validate.js.map +0 -1
  344. package/dist/infra/providers.d.ts +0 -97
  345. package/dist/infra/providers.js +0 -1396
  346. package/dist/infra/providers.js.map +0 -1
  347. package/dist/usecases/direct.d.ts +0 -27
  348. package/dist/usecases/direct.js +0 -4172
  349. package/dist/usecases/direct.js.map +0 -1
  350. package/dist/usecases/parse.d.ts +0 -15
  351. package/dist/usecases/parse.js +0 -429
  352. package/dist/usecases/parse.js.map +0 -1
  353. package/dist/usecases/script/add-speaker.js +0 -22
  354. package/dist/usecases/script/add-speaker.js.map +0 -1
  355. package/dist/usecases/script/export.d.ts +0 -3
  356. package/dist/usecases/script/export.js +0 -111
  357. package/dist/usecases/script/export.js.map +0 -1
  358. package/dist/usecases/script/overlap.d.ts +0 -3
  359. package/dist/usecases/script/overlap.js +0 -21
  360. package/dist/usecases/script/overlap.js.map +0 -1
  361. package/dist/usecases/script/speakers.d.ts +0 -3
  362. package/dist/usecases/script/speakers.js +0 -28
  363. package/dist/usecases/script/speakers.js.map +0 -1
  364. package/dist/usecases/script/state-change.d.ts +0 -3
  365. package/dist/usecases/script/state-change.js +0 -24
  366. package/dist/usecases/script/state-change.js.map +0 -1
  367. package/skills/scriptctl/references/direct-workflow.md +0 -72
@@ -0,0 +1,235 @@
1
+ // Video roster merge (§10.2 pass3, ported from cezhi-verify 03b). Cross-episode
2
+ // identity resolution driven by VISUAL ANCHORS — the same character transcribed
3
+ // independently per episode gets different names (本名 vs 职务/外貌 vs 听错), and
4
+ // only anchor + role continuity can re-unify them. Four stages, all VERBATIM from
5
+ // the tuned scaffold prompts:
6
+ // A recall — one global pass, recall-first, over-cluster candidate groups
7
+ // B refine — per candidate group, split mis-merges + pick canonical
8
+ // C canon — pick a canonical name for a confirmed cluster (本名优先)
9
+ // D remerge — global recompose, merge-only, recover B's conservative splits
10
+ // Structured output via forced tool calls; the tool-input JSON schemas carry the
11
+ // scaffold's field descriptions verbatim (they are tuned prompt content).
12
+ function joinEvidence(xs, max = 4) {
13
+ const v = xs.map((s) => s.trim()).filter(Boolean).slice(0, max);
14
+ return v.length ? v.join(" / ") : "无";
15
+ }
16
+ function evidenceBlock(m) {
17
+ return `- 「${m.name}」 ep[${m.episodes.join(",")}]\n 锚点: ${joinEvidence(m.anchors)}\n 作用: ${joinEvidence(m.roles, 3)}\n 外观: ${joinEvidence(m.appearances, 3)}`;
18
+ }
19
+ // ===== Stage A: recall (high recall candidate grouping) =====
20
+ export const RECALL_SYSTEM = "你是长篇剧集人物花名册的『候选粗聚』器,是两阶段归并的第一步。" +
21
+ "同一角色跨集常被记成不同名字(听到本名 vs 按职务/外貌称呼、听错写成近音字、某集认错人)。" +
22
+ "你会拿到完整花名册,每个名字带跨集锚点、外观、剧情作用、出现集号。" +
23
+ "任务:把**可能是同一个人**的名字聚成候选组。" +
24
+ "**这一步召回优先**:后面有专门的精判环节会逐组复核、把不该合的拆出去,所以你现在宁可多合、别漏——" +
25
+ "只要两个名字在视觉锚点或剧情身份上**有合理的同人可能**,就放进同一候选组,哪怕不完全确定。" +
26
+ "唯一的硬边界:锚点直接冲突(性别不同、年龄段明显不符)的绝不放同组。" +
27
+ "canonical 这一步随便选组内一个名字即可(后面会重定)。每组至少 2 个名字才有意义。";
28
+ // MergeDecisionSchema (recall reuses it) — verbatim field descriptions.
29
+ const MERGE_DECISION_SCHEMA = {
30
+ type: "object",
31
+ additionalProperties: false,
32
+ required: ["groups"],
33
+ properties: {
34
+ groups: {
35
+ type: "array",
36
+ description: "每组=一个真实人物+其所有别名。只输出确实需要合并(至少 1 个别名)的组;独一无二的人物不必输出",
37
+ items: {
38
+ type: "object",
39
+ additionalProperties: false,
40
+ required: ["canonical", "aliases", "reason"],
41
+ properties: {
42
+ canonical: { type: "string", description: "这一组同人的规范名。优先用有名有姓的本名,其次用信息最完整的称呼;避免用纯职务/敬称/临时描述作为规范名(除非整组都没有本名)" },
43
+ aliases: { type: "array", items: { type: "string" }, description: "与 canonical 确认为同一个人的其他名字(不含 canonical)。只放视觉锚点确实吻合的" },
44
+ reason: { type: "string", description: "判定同人的依据:视觉锚点/剧情作用/出现集,一句话" },
45
+ },
46
+ },
47
+ },
48
+ },
49
+ };
50
+ export function mergePrompt(actors) {
51
+ const lines = actors.map(evidenceBlock).join("\n");
52
+ return `下面是整部剧的完整人物花名册(${actors.length} 个名字)。找出所有『其实是同一个人』的名字并分组:
53
+
54
+ ${lines}
55
+
56
+ 要求:
57
+ - 只输出需要合并的组(每组至少 1 个别名);独一无二的人物不用输出。
58
+ - 每组选 canonical(优先本名,不用职务/敬称),其余同人放 aliases。
59
+ - 判据是视觉锚点吻合 + 剧情作用连续;名字字面是否相同不重要。
60
+ - 锚点确实吻合就合,别因别名多而犹豫;真拿不准(锚点冲突或信息太少)才保守不合。
61
+ - 尤其:两个名字都只出现在很少几集、锚点又都很泛时,证据不足,倾向不合。
62
+ 严格按 schema 返回。`;
63
+ }
64
+ // ===== Stage B: refine (per-group high-precision split) =====
65
+ export const REFINE_SYSTEM = "你是人物同人判定的『精判』器,是两阶段归并的第二步。" +
66
+ "上一步用宽松口径把一批**可能同人**的名字聚成了一个候选组。你的任务:只看这一个候选组,把它**拆成若干真正是同一个人的子簇**。" +
67
+ "判据**同等看重两类**:①视觉锚点(发型/发色/脸型五官/体型/年龄段/性别/疤痣眼镜胡须配饰等标志特征);②剧情身份(所属队伍/组织/阵营/职务定位、与主角的关系、出场场合)。" +
68
+ "**关键:外貌像 ≠ 同人。** 一部剧里常有多个外貌高度相似、只能靠身份区分的角色(例如同类职务的一批人:几个平头运动服的男教练、一批花白头发西装的中老年高管、几个同款球衣的球员)——" +
69
+ "这些人视觉锚点几乎一样,光看外貌必然误合。**必须先看剧情身份:所属不同(不同队/不同组织/不同阵营/不同场合)、或角色定位矛盾,就是不同的人,哪怕长相一模一样也绝不合。**" +
70
+ "**核心心态:拆和合两个方向的错误一样有害——既不能把不同人误合,也不能把同一个人拆散。** " +
71
+ "拆的唯一正当理由是『存在真实矛盾』(身份冲突或锚点冲突);**没有矛盾就必须合,别因为名字字面不同就拆。** " +
72
+ "候选组本就是同一角色跨集被记成不同名字的高发结果——一个人在不同集常被同时叫本名、职务、编号、外貌描述、临时称呼(例如同一个『某号+某发型』的配角,可能分别被记成本名、『X号队员』、『某发型队员』、『队长』等),别名多、叫法杂是正常现象,不是拆的理由。" +
73
+ "原则:" +
74
+ "① **身份一致 + 锚点吻合 → 必须合,这是硬性要求**:只要几个名字的所属(同一支队伍/组织)、角色定位、以及具体视觉锚点(如同一组『编号球衣+发型+配饰』特征)都对得上,就是同一个人,**哪怕一个是编号称呼、一个是职务称呼、一个是本名、一个是外貌描述,也必须合进一簇。名字字面不同绝不是拆的理由。**" +
75
+ "② **只在真实矛盾时才拆**:(a)身份冲突——分属对立的不同队伍/组织/阵营、角色定位对立(一个主角亲信一个对手阵营)、活动场合完全不同;(b)锚点冲突——性别不同、年龄段明显不符、发型/脸型明显对不上(如一个大背头一个寸头)。**有明确的这类矛盾才拆,没有就合。**" +
76
+ "③ 身份冲突时优先级高于外貌像:不同队/不同阵营的两个人,即使发型脸型服装都像也必须拆(防止把对立两方的相似角色误合)。" +
77
+ "④ 拆一个名字前先自问:它和簇里其他人,有哪一条身份或锚点是**明确冲突**的?答不上来(只是名字不同、或描述简单)就**不许拆**。" +
78
+ "⑤ 同名不一定同人:字面相同但身份/锚点明确冲突,才是两个人。" +
79
+ "⑥ canonical 强制本名优先(见 schema)。";
80
+ const REFINE_DECISION_SCHEMA = {
81
+ type: "object",
82
+ additionalProperties: false,
83
+ required: ["clusters"],
84
+ properties: {
85
+ clusters: {
86
+ type: "array",
87
+ description: "把输入候选组拆成若干真正同人的子簇。不该合的名字各自单独成簇(members 只含自己)。",
88
+ items: {
89
+ type: "object",
90
+ additionalProperties: false,
91
+ required: ["canonical", "members", "reason"],
92
+ properties: {
93
+ canonical: { type: "string", description: "这个子簇的规范名。**强制本名优先**:只要簇内有一个是像本名的(有名有姓/像人名),就必须选它,不要选职务('X总''董事长''助理')、外貌('黑衣女子')、临时称呼。全簇都没本名时才退而用信息最全的称呼" },
94
+ members: { type: "array", items: { type: "string" }, description: "确认是同一个人的名字(含 canonical 本身)。至少 1 个" },
95
+ reason: { type: "string", description: "为什么这些是同人、以及(若有)为什么把某些名字排除出去,一句话" },
96
+ },
97
+ },
98
+ },
99
+ },
100
+ };
101
+ export function refinePrompt(members) {
102
+ const lines = members.map(evidenceBlock).join("\n");
103
+ return `上一步把下面这 ${members.length} 个名字聚成了一个「可能同人」候选组。请复核,把它拆成真正是同一个人的子簇:
104
+
105
+ ${lines}
106
+
107
+ 要求:
108
+ - **判同人前先对身份**:看每个名字的「作用」——所属队伍/组织/阵营、职务定位、与主角关系。身份一致再看外貌是否吻合。
109
+ - **身份不同就拆,哪怕外貌一样**:分属对立两方、角色定位矛盾、活动场合完全不同的,是不同的人,即使发型脸型服装都像也必须拆开。(尤其一批同类职务、同款服装的相似角色,必须靠身份区分,别因为外貌像就合。)
110
+ - 身份一致 + 外貌吻合的若干称呼(哪怕措辞略有出入、一个本名一个职务、只出现一集),属于同人,合。
111
+ - 每个子簇选 canonical:**本名优先**,有本名就用本名,别用职务/外貌/临时称呼。
112
+ - 覆盖输入的每一个名字:每个名字必须恰好出现在一个子簇里。
113
+ 严格按 schema 返回。`;
114
+ }
115
+ // ===== Stage C: canonical selection for a confirmed cluster =====
116
+ export const CANON_SYSTEM = "你要为『同一个人物的一组不同称呼』挑一个规范名(canonical)。这些名字已确认是同一个人。" +
117
+ "挑选优先级:① 有名有姓、像真实姓名的(如「张伟」「李文博」)最优先;" +
118
+ "② 其次是含姓的称呼(如「张总」「李董」);③ 最不优先的是纯职务/身份/外貌/占位描述(如「助理」「秘书」「黑衣女子」「前上司」「男员工」)和英文/编号名。" +
119
+ "在同一优先级里,选出现集数多、信息更完整的。" +
120
+ "只能从给定列表里原样挑一个,不要创造新名字。";
121
+ const CANON_SCHEMA = {
122
+ type: "object",
123
+ additionalProperties: false,
124
+ required: ["canonical"],
125
+ properties: {
126
+ canonical: { type: "string", description: "从给定名字里挑一个当规范名。**必须原样是列表中的某个名字**,不要改写、不要合成新名。" },
127
+ },
128
+ };
129
+ export function canonPrompt(members) {
130
+ const lines = members.map((m) => `- ${m.name}(出现 ${m.episodes.length} 集)`).join("\n");
131
+ return `这些名字是同一个人的不同称呼,挑一个当规范名(本名优先,其次含姓称呼,最后才用职务/描述名):\n\n${lines}\n\n只从上面原样选一个,按 schema 返回。`;
132
+ }
133
+ // ===== Stage D: global remerge (merge-only) =====
134
+ export const REMERGE_SYSTEM = "你是人物花名册的『全局复合』器,是归并的最后一步。前面的步骤已把名字聚成了一批规范名,每个规范名代表一个人。" +
135
+ "但前面的精判偏保守(宁可拆开也别误合),可能把**同一个人**拆成了几个独立规范名——" +
136
+ "例如同一个角色,在不同集分别被记成本名、编号称呼、职务、外貌描述,前面没能全部并到一起,就散成了好几个条目。" +
137
+ "你的**唯一任务**:通读全部规范名 + 它们的视觉锚点、剧情作用、出现集,找出其中**其实是同一个人**的,把它们合并。" +
138
+ "**只做合并,绝不拆分**:你不能把任何一个规范名拆开,只能指出'这几个规范名是同一个人、应合成一个'。" +
139
+ "判据:①所属/身份一致(同一支队伍/组织、同一角色定位、同一关系);②视觉锚点吻合(同一组具体特征:发型+配饰+服装编号等)。两者都对得上才合。" +
140
+ "铁律:" +
141
+ "① **只合确实同人的**:所属身份一致 + 锚点吻合,才判同人合并。典型可合信号:同一批具体特征(如同号球衣+同发型+同配饰)反复出现,只是被记成了不同称呼(本名/编号/职务/外貌)。" +
142
+ "② **身份或锚点冲突的绝不合**:分属对立的不同队伍/组织/阵营、角色定位相反、或外貌锚点直接冲突(性别不同、一个背头一个寸头、年龄段明显不符)——这些是不同的人,即使名字或球衣号相近也不能合。" +
143
+ "③ 拿不准就**不合**:只输出你有把握是同一人的合并;宁可漏合,不可错合。" +
144
+ "④ into 与 names 里的每个名字,都必须原样是给定列表中的规范名,不要造新名。";
145
+ const REMERGE_SCHEMA = {
146
+ type: "object",
147
+ additionalProperties: false,
148
+ required: ["merges"],
149
+ properties: {
150
+ merges: {
151
+ type: "array",
152
+ description: "需要再合并的组。只输出**确实是同一个人被拆散**的组;拿不准就不要合。没有要合的返回空数组。",
153
+ items: {
154
+ type: "object",
155
+ additionalProperties: false,
156
+ required: ["into", "names", "reason"],
157
+ properties: {
158
+ into: { type: "string", description: "合并后保留的规范名(本名优先;从这一组里挑最像本名的,或出现集数最多的)。必须原样是给定列表中的某个名字。" },
159
+ names: { type: "array", items: { type: "string" }, description: "要合并到 into 的其他规范名(不含 into 自身),至少 1 个。必须都是给定列表中的名字。" },
160
+ reason: { type: "string", description: "为什么这些是同一个人:所属/身份 + 视觉锚点如何吻合,一句话。" },
161
+ },
162
+ },
163
+ },
164
+ },
165
+ };
166
+ export function remergePrompt(entries) {
167
+ const lines = entries.map((e) => {
168
+ const anc = joinEvidence(e.anchors, 3);
169
+ const role = joinEvidence(e.roles, 2);
170
+ return `- 「${e.name}」 ep[${e.episodes.join(",")}] 锚点: ${anc} 作用: ${role}`;
171
+ }).join("\n");
172
+ return `下面是全剧当前的 ${entries.length} 个规范名(每个本应代表一个人)。请找出其中**其实是同一个人、被拆成了多个规范名**的,合并它们:
173
+
174
+ ${lines}
175
+
176
+ 要求:
177
+ - 只合并**确实是同一个人**的规范名:所属/身份一致 + 视觉锚点吻合,才合。
178
+ - 分属不同队伍/组织/阵营、角色定位对立、或外貌锚点冲突(如一个背头一个寸头)的,绝不合。
179
+ - 每组指定 into(合并后保留的名,本名优先)和要并入的其他规范名。
180
+ - 拿不准就不要合。没有要合的返回空 merges 数组。
181
+ 严格按 schema 返回。`;
182
+ }
183
+ // ===== run helpers (forced tool call → structured object) =====
184
+ const asList = (v) => (Array.isArray(v) ? v : []);
185
+ const str = (v) => (typeof v === "string" ? v : "");
186
+ const strList = (v) => asList(v).map(str).filter(Boolean);
187
+ function tool(name, description, schema) {
188
+ return { name, description, inputSchema: schema };
189
+ }
190
+ export async function runRosterRecall(executor, entries, signal) {
191
+ const res = await executor.generateTool("ingest.roster-recall", {
192
+ system: RECALL_SYSTEM,
193
+ user: mergePrompt(entries),
194
+ tool: tool("submit_groups", "提交同人候选分组", MERGE_DECISION_SCHEMA),
195
+ ...(signal ? { signal } : {}),
196
+ });
197
+ const input = res.input;
198
+ return asList(input?.["groups"]).map((g) => {
199
+ const o = g;
200
+ return { canonical: str(o["canonical"]), aliases: strList(o["aliases"]), reason: str(o["reason"]) };
201
+ });
202
+ }
203
+ export async function runRosterRefine(executor, members, signal) {
204
+ const res = await executor.generateTool("ingest.roster-refine", {
205
+ system: REFINE_SYSTEM,
206
+ user: refinePrompt(members),
207
+ tool: tool("submit_clusters", "提交同人子簇拆分", REFINE_DECISION_SCHEMA),
208
+ ...(signal ? { signal } : {}),
209
+ });
210
+ const input = res.input;
211
+ return asList(input?.["clusters"]).map((c) => strList(c["members"]));
212
+ }
213
+ export async function runRosterCanon(executor, members, signal) {
214
+ const res = await executor.generateTool("ingest.roster-canon", {
215
+ system: CANON_SYSTEM,
216
+ user: canonPrompt(members),
217
+ tool: tool("submit_canonical", "提交规范名", CANON_SCHEMA),
218
+ ...(signal ? { signal } : {}),
219
+ });
220
+ return str(res.input?.["canonical"]);
221
+ }
222
+ export async function runRosterRemerge(executor, entries, signal) {
223
+ const res = await executor.generateTool("ingest.roster-remerge", {
224
+ system: REMERGE_SYSTEM,
225
+ user: remergePrompt(entries),
226
+ tool: tool("submit_merges", "提交需再合并的规范名组", REMERGE_SCHEMA),
227
+ ...(signal ? { signal } : {}),
228
+ });
229
+ const input = res.input;
230
+ return asList(input?.["merges"]).map((m) => {
231
+ const o = m;
232
+ return { into: str(o["into"]), names: strList(o["names"]), reason: str(o["reason"]) };
233
+ });
234
+ }
235
+ //# sourceMappingURL=roster-merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roster-merge.js","sourceRoot":"","sources":["../../../../src/llm/tasks/ingest/roster-merge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { LlmTaskExecutor } from "../../types.js";
2
+ export declare const STATE_MERGE_SYSTEM: string;
3
+ export declare function stateMergePrompt(personName: string, states: Array<{
4
+ name: string;
5
+ count: number;
6
+ }>): string;
7
+ export declare function runStateMerge(executor: LlmTaskExecutor, person: string, states: Array<{
8
+ name: string;
9
+ count: number;
10
+ }>, signal?: AbortSignal): Promise<Record<string, string>>;
@@ -0,0 +1,84 @@
1
+ // Video state merge (§10.2 pass3c, ported from cezhi-verify 03c). Per character,
2
+ // collapse the many free-form outfit wordings (Gemini describes the same outfit
3
+ // differently each episode) into canonical states. Distinct from govern's
4
+ // state-reconcile: this dedups the wording fragmentation before convergence;
5
+ // govern later selects default + drops transient. Prompt is VERBATIM from the
6
+ // tuned scaffold; the tool-input schema carries its field descriptions verbatim.
7
+ export const STATE_MERGE_SYSTEM = "你是剧集人物『造型状态』的归并器。同一个角色在不同镜头/不同集里穿的**同一套衣服**," +
8
+ "常被逐集自由描述成很多不同措辞——有的写全(连颜色、款式、编号都写上),有的只写个大概(只有『白衣』『球服』这种笼统类别)," +
9
+ "还有的把颜色/款式说岔(把某个颜色写成邻近色、把一种服装误认成另一种)。" +
10
+ "你的任务:只看**一个人物**的全部状态措辞,把其中**其实是同一套装扮**的合并成一组,每组选一个规范状态名。" +
11
+ "核心判据:**是不是同一套服装造型**(同一件/同一套衣服),而不是颜色字面像不像。" +
12
+ "原则:" +
13
+ "① 同一套的不同繁简说法要合:同一套衣服,有的措辞带全颜色+款式+编号、有的只写大类——只要都指这人那一套,就合成一组,canonical 取信息最完整的。" +
14
+ "② 明显的口误/误认要并进对应造型:把某类服装误写成相近的另一类、把某颜色写成邻近色调,只要指的是同一套,就并进去。" +
15
+ "③ **不同造型绝不合**:不同颜色/不同主色的两套(如一套浅色、一套深色)、正装 vs 便装、正常 vs 受伤/特殊状态,是不同造型,各自成组。" +
16
+ "④ 拿不准是不是同一套时,宁可**不合**、让它单独成组——错合(把两套并成一套)比漏合更糟。" +
17
+ "⑤ canonical 必须原样是组内出现过的措辞,不要合成新词、不要改字。" +
18
+ "⑥ 覆盖每一个输入措辞:每个措辞必须恰好属于一个组(哪怕它独自成组)。";
19
+ const STATE_MERGE_SCHEMA = {
20
+ type: "object",
21
+ additionalProperties: false,
22
+ required: ["groups"],
23
+ properties: {
24
+ groups: {
25
+ type: "array",
26
+ description: "把输入的状态措辞分组。每组=同一套造型的不同说法。只指同一套装扮的才归一组;不同颜色/不同款式/不同场合的造型各自单独成组。",
27
+ items: {
28
+ type: "object",
29
+ additionalProperties: false,
30
+ required: ["canonical", "members", "reason"],
31
+ properties: {
32
+ canonical: { type: "string", description: "这一组的规范状态名。从组内措辞里挑**信息最完整、最清晰**的那个:同时含颜色+款式+编号/标识细节的,优于只有笼统类别的(信息全的一版优于「白衣」「运动服」这种大概说法)。必须原样是组内出现过的某个措辞,不要新造。" },
33
+ members: { type: "array", items: { type: "string" }, description: "确认是**同一套服装造型**的所有措辞(含 canonical 自身)。只放真正指同一套的。" },
34
+ reason: { type: "string", description: "为什么这些是同一套造型,一句话(如:都是同一套主色球衣,只是措辞繁简不同)。" },
35
+ },
36
+ },
37
+ },
38
+ },
39
+ };
40
+ export function stateMergePrompt(personName, states) {
41
+ const lines = states.map((s) => `- 「${s.name}」(出现 ${s.count} 场)`).join("\n");
42
+ return `下面是角色「${personName}」在全剧里被记录到的所有造型状态措辞。请把其中**其实是同一套装扮**的合并分组:
43
+
44
+ ${lines}
45
+
46
+ 要求:
47
+ - 判据是「是不是同一套服装造型」,不是颜色字面像不像。
48
+ - 同一套衣服的繁简/口误/误认说法合成一组,canonical 取信息最完整清晰的那个措辞(原样照抄,别新造)。
49
+ - 不同颜色/款式/场合的造型(如一套浅色装 vs 一套深色装 vs 正装 vs 受伤/特殊状态)各自单独成组。
50
+ - 拿不准就让它单独成组,别硬合。
51
+ - 覆盖每一个措辞:每个措辞恰好属于一个组。
52
+ 严格按 schema 返回。`;
53
+ }
54
+ // Returns { wording -> canonical } for the merged (≥2-member) groups only,
55
+ // mirroring the scaffold mergePerson: canonical from LLM if valid, else the
56
+ // highest-count wording; hallucinated members dropped.
57
+ export async function runStateMerge(executor, person, states, signal) {
58
+ const valid = new Set(states.map((s) => s.name));
59
+ const cnt = new Map(states.map((s) => [s.name, s.count]));
60
+ const res = await executor.generateTool("ingest.state-merge", {
61
+ system: STATE_MERGE_SYSTEM,
62
+ user: stateMergePrompt(person, states),
63
+ tool: { name: "submit_state_groups", description: "提交造型状态归并分组", inputSchema: STATE_MERGE_SCHEMA },
64
+ ...(signal ? { signal } : {}),
65
+ });
66
+ const groups = Array.isArray(res.input?.["groups"]) ? res.input["groups"] : [];
67
+ const map = {};
68
+ for (const g of groups) {
69
+ const o = g;
70
+ const membersRaw = Array.isArray(o["members"]) ? o["members"] : [];
71
+ const mem = membersRaw.map((m) => (typeof m === "string" ? m : "")).filter((m, i, a) => valid.has(m) && a.indexOf(m) === i);
72
+ if (mem.length < 2)
73
+ continue;
74
+ const canonRaw = typeof o["canonical"] === "string" ? o["canonical"] : "";
75
+ let canon = valid.has(canonRaw) && mem.includes(canonRaw) ? canonRaw : "";
76
+ if (!canon)
77
+ canon = mem.reduce((b, s) => ((cnt.get(s) ?? 0) > (cnt.get(b) ?? 0) ? s : b), mem[0]);
78
+ for (const s of mem)
79
+ if (s !== canon)
80
+ map[s] = canon;
81
+ }
82
+ return map;
83
+ }
84
+ //# sourceMappingURL=state-merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-merge.js","sourceRoot":"","sources":["../../../../src/llm/tasks/ingest/state-merge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { StateGroup } from "../../../domain/ingest/state-reconcile.js";
2
+ import type { LlmTaskExecutor } from "../../types.js";
3
+ export declare const STATE_RECONCILE_SYSTEM: string;
4
+ export declare function parseStateGroups(raw: string, validIds: Set<string>): StateGroup[];
5
+ export declare function runStateReconcile(executor: LlmTaskExecutor, states: Array<{
6
+ id: string;
7
+ name: string;
8
+ }>, signal?: AbortSignal): Promise<StateGroup[]>;
@@ -0,0 +1,44 @@
1
+ // ingest.state-reconcile task (§9.8). Per canonical owner, merge state names
2
+ // that describe the same stable visual appearance. No roster recall — the whole
3
+ // owner's states go to the model at once. Emotion/pose/transient states should
4
+ // already be absent; the model only groups equivalent durable looks.
5
+ import { generateOptional } from "./asset-govern.js";
6
+ export const STATE_RECONCILE_SYSTEM = [
7
+ "你在给某个角色/地点/道具**筛选并归并稳定外观造型**。给你它名下全部候选状态(每行 <id> | <状态名>)。",
8
+ "目标是得到一小组**互不相同、值得单独出图的外观档位**。",
9
+ "只保留可视**外观造型**(穿什么、发型妆造、形态):",
10
+ "- 保留:整套着装造型(婚服/喜服/常服/黑衣/睡衣/粗布衣裳)、发型、持续性妆造、非人形态/兽化/神化、巨大化、**持续性伤势造型**(满身血污、胸口缠绷带这种跨镜头持续的受伤外观)。",
11
+ "- 删除(绝不列出):情绪与神态(悲痛/冰冷/狂喜/惨白/濒死/身姿挺拔)、姿势与动作(坐轮椅/站立/苏醒/昏迷/睡着/被绑/转身/跪地)、位置情境(门外/在牢内/台上)、镜头、瞬时或过程描述(眼眶发红/满嘴油光/嘴角沾米粒/从X变Y)、纯剧情态。",
12
+ "**大胆合并**:描述同一套外观的不同写法要并成一组(各种“受伤/血污/绷带/刀伤”并成一个受伤造型;同一套衣服的不同细节写法并一组)。只有**真正不同的造型**才分开。宁可少而清,别把近义拆成一堆。",
13
+ "canonical 名只能从输入的状态名里选,挑最能代表该造型的那个。",
14
+ "输出:每个保留的外观组一行 `<canonical名>: id1, id2, ...`(单个也要一行);**没列出的 id 一律删除**。",
15
+ "如果没有任何值得保留的稳定外观,就输出一行 `无`。不要输出别的文字。",
16
+ ].join("\n");
17
+ // Each output line is a KEPT durable-state group (canonical + ≥1 member id).
18
+ // Ids not present in any group are dropped by the caller.
19
+ export function parseStateGroups(raw, validIds) {
20
+ const groups = [];
21
+ for (const line of raw.split("\n")) {
22
+ const t = line.trim();
23
+ if (t === "" || t === "无" || t.startsWith("#"))
24
+ continue;
25
+ const m = /^(.+?)\s*[::]\s*(.+)$/.exec(t);
26
+ if (!m)
27
+ continue;
28
+ const ids = [...new Set(m[2].split(/[,,、]/).map((s) => s.trim()).filter((s) => validIds.has(s)))];
29
+ if (ids.length >= 1)
30
+ groups.push({ canonical: m[1].trim(), state_ids: ids });
31
+ }
32
+ return groups;
33
+ }
34
+ export async function runStateReconcile(executor, states, signal) {
35
+ if (states.length < 1)
36
+ return [];
37
+ const text = await generateOptional(executor, "ingest.state-reconcile", {
38
+ system: STATE_RECONCILE_SYSTEM,
39
+ user: states.map((s) => `${s.id} | ${s.name}`).join("\n"),
40
+ ...(signal ? { signal } : {}),
41
+ });
42
+ return parseStateGroups(text, new Set(states.map((s) => s.id)));
43
+ }
44
+ //# sourceMappingURL=state-reconcile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-reconcile.js","sourceRoot":"","sources":["../../../../src/llm/tasks/ingest/state-reconcile.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,gFAAgF;AAChF,+EAA+E;AAC/E,qEAAqE;AAIrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,4DAA4D;IAC5D,+BAA+B;IAC/B,6BAA6B;IAC7B,+FAA+F;IAC/F,8HAA8H;IAC9H,sGAAsG;IACtG,qCAAqC;IACrC,uEAAuE;IACvE,qCAAqC;CACtC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,6EAA6E;AAC7E,0DAA0D;AAC1D,MAAM,UAAU,gBAAgB,CAAC,GAAW,EAAE,QAAqB;IACjE,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACtB,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACzD,MAAM,CAAC,GAAG,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1C,IAAI,CAAC,CAAC;YAAE,SAAS;QACjB,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnG,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,QAAyB,EAAE,MAA2C,EAAE,MAAoB;IAClI,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,QAAQ,EAAE,wBAAwB,EAAE;QACtE,MAAM,EAAE,sBAAsB;QAC9B,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACzD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9B,CAAC,CAAC;IACH,OAAO,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { Chunk } from "../../../domain/ingest/chunker.js";
2
+ import type { IngestRecord } from "../../../domain/ingest/records.js";
3
+ import type { LlmTaskExecutor } from "../../types.js";
4
+ export declare const TEXT_NORMALIZE_SYSTEM: string;
5
+ export declare function buildNormalizeUser(chunk: Chunk): string;
6
+ export declare function parseNormalizeOutput(markdown: string, chunk: Chunk): IngestRecord[];
7
+ export declare function runTextNormalize(executor: LlmTaskExecutor, chunk: Chunk, signal?: AbortSignal): Promise<{
8
+ markdown: string;
9
+ records: IngestRecord[];
10
+ }>;
@@ -0,0 +1,89 @@
1
+ // ingest.text-normalize task (§3.3/§pass2). The cheap model rewrites one source
2
+ // chunk as a lean markdown document; a deterministic parser (markdown-parser.ts)
3
+ // turns that into records (§7) with inline entity tags (§7.6). Presence/coverage
4
+ // is chunk-granular: a non-empty chunk must yield ≥1 record. No repair prompt and
5
+ // no downgrade-to-raw on failure (§12.3).
6
+ import { CliError, EXIT_RUNTIME } from "../../../common.js";
7
+ import { parseNormalizeMarkdown, MarkdownParseError } from "../../../domain/ingest/markdown-parser.js";
8
+ export const TEXT_NORMALIZE_SYSTEM = [
9
+ "你是剧本规范化器。把给定的源文本块改写成一份精简 markdown,严格按源文本发生顺序。",
10
+ "只输出该 markdown 文档:不要代码围栏,前后不要任何多余文字或解释。",
11
+ "",
12
+ "严格保持源文本顺序,绝不重排:块开头若在任何『集/场』标记之前就有正文(上一块没写完的戏),",
13
+ "必须把这些正文**原位放在输出最前面**(不加任何头),`# 第N集`/`## 场景` 只在它在源文本里真正出现的位置输出。",
14
+ "绝不要把靠后出现的 `# 第N集` 提到开头。",
15
+ "",
16
+ "结构(只有源文本里真的出现对应边界时才写,绝不臆造;块可能从一场戏中间开始,那就直接从正文写起):",
17
+ "# 第N集 标题 (出现集边界时;标题可省)",
18
+ "## 场景 (出现新场景/新地点/新时空时)",
19
+ "- 时空: 内 日 (紧跟场景头,可省;内|外 日|夜)",
20
+ "- 地点: 地点名 (紧跟场景头,可省;地点是场级)",
21
+ "- 人物: 甲【状态】, 乙, 丙 (本场在场的具体单人角色;【】内写整套外观状态,可省)",
22
+ "- 道具: 关键道具1, 道具2 (本场需要跨镜头连续性的关键道具;通用/一次性物件不列)",
23
+ "",
24
+ "正文每行一条,按发生顺序。动作/环境描写就是**裸行**(不加任何前缀);只有对白/内心独白/转场/批注需要标记:",
25
+ "动作/环境:直接写一行纯文本,不加前缀(这是默认)",
26
+ "[dlg|说话人] 对白 [dlg|说话人|情绪] 带情绪的对白",
27
+ "[think|说话人] 内心独白/画外音(含 OS/旁白)",
28
+ "[trans] 转场",
29
+ "[note|author] 作者批注 [note|format] 排版说明",
30
+ "",
31
+ "铁律——逐行转换,绝不原样保留源标记。输入往往已是半结构化剧本,把每一行都转成上面的结构:",
32
+ " 源 `场3-2 日/内 客厅` → `## 场景` + `- 时空: 内 日` + `- 地点: 客厅`",
33
+ " 源 `人物:甲【白衣,昏迷】,乙` → `- 人物: 甲【白衣,昏迷】, 乙`",
34
+ " 源 `道具:婚书,玉佩` → `- 道具: 婚书, 玉佩`",
35
+ " 源 `△ 甲推门而入。` → `甲推门而入。`(裸行,动作不加前缀)",
36
+ " 源 `甲:你来了` → `[dlg|甲] 你来了`",
37
+ " 源 `甲(冷笑):你来了` → `[dlg|甲|冷笑] 你来了`",
38
+ " 源 `甲OS:(恐慌)他没死吧?` → `[think|甲|恐慌] 他没死吧?`",
39
+ " 源 `【字幕:回门日】` → `字幕:回门日`(画面文字是动作,裸行)",
40
+ "出现残留的 △ 前缀、裸『名:台词』(该转成 [dlg])就是错的。",
41
+ "",
42
+ "正文口径:**纯文本、逐字保留**,不加任何标注,不润色、不扩写、不纠错、不加戏。人物/道具的在场只写在场景头的 `- 人物`/`- 道具`,不要写进正文。",
43
+ "",
44
+ "- 人物 只列**具体单人角色**;家族/机构/群体/泛称(顾家、大房、林家、众人、随从们、丫鬟们…)绝不列入 - 人物(群体喊话用 group 说话人或写进 act 旁白)。",
45
+ "- 说话人:有名字/有台词/露脸的具体人物一律用角色名作 actor 说话人(如 [dlg|传旨太监]),全剧同一人保持一致;kind 枚举 system|broadcast|offscreen|group 只给真正无实体的声音(画外广播、无法归到具体人的群体喊话)。同一个人不要一会儿 system 一会儿 actor。",
46
+ "- 画面上出现的文字(字幕/标题卡/人物介绍条/时间地点卡)是画面内容,用 > [act] 记录并把文字原样写进正文;与某句对白完全重复的冗余字幕并入对应 [dlg]。",
47
+ "- note 只用于真正无法归入剧本、也不属于画面/对白的内容;能归入 act/dlg 的绝不用 note。",
48
+ "源文本块的实质内容不得丢失。",
49
+ ].join("\n");
50
+ export function buildNormalizeUser(chunk) {
51
+ // Do NOT expose the chunk index — the model conflates a chunk number with an
52
+ // episode number (`#15` → `# 第15集`). Chunk source spans are assigned by the
53
+ // parser, not the model.
54
+ return ["【待规范化的原文片段(可能从一场戏中间开始)】", "", chunk.text].join("\n");
55
+ }
56
+ function runtimeError(message, received) {
57
+ return new CliError("INGEST NORMALIZE FAILED", message, {
58
+ exitCode: EXIT_RUNTIME,
59
+ errorCode: "INGEST_NORMALIZE_FAILED",
60
+ received,
61
+ nextSteps: ["Inspect the chunk error sidecar and rerun `scriptctl ingest`."],
62
+ });
63
+ }
64
+ // Parse the model's markdown for one chunk and enforce presence: a chunk whose
65
+ // source text has real content must produce at least one record.
66
+ export function parseNormalizeOutput(markdown, chunk) {
67
+ let records;
68
+ try {
69
+ records = parseNormalizeMarkdown(markdown, chunk);
70
+ }
71
+ catch (err) {
72
+ if (err instanceof MarkdownParseError)
73
+ throw runtimeError(`chunk ${chunk.index}: ${err.message}`, [err.message]);
74
+ throw err;
75
+ }
76
+ if (records.length === 0 && chunk.text.trim() !== "") {
77
+ throw runtimeError(`chunk ${chunk.index}: non-empty chunk produced no records`, ["records: 0"]);
78
+ }
79
+ return records;
80
+ }
81
+ export async function runTextNormalize(executor, chunk, signal) {
82
+ const response = await executor.generateText("ingest.text-normalize", {
83
+ system: TEXT_NORMALIZE_SYSTEM,
84
+ user: buildNormalizeUser(chunk),
85
+ ...(signal ? { signal } : {}),
86
+ });
87
+ return { markdown: response.text, records: parseNormalizeOutput(response.text, chunk) };
88
+ }
89
+ //# sourceMappingURL=text-normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-normalize.js","sourceRoot":"","sources":["../../../../src/llm/tasks/ingest/text-normalize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { TaskVideoPort, VideoFileHandle } from "../../types.js";
2
+ export declare const VIDEO_CORRECT_SYSTEM: string;
3
+ export declare function videoCorrectPrompt(episodeNo: number, contextMd: string, pass1Md: string): string;
4
+ export interface VideoCorrection {
5
+ name_fixes: Array<{
6
+ from: string;
7
+ to: string;
8
+ scope: string;
9
+ reason: string;
10
+ }>;
11
+ same_person: Array<{
12
+ names: string[];
13
+ reason: string;
14
+ }>;
15
+ speaker_fixes: Array<{
16
+ ts: string;
17
+ from: string;
18
+ to: string;
19
+ reason: string;
20
+ }>;
21
+ dialogue_fixes: Array<{
22
+ ts: string;
23
+ speaker: string;
24
+ wrong: string;
25
+ right: string;
26
+ reason: string;
27
+ }>;
28
+ notes: string[];
29
+ }
30
+ export declare function runVideoCorrect(port: TaskVideoPort, file: VideoFileHandle, episodeNo: number, contextMd: string, pass1Md: string, fps: number, signal?: AbortSignal): Promise<VideoCorrection>;