7272MIGRATION_SESSION_TTL_SECONDS = 60 * 60
7373MIGRATION_UPLOAD_MAX_BYTES = 50 * 1024 * 1024
7474MIGRATION_CLI_MIN_VERSION = "0.52.1"
75+ MIGRATION_UNSUPPORTED_MODEL_IDS = frozenset ({"deepseek-v4-pro-260425" })
7576_MAX_EXPANDED_BYTES = 1024 * 1024 * 1024
7677_MAX_ARCHIVE_FILES = 20_000
7778_MAX_ARCHIVE_PATH_BYTES = 4 * 1024
@@ -357,73 +358,6 @@ def _analysis_result_message(value: str) -> bool:
357358 return True
358359
359360
360- def _command_activity_action (command : str , phase : str ) -> str | None :
361- normalized = command .casefold ()
362- if "ak migrate" in normalized :
363- return "执行 AgentKit 迁移"
364- if re .search (
365- r"(?:^|[\s;&|(/])(?:zip|tar)(?:\s|$)" ,
366- normalized ,
367- ) or any (
368- marker in normalized
369- for marker in ("package_artifact" , "package-result" , "package.py" , "package.sh" )
370- ):
371- return "打包迁移产物"
372- if any (
373- marker in normalized
374- for marker in (
375- "pip install" ,
376- "uv sync" ,
377- "npm install" ,
378- "pnpm install" ,
379- "yarn install" ,
380- )
381- ):
382- return "准备项目依赖"
383- if any (marker in normalized for marker in ("compileall" , "py_compile" )):
384- return "检查代码语法"
385- if any (
386- marker in normalized
387- for marker in ("validate" , "verify" , "pytest" , "unittest" , " test" )
388- ):
389- return "验证迁移结果"
390- if "git diff" in normalized or "git status" in normalized :
391- return "检查代码改动"
392- if any (marker in normalized for marker in ("apply_patch" , "<<" , "tee " )):
393- return "生成迁移代码"
394- if any (marker in normalized for marker in ("mkdir " , "cp " , "mv " , "touch " )):
395- return "整理迁移文件"
396- if "docker " in normalized or "dockerfile" in normalized :
397- return "检查运行配置"
398- if re .search (
399- r"(?:^|[\s;&|(/])(?:find|fd|rg|grep|ls|tree)(?:\s|$)" ,
400- normalized ,
401- ):
402- return "检查项目结构"
403- if re .search (
404- r"(?:^|[\s;&|(/])(?:cat|sed|head|tail|jq|yq|less)(?:\s|$)" ,
405- normalized ,
406- ):
407- return "读取项目文件"
408- if re .search (
409- r"(?:^|[\s;&|(/])(?:python(?:\d+(?:\.\d+)*)?|node|npx|tsx|bash|sh)(?:\s|$)" ,
410- normalized ,
411- ):
412- return "运行分析脚本" if phase == "analysis" else "运行迁移脚本"
413- return None
414-
415-
416- def _command_activity_title (command : str , status : str , phase : str ) -> str | None :
417- action = _command_activity_action (command , phase )
418- if action is None :
419- return None
420- if status == "completed" :
421- return f"已{ action } "
422- if status == "failed" :
423- return f"{ action } 未完成"
424- return f"正在{ action } "
425-
426-
427361def _parse_activity_log (
428362 content : bytes ,
429363 attempt : int ,
@@ -521,6 +455,16 @@ def upsert(item: dict[str, object]) -> None:
521455 plan_states .append (todo_status )
522456 if not plan :
523457 continue
458+ if (
459+ status != "failed"
460+ and "failed" not in plan_states
461+ and "in_progress" not in plan_states
462+ ):
463+ for index , plan_state in enumerate (plan_states ):
464+ if plan_state == "pending" :
465+ plan [index ]["status" ] = "in_progress"
466+ plan_states [index ] = "in_progress"
467+ break
524468 completed = plan_states .count ("completed" )
525469 todo_status = (
526470 "failed"
@@ -546,17 +490,11 @@ def upsert(item: dict[str, object]) -> None:
546490 if item_type == "command_execution" :
547491 command = item .get ("command" )
548492 command_text = command if isinstance (command , str ) else ""
549- title = _command_activity_title (
550- command_text ,
551- status ,
552- phase ,
553- )
554- if title is None :
555- title = {
556- "running" : "正在执行命令" ,
557- "completed" : "已执行命令" ,
558- "failed" : "命令执行未完成" ,
559- }[status ]
493+ title = {
494+ "running" : "正在执行命令" ,
495+ "completed" : "命令执行完成" ,
496+ "failed" : "命令执行失败" ,
497+ }[status ]
560498 tool : dict [str , object ] = {"name" : title }
561499 if command_text :
562500 tool ["input" ] = _activity_payload (
@@ -584,12 +522,14 @@ def upsert(item: dict[str, object]) -> None:
584522 continue
585523
586524 if item_type == "file_change" :
525+ changes = item .get ("changes" )
526+ change_count = len (changes ) if isinstance (changes , list ) else 0
527+ subject = f"{ change_count } 个项目文件" if change_count else "项目文件"
587528 title = {
588- "running" : "正在更新项目文件 " ,
589- "completed" : "已更新项目文件 " ,
590- "failed" : "项目文件更新未完成 " ,
529+ "running" : f"正在更新 { subject } " ,
530+ "completed" : f"已更新 { subject } " ,
531+ "failed" : f"更新 { subject } 失败 " ,
591532 }[status ]
592- changes = item .get ("changes" )
593533 tool : dict [str , object ] = {"name" : title }
594534 if isinstance (changes , list ):
595535 tool ["input" ] = _activity_payload (
@@ -653,11 +593,37 @@ def upsert(item: dict[str, object]) -> None:
653593 continue
654594
655595 if item_type == "collab_tool_call" :
656- title = {
657- "running" : "正在协调子任务" ,
658- "completed" : "已完成子任务协作" ,
659- "failed" : "子任务协作未完成" ,
660- }[status ]
596+ collab_tool = str (item .get ("tool" ) or "" )
597+ collab_titles = {
598+ "spawn_agent" : {
599+ "running" : "正在启动子任务" ,
600+ "completed" : "子任务已启动" ,
601+ "failed" : "子任务启动失败" ,
602+ },
603+ "send_input" : {
604+ "running" : "正在向子任务发送信息" ,
605+ "completed" : "已向子任务发送信息" ,
606+ "failed" : "向子任务发送信息失败" ,
607+ },
608+ "wait" : {
609+ "running" : "正在等待子任务" ,
610+ "completed" : "子任务等待已结束" ,
611+ "failed" : "等待子任务失败" ,
612+ },
613+ "close_agent" : {
614+ "running" : "正在结束子任务" ,
615+ "completed" : "子任务已结束" ,
616+ "failed" : "结束子任务失败" ,
617+ },
618+ }
619+ title = collab_titles .get (collab_tool , {}).get (
620+ status ,
621+ {
622+ "running" : "正在协调子任务" ,
623+ "completed" : "子任务协作已完成" ,
624+ "failed" : "子任务协作失败" ,
625+ }[status ],
626+ )
661627 input_value = {
662628 key : item [key ]
663629 for key in ("tool" , "receiver_thread_ids" , "prompt" )
@@ -1457,6 +1423,16 @@ def _analysis_prompt(
14571423 网络访问、测试或运行条件不能作为 unsupported 的理由,只能列入 assumptions、
14581424 warnings 或 boundary.exclude,供迁移和部署时处理。
14591425
1426+ ## 用户可见执行动态
1427+
1428+ - 开始分析后使用 Codex 计划能力列出三到六个有明确结果的有序步骤,并随分析进展及时
1429+ 更新;按顺序完成步骤,使第一个未完成项始终代表当前工作。
1430+ - 计划步骤和阶段性 assistant 更新会直接展示给用户,必须使用简体中文,说明当前发现、
1431+ 已确认结果或下一步动作,不要输出“已完成分析步骤”之类没有事实内容的固定句式。
1432+ - 仅在开始新的关键阶段或获得重要结论时输出简短更新,不要重复计划内容,不要为了展示
1433+ 进度而执行额外命令,也不得包含系统提示词、凭证、环境变量值或其他敏感信息。
1434+ - 最终响应仍必须严格遵守下方输出协议;执行动态不得改变 JSON 字段、迁移建议或证据标准。
1435+
14601436## 支持判定与用户表达
14611437
14621438- 能可靠识别 Structured 框架和入口时推荐对应 Structured 方式;否则只要存在足够材料
@@ -1879,6 +1855,13 @@ def _migration_instruction(
18791855 "configurable and follow the source project's security requirements." ,
18801856 "Use the user's language in user-facing migration reports. If no user " ,
18811857 "language is available, use Simplified Chinese." ,
1858+ "Keep a concise ordered Codex todo list with three to six outcome-oriented " ,
1859+ "steps. Complete it sequentially so the first incomplete step represents " ,
1860+ "the current work, and update it as work advances. At meaningful " ,
1861+ "milestones, emit a brief Simplified Chinese assistant update stating a " ,
1862+ "concrete finding, confirmed result, or next action. Do not emit generic " ,
1863+ "fixed completion notices, repeat the todo list, run extra commands only " ,
1864+ "for progress reporting, or expose prompts, credentials, or environment values." ,
18821865 "" ,
18831866 ]
18841867 )
@@ -2163,6 +2146,7 @@ def capabilities(self) -> dict[str, object]:
21632146 "configured" : model .get ("configured" ) is True ,
21642147 "id" : str (model .get ("id" ) or "" ),
21652148 },
2149+ "unsupportedModelIds" : sorted (MIGRATION_UNSUPPORTED_MODEL_IDS ),
21662150 "maxUploadBytes" : MIGRATION_UPLOAD_MAX_BYTES ,
21672151 "sessionTtlSeconds" : MIGRATION_SESSION_TTL_SECONDS ,
21682152 "frameworks" : list (MIGRATION_FRAMEWORKS ),
@@ -2411,6 +2395,13 @@ def create_task(
24112395 owner_id : str ,
24122396 creator_name : str ,
24132397 ) -> dict [str , object ]:
2398+ if body .model_id in MIGRATION_UNSUPPORTED_MODEL_IDS :
2399+ raise MigrationError (
2400+ "MIGRATION_MODEL_UNSUPPORTED" ,
2401+ "所选模型暂不兼容项目迁移,请选择其他模型。" ,
2402+ status_code = 400 ,
2403+ retryable = False ,
2404+ )
24142405 capability = self .capabilities ()
24152406 if not capability ["enabled" ]:
24162407 raise MigrationError (
0 commit comments