@starreel/mcp 0.1.200 → 0.1.202

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.
@@ -300,7 +300,9 @@ export const REVIEW_GATE_RULE = '五道闸全部免费、服务端强制(跳过
300
300
  '一键 / 混合模式的托管环节里,白名单内的启发式 error 带 auto_acknowledge:true 放行(平台判、写决策日志),其余自修后复审,修不好就汇总问客户(get_autopilot_log)。' +
301
301
  '`review_all` 是整集体检、不发 token。' +
302
302
  '★每一层审查之后你(agent)还要**亲自质检**:按返回的 agent_qc 清单逐件看素材(图下载看、视频抽帧看、文本通读),' +
303
- '调 submit_qc_report 提交报告;灰度剧没交报告 token 不能用,报告有问题与 error 同样默认拦。平台判有没有,你判像不像、对不对。';
303
+ '调 submit_qc_report 提交报告;灰度剧没交报告 token 不能用,报告有问题与 error 同样默认拦。平台判有没有,你判像不像、对不对。' +
304
+ '★客户对某一镜/整集提了具体要求(身份、服装、道具颜色、嘴型、画面瑕疵等)→ add_acceptance_criteria 登记;review_frames / review_videos 会把它追加进 agent_qc.checklist(req: 开头)逐条核对,'
305
+ + '平台自动审查、你的结论、客户确认三个状态分开记(get_acceptance_criteria);客户确认用 confirm_acceptance_criterion,只能在客户本人表态后调。';
304
306
  export const QA_TOOLS = [
305
307
  { symptom: '话没说完就切 / 台词跑到别的镜上', run: 'scan_dialogue_coverage', then: ['repair_episode_dialogue(首选:换音频不重生,便宜)', 'regenerate_shot_video(特写镜或画面也错时)', 'compose_episode'] },
306
308
  { symptom: '切太快 / 一个镜头里画面跳来跳去', run: 'scan_intra_shot_cuts', then: ['update_project_settings(video_engine 改 seedance-2.5 或 hailuo-3,以 get_production_menu 当前可用为准)', 'regenerate_shot_video'] },
@@ -749,16 +751,16 @@ export function buildInstructions() {
749
751
  '★第一步永远是判「客户手上有什么材料」——它决定入口,选错入口的返工都是真扣费(全表与要点:get_capabilities_guide):',
750
752
  entry,
751
753
  '',
752
- '产线顺序(不跳步):create_drama(建剧即设好 setting_brief/画幅/video_engine/image_model/一致性锚,全免费) → set_script → rewrite_script → ★review_script → extract_assets → quote/generate_storyboards → ★review_storyboards → 定妆图+设定图 / 场景图 / 道具图 / 色彩脚本 → ★review_assets → run_precheck → quote/generate_frames(默认只出首帧) → tail_frame_plan(哪几镜要独立尾帧) → quote/generate_frames(last_frame) → chain_frames(dry_run→确认后执行)→get_jobs → ★review_frames → quote/generate_videos → 音频 → ★review_videos → compose_episode → get_final_cut → review_final_cut。用 get_pipeline_status 查进度。',
754
+ '产线顺序(不跳步):create_drama(建剧即设好世界观/画幅/引擎/模型/一致性锚,全免费) → set_script → rewrite_script → ★review_script → extract_assets → quote/generate_storyboards → ★review_storyboards → 定妆图+设定图 / 场景图 / 道具图 / 色彩脚本 → ★review_assets → run_precheck → quote/generate_frames(默认只出首帧) → tail_frame_plan(哪几镜要独立尾帧) → quote/generate_frames(last_frame) → chain_frames(dry_run→确认后执行)→get_jobs → ★review_frames → quote/generate_videos → 音频 → ★review_videos → 成片前自检(recommend_trim_window/audit_seams) → compose_episode → get_final_cut → review_final_cut。用 get_pipeline_status 查进度。',
753
755
  '五道免费硬闸(跳过 → 400):review_script(extract_assets/分镜前)· review_storyboards(出图前,含单镜 generate_shot_frame)· review_assets(出图前,token 作 asset_review_token)· review_frames(出视频前,含单镜 regenerate_shot_video)· review_videos(compose_episode 前);review_token 随下游收费工具传(批量与单镜工具都要),findings 逐条原样告诉客户。',
754
756
  '★每层审查之后你还要亲自质检:按返回的 agent_qc 逐件看素材,submit_qc_report 提交报告;灰度剧没交报告进不了下一步。',
755
757
  '计费纪律:预付费不透支;大额步 quote_* → 把 estimated_points 原样告诉客户 → 客户同意后 generate_*(quote_id),绝不擅自确认;' +
756
758
  '出图类报价给 estimated_points(上界,按它准备余额)与 typical_points(通常花费),两个都说;' +
757
759
  '文本步按 token 后付;402 就停下让客户充值,别重试。',
758
- '长任务异步:generate_* 立即返回,用 get_pipeline_status / get_storyboards / get_run_status 轮询;图片 pending = 还在生成,别重复调(重复扣费)。',
760
+ '长任务异步:generate_* 立即返回,用 get_pipeline_status / get_storyboards / get_run_status 轮询;图片 pending=还在生成,别重复调(重复扣费)。',
759
761
  '改写成功后只 edit_rewritten_script 点改,别重跑 rewrite_script;角色外观唯一真相源是人物档案(update_character),别写进 visual_lock/art_bible。',
760
762
  '选图片模型/视频引擎、客户问「哪个模型好 / 怎么更省」→ get_capabilities_guide(section=model_guide):各模型实测优劣、价格、决策步骤与推荐组合;建剧时就主动讲给客户定。',
761
763
  '做动作 / 打斗 / 技能展示片 → get_capabilities_guide(section=action_film):分镜表直通道、首帧停在起势、并组与节奏剪裁。',
762
- '不确定该用哪个工具、客户问「你们能做什么」→ 先调 get_capabilities_guide(免费、本地、不联网)。',
764
+ '不确定该用哪个工具、客户问「你们能做什么」→ get_capabilities_guide(免费、本地)。',
763
765
  ].join('\n');
764
766
  }
@@ -85,7 +85,9 @@ const AGENT_QC_HINT = '\n★【增量复审·省时省量】review_storyboards /
85
85
  '\n★【agent 亲自质检·免费】返回的 agent_qc 里有本层清单(checklist)和本集要看的素材(materials,每件一个 ref,图/视频给链接)。' +
86
86
  '平台只判「有没有、在不在、被没被拒」,「像不像、对不对、好不好」要你自己看:图片下载打开看、视频至少抽开头/中间/结尾帧、文本用 tool 通读。' +
87
87
  '逐条回答清单(ok / issue / na,issue 写明哪件什么问题),看过的每个 ref 都列进 inspected,调 submit_qc_report 提交;质检结论原样讲给客户。' +
88
- 'agent_qc.required=true 时没提交报告这张 review_token 进不了下一步(400 agent_qc_required),报告有问题与平台 error 同样默认拦。';
88
+ 'agent_qc.required=true 时没提交报告这张 review_token 进不了下一步(400 agent_qc_required),报告有问题与平台 error 同样默认拦。' +
89
+ '\n★【客户验收项】review_frames / review_videos 会把客户登记的具体要求(add_acceptance_criteria)追加进 checklist(id 以 req: 开头),附 acceptance_criteria:' +
90
+ '平台通用审查不知道这些要求,通过了不代表满足——逐条亲眼核对后在 submit_qc_report 里答,结论讲给客户,由客户 confirm_acceptance_criterion 确认。';
89
91
  /** 增量复审(缺口 #16):上一张本层 token,已交质检且 pass 时只重审变更镜。 */
90
92
  const BASE_REVIEW_ARG = z.string().optional().describe('增量复审:上一张本层 review_token(已交 agent 质检且 verdict=pass)。带上只重审变更镜及其相邻镜,素材与质检只覆盖它们;不满足条件自动退回整集');
91
93
  /** 资产层 token:出镜头图同时要分镜层(review_token)与资产层两张,资产层这张单独一个参数。 */
@@ -105,6 +107,19 @@ const ASSET_QC_HINT = '\n★【完成后质检·免费】出完等图落库(轮
105
107
  const VIDEO_QC_HINT = '\n★【完成后质检·免费】出完等它落库(get_storyboards 看 video_status / get_pipeline_status),再调 review_videos 审视频与配音层' +
106
108
  '(缺视频 / 旧帧出的视频 / 片尾硬切 / 意外人声 / 配音缺失 / 音色未分配 / 原声声线过期),修完复审;' +
107
109
  '再按 agent_qc 亲自逐镜看视频、听配音(动作到位、不变脸、台词念全、嗓音对),submit_qc_report 提交;compose_episode 要带它的 review_token。';
110
+ /**
111
+ * 成片前自检:review_videos 之后、compose_episode 之前(2026-10-19 外部 agent 复盘:五个工具全在却一个没调,
112
+ * 靠反复导出整片、手工试转场定位问题)。只是提示,不加闸;trim_shot / audit_seams 不进视频层指纹,做完不用复审。
113
+ */
114
+ export const PRE_COMPOSE_CHECK_HINT = '★★【成片前自检·review_videos 通过后、compose_episode 之前】别靠反复导出整片、手工试转场来找问题——逐镜与接缝的问题有专门工具,拼之前按序过一遍:' +
115
+ '①这一集换过定妆图 / 设定图 / 场景图 / 镜头帧 → 先 get_stale_portrait_frames(episode_id,免费)看哪些镜的在用帧还基于旧资产,' +
116
+ '拿不准该用哪一版的镜再 get_asset_versions(storyboard_id,免费)逐镜看版本与依赖(unknown 是证据不足、不是已过期;重出要付费,清单讲给客户由客户定);' +
117
+ '②scan_intra_shot_cuts(episode_id,免费)查厂商在单镜内自行硬切的镜;' +
118
+ '③逐镜 recommend_trim_window(storyboard_id,免费)看冻结尾段与有效动作区间(组内镜返回 applicable=false,跳过),有推荐就 trim_shot 应用(免费);' +
119
+ '④audit_seams(episode_id)查相邻镜接缝的剪辑连续性:默认 dry_run 只列待审范围(免费),真执行按实际文本 token 计费,' +
120
+ '讲给客户、客户明确接受后才带 dry_run:false + confirm_usage_billing:true,结果看 get_storyboards 的 seam_review;' +
121
+ '⑤再 compose_episode。拼完成片发现跳切 / 拖尾 / 接不上,回到这几个工具按镜定位后只修那几镜,别整片反复导出试错。' +
122
+ '这些都是提示性检查,不过闸;裁剪与接缝审核不让 review_videos 的 token 失效。';
108
123
  /** 三个被闸的收费工具共用的参数(zod 形状,展开进各自 schema)。 */
109
124
  const REVIEW_ARGS = {
110
125
  review_token: z.string().optional().describe('★来自本层 review_* 工具的凭据(受闸项目必填;缺了会 400 并告诉你该调哪个审查工具)'),
@@ -216,6 +231,7 @@ export const WORKFLOW_HINT = AUTOPILOT_HINT +
216
231
  '★★【④⑤两道新硬闸 + agent 亲自质检】④资产层:定妆图/设定图/场景图/道具图出完 → review_assets' +
217
232
  '(在 generate_frames / generate_shot_frame 之前,它的 token 作为 asset_review_token、与分镜层 review_token 一起传);' +
218
233
  '⑤视频与配音层:视频/配音/配乐出完 → review_videos(在 compose_episode 之前);成片出来后跑 review_final_cut。' +
234
+ PRE_COMPOSE_CHECK_HINT +
219
235
  '★每一层审查之后你还要**亲自质检**:按返回的 agent_qc 逐件看素材(图下载看、视频抽帧看、文本通读),逐条答清单,' +
220
236
  'submit_qc_report 提交;灰度剧没交报告那张 review_token 进不了下一步,报告有问题与 error 同样默认拦。平台判有没有,你判像不像、对不对。' +
221
237
  '★★【单镜路径同样受审查闸】:generate_shot_frame 同样要带分镜层 review_token,regenerate_shot_video 同样要带镜头图片层 review_token——' +
@@ -658,7 +674,18 @@ export function registerProduceTools(server, client) {
658
674
  replace_user_frame: z.boolean().optional().describe('本镜该帧是上传/定稿帧时,确认替换才传 true'),
659
675
  }, async ({ storyboard_id, ...body }) => jsonResult(await client.producePost(`/storyboards/${storyboard_id}/frame-workbench/adopt`, body)));
660
676
  server.tool('get_stale_portrait_frames', '免费只读查询一集里哪些镜头的在用首帧/尾帧,是拿角色已被换掉的旧资产(旧定妆图、旧设定图、旧身体/发型图)画的。换定妆图后用它确认影响范围,再由客户决定重出哪些镜(重出要付费,本工具不改任何数据)。verdict=stale 必附证据:ledger-old-asset(派发台账直接记录用了旧图)/ references-previous-asset(参考图里有该角色以前的旧资产)/ portrait-born-after-frame(当前定妆图晚于该帧诞生);证不了的标 unknown 并给原因,不当作已过期。has_video=true 表示该镜视频也基于旧帧,重出帧后视频需重生。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.produceGet(`/episodes/${episode_id}/stale-portrait-frames`)));
661
- server.tool('get_asset_versions', '只读查询一个镜头的全部图片/视频版本、历史查看链接、依赖 ID 与版本哈希,以及已生成→与当前输入一致→审核有效→已采用四项独立事实。历史缺少依赖记录时显示未知,不补造快照。免费。', { storyboard_id: z.number().int().positive() }, async ({ storyboard_id }) => jsonResult(await client.produceGet(`/storyboards/${storyboard_id}/asset-versions`)));
677
+ server.tool('get_asset_versions', '只读查询一个镜头的全部图片/视频版本、历史查看链接、依赖 ID 与版本哈希,以及已生成→与当前输入一致→审核有效→已采用四项独立事实。历史缺少依赖记录时显示未知,不补造快照。免费。' +
678
+ 'video_lock 是客户锁定的已验收视频(lock_shot_video):locked=true 时这条按沿用算有效,frames_changed_since_lock=true 表示锁定后帧又改过但不需要重出。', { storyboard_id: z.number().int().positive() }, async ({ storyboard_id }) => jsonResult(await client.produceGet(`/storyboards/${storyboard_id}/asset-versions`)));
679
+ server.tool('lock_shot_video', '锁定某镜当前在用的视频为客户已验收版本(免费,不生成、不改素材)。用于客户明确说「这一镜就用这条」之后,首尾帧/场景/角色又被改过的情形:' +
680
+ '锁定后资产变动不再把这镜判过期——review_videos 不报 video_stale(改报 warning video_locked_frames_changed)、get_asset_versions 里这条 acceptance_locked=true 仍算当前有效(漂移原因照实列在 drift_while_locked)、' +
681
+ '原声剧声线 stale_shots 不列它、终拼预检不报编辑后未重生;regenerate_shot_video / edit_video_shot / repaint / reskin / 上传或清除实拍 / 整集 generate_videos 会替换它时一律 409 VIDEO_LOCKED。' +
682
+ '锁里冻结视频行 id 与它当时的生成输入(首尾帧 URL、prompt、参考清单)。只能锁当前在用的那条(传 generation_id 时必须正好是它,否则 409 NOT_CURRENT_VIDEO;本镜没视频 409 NO_VIDEO)。' +
683
+ '视频被别的路径换掉后锁自动失效(get_asset_versions 的 video_lock.broken=true)。★只有客户明确确认沿用才锁,不得把生成完成或模型判断当客户验收;要重出须客户同意后先 unlock_shot_video。', {
684
+ storyboard_id: z.number().int().positive(),
685
+ generation_id: z.number().int().positive().optional().describe('可选:核对要锁的视频行(来自 get_asset_versions 的 versions[].id),不是当前在用的会 409'),
686
+ note: z.string().max(500).optional().describe('可选:客户确认的原话或场合,留痕'),
687
+ }, async ({ storyboard_id, generation_id, note }) => jsonResult(await client.producePost(`/storyboards/${storyboard_id}/video-lock`, { ...(generation_id ? { generation_id } : {}), ...(note ? { note } : {}) })));
688
+ server.tool('unlock_shot_video', '解除某镜的已验收视频锁(免费)。解锁后立刻恢复原判:帧改过的镜重新报 video_stale,可以 regenerate_shot_video 重出。★只在客户同意放弃锁定版本、要换新视频时调用。返回 was_locked。', { storyboard_id: z.number().int().positive() }, async ({ storyboard_id }) => jsonResult(await client.produceDelete(`/storyboards/${storyboard_id}/video-lock`)));
662
689
  server.tool('get_asset_binding_repair_plan', '只读生成一集历史缺失场景绑定的可审核修复提案。返回原始地点/时段、候选 ID、匹配依据、歧义和 proposed_scene_id;applied=false,绝不按名字自动回填。逐项核对后才由客户决定是否改绑。免费。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.produceGet(`/episodes/${episode_id}/asset-binding-repair-plan`)));
663
690
  server.tool('get_storyboards', '读某一集的分镜列表(供审阅/查进度)。含每镜首帧(first_frame_image)与视频(video_url)是否就绪。' +
664
691
  'asset_versions 按首帧/尾帧/视频分别给出 generated(已生成)、input_consistent(与当前输入一致)、review_valid(审核有效)、adopted(已采用)、current_effective(当前有效)。null=缺少证据;ready 只表示存在产物,不代表当前有效。旧素材仍可查看。' +
@@ -828,6 +855,7 @@ export function registerProduceTools(server, client) {
828
855
  '首帧换过而视频还是旧帧出的(video_stale);疑似片尾硬切、无台词镜意外人声(warning);' +
829
856
  '配音剧:台词镜缺配音、会开口的角色没音色、缺旁白角色;原声剧:出片时没挂上当前声线的镜、没锁声线的角色;缺配乐(warning)。' +
830
857
  '★出完视频、配音(generate_tts)、配乐后都调一次;compose_episode 之前必须审过,带返回的 review_token。' +
858
+ '\n' + PRE_COMPOSE_CHECK_HINT + '返回体的 pre_compose_checks 是同一份清单(结构化,逐项 tool/scope/when/cost)。\n' +
831
859
  '更细的检查(要解码画面/音轨)另有 scan_intra_shot_cuts / scan_dialogue_coverage / audit_silent_shot_audio;拼完成片再跑 review_final_cut。' +
832
860
  '★返回带 post_production_advisory = 剧本标注「后期」的叙事必需特效还没处理(平台不会合成):原样告诉客户,用 get_post_production_checklist 看清单。' + AGENT_QC_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/videos`)));
833
861
  server.tool('submit_qc_report', '提交你(agent)亲自看素材做的质检报告。免费。先调对应层的 review_*(script / storyboards / assets / frames / videos),' +
@@ -997,6 +1025,7 @@ export function registerProduceTools(server, client) {
997
1025
  '补齐后重试;确要拼部分成片传 force=true(缺视频镜被跳过,成片缺镜,留痕)。' +
998
1026
  '响应里的 advisories(时长偏差/孤儿任务/缺TTS/post_production_pending 后期特效未完成)只提醒不阻断,建议逐条处理后再拼。发起后用 get_final_cut 轮询。' +
999
1027
  REVIEW_GATE_HINT('review_videos', '视频与配音') +
1028
+ '\n' + PRE_COMPOSE_CHECK_HINT +
1000
1029
  '\n★【完成后质检·免费】成片出来后跑 review_final_cut(缺镜/字幕放不下/身份锚/跳轴/错别字,proofread=false 则不扣费),' +
1001
1030
  '结论讲给客户;客户确认后才 confirm_delivery_acceptance' + ALWAYS_ASK('交付验收') + '。force 只越过终拼预检,不越过审查闸。', {
1002
1031
  episode_id: z.number().int().positive(),
@@ -1774,6 +1803,48 @@ export function registerProduceTools(server, client) {
1774
1803
  status: z.enum(['done_external', 'waived', 'pending']),
1775
1804
  note: z.string().max(500).optional().describe('备注,如外部完成的工具/版本'),
1776
1805
  }, async ({ episode_id, key, status, note }) => jsonResult(await client.producePost(`/episodes/${episode_id}/post-production-checklist/mark`, { key, status, ...(note ? { note } : {}) })));
1806
+ // ---------- 客户验收项(客户在这一镜 / 这一集的具体要求;review_frames / review_videos 逐条交你核对) ----------
1807
+ const ACCEPTANCE_CATEGORY = z.enum(['identity', 'wardrobe', 'prop_color', 'lip_sync', 'artifact'])
1808
+ .describe('可选类别:identity 身份(脸像不像)/ wardrobe 服装 / prop_color 道具颜色 / lip_sync 嘴型 / artifact 画面瑕疵(毛边、穿帮等)');
1809
+ const ACCEPTANCE_STAGES = z.array(z.enum(['frames', 'videos'])).min(1)
1810
+ .describe('在哪一层核对:frames 镜头图片层(review_frames)/ videos 视频层(review_videos);不填 = 两层都核对');
1811
+ server.tool('get_acceptance_criteria', '读本集的**客户验收项**(免费):客户对某一镜或整集提的具体要求,每项三个状态分开存——' +
1812
+ 'auto_review(平台自动审查,按层;not_covered=平台没有能判这一类的判据,no_related_finding 不等于满足要求)、' +
1813
+ 'agent_qc(你在 submit_qc_report 里逐条核对的结论,按层)、client(客户本人确认)。' +
1814
+ '★平台审查通过、你的通用质检通过,都不代表客户的具体要求通过——交付前对照这里逐条讲给客户。orphan=true 表示绑的镜已删除(改绑或删除)。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.produceGet(`/episodes/${episode_id}/acceptance-criteria`)));
1815
+ server.tool('add_acceptance_criteria', '登记客户验收项(免费):客户对某一镜或整集提出**具体要求**(身份、服装、道具颜色、嘴型、画面瑕疵等)时,按客户原话登记成一条,' +
1816
+ '之后 review_frames / review_videos 会把它追加进 agent_qc.checklist(id 以 req: 开头)让你逐条核对,结论单独记账。' +
1817
+ '★一条只写一项要求;镜头级用 shot_number(当前镜号)或 storyboard_id,不填 = 整集。登记后已拿到的本层 review_token 会失效,需复审。' +
1818
+ '★只登记客户说过的要求,别替客户编。', {
1819
+ episode_id: z.number().int().positive(),
1820
+ items: z.array(z.object({
1821
+ text: z.string().min(2).max(500).describe('客户的具体要求(尽量用客户原话)'),
1822
+ category: ACCEPTANCE_CATEGORY.optional(),
1823
+ shot_number: z.number().int().positive().optional().describe('镜头级:当前镜号'),
1824
+ storyboard_id: z.number().int().positive().optional().describe('镜头级:分镜 id(与 shot_number 二选一)'),
1825
+ stages: ACCEPTANCE_STAGES.optional(),
1826
+ })).min(1).max(100),
1827
+ }, async ({ episode_id, items }) => jsonResult(await client.producePost(`/episodes/${episode_id}/acceptance-criteria`, { items })));
1828
+ server.tool('update_acceptance_criterion', '改一条客户验收项(免费)。改文字 / 类别 / 镜头 / 适用层后,这条的三个状态清回初始(要求变了,旧结论不算数),需重新 review 核对。' +
1829
+ '删除用 delete_acceptance_criterion。item_id 取自 get_acceptance_criteria。' + ALWAYS_ASK('修改客户验收项'), {
1830
+ episode_id: z.number().int().positive(),
1831
+ item_id: z.string().min(1),
1832
+ text: z.string().min(2).max(500).optional(),
1833
+ category: ACCEPTANCE_CATEGORY.nullable().optional().describe('传 null 清掉类别'),
1834
+ shot_number: z.number().int().positive().optional(),
1835
+ storyboard_id: z.number().int().positive().optional(),
1836
+ scope: z.literal('episode').optional().describe("'episode' = 改成整集项(解绑镜头)"),
1837
+ stages: ACCEPTANCE_STAGES.optional(),
1838
+ }, async ({ episode_id, item_id, ...patch }) => jsonResult(await client.producePut(`/episodes/${episode_id}/acceptance-criteria/${encodeURIComponent(item_id)}`, patch)));
1839
+ server.tool('delete_acceptance_criterion', '删除一条客户验收项(免费,删了这条的三个状态一并没了)。item_id 取自 get_acceptance_criteria。' + ALWAYS_ASK('删除客户验收项'), { episode_id: z.number().int().positive(), item_id: z.string().min(1) }, async ({ episode_id, item_id }) => jsonResult(await client.produceDelete(`/episodes/${episode_id}/acceptance-criteria/${encodeURIComponent(item_id)}`)));
1840
+ server.tool('confirm_acceptance_criterion', '记录**客户本人**对一条验收项的确认(免费):accepted 满足 / rejected 不满足 / pending 撤销。只写客户确认,不改平台自动审查与你的质检结论。' +
1841
+ '★必须是客户明确表态之后才调,并带 confirm_client_statement:true;你不能替客户确认,托管也不例外。', {
1842
+ episode_id: z.number().int().positive(),
1843
+ item_id: z.string().min(1),
1844
+ status: z.enum(['accepted', 'rejected', 'pending']),
1845
+ confirm_client_statement: z.boolean().optional().describe('true = 这是客户本人的明确表态(accepted / rejected 必填)'),
1846
+ note: z.string().max(500).optional().describe('客户原话或补充说明'),
1847
+ }, async ({ episode_id, item_id, ...body }) => jsonResult(await client.producePost(`/episodes/${episode_id}/acceptance-criteria/${encodeURIComponent(item_id)}/client-verdict`, body)));
1777
1848
  server.tool('get_shot_name_card', '读这一镜的**人物名片**现值(免费):名字、头衔、显示位置那些。'
1778
1849
  + '\n★改之前先读一遍——set_shot_name_card 是**整体覆盖**,不读就改会把没打算动的字段清掉。', { storyboard_id: z.number().int().positive() }, async ({ storyboard_id }) => jsonResult(await client.produceGet(`/storyboards/${storyboard_id}/name-card`)));
1779
1850
  server.tool('generate_bgm', '给整集生成/更换 AI 配乐(按情绪弧线)。后台异步,按用量后付不欠费。返回情绪弧线段数与预估耗时;' +