oh-my-openagent 配置迁移缺陷排查:2026-08-reasoning-unification 与模型配置链的一致性修复

oh-my-openagent 配置迁移缺陷排查:2026-08-reasoning-unification 与模型配置链的一致性修复

【免费下载链接】oh-my-openagentOmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent

本文基于 oh-my-openagent(OmO)仓库中的 Discord 配置迁移分诊记录(triage 文档),完整还原一次"迁移写入的配置被运行时 Schema 拒绝"的仓库自有缺陷(repository-owned defect)的定位过程:从 Discord 报告、GitHub issue #6567 的表象,到2026-08-reasoning-unification迁移的源码级实现、AgentOverrideConfigSchema

的校验缺口、config-chain 模型输入的遗漏,再到测试验证与修复边界。读完本文,你将掌握 OmO 配置迁移引擎的完整调用链、reasoning 字段统一化的归一化规则,以及如何通过 fixture 测试锁定这类"迁移端与校验端不一致"的回归。

问题表象:迁移之后配置失效、委派不再遵循模型

2026-08-05(KST)的分诊记录显示,用户通过 Discord 报告(报告 ID1534283281300979743

)反馈了与配置迁移/委派相关的异常,核心现象有两个:

  1. 迁移会留下非法配置:2026-08-reasoning-unification迁移把规范形态(canonical)写入agents.*.models,但当时origin/dev的AgentOverrideConfigSchema仍然拒绝该键;
  2. 委派停止遵循已配置的模型:由于models键被从 config-chain 的模型输入中省略,运行时拿不到迁移写入的模型链,导致 delegation 不再按用户配置选择模型。

分诊小组检查了关联的 Discord 消息及周边迁移/委派讨论、GitHub issue #6567 与相关 PR #6560、#6516,并核对了最新origin/dev上的迁移、插件 schema、config-chain、doctor 与运行时加载路径,最终给出所有权判定:PASS,这是仓库自有缺陷

。

OmO 配置迁移体系全貌:发现、计划、执行、转换

要理解这个缺陷,先看2026-08-reasoning-unification

在迁移体系中的位置。根据 config-migration/AGENTS.md,该模块负责:

  • 发现(discovery):定位遗留配置文件(oh-my-opencode/oh-my-openagentJSON[C]、~/.omo/config.jsonc),由 discovery.ts 及discovery-roots.ts、discovery-paths.ts实现,涉及OPENCODE_CONFIG_MIGRATION_ID与CONFIG_JSONC_MIGRATION_ID两个组;
  • 计划(planning):由 migration-plans.ts 的createLegacyConfigMigrationPlans构建迁移计划;
  • 执行(execution):由 migration-executor.ts 的executeLegacyConfigMigrationPlan落地,支持 dry-run、备份与日志;
  • 内容转换(transform):transform-opencode.ts/transform-config-jsonc.ts,以及本次缺陷核心的 reasoning-unification.ts。

关键约定(见 AGENTS.md):

  • 转换是纯函数:transform 只把加载的源返回为ConfigMigrationTransformResult,所有文件系统副作用都经由 omo-config-core 的迁移引擎完成,绝不在转换层直接写配置;
  • 迁移 ID 必须稳定:每个迁移以固定 id 记录在目标文件的_migrations数组中,已发布的 id 不得复用或改名,历史 id 追加到legacy-history.ts;
  • no-clobber 语义:deep-diff.ts保证已存在的目标值总是优先于迁移值。

迁移引擎本体位于packages/omo-config-core/src/migration/。以 commit.ts 为例,提交阶段会把_migrations标记写入目标文档(const document = { ...merged.merged, _migrations: marker }),使迁移具备幂等性:predicate.ts

检查目标中的标记,已应用过的迁移不会重复执行。整个链路在 OpenCode 插件启动、Senpi 启动、安装流程与 CLI 中都会触发(详见 configuration.md 对两个迁移组的说明)。

2026-08-reasoning-unification

迁移实现剖析

迁移的 ID 常量定义在 reasoning-unification.ts:

export const REASONING_UNIFICATION_MIGRATION_ID = "2026-08-reasoning-unification"

入口transformReasoningUnification(document)(L190-L197)返回{ diagnostics, document },其中 diagnostics 收集冲突告警,document 是规范化后的配置树。转换逻辑对根级categories、agents、models、[senpi]/[codex]/[opencode]分块以及嵌套profiles

做递归处理。

底层归一化:legacy 模型字段 → canonical 字段

迁移的核心归一化来自 omo-config-core 的 fallback-models.ts:

  • normalizeLegacyModelFields(L38-L72)删除variant、reasoningEffort、thinking、textVerbosity、maxTokens、providerOptions等 legacy 键,并按下述优先级收敛出规范reasoning

    :

    reasoning > reasoningEffort > variant > (thinking.type === "disabled" ? "off")

    即:显式reasoning优先,其次reasoningEffort,再次variant;若只写了thinking: { type: "disabled" },则归一化为reasoning: "off"

    。

  • thinking: { type: "enabled", budgetTokens }与textVerbosity被并入provider_options

    ;

  • maxTokens(camelCase)归一化为max_tokens

    ;

  • canonicalModelString(L24-L36)把三种模型字符串写法统一为provider/model:reasoning

    冒号后缀形式:

    • "p/m(xhigh)"括号后缀 →p/m:xhigh
    • "p/m high"空格后缀 →p/m:high
    • 已带冒号后缀的p/m:minimal保持不变

迁移的八条规则与 fixture 验证

迁移测试 reasoning-unification.test.ts 的第一个用例声明"rules one through eight match exact expected output",使用仓库内置 fixture 对拍:

  • 输入 fixture:2026-08-reasoning-unification/fixture-input.json
  • 期望输出:2026-08-reasoning-unification/fixture-expected.json

以categories.quick

为例,输入为:

{ "model": "apitopia/kimi-for-coding-highspeed-unlocked", "reasoningEffort": "minimal", "fallback_models": [ { "model": "apitopia/z-ai/glm-5.2-ultrafast-unlocked", "reasoningEffort": "none" }, "openai-codex/gpt-5.6-luna-fast minimal" ], "maxTokens": 8192 }

迁移后变为:

{ "models": [ { "model": "apitopia/kimi-for-coding-highspeed-unlocked", "reasoning": "minimal" }, { "model": "apitopia/z-ai/glm-5.2-ultrafast-unlocked", "reasoning": "off" }, "openai-codex/gpt-5.6-luna-fast:minimal" ], "max_tokens": 8192 }

可归纳出如下规则:

  1. 模型链合并:model(主模型)+ 已有models+fallback_models(后备模型)合并进统一的models数组,首项为主模型,其余为后备;随后删除顶层model与fallback_models;
  2. reasoningEffort→reasoning:对象项内的 legacy 推理级别归一为reasoning("minimal"、"none"→"off"等);
  3. 模型字符串后缀归一:"gpt-5.6-luna-fast minimal"这类空格后缀改写为冒号形式"gpt-5.6-luna-fast:minimal";
  4. maxTokens→max_tokens:snake_case 规范化;
  5. 无fallback_models且无冲突时保留单模型形态:如categories.deep仅model + variant,迁移后为{ "model": "...", "reasoning": "medium" };
  6. 冲突诊断:同一实体同时出现reasoning、reasoningEffort、variant等多个推理字段时,按优先级保留一个,其余写入 diagnostics,如conflict: categories.conflict dropped variant="high" kept reasoningEffort="xhigh";
  7. [opencode]分块单独处理:normalizeOpenCodeBlock只重写 agent/category 已知键,native、provider等无关结构原样保留(见 L174-L188);[senpi]/[codex]则复用同一normalizeTypedBlock递归(L158-L160);
  8. profiles递归:profile 内的整块配置继续递归规范化(L164-L170)。

注意第 7 条是缺陷的关键背景:迁移对[opencode]块同样会写入规范形态(如把variant: "high"变成reasoning: "high"、把model + fallback_models合并为models

数组),而 OpenCode 侧的校验 Schema 尚未同步放开。

缺陷定位:Schema 拒绝、config-chain 遗漏、doctor 告警

分诊确认了三个相互咬合的问题点:

1.AgentOverrideConfigSchema拒绝models

键

OpenCode 侧的 agent 覆盖 Schema 定义在 agent-overrides.ts。该 Schema 明确允许:

  • model(标为 deprecated,注释说明"模型应从 category 默认值继承")
  • models(有序模型链,首项主模型、其余后备)

……但分诊指出,最新origin/dev的AgentOverrideConfigSchema仍拒绝迁移写入的agents.*.models键——即 Schema 的z.object

默认 strict/未知键剔除行为把合法迁移结果拦在了校验层之外,配置在校验阶段即被判为非法,这正是"迁移留下 invalid config"的直接原因。

2. config-chain 模型输入遗漏models

即使校验放行,config-chain 构造模型输入时也遗漏了models键

:迁移合并出的完整模型链没有进入运行时模型解析的输入,导致 delegation 解析不到配置的模型,行为回退到默认值。这与 Discord 报告中"委派停止遵循配置模型"的现象完全吻合。

3.[opencode]的variant/fallback_models

兼容告警

分诊还观察到,doctor/校验链路对[opencode]块中仍然支持的 legacy 键(variant、fallback_models)发出警告。也就是说,迁移端已经完成variant→reasoning、fallback_models→models的统一化,而运行时校验端仍处于"兼容旧键"阶段,两端语义不一致:迁移改写后variant消失、models

出现,但校验端还在期待旧键、拒绝新键。

三个问题叠加,形成完整闭环:迁移(写 canonical)→ Schema(拒 canonical)→ config-chain(漏 canonical)→ doctor(报 legacy 兼容警告)

,四个环节步调不一。

测试验证:用 fixture 与引擎 E2E 锁定行为

迁移测试 reasoning-unification.test.ts 提供了四层验证:

  1. 纯转换对拍:transformReasoningUnification(fixtureInput)的输出与fixtureExpected完全相等,并断言 diagnostics 包含冲突记录conflict: categories.conflict dropped variant="high" kept reasoningEffort="xhigh";
  2. 优先级语义:输入{ reasoning: "low", reasoningEffort: "xhigh", variant: "high" }时,迁移结果保留reasoning: "low",且 journal 中不会出现kept reasoningEffort="xhigh"——验证 canonical 键在冲突中的胜出;
  3. 引擎级 E2E:在临时 HOME 下写入.omo/omo.jsonc,经createLegacyConfigMigrationPlans找到 reasoning 迁移计划后依次执行 dry-run(返回status: "planned"且 preview 与期望一致)与正式执行(返回status: "migrated"),最终文件等于fixtureExpected + { _migrations: ["2026-08-reasoning-unification"] },journal diagnostics 正确落盘,且目录下出现omo.jsonc.bak.*备份——印证了 dry-run、备份、journal、原子提交、幂等标记五件套;
  4. typed block 与 opencode 边界:[senpi]/[codex]递归规范化、profiles递归、[opencode]只改已知 agent/category 键、native/provider无关结构不被触碰。

测试还关联了启动路径:packages/omo-opencode/src/startup-migration.test.ts与packages/omo-senpi/src/components/config-startup/index.test.ts

均涉及该迁移 ID,说明转换确实在 OpenCode/Senpi 启动期被拉起。

修复边界与 PR 状态:只合入可复现、已测试的子集

分诊对相关 PR 做了明确切割:

  • PR #6560:修复的是生成 JSON Schema 的 identity/required-array 问题,与本次运行时迁移路径无关,保持独立、另行合入;
  • PR #6516:目标是规范化的运行时模型链,但它是无专属测试的草稿外部 PR,且包含更广的 fallback 改动,风险不可控;
  • 本次分支:仅实现当前origin/dev上可复现、有测试覆盖的缺陷子集——即修复AgentOverrideConfigSchema对models的接纳、补上 config-chain 模型输入中的models传递、同步[opencode]的 legacy 键告警策略,不夹带 PR #6516 的宽泛改动。

这一策略体现了分诊的工程纪律:修复范围与证据范围严格对齐,未经验证的改动不进主线。

分诊流程规范:证据留存与清理

triage 文档还记录了缺陷处置的流程性内容,值得作为团队协作参考:

  • 来源证据:保留 issue #6567、PR #6560、PR #6516 的引用作为审计线索;
  • 信息脱敏(Omitted or sanitized):Discord 凭证、token、认证头与原始凭证文件一律不落入文档;无关频道消息与 issue/PR JSON 被剔除;缺失的 Discord 可视化负载不臆造,由文本、issue、代码与运行时证据独立确立所有权;
  • Triage cleanup:确认无残留的 Discord 浏览器标签、桌面窗口、服务器进程、端口、临时文件或登录会话,避免敏感会话泄漏。

总结:从一条 Discord 报告到一次体系级修复

这次缺陷的本质,是迁移端与校验端对"canonical 配置形态"的定义不同步:2026-08-reasoning-unification按照"统一reasoning、合并models链"的既定方向改写配置,而AgentOverrideConfigSchema、config-chain 与 doctor 仍停留在 legacy 键时代,导致同一份配置在"写入"与"读取"两个阶段被不同对待。修复路径由此清晰:让校验层与运行时接受迁移产物(agents.*.models、canonicalreasoning),同时把[opencode]

的 legacy 键兼容告警收敛到与新形态一致。

对使用者而言,如果遇到"升级后配置看似迁移成功、但委派模型不生效"的问题,可先检查~/.omo/omo.jsonc是否已出现_migrations: ["2026-08-reasoning-unification"]标记(代表迁移已应用),再核对agents.*.models

是否被运行时接受;对维护者而言,本文梳理的 reasoning-unification.ts、agent-overrides.ts、fallback-models.ts 与 fixture 对 四件套,是复现与回归验证该缺陷的完整入口。

【免费下载链接】oh-my-openagentOmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考