@starreel/mcp 0.1.206 → 0.1.208

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.
@@ -90,7 +90,7 @@ export const ENTRY_POINTS = [
90
90
  'upload_shot_frame(只用于客户自有真实素材)',
91
91
  'upload_shot_footage(客户自有整段视频当某镜成片:录屏/产品实拍/已有片段;登记后该镜不再 AI 出图出视频,终拼原样用,时长按素材回写;清除用 clear_shot_footage)',
92
92
  ],
93
- avoid: '要「改某一镜画面」走 `generate_shot_frame`(平台自动带该镜身份锚·场景道具参考·画风锚);别在外部工具画好再 `upload_shot_frame`——外部图没有任何锚,人物/服装/画风必漂。',
93
+ avoid: '要「改某一镜画面」走 `generate_shot_frame`(平台自动带该镜身份锚·场景道具参考·画风锚);只改一小块(修脸/闭嘴/去毛边/擦掉多余物件)走 `quote_local_frame_edit` → `local_frame_edit`(框外像素原样保留);别在外部工具画好再 `upload_shot_frame`——外部图没有任何锚,人物/服装/画风必漂。',
94
94
  flow: 'upload_image · set_character_portrait · upload_scene_image · upload_prop_sheet · upload_shot_frame(仅客户自有素材;要改画面走 generate_shot_frame) · upload_shot_footage(整段实拍/录屏当某镜成片)',
95
95
  },
96
96
  {
@@ -253,7 +253,7 @@ export const PIPELINE = [
253
253
  },
254
254
  {
255
255
  step: '7 出帧(镜头图)',
256
- tools: ['run_precheck(免费,揪出必被厂商拒的镜)', 'quote_frames', 'generate_frames', 'tail_frame_plan(免费,首帧出完必调)', 'quote_shot_frame', 'generate_shot_frame(单镜重生)', 'chain_frames(先 dry_run:true 看计划与预估)', 'chain_shot_from_prev(免费,单道接缝)', 'quote_chain_shot_from', 'chain_shot_from', 'quote_align_prev_shot', 'align_prev_shot', 'upload_shot_frame'],
256
+ tools: ['run_precheck(免费,揪出必被厂商拒的镜)', 'quote_frames', 'generate_frames', 'tail_frame_plan(免费,首帧出完必调)', 'quote_shot_frame', 'generate_shot_frame(单镜重生)', 'quote_local_frame_edit', 'local_frame_edit(局部返修·只改框内,灰度剧)', 'get_local_frame_edit', 'adopt_local_frame_edit', 'chain_frames(先 dry_run:true 看计划与预估)', 'chain_shot_from_prev(免费,单道接缝)', 'quote_chain_shot_from', 'chain_shot_from', 'quote_align_prev_shot', 'align_prev_shot', 'upload_shot_frame'],
257
257
  billing: '报价确认后扣点',
258
258
  gate: 'review_frames(镜头图审查;免费;generate_videos 前必过)',
259
259
  note: '★出帧是**两趟**:generate_frames 默认只出首帧,首帧出完必须调一次免费的 `tail_frame_plan` ' +
@@ -319,7 +319,8 @@ export const QA_TOOLS = [
319
319
  ] },
320
320
  { symptom: '动作发生在裁剪窗口之外', run: 'recommend_trim_window', then: ['trim_shot'] },
321
321
  { symptom: '转场 / 接缝看着不对(跳、重影、接不上)', run: 'preview_seams(免费;get_seam_previews 轮询,逐道缝看短片与对照图,别反复导出整片)', then: ['recommend_trim_window → trim_shot(动作没接上)', 'update_shot(transition_type / transition_duration_ms 改转场,免费;之后 compose_episode 重拼才生效)', 'preview_seams(改完只传那道缝的 seams 序号复查——叠化/插镜/遮挡这类补救常带出新问题)'] },
322
- { symptom: '画面多出一个人 / 多出一件道具', run: 'get_storyboards(先看该镜实际用的首帧)', then: ['generate_shot_frame(首帧本身就有→重生首帧再重生视频)', 'split_shot(帧干净、片中长出来→拆成 3~5 秒短镜)'] },
322
+ { symptom: '画面多出一个人 / 多出一件道具', run: 'get_storyboards(先看该镜实际用的首帧)', then: ['local_frame_edit(首帧本身就有、其余都对→edit_kind=object_remove 只擦那一块;灰度剧)', 'generate_shot_frame(首帧本身就有→重生首帧再重生视频)', 'split_shot(帧干净、片中长出来→拆成 3~5 秒短镜)'] },
323
+ { symptom: '镜头图只有一小块不对(脸不像 / 嘴张着 / 边缘毛边 / 手指畸形),其余已经确认', run: 'quote_local_frame_edit(框出那一块,报价免费)', then: ['local_frame_edit(框外像素原样保留;修脸带 character_id)', 'get_local_frame_edit(看 composited_url 与 verdict)', 'adopt_local_frame_edit(客户选定后才写帧)'] },
323
324
  { symptom: '出图 / 出视频前想知道哪些镜会被厂商拒', run: 'run_precheck', then: ['update_shot', 'generate_shot_frame'] },
324
325
  { symptom: '要跑全集 / 想知道整部剧有多少问题、该先修哪几集', run: 'run_drama_precheck', then: ['run_precheck(对 attention 里那几集拿逐条明细)', 'plan_precheck_fix', 'update_shot'] },
325
326
  { symptom: '场景图(空景基板)不对 / 重出还是同一类图', run: 'get_scene_prompt', then: ['update_scene(改 image_prompt 正文——只改地点/时段是让平台重拼,拧不过来)', 'quote_regenerate_scene_image(先报价)', 'regenerate_scene_image(单场重出,覆盖旧图)', 'upload_scene_image(客户自有实拍/外部精修图)', 'quote_scene_lighting_ref → generate_scene_lighting_ref(换图后补打光参考图,先报价)', 'generate_shot_frame(下游镜头帧不会自动跟着重出)'] },
@@ -206,6 +206,7 @@ export const WORKFLOW_HINT = AUTOPILOT_HINT +
206
206
  'get_jobs 清空后再用 get_storyboards 核对 seam_state:晋升被拒的替换图不留在 get_jobs 里,那道缝会仍是 unchained(review_frames 也会报 seam_unchained),按它的 action 重接。' +
207
207
  '没落地就审,审的是旧首帧,token 在新首帧落地后作废。' +
208
208
  '逐镜/批量出帧的集平台多半没自动接过(get_storyboards 的 seam_state=unchained 就是),跳过它不报错——代价是镜与镜之间画面对不上,成片一处一个跳切。' +
209
+ '接完还担心拼起来跳(人物左右位置、运动方向、景别对不上),出视频前可选 audit_seam_directions(查主体跳边/运动反向/景别跳级)(先 dry_run 报价,按 token 扣费,提示性不拦),结论用 get_seam_directions 读、讲给客户挑着修。' +
209
210
  '★★【尾帧别跳·出帧是两趟】generate_frames 默认只出首帧。约三成的镜**末态≠首态**(大运镜/物体脱手/状态改变),这些镜需要一张独立尾帧,而判据在平台侧、你从分镜文本猜不出来——' +
210
211
  '所以首帧出完必须调一次免费的 tail_frame_plan 拿逐镜清单,再 frame_type=last_frame 补上(generate_frames 的响应体里 shots_needing_last_frame 就是这个数,不为 0 别直接去 review_frames)。' +
211
212
  '跳过不报错、不拦你——代价是那些镜出视频时只有首帧一个锚,末态由模型自由发挥:动作做不到位、大运镜结束又回到起点构图。生产实测 32 集里 30 集整集只出了首帧,其中 23 集一路出完了视频。' +
@@ -843,7 +844,9 @@ export function registerProduceTools(server, client) {
843
844
  'generate_shot_frame 单镜重生)再复审。' +
844
845
  '★还会报**镜间接缝**(warning,不拦):code=seam_unchained(标了连续却从没做过首尾帧衔接,或发起的衔接重生没落地 / 衔接后首帧又被重生)/' +
845
846
  'seam_broken(接过但没接上,或上一镜尾帧在衔接之后又重生过),shots 是「与上一镜的缝有问题」的那一镜。出视频前修最便宜——视频按首帧生成,' +
846
- '首帧与上镜尾帧对不上,成片就是一次跳切。修法看 action(整集 chain_frames 先 dry_run;单道 chain_shot_from_prev / chain_shot_from)。' + AGENT_QC_HINT, { episode_id: z.number().int().positive(), base_review_token: BASE_REVIEW_ARG }, async ({ episode_id, base_review_token }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/frames`, base_review_token ? { base_review_token } : undefined)));
847
+ '首帧与上镜尾帧对不上,成片就是一次跳切。修法看 action(整集 chain_frames 先 dry_run;单道 chain_shot_from_prev / chain_shot_from)。' +
848
+ '★有相邻镜时响应还带 seam_direction_advisory(提示性,不影响 review_token):相邻镜方向对照(主体跳边/运动反向/景别跳级)的已有结论;' +
849
+ '还没观察过的接缝要先 audit_seam_directions dry_run 报价,客户同意后再执行。' + AGENT_QC_HINT, { episode_id: z.number().int().positive(), base_review_token: BASE_REVIEW_ARG }, async ({ episode_id, base_review_token }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/frames`, base_review_token ? { base_review_token } : undefined)));
847
850
  server.tool('review_assets', '【资产层硬闸·免费】审查本集**出镜**用到的角色形象图、场景图、道具图——覆盖与质量:' +
848
851
  '出镜角色缺定妆图 / 缺设定图 / 还在生成 / 生成失败 / 设定图被一致性闸拒收(hint 带拒收理由);' +
849
852
  '场景缺空景基板;分镜引用的道具缺道具图 / 被拒;道具图与本镜时代形制冲突;缺色彩脚本(warning)。' +
@@ -992,6 +995,41 @@ export function registerProduceTools(server, client) {
992
995
  allow_era_ref_conflict: z.boolean().optional().describe('★默认不传。时代审核判定角色定妆参考与时代设定冲突时,出图会被拦下(未扣费):客户上传的定妆图及其派生图报 400 ERA_USER_REF_CONFLICT,平台生成的定妆图报 era_identity_ref_blocked。**先把冲突原因告诉客户、由客户确认造型无误**后才带 true' + ALWAYS_ASK('allow_era_ref_conflict ') + ':这些图保留不剔、照常出图。造型真不对就改定妆图,时代设定错了就 set_era_contract'),
993
996
  ...SHOT_REGEN_ARG,
994
997
  }, async ({ storyboard_id, quote_id, review_token, asset_review_token, acknowledge_review, auto_acknowledge, frame_type, replace_user_frame, reopen_pair_id, image_model, allow_missing_terminal, allow_era_ref_conflict, customer_approved_more_regens }) => jsonResult(await client.producePost(`/storyboards/${storyboard_id}/frame/generate`, { quote_id, review_token, asset_review_token, acknowledge_review, auto_acknowledge, frame_type, replace_user_frame, reopen_pair_id, image_model, allow_missing_terminal, allow_era_ref_conflict, customer_approved_more_regens })));
998
+ // ---------- 镜头图局部返修(只修一小块,其余像素原样保留;按剧灰度) ----------
999
+ const LOCAL_EDIT_ARGS = {
1000
+ storyboard_id: z.number().int().positive(),
1001
+ frame_type: z.enum(['first_frame', 'last_frame']).optional().describe('默认 first_frame'),
1002
+ base_image_url: z.string().describe('★该镜**当前**这一帧的地址(get_storyboards 的 first_frame_image / last_frame_image 原样传)。帧换过会 409 BASE_FRAME_CHANGED,防止改的是旧图'),
1003
+ regions: z.array(z.object({ x: z.number(), y: z.number(), w: z.number(), h: z.number() })).describe('修改区:归一化矩形(0~1,相对画面宽高,x/y 是左上角),1~8 块,合计不超过画面 40%(超过请走 generate_shot_frame 整张重画)。框紧一点:框外像素平台保证不变,框越大越容易连带改到已确认的细节'),
1004
+ instruction: z.string().describe('这块要改成什么,一句话说清(≤500 字),例如「嘴闭上、表情平静」「去掉发丝边缘的白边」'),
1005
+ edit_kind: z.enum(['face', 'mouth', 'hair', 'costume', 'hand', 'edge_cleanup', 'object_remove', 'other']).describe('修改类型。face / mouth = 修脸类:必须带 character_id;face / mouth / hair / costume 生成后会过身份审计'),
1006
+ character_id: z.number().int().positive().optional().describe('修脸类(face / mouth)必填:改的是哪个角色的脸。平台只送这个角色的定妆图当身份锚。hair / costume 可选'),
1007
+ candidates: z.number().int().min(1).max(4).optional().describe('出几个候选,默认 2,最多 4。每个候选单独计费'),
1008
+ };
1009
+ server.tool('quote_local_frame_edit', '报价:**局部修改某一镜的某一帧**(修脸、闭嘴、去毛边、擦掉多出来的东西)要多少点。零扣费。' +
1010
+ '客户只想改画面的一小块时用它,别拿外部图像工具改完再 upload_shot_frame——外部工具会连带改动已确认的其他细节、绕开身份锚与审计、来源链也断了;' +
1011
+ '要整张重画才用 quote_shot_frame / generate_shot_frame。' +
1012
+ '平台把修改区按羽化蒙版贴回原图:框外像素原样保留,只有框内被改。' +
1013
+ '★计费:每个候选按一次出图扣(unit_points × candidates;张数固定,estimated_points 就是实扣,typical_points 与它相等)。' +
1014
+ '**被平台判据拒掉的候选(贴回有接缝、没改到、身份审计不过)一律不退点**——报价时就要把这一点和点数一起讲给客户。厂商出图失败由平台自动退。' +
1015
+ '★只在灰度剧开放,未开放回 403 LOCAL_FRAME_EDIT_UNAVAILABLE(这时只能 generate_shot_frame 整张重画)。' +
1016
+ '修脸类不带 character_id 回 400 CHARACTER_REQUIRED;角色没有定妆图回 400 CHARACTER_NO_PORTRAIT。', LOCAL_EDIT_ARGS, async ({ storyboard_id, ...rest }) => jsonResult(await client.producePost(`/storyboards/${storyboard_id}/frame/local-edit/quote`, rest)));
1017
+ server.tool('local_frame_edit', '确认后**局部修改某一镜的某一帧**(异步,扣费)。参数必须与 quote_local_frame_edit 那次完全相同(区域 / 指令 / 类型 / 角色 / 候选数 / 底图任一项变了 → 409 QUOTE_PARAMS_CHANGED,重新报价即可,免费)。' +
1018
+ '返回 edit_id,之后用 get_local_frame_edit 轮询。' +
1019
+ '★**不会自动替换当前帧**:候选出完先给客户看(composited_url),客户选定后调 adopt_local_frame_edit 才写进这一镜、并把本镜视频标为待重生;不采用,当前帧就不变。' +
1020
+ '★被判据拒掉的候选照常计费、不退点。' + REVIEW_GATE_HINT('review_storyboards', '分镜') + ASSET_GATE_HINT + CONFIRM_HINT, { ...LOCAL_EDIT_ARGS, quote_id: z.string().describe('来自 quote_local_frame_edit'), ...REVIEW_ARGS, ...ASSET_REVIEW_ARG }, async ({ storyboard_id, ...rest }) => jsonResult(await client.producePost(`/storyboards/${storyboard_id}/frame/local-edit/generate`, rest)));
1021
+ server.tool('get_local_frame_edit', '查一次局部返修的进度与结果(免费)。status=running 继续等;completed 后逐个候选看:' +
1022
+ 'composited_url(贴回原图后的成品,给客户看的就是它)、verdict(pass=可直接用;warn=能用但要人看——REGION_SEAM_WARN 边界可能有接缝 / EDIT_NOT_APPLIED 基本没改到 / IDENTITY_AUDIT_UNAVAILABLE 身份没审成;' +
1023
+ 'reject=不建议用——REGION_MISALIGNED 贴回错位 / IDENTITY_AUDIT_REJECTED 不像本人)、metrics、identity_audit。' +
1024
+ '下载 composited_url 亲眼看过、讲给客户,客户选定后用 adopt_local_frame_edit。被拒候选不退点。', { storyboard_id: z.number().int().positive(), edit_id: z.string().describe('来自 local_frame_edit') }, async ({ storyboard_id, edit_id }) => jsonResult(await client.produceGet(`/storyboards/${storyboard_id}/frame/local-edit/${encodeURIComponent(edit_id)}`)));
1025
+ server.tool('adopt_local_frame_edit', '采用某个局部返修候选:把它写成这一镜的当前帧(免费)。与上传帧同一套后续处理:本镜视频标为待重生、相邻接缝跟着修。' +
1026
+ '★只在客户看过并选定后调用。返修之后这一帧若已换过 → 409 BASE_FRAME_CHANGED(对当前帧重新发起);' +
1027
+ 'verdict=reject 的候选默认拒绝采用(409 CANDIDATE_REJECTED),客户看过坚持要用才带 accept_rejected:true。', {
1028
+ storyboard_id: z.number().int().positive(),
1029
+ edit_id: z.string().describe('来自 local_frame_edit'),
1030
+ generation_id: z.number().int().positive().describe('要采用的候选(get_local_frame_edit 里的 generation_id)'),
1031
+ accept_rejected: z.boolean().optional().describe('默认不传。verdict=reject 的候选客户看过仍坚持要用时才传 true'),
1032
+ }, async ({ storyboard_id, edit_id, generation_id, accept_rejected }) => jsonResult(await client.producePost(`/storyboards/${storyboard_id}/frame/local-edit/${encodeURIComponent(edit_id)}/adopt`, { generation_id, accept_rejected })));
995
1033
  // ---------- 出视频(videos,大额) ----------
996
1034
  server.tool('quote_videos', '报价:给某一集批量出视频要多少点(与实际扣费同函数,较准)。返回 estimated_points、quote_id。零扣费。' +
997
1035
  '⚠️ 视频是大额花费,务必把点数清楚告诉用户并等其确认。' +
@@ -2344,6 +2382,23 @@ export function registerProduceTools(server, client) {
2344
2382
  + 'left/right(两侧在源视频上的入出点 src_in_ms/src_out_ms,已含 trim)、preview_url(短片)、'
2345
2383
  + 'compare_image_url(左=前镜转场开始前最后一帧,右=后镜转场结束后第一帧)、audit(audit_seams 结论;not_enabled=本剧未开启)。'
2346
2384
  + 'result.synth_skipped_shot_ids 非空表示这些镜配音尚未合成,预览按无配音处理。单镜集 seam_count=0。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.produceGet(`/episodes/${episode_id}/seam-previews`)));
2385
+ server.tool('audit_seam_directions', '出视频前的接缝方向对照(提示性,不拦任何步骤):对每对相邻镜拿「上一镜尾帧(没有就首帧)↔ 本镜首帧」看五件事——' +
2386
+ '同一主体在画面左右跳边(视线也反了会一并写明)、运动方向反了、景别跳级(同一主体大远景↔特写)。单镜都对、拼起来跳,多半是这几维。' +
2387
+ '视线单独反向、主光方向会被观察但不报:生产回放里这两维误报率太高(宁漏不扰)。' +
2388
+ '与 audit_seams 互补:那个比身份/服装/道具/物体状态,不管方向和位置。' +
2389
+ '★何时调:首帧(和要的尾帧)都出完、chain_frames 跑过之后,review_frames / generate_videos 之前。' +
2390
+ '换场、声明跳切、插入镜、字卡黑场的接缝自动跳过。' +
2391
+ '★先 dry_run(默认,免费):回待观察接缝与 estimated_points(上界)/ typical_points(通常花费),两个都告诉客户,按实际文本 token 扣费;' +
2392
+ '客户同意后传 dry_run=false + confirm_usage_billing=true。执行是异步的,立即返回;过一两分钟用 get_seam_directions 读结论。' +
2393
+ '已观察过且两侧帧没换的接缝不重复扣费。', {
2394
+ episode_id: z.number().int().positive(),
2395
+ dry_run: z.boolean().optional().describe('默认 true:只回计划与报价'),
2396
+ confirm_usage_billing: z.boolean().optional().describe('仅客户看过报价、明确同意按实际文本 token 扣费后传 true'),
2397
+ }, async ({ episode_id, dry_run, confirm_usage_billing }) => jsonResult(await client.producePost(`/episodes/${episode_id}/audit-seam-directions`, { dry_run: dry_run !== false, confirm_usage_billing: confirm_usage_billing === true })));
2398
+ server.tool('get_seam_directions', '(免费)读 audit_seam_directions 的结论:issues 每条带接缝镜号、问题(主体跳边、运动反向、景别跳级)与 fixes 修复建议' +
2399
+ '(chain_shot_from mode=reframe 以上一镜尾帧为锚重出本镜首帧 / update_shot 写明朝向后 generate_shot_frame / 上一镜已出视频时 align_prev_shot / split_shot 插过渡景别)。' +
2400
+ '结论是提示性的:讲给客户,由客户挑要修的;有意的反打、转身、冲击剪辑可忽略。not_audited>0 = 还有接缝没观察或帧已换过;running=true = 还在跑。' +
2401
+ 'review_frames 的 seam_direction_advisory 是同一份结论的摘要。本工具只看出视频前的静帧;视频出完后查接缝用 preview_seams(免费短片预览)。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.produceGet(`/episodes/${episode_id}/seam-directions`)));
2347
2402
  // ========== P2 · 连续性 / 场景组 / 多画幅 ==========
2348
2403
  server.tool('chain_frames', '整集首尾帧接力链:让每镜首帧承接上镜尾帧,镜间画面连续(比各镜独立出帧更顺)。' +
2349
2404
  '★何时调:首帧出完、tail_frame_plan 要的尾帧也补完之后,review_frames / generate_videos 之前。' +