{
  "_comment": "机器契约。AI Agent 读取本文件后按 targets[] 定位宿主配置并写入。人类说明见 AGENT.md。",
  "version": 1,
  "specVersion": "2026-10-24",
  "canonical": {
    "name": "WorkBuddy builtin model capability set",
    "description": "内置模型的思考强度与上下文窗口参数的规范化描述，用于跨宿主同步",
    "extractedFrom": "客户端产品配置 acc-product-config-v3 → models[]",
    "readOnlyUpstream": true,
    "note": "内置模型定义由服务端下发，本文件是快照，不是可写配置源"
  },
  "sources": {
    "agentGuide": "./AGENT.md",
    "template": "./models.template.json",
    "matrix": "./builtin-models.json"
  },
  "placeholders": {
    "baseUrl": "{{BASE_URL}}",
    "apiKey": "{{API_KEY}}"
  },

  "capabilityFields": {
    "contextWindow.defaultLength": "默认上下文长度（token），界面初始档位",
    "contextWindow.supportedLengths": "可切换的上下文档位；单元素等于实际固定，不代表可调",
    "reasoning.defaultEffort": "默认思考强度",
    "reasoning.supportedEfforts": "可切换的思考强度档位；缺失表示只有单档",
    "reasoning.canDisableThinking": "是否允许关闭思考；缺省表示宿主/模型未声明",
    "reasoning.effort": "固定的单档强度，与 defaultEffort 同时存在时以 defaultEffort 为准",
    "onlyReasoning": "该模型只有推理模式（不等于可关闭思考）",
    "maxInputTokens": "输入 token 上限",
    "maxOutputTokens": "输出 token 上限",
    "maxAllowedSize": "宿主允许配置的上下文最大上限",
    "temperature": "采样温度",
    "supportsToolCall": "支持工具调用",
    "supportsImages": "支持图像输入"
  },

  "inventory": {
    "required": true,
    "performedBy": "agent",
    "description": "Agent 必须自己读取目标配置文件并列出已有模型条目 id，作为对齐输入。禁止要求用户手动粘贴配置或口头报模型清单。",
    "output": "本机已有模型 id 列表"
  },

  "alignment": {
    "_comment": "本机 id → 内置 id 的对齐算法。按 levels 顺序执行，每级只在唯一命中时自动采用。",
    "levels": [
      { "order": 1, "rule": "exact", "description": "字符串完全相等", "auto": true },
      { "order": 2, "rule": "user-mapping", "description": "用户显式给出的 localId → canonicalId 映射，覆盖其余所有规则", "auto": true },
      { "order": 3, "rule": "normalized", "description": "normalize(local) == normalize(canonical)", "auto": "unique-only" },
      { "order": 4, "rule": "suffix-stripped", "description": "normalize(stripSuffix(local)) == normalize(stripSuffix(canonical))", "auto": "unique-only" }
    ],
    "normalize": "转小写后移除所有不属于 [a-z0-9] 的字符",
    "stripSuffixes": ["-preview", "-ioa", "-aicc", "-volc", "-lkeap", "-taco", "-dev", "-1m", "-gf"],
    "stripSuffixRepeat": true,
    "onAmbiguous": "列出全部候选交给用户选择，禁止自动挑选",
    "onUnmatched": "不自动套参数，按 presets 章节列出建议档位并等待用户确认",
    "forbidden": [
      "substring / contains 匹配",
      "编辑距离或相似度打分",
      "按『名字最像』自行选定"
    ],
    "knownTraps": [
      {
        "local": "glm-5.3-flash",
        "wrong": "glm-5.3-flashx",
        "right": "glm-5.3-flash-ioa",
        "reason": "同族命名，包含匹配会误命中；必须走后缀剥离"
      }
    ],
    "verifiedSamples": [
      { "local": "glm-5.3", "canonical": "glm-5.3-ioa", "level": "suffix-stripped" },
      { "local": "glm-5.3-flash", "canonical": "glm-5.3-flash-ioa", "level": "suffix-stripped" },
      { "local": "claude-opus-5-5", "canonical": "claude-opus-5.5", "level": "normalized" },
      { "local": "claude-sonnet-5-5", "canonical": "claude-sonnet-5.5", "level": "normalized" },
      { "local": "qwen3.7-plus", "canonical": null, "level": "unmatched" },
      { "local": "claude-haiku-5-5", "canonical": null, "level": "unmatched" }
    ]
  },

  "internalModels": {
    "_comment": "腾讯内部自用模型的识别与处理规则。外部网关不识别 -ioa 名字。",
    "suffix": "-ioa",
    "meaning": "腾讯内部专用模型 id 后缀，非公开，外部配置不可直接使用",
    "templatePolicy": "models.template.json 已把所有 -ioa 条目转换为公共 id（去除后缀），能力参数保持不变；原始 id 与 publicId 保留在 builtin-models.json 的 internal:true 条目里",
    "agentRules": [
      "本机 id 不带 -ioa（正常外部用法）→ 按模板直接写入",
      "本机 id 带 -ioa（内网环境）→ 保留用户 id 不改，只同步 contextWindow / reasoning 等能力字段",
      "新增条目时禁止写入 -ioa 名字，除非用户明确声明在内网环境",
      "生成报告时，若吞掉了 -ioa 后缀，要说明原 id 与写入 id 的对应关系"
    ],
    "channelSuffixes": {
      "_comment": "渠道或环境后缀，模型本身公开，按原样保留只改能力字段",
      "suffixes": ["-aicc", "-dev", "-gf", "-lkeap", "-volc"],
      "field": "builtin-models.json 中标记为 channel"
    },
    "affectedCount": 20
  },

  "presets": {
    "_comment": "模板无对应模型（上游自建/中转模型）时的通用参数兜底。Agent 在 template 找不到 id 且用户要求补齐时使用。",
    "conservative": {
      "label": "保守 256K / 单档上下文 / 三档思考",
      "contextWindow": { "defaultLength": 256000, "supportedLengths": [256000] },
      "reasoning": {
        "canDisableThinking": true,
        "defaultEffort": "medium",
        "supportedEfforts": ["low", "medium", "high"],
        "summary": "auto"
      },
      "maxInputTokens": 256000,
      "maxOutputTokens": 32000
    },
    "standard": {
      "label": "标准 300K / 三档上下文 / 三档思考",
      "contextWindow": { "defaultLength": 300000, "supportedLengths": [300000, 600000, 1000000] },
      "reasoning": {
        "canDisableThinking": true,
        "defaultEffort": "high",
        "supportedEfforts": ["low", "high", "max"],
        "summary": "auto"
      },
      "maxInputTokens": 1000000,
      "maxOutputTokens": 64000
    },
    "aggressive": {
      "label": "激进 1M / 三档上下文 / 五档思考",
      "contextWindow": { "defaultLength": 1000000, "supportedLengths": [300000, 600000, 1000000] },
      "reasoning": {
        "canDisableThinking": true,
        "defaultEffort": "high",
        "supportedEfforts": ["low", "medium", "high", "xhigh", "max"],
        "summary": "auto"
      },
      "maxInputTokens": 1000000,
      "maxOutputTokens": 128000
    }
  },

  "targets": [
    {
      "host": "workbuddy",
      "label": "WorkBuddy",
      "detect": [
        "~/.workbuddy/models.json",
        "~/.workbuddy/settings.json"
      ],
      "file": "~/.workbuddy/models.json",
      "format": "json",
      "structure": "array",
      "containerPath": null,
      "matchKey": "id",
      "createIfMissing": true,
      "templateFilter": "category:chat",
      "fieldMap": {
        "contextWindow": "contextWindow",
        "reasoning": "reasoning",
        "maxInputTokens": "maxInputTokens",
        "maxOutputTokens": "maxOutputTokens",
        "maxAllowedSize": "maxAllowedSize",
        "onlyReasoning": "onlyReasoning",
        "supportsReasoning": "supportsReasoning",
        "supportsImages": "supportsImages",
        "supportsToolCall": "supportsToolCall",
        "temperature": "temperature"
      },
      "overwriteKeys": [
        "contextWindow",
        "reasoning",
        "maxInputTokens",
        "maxOutputTokens",
        "maxAllowedSize",
        "onlyReasoning",
        "supportsReasoning",
        "supportsImages",
        "supportsToolCall",
        "temperature"
      ],
      "preserveKeys": [
        "apiKey",
        "url",
        "model",
        "name",
        "vendor",
        "bypassProxy",
        "useCustomProtocol",
        "maxTokens",
        "id"
      ],
      "seedOnCreate": {
        "vendor": "Custom",
        "bypassProxy": true,
        "useCustomProtocol": false
      },
      "limitations": [],
      "reload": "重启 WorkBuddy",
      "confidence": "verified"
    },
    {
      "host": "zcode",
      "label": "ZCode",
      "detect": [
        "~/.zcode/v2/config.json",
        "~/.zcode/cli/setting.json"
      ],
      "file": "~/.zcode/v2/config.json",
      "format": "json",
      "structure": "object",
      "containerPath": "provider.<provider-id>.models",
      "matchKey": "id",
      "createIfMissing": false,
      "templateFilter": "category:chat",
      "providerTemplate": {
        "name": "<显示名>",
        "kind": "openai-compatible",
        "source": "custom",
        "options": {
          "apiKey": "{{API_KEY}}",
          "baseURL": "{{BASE_URL}}",
          "apiKeyRequired": true
        }
      },
      "fieldMap": {
        "contextWindow.defaultLength": "limit.context",
        "maxOutputTokens": "limit.output"
      },
      "overwriteKeys": [
        "limit"
      ],
      "preserveKeys": [
        "name",
        "displayName",
        "vision",
        "tool_call",
        "reasoning",
        "input",
        "output"
      ],
      "limitations": [
        "不支持多档上下文切换：只写 defaultLength 到 limit.context，supportedLengths 无效",
        "不支持按模型配置思考强度档位；模型在 provider 层声明",
        "编辑前必须先退出 ZCode，否则运行中的内存副本会覆盖磁盘修改",
        "文件权限需保持 0600"
      ],
      "reload": "退出后重新启动 ZCode",
      "confidence": "verified"
    },
    {
      "host": "deepseek-harness",
      "label": "DeepSeek Harness",
      "detect": [
        "$DSH_HOME/settings.yaml",
        "~/Library/Application Support/DeepSeekHarness/config.yaml",
        "~/.config/DeepSeekHarness/config.yaml",
        "%APPDATA%/DeepSeekHarness/config.yaml"
      ],
      "file": "$DSH_HOME/settings.yaml",
      "format": "yaml",
      "structure": "object",
      "containerPath": "llm-pi-ai.providers.<provider-id>.models",
      "matchKey": "id",
      "createIfMissing": false,
      "templateFilter": "category:chat",
      "fieldMap": {
        "id": "id"
      },
      "overwriteKeys": [],
      "preserveKeys": ["*"],
      "limitations": [
        "版本差异大：settings.yaml(llm-pi-ai) 与桌面端 config.yaml 结构不同，必须先探测实际文件再适配",
        "只保证 models[].id 可写入，其余能力字段按现有结构就地适配，禁止新造键名",
        "apiKeyEnv 存的是环境变量名而非密钥本身，不要写入真实 key",
        "现有结构若无法表达某个能力参数，跳过并向用户说明该版本不支持"
      ],
      "reload": "重启 DeepSeek Harness",
      "confidence": "probe-required"
    },
    {
      "host": "codex",
      "label": "Codex CLI",
      "detect": [
        "~/.codex/config.toml"
      ],
      "file": "~/.codex/config.toml",
      "format": "toml",
      "structure": "object",
      "containerPath": null,
      "matchKey": null,
      "createIfMissing": false,
      "templateFilter": null,
      "fieldMap": {
        "reasoning.defaultEffort": "model_reasoning_effort"
      },
      "overwriteKeys": ["model_reasoning_effort"],
      "preserveKeys": ["model", "model_provider", "model_providers", "*"],
      "limitations": [
        "全局单一 effort，不支持按模型分别设置",
        "上下文窗口不可配置，由模型与宿主自行决定",
        "写入前先确认 config.toml 能被 TOML 解析"
      ],
      "reload": "新会话生效",
      "confidence": "partial"
    },
    {
      "host": "claude-code",
      "label": "Claude Code",
      "detect": [
        "~/.claude/settings.json",
        "~/.claude.json"
      ],
      "file": "~/.claude/settings.json",
      "format": "json",
      "structure": "object",
      "containerPath": null,
      "matchKey": null,
      "createIfMissing": false,
      "templateFilter": null,
      "fieldMap": {},
      "overwriteKeys": [],
      "preserveKeys": ["*"],
      "limitations": [
        "不支持自定义模型能力参数表，仅支持全局 MAX_THINKING_TOKENS 等环境变量",
        "上下文窗口不可配置",
        "本站点对该宿主只提供参数参考，不执行写入"
      ],
      "reload": "新会话生效",
      "confidence": "not-supported"
    }
  ],

  "scopePolicy": {
    "default": "patch-only",
    "modes": {
      "patch-only": "只修补目标文件中已存在 id 的能力字段，不新增任何条目，不删除任何条目",
      "selected": "只处理用户给出的 id 清单；清单内本机不存在的跳过，除非用户明确要求新增",
      "full": "导入模板全部条目，执行前必须向用户二次确认"
    },
    "trigger": {
      "patch-only": "用户只说同步/修正档位，未提新增模型",
      "selected": "用户给出模型 id 清单，或页面生成的指令里带「范围：只处理下面这些模型 id」",
      "full": "用户明确要求全部导入 / 把这些模型都加上"
    },
    "neverInfer": "模板条目数不等于应写入条数。full 永远不是默认值，也不要从「同步成模板的样子」这类措辞推断为 full。",
    "rationale": "一次性写入 86 条会冲掉用户精心维护的精简模型列表，属破坏性操作"
  },

  "writePolicy": {
    "backup": true,
    "backupSuffix": ".bak-{YYYYMMDD-HHMMSS}",
    "atomicWrite": true,
    "neverDeleteEntries": true,
    "neverOverwriteNonEmpty": ["apiKey", "url", "baseURL", "model", "vendor"],
    "requireUserProvidedSecret": true,
    "noPlaceholderInProduction": true,
    "scopeFollowsUserIntent": true,
    "note": "用户只要求改某一类参数时，不要扩大写入范围；占位符不得写进生产配置"
  },

  "validation": {
    "afterWrite": [
      "文件可解析（JSON/YAML/TOML）",
      "matchKey 值唯一",
      "defaultEffort ∈ supportedEfforts（两者都存在时）",
      "defaultLength ∈ supportedLengths（后者非空时）",
      "条目总数不减少"
    ],
    "onFailure": "用备份回滚，报告失败原因，不重试写入"
  },

  "report": {
    "format": "text",
    "requiredFields": [
      "host",
      "file",
      "backupPath",
      "added",
      "patched",
      "skipped",
      "patchDetails",
      "limitations",
      "nextAction"
    ]
  }
}
