@starreel/mcp 0.1.149 → 0.1.151

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.
@@ -81,7 +81,8 @@ const ASSET_GATE_HINT = '\n★【资产层硬闸·免费】还要先跑 review_a
81
81
  /** 资产生成工具的「完成后质检」引导(每一步完成后都要质检,2026-10-14 用户拍板)。 */
82
82
  const ASSET_QC_HINT = '\n★【完成后质检·免费】出完等图落库(轮询对应 get_* 看状态,pending 别重复调),再调 review_assets 审资产层' +
83
83
  '(缺图 / 生成中 / 失败 / 被一致性闸拒收 / 道具时代冲突 / 色彩脚本),findings 原样告诉客户、按 action 修完复审;' +
84
- '再按它返回的 agent_qc 亲自逐张看图(像不像档案、是不是同一个人、时代对不对),submit_qc_report 提交质检报告;' +
84
+ '再按它返回的 agent_qc 亲自逐张看图(像不像档案、是不是同一个人、时代对不对;设定图本身各角度齐不齐、比例与结构对不对;' +
85
+ '道具与场景图放在剧情里合不合理——对照素材 used_in_shots 那几镜),submit_qc_report 提交质检报告;' +
85
86
  '出镜头图时把它的 review_token 作为 asset_review_token 传给 generate_frames / generate_shot_frame。';
86
87
  /** 视频与配音生成工具的「完成后质检」引导。 */
87
88
  const VIDEO_QC_HINT = '\n★【完成后质检·免费】出完等它落库(get_storyboards 看 video_status / get_pipeline_status),再调 review_videos 审视频与配音层' +
@@ -130,6 +131,8 @@ export const WORKFLOW_HINT = '★三档执行策略(别把三档混着问客户)
130
131
  '出视频后同样看 video_status;成片前用 get_pipeline_status 确认没有缺镜。' +
131
132
  '★**禁止一路 generate 到底**:不审查就连推的做法,问题会在每一层被放大,最后整集废掉重来——' +
132
133
  '而重来的每一次出图/出视频都是真扣费。审查全部免费,拦下来一分钱不花。' +
134
+ '★换服装改的是结构化绑定:update_shot 的 active_wardrobe_id(整镜)/ appearance_wardrobes(多人逐角色),可覆盖、可清空——' +
135
+ '改文字描述不换衣服,也不用让客户去网页改。' +
133
136
  '②【产线主干·按序不跳步·★先分镜再建资产】set_script→rewrite_script→★review_script→extract_assets→storyboards(先分镜·纯文本拆镜)→★review_storyboards→' +
134
137
  '★剧本纪律(端点强制,绕不过):原始素材(梗概/大纲/成品稿都算)一律放 set_script,**必须经 rewrite_script 产出 AI 改写稿**——' +
135
138
  '把自己写好的剧本直接贴进 edit_rewritten_script 绕过改写会被 400 拒(没有改写稿就没有可改的对象),extract_assets 同样要求基于改写稿。' +
@@ -618,9 +621,11 @@ export function registerProduceTools(server, client) {
618
621
  '原样传给 generate_shot_frame 的 reopen_pair_id 参数可以只重掷有问题的那一侧(省一半算力/费用,' +
619
622
  '不会拿去生成一张这次根本没打算重做的图)。没有这个字段就按 fail_reason/retryable 走常规重试。免费。' +
620
623
  '★每镜还带**绑定**(只有 ID):character_ids(本镜绑定的角色)、character_bindings[{character_id,presence}]' +
621
- '(presence=on_screen 在画面 / voice_only 只有声音、不进画面)、scene_id、prop_ids、active_wardrobe_id。' +
624
+ '(presence=on_screen 在画面 / voice_only 只有声音、不进画面)、scene_id、prop_ids、active_wardrobe_id(整镜服装)、' +
625
+ 'appearance_wardrobes[{character_id,wardrobe_id}](逐角色服装锚,只列声明了服装的角色)。' +
622
626
  '出图注入谁的定妆图/哪张场景图/哪些道具图由这几项决定;角色名对照 get_characters、道具名对照 get_props。' +
623
627
  '改绑用 update_shot 的 character_ids(★全量覆盖,先从这里读现值再改,漏传即解绑)与 character_presence;' +
628
+ '改服装用 update_shot 的 active_wardrobe_id / appearance_wardrobes(可覆盖、可传 null 清空);' +
624
629
  '改完再读一次本工具核对。character_bindings 缺席 = 这次没读到(不是没绑),空数组才是没绑。' +
625
630
  '★启用双边版本审核时,seam_review.status 为 passed/failed/not_checked/stale;stale 是证据过期,不是画面失败,不据此付费重生。continuity_intent 可用 update_shot 明确。' +
626
631
  '★每镜带 **seam_state**(本镜与上一镜的镜间接缝,与官网帧时间线同一判据):' +
@@ -722,7 +727,10 @@ export function registerProduceTools(server, client) {
722
727
  server.tool('review_script', '【第①道硬闸·免费】审查 AI 改写稿:对白行长度、情绪转折完整性、开场钩子、末场悬念。' +
723
728
  '★extract_assets 与 generate_storyboards 之前必须先跑本工具——人物档案和分镜都从改写稿派生,' +
724
729
  '稿子里的问题会被逐层放大到定妆图/出图/出视频,等成片才发现就是整集重来。' +
725
- '把 findings 逐条告诉客户、按 action 修完(改稿走 edit_rewritten_script)后复审,再拿 review_token 往下走。' + AGENT_QC_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/script`)));
730
+ '把 findings 逐条告诉客户、按 action 修完(改稿走 edit_rewritten_script)后复审,再拿 review_token 往下走。' + AGENT_QC_HINT +
731
+ '\n★【剧本评估与建议】materials 里有客户原稿(script:original)和改写稿(script):先评估客户给的稿子本身(主线、动机、冲突、节奏、漏洞、能不能拍),' +
732
+ '再核对改写稿;每个问题在 submit_qc_report 的 suggestions 里给出具体改法(能直接改字的给 before/after)。' +
733
+ '提交后把评估和建议逐条讲给客户,让客户选:采纳其中几条 / 全部直接执行 / 不改直接往下做——由客户决定,不要替他选,也不要没问就改。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/script`)));
726
734
  server.tool('review_storyboards', '【第②道硬闸·免费】审查分镜:禁区词(会被厂商审核拒、白扣费)、镜头时长分布、相邻构图重复、' +
727
735
  '同场景角色站位漂移、情绪曲线峰谷、关键镜标记。★generate_frames 之前必须先跑本工具——' +
728
736
  '分镜里的问题一旦整集出图就变成整集废图,单镜修不回来。按 findings.action 用 update_shot/split_shot 修完再复审。' +
@@ -755,6 +763,8 @@ export function registerProduceTools(server, client) {
755
763
  'materials 里的每个 ref 都要列进 inspected(漏一件就拒收并点名漏了哪些);有问题就 verdict=issues 并在 issues 里逐条写,有问题却报 pass 会被拒。' +
756
764
  '★别走过场:没打开看就列进 inspected,等于替客户签了字——出了废片照常扣费。' +
757
765
  '★verdict=issues 时这张 token 默认进不了下一步:把问题讲给客户,修完重新 review + 质检;客户明确知情并坚持照现状继续,下一步才带 acknowledge_review:true。' +
766
+ '★suggestions(修改建议):剧本层(agent_qc.suggestions_required=true)报了问题就必须给,其它层可选。每条写 problem + proposal,能直接改字的再给 before/after;' +
767
+ '返回会给每条编号(s1…)。把建议讲给客户,客户选采纳几条 / 全部直接执行 / 不改直接往下做,照客户的选择办。' +
758
768
  '审查之后内容又变了,报告会被拒,重新 review 后按新的 agent_qc 再质检。', {
759
769
  episode_id: z.number().int().positive(),
760
770
  review_token: z.string().describe('对应层 review_* 返回的 review_token(资产层也是这个值,出图时再作为 asset_review_token 传)'),
@@ -771,6 +781,14 @@ export function registerProduceTools(server, client) {
771
781
  action: z.string().optional().describe('建议怎么修(用哪个工具)'),
772
782
  })).optional().describe("verdict='issues' 时至少一条"),
773
783
  summary: z.string().optional().describe('给客户看的一段质检小结'),
784
+ suggestions: z.array(z.object({
785
+ ref: z.string().optional().describe('针对哪一件(materials 的 ref,如 script:original / script / prop:12)'),
786
+ problem: z.string().describe('什么问题'),
787
+ proposal: z.string().describe('具体怎么改(给客户看、也是你执行修改的依据)'),
788
+ before: z.string().optional().describe('能直接改字时:原文片段(逐字,便于定位替换)'),
789
+ after: z.string().optional().describe('能直接改字时:改成什么'),
790
+ priority: z.enum(['must', 'should', 'could']).optional().describe('must 不改会出问题 / should 建议改 / could 可改可不改;默认 should'),
791
+ })).max(50).optional().describe('修改建议,最多 50 条。剧本层报了问题必填,客户可采纳或直接执行'),
774
792
  }, async ({ episode_id, ...report }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/qc-report`, report)));
775
793
  server.tool('review_all', '(推荐·免费)三层一次跑完的整体体检:剧本+分镜+镜头图片。开工前摸底、交付前复查用。' +
776
794
  '★它**不发 review_token** —— 进收费步前仍需对应层的 review_script / review_storyboards / review_frames 各跑一次' +
@@ -1007,16 +1025,28 @@ export function registerProduceTools(server, client) {
1007
1025
  + '只改文字、不改绑定,出图照旧穿原来那身。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/wardrobe-timeline`, {})));
1008
1026
  server.tool('apply_wardrobe_timeline', '把客户勾选的换装建议落库(第②步,免费)。改的是每镜的 **active_wardrobe_id 结构化绑定**,'
1009
1027
  + '不是文字描述——这正是「改了 update_shot 的文字却不生效」那个坑的正解。\n'
1010
- + '★只传客户点头的那几条;一次最多 200 条。\n'
1011
- + '★多角色同框的镜头单列装不下,那些走 apply_wardrobe_appearances。', {
1028
+ + '★items 只要 {storyboard_id, wardrobe_id}:可以传 build 给的建议(只传客户点头的),'
1029
+ + '也可以**不跑 build、手工指定**(客户点名「这几镜换成这件」时直接传,免得为一次指定花 LLM 的钱)。一次最多 200 条。\n'
1030
+ + '★改单镜、或要**清空**绑定,用 update_shot 的 active_wardrobe_id(传 null 清空);本工具只能设、不能清。\n'
1031
+ + '★多角色同框的镜头单列装不下,那些走 update_shot 的 appearance_wardrobes(或本工具的多角色版 apply_wardrobe_appearances)。', {
1012
1032
  episode_id: z.number().int().positive(),
1013
- items: z.array(z.any()).describe('build_wardrobe_timeline 给的建议,只放客户勾选的那几条'),
1033
+ items: z.array(z.object({
1034
+ storyboard_id: z.number().int().positive().describe('本集里的镜头 id'),
1035
+ wardrobe_id: z.number().int().positive().describe('服装资产 id(list_wardrobe 查;必须是本剧的)'),
1036
+ })).min(1).max(200).describe('[{storyboard_id, wardrobe_id}]。build 的建议或手工指定都行'),
1014
1037
  }, async ({ episode_id, items }) => jsonResult(await client.producePost(`/episodes/${episode_id}/wardrobe-timeline/apply`, { items })));
1015
1038
  server.tool('apply_wardrobe_appearances', '逐角色回填 appearances 的服装绑定(第②步的多角色版,免费)。'
1016
1039
  + '用于**多角色同框**的镜头:单个 active_wardrobe_id 装不下几个人各穿各的,'
1017
- + '这条按角色逐个绑。同样只传客户勾选的那几条,一次最多 200 条。', {
1040
+ + '这条按角色逐个绑。items 可以是 build 的建议,也可以手工指定;一次最多 200 条。\n'
1041
+ + '★它是**补空位**:该角色在这一镜已有服装选择的会被跳过(绝不覆盖);角色在这一镜必须已有出场条目,'
1042
+ + '同一角色在一镜出现两次(同源分身)也跳过。要**覆盖改错的、清空、或给还没有出场条目的已绑定角色设服装**,'
1043
+ + '用 update_shot 的 appearance_wardrobes(逐镜,显式覆盖,wardrobe_id:null 清空)。回执的 skipped/errors 要看。', {
1018
1044
  episode_id: z.number().int().positive(),
1019
- items: z.array(z.any()).describe('build_wardrobe_timeline 给的建议,只放客户勾选的那几条'),
1045
+ items: z.array(z.object({
1046
+ storyboard_id: z.number().int().positive().describe('本集里的镜头 id'),
1047
+ character_id: z.number().int().positive().describe('该镜里的角色 id'),
1048
+ wardrobe_id: z.number().int().positive().describe('该角色自己的服装资产 id(list_wardrobe 查)'),
1049
+ })).min(1).max(200).describe('[{storyboard_id, character_id, wardrobe_id}]。build 的建议或手工指定都行'),
1020
1050
  }, async ({ episode_id, items }) => jsonResult(await client.producePost(`/episodes/${episode_id}/wardrobe-timeline/apply-appearances`, { items })));
1021
1051
  server.tool('get_world_state', '读本集的**世界状态账本**:被追踪的实体(道具/服装/伤痕一类)在各镜之间的状态,'
1022
1052
  + '以及对不上的地方(conflicts,带命中的两个镜号)。免费。'
@@ -1498,9 +1528,12 @@ export function registerProduceTools(server, client) {
1498
1528
  const suffix = qs.toString() ? `?${qs.toString()}` : '';
1499
1529
  return jsonResult(await client.produceGet(`/episodes/${episode_id}/dialogue-coverage${suffix}`));
1500
1530
  });
1501
- server.tool('update_shot', '逐镜编辑:改单个分镜的文本内容(景别/动作/台词/画面描述/运镜等)、面部表情(expression_hint)、时长(duration)、成片变速(speed_factor)、角色绑定(character_ids)与画外音标记(character_presence)。只传要改的字段、其余不动。★改镜(成功或被拒)都会作废覆盖本镜的未消费报价,生成时回 409 QUOTE_INVALIDATED——先改成功、回读核对,再报价。' +
1531
+ server.tool('update_shot', '逐镜编辑:改单个分镜的文本内容(景别/动作/台词/画面描述/运镜等)、面部表情(expression_hint)、时长(duration)、成片变速(speed_factor)、角色绑定(character_ids)、画外音标记(character_presence)与服装绑定(active_wardrobe_id / appearance_wardrobes)。只传要改的字段、其余不动。★改镜(成功或被拒)都会作废覆盖本镜的未消费报价,生成时回 409 QUOTE_INVALIDATED——先改成功、回读核对,再报价。' +
1502
1532
  '**免费**(纯文本写库)。★改 dialogue 会自动失效本镜已生成的 TTS 配音与字幕(需重出 tts);' +
1503
- '改文本不会自动重出图/视频,如需让画面跟上文本改动,改完再 regen 对应镜。用 get_storyboards 查改后结果(每镜带 character_ids / character_bindings / scene_id / prop_ids)。' +
1533
+ '改文本不会自动重出图/视频,如需让画面跟上文本改动,改完再 regen 对应镜。用 get_storyboards 查改后结果(每镜带 character_ids / character_bindings / scene_id / prop_ids / active_wardrobe_id / appearance_wardrobes)。' +
1534
+ '★★【换衣服改这里,不是改文字】出图穿什么由**服装绑定**决定:单人镜用 active_wardrobe_id(整镜一件),' +
1535
+ '多人同框各穿各的用 appearance_wardrobes(逐角色)。把「穿某某衣服」写进 description/action 不会换衣服。' +
1536
+ '服装 id 用 list_wardrobe 查;改完不会自动重出图,要让画面跟上需重生本镜首帧再重生视频。' +
1504
1537
  '★★原声镜(厂商原生音频)改 dialogue 后,本镜视频会被标记「待重生」——因为台词是**烤进视频人声**的,' +
1505
1538
  '不重生就终拼,成片里念的仍是改动前的台词(典型现象:台词像是跑到了别的镜头上)。' +
1506
1539
  'compose_episode 会以 advisory `stale_video_after_edit` 列出这些镜;正确处置是先 regenerate_shot_video 再终拼。' +
@@ -1528,6 +1561,19 @@ export function registerProduceTools(server, client) {
1528
1561
  '出图时他的定妆图、人脸锁、站位、主体脸锚全部跳过,台词与配音不受影响;on_screen = 恢复在画面。' +
1529
1562
  '角色必须在本镜绑定里(可与 character_ids 同一请求,先改绑再标)。只列要改的角色,没列的不动(空数组 = 不改任何标记)。' +
1530
1563
  '★完全没出现在画面、台词也不是他说的 → 直接从 character_ids 去掉,不用标 voice_only'),
1564
+ active_wardrobe_id: z.number().int().positive().nullable().optional()
1565
+ .describe('整镜服装绑定(服装资产 id,list_wardrobe 查,必须是本剧的);null = 清空(回到按角色自动匹配)。' +
1566
+ '单人镜换衣服用它;直接覆盖现值。多人同框它只装得下一件(会被当成那件服装所属角色的),各穿各的用 appearance_wardrobes'),
1567
+ appearance_wardrobes: z.array(z.object({
1568
+ character_id: z.number().int().positive(),
1569
+ wardrobe_id: z.number().int().positive().nullable(),
1570
+ })).min(1).optional()
1571
+ .describe('逐角色服装绑定:[{character_id, wardrobe_id}],wardrobe_id=null 清空该角色的服装锚。' +
1572
+ '**显式覆盖**已有选择(apply_wardrobe_appearances 只补空位、改不回改错的,改错了用这里)。只列要改的角色,没列的不动。' +
1573
+ '角色必须在本镜绑定里(可与 character_ids 同一请求);服装必须是本剧里**该角色自己的**(别集专属的不行),否则 400 且本请求一个字段都不写。' +
1574
+ '同一角色在本镜出现两次(同源分身)不猜,400。★本镜的整镜绑定 active_wardrobe_id 若是你点名角色的另一件,出图时它优先、会遮住逐角色服装 ⇒ 409 WARDROBE_SHADOWED_BY_ACTIVE,同一请求加 active_wardrobe_id:null 即可。' +
1575
+ '出场数据是旧格式时 409 APPEARANCES_NONCANONICAL(一字不写),单人镜改用 active_wardrobe_id。回执带 appearance_wardrobes(现值)与 appearance_wardrobes_effective:' +
1576
+ 'false = 已落库但本剧尚未开启逐角色服装读取(灰度),出图暂不按它选——单人镜请改用 active_wardrobe_id'),
1531
1577
  title: z.string().optional().describe('镜头标题'),
1532
1578
  description: z.string().optional().describe('画面描述'),
1533
1579
  shot_type: z.string().optional().describe('景别(如 特写/中景/全景/远景)'),