OpenCode 2.0 迁移指南:插件 API、服务端契约与配置格式的破坏性变更
Migrating to OpenCode 2.0
OpenCode 2.0 带来三处有意为之的破坏性变更:插件改用新 plugin API,服务端 API 与客户端契约更新,终端客户端配置从分层 tui.json(c) 文件改为单一全局 cli.json(自动迁移)。
OpenCode 1 和 OpenCode 2 都使用 opencode 命令,默认不再并排安装。在安装 V2 之前,请移除由包管理器管理的 V1 安装;V2 的 curl 安装程序会替换 V1 二进制文件。
破坏性变更
V2 有三项有意的破坏性变更:
- 插件使用新的插件 API。
- 服务器 API 和客户端有了新的契约。
- 终端客户端配置从分层的
tui.json(c)文件迁移到一个全局cli.json文件(自动迁移)。
这些区域之外受支持的 V1 功能旨在与 V1 保持兼容。V1 schema 接受的一些字段从未有 V2 等价物,会被有意忽略;这些列在已接受但不支持的字段下。
.opencode/ 中现有的受支持服务器配置字段、代理定义、命令定义、技能和其他文件应继续无需更改即可工作。如果本指南中描述的受支持行为在 V2 中停止工作,请将其视为兼容性错误,而不是预期的迁移要求。
安装 V2
使用终端启动指南安装 V2 终端客户端。
你不需要在开始使用 V2 之前重写所有内容。一个实用的迁移方式是:
- 保留你现有的配置和基于文件的定义。
- 启动 V2 并验证模型、凭据、代理、权限和 MCP 服务器。
- 移植插件,因为 V1 插件实现无法在 V2 中运行。
- 移植调用服务器 API 的集成。
- 当你准备好时,将配置转换为原生 V2 形式。此步骤是可选的。
在验证迁移期间保留一份 V1 设置的副本。V1 和 V2 使用相同的配置位置,因此不要将 V1 指向已转换为仅原生 V2 形式的文件。
配置
本节涵盖 JSON/JSONC 配置以及 .opencode/ 下基于文件的定义。
使用你现有的配置
V2 从与 V1 相同的位置读取现有的全局和项目配置:
~/.config/opencode/opencode.json(c)
<project>/opencode.json(c)
<project>/.opencode/opencode.json(c)V2 读取这些相同的位置。它在内存中规范化受支持的 V1 和原生 V2 字段,而不重写源文件。现有的受支持 V1 配置旨在继续工作,因此你无需转换它即可尝试或采用 V2。
让 OpenCode 进行迁移
V1 配置格式仍然受支持。原生 V2 格式是可选的,它使若干设置更加明确且符合人体工程学。
推荐的迁移路径是让 OpenCode 为你更新配置:
Migrate my OpenCode configuration, including file-based definitions, from the V1 format to the native V2 format.
Preserve its behavior and all unrelated settings.OpenCode 可以检查完整文件,应用下面的相关更改,并避免重写不需要更改的设置。转换不需要一次性完成:受支持的 V1 和原生 V2 字段可以在顶层共存。当两种形式设置相同的规范值时,有效的原生 V2 值优先,无论 JSON 键的顺序如何。
嵌套混合是有意限制的。OpenCode 识别 mcp、compaction 和 experimental 内的混合 V1 和 V2 成员,但它不会递归推断单个代理、提供者、命令或模型内部的格式。请将每个嵌套条目完全保持为一种格式。受支持的 V1 语法本身保持静默;格式错误的值、不支持的旧字段以及冲突的 V1/V2 值会产生警告,同时无关的有效设置继续加载。
共享
已弃用的 V1 autoshare 布尔值变为显式的 share 策略:
// V1
{ "autoshare": true }
// V2
{ "share": "auto" }使用 "manual"、"auto" 或 "disabled"。如果 V1 文件已经使用 share,则无需更改。
权限和工具
V1 按工具对权限效果进行分组。V2 使用一个有序的 permissions 数组,使优先级和例外情况变得明确:
// V1
{
"permission": {
"bash": {
"git push *": "ask"
},
"edit": "allow"
},
"tools": {
"websearch": false
}
}
// V2
{
"permissions": [
{ "action": "shell", "resource": "git push *", "effect": "ask" },
{ "action": "edit", "resource": "*", "effect": "allow" },
{ "action": "websearch", "resource": "*", "effect": "deny" }
]
}权限操作也发生了变化:bash 现在是 shell,task 现在是 subagent,而 write 和 patch 现在是 edit。
有关有序的 V2 规则格式,请参阅 Permissions。
代理与模式
单数的 agent 和已弃用的 mode 映射变为 agents。代理字段与 V2 配置的其余部分变得更加一致:
// V1
{
"agent": {
"reviewer": {
"prompt": "Review for correctness and missing tests.",
"model": "anthropic/claude-sonnet-4-5",
"variant": "high",
"disable": false,
"permission": {
"edit": "deny"
}
}
}
}
// V2
{
"agents": {
"reviewer": {
"system": "Review for correctness and missing tests.",
"model": "anthropic/claude-sonnet-4-5#high",
"disabled": false,
"permissions": [
{ "action": "edit", "resource": "*", "effect": "deny" }
]
}
}
}prompt 变为 system,disable 变为 disabled,并且一个单独的 variant 在 # 之后加入模型引用。
temperature、top_p 和特定于提供商的 options 移到 request.body 下。maxSteps 变为 steps。来自
旧 mode 映射的条目成为主代理。
快照
将单数的 snapshot 字段重命名为 snapshots。其布尔值不变:
// V1
{ "snapshot": false }
// V2
{ "snapshots": false }媒体
将单数的 attachment 对象重命名为 media。嵌套的图像设置保持相同的名称:
// V1
{ "attachment": { "image": { "auto_resize": true } } }
// V2
{ "media": { "image": { "auto_resize": true } } }MCP 服务器
V2 将服务器分组到 mcp.servers 下,用相反的 disabled 替换 enabled,并区分超时用途:
// V1
{
"mcp": {
"playwright": {
"type": "local",
"command": ["npx", "@playwright/mcp"],
"enabled": true,
"timeout": 30000
}
}
}
// V2
{
"mcp": {
"servers": {
"playwright": {
"type": "local",
"command": ["npx", "@playwright/mcp"],
"disabled": false,
"timeout": {
"catalog": 30000,
"execution": 30000
}
}
}
}
}远程 OAuth 字段使用蛇形命名:clientId 变为 client_id,clientSecret 变为 client_secret,
callbackPort 变为 callback_port,而 redirectUri 变为 redirect_uri。V1 的 experimental.mcp_timeout 值
也会成为默认的 mcp.timeout.catalog 和 mcp.timeout.execution 值。请参阅 MCP servers。
压缩
V2 将保留上下文的 token 预算分组到 keep 下,并为预留部分赋予更清晰的名称:
// V1
{
"compaction": {
"preserve_recent_tokens": 8000,
"reserved": 20000
}
}
// V2
{
"compaction": {
"keep": {
"tokens": 8000
},
"buffer": 20000
}
}auto 保持其名称。V2 没有原生的 tail_turns 或 prune 字段;这两个旧字段都会被忽略并给出警告。最近的
上下文改为通过 token 预算来保留。请参阅 Compaction。
技能
V1 将额外的技能路径和 URL 分开。V2 将两者合并为一个有序数组:
// V1
{
"skills": {
"paths": ["./team-skills"],
"urls": ["https://example.com/skills/"]
}
}
// V2
{
"skills": ["./team-skills", "https://example.com/skills/"]
}现有的技能文件和自动 .opencode/skills/ 发现不会改变。请参阅 Skills。
命令
将单数的 command 映射重命名为 commands,将 subtask 重命名为 subagent。将单独的模型 variant 加入模型引用:
// V1
{
"command": {
"review": {
"template": "Review the current changes.",
"model": "anthropic/claude-sonnet-4-5",
"variant": "high",
"subtask": true
}
}
}
// V2
{
"commands": {
"review": {
"template": "Review the current changes.",
"model": "anthropic/claude-sonnet-4-5#high",
"subagent": true
}
}
}template、description 和 agent 保持其名称。旧版 subtask 仍被接受;委托的命令现在会
自动在后台运行,并将其结果报告给父会话。现有的 Markdown 命令定义仍受支持。
请参阅 Commands。
引用
将已弃用的单数 reference 映射重命名为 references:
// V1
{ "reference": { "docs": "../docs" } }
// V2
{ "references": { "docs": "../docs" } }V1 已经接受 references,因此当文件使用它时无需更改。引用条目保持
相同的结构。请参阅 References。
提供商
将单数的 provider 映射重命名为 providers。V2 将运行时包、端点和请求设置分开:
// V1
{
"provider": {
"acme": {
"npm": "@ai-sdk/openai-compatible",
"api": "https://llm.example.com/v1",
"options": {
"apiKey": "{env:ACME_API_KEY}"
}
}
}
}
// V2
{
"providers": {
"acme": {
"package": "aisdk:@ai-sdk/openai-compatible",
"settings": {
"baseURL": "https://llm.example.com/v1",
"apiKey": "{env:ACME_API_KEY}"
}
}
}
}V1 的 npm 变为 package,AI SDK 包获得 aisdk: 前缀。api 变为 settings.baseURL。提供商的
options 根据其请求角色被分为 settings、headers 和 body。请参阅 Providers。
V2 合并了两个旧版提供商命名空间:
| V1 提供商 ID | 规范的 V2 提供商 ID |
|---|---|
azure-cognitive-services | azure |
google-vertex-anthropic | google-vertex |
对无歧义的 V1 提供商、代理、命令和提供商过滤字段的迁移使用这些规范 ID。共享的
顶层 model 字段保留其确切的提供商 ID,因为相同的语法在原生的 V2 配置中有效;在迁移旧版内置提供商时,请将该字段
更新为规范 ID。
模型与变体
模型仍嵌套在其提供商下,但若干模型字段变得更加明确:
id变为modelID。tool_call和modalities变为capabilities.tools、capabilities.input和capabilities.output。"deprecated"的status变为disabled: true。- 缓存成本从
cache_read和cache_write移到cache.read和cache.write。 - 特定于提供商的
options变为settings。 - V1 的变体对象变为 V2 数组,每个条目上带有一个
id。
// V1
{
"variants": {
"high": {
"reasoningEffort": "high"
}
}
}
// V2
{
"variants": [
{
"id": "high",
"settings": {
"reasoningEffort": "high"
}
}
]
}有关完整的原生模型结构,请参阅 Models。
没有直接原生对应项的受支持字段
大多数保持相同结构的字段,包括 shell、model、default_agent、watcher、formatter、
instructions、enterprise 和 tool_output,无需迁移。
V2 接受并保留 lsp 配置,但它不会运行语言服务器、暴露 LSP 工具或生成 LSP 诊断信息。请将依赖这些能力的工作流替换为项目的 lint、typecheck 或编译器命令。
V1 的 provider 过滤器没有一对一的原生 V2 配置字段,但其行为仍通过 policies 得到支持:
enabled_providers变为一个内部的默认拒绝 provider 策略,随后为列出的 provider 添加允许项。disabled_providers变为针对所列 provider 的内部拒绝策略。autoupdate变为update:false映射到"disable","notify"映射到"notify",true映射到"auto"。small_model变为内置titleagent 的model选择。原生 V2 配置应改用agents.title.model。
你可以保留这些字段的 V1 语法。OpenCode 会在不发出警告的情况下将其规范化。
已接受但不支持的字段
V1 schema 还接受了一些在 V2 中没有受支持行为的字段。V2 会忽略这些值并发出警告,以免它们被误认为是生效的配置:
logLevel:启动 OpenCode 时使用OPENCODE_LOG_LEVEL。server:使用 V2 服务和显式 server 选项;server API 是有意为之的破坏性变更。- 顶层
subagent_depth:改用experimental.subagent_depth。 compaction.tail_turns和compaction.prune:V2 改用compaction.keep.tokens和基于 checkpoint 的压缩。- V1 JSON 配置中的 agent
name。 - 仅启用、没有
type的 V1 MCP 条目。 - V1 实验性字段
batch_tool、openTelemetry、primary_tools和continue_loop_on_deny。 - V1 provider 字段
id、whitelist和blacklist。 - V1 provider-model 字段
release_date、attachment、reasoning、temperature、experimental、非deprecated的status,以及布尔值interleaved。
忽略这些字段是有意为之,并非兼容性回归。如果 V2 未保留本指南其他地方标识为受支持的行为,请遵循 Troubleshooting 中的问题报告指引。
Agent 文件
V1 agent 文件可以使用 agent/、agents/、mode/ 或 modes/。V2 仍会发现全部四个目录。首选的 V2 位置是:
.opencode/agents/<name>.mdV1 mode/ 或 modes/ 目录下的文件代表主 agent。将其移入 agents/ 时,请在其 frontmatter 中添加 mode: primary。agent/ 下的文件可以移到 agents/,而无需更改其由路径派生的 ID。
将 frontmatter 转换为原生 V2 字段时:
- 保留 Markdown 正文作为 agent 的系统指令。
- 当
prompt出现在 JSON 配置中时,将其重命名为system;文件正文不需要system字段。 - 将
disable重命名为disabled,将permission重命名为permissions。 - 将
model和variant合并为provider/model#variant。 - 将
temperature、top_p和 provider 特定选项移到request.body下。
V2 会自动转换旧版 agent frontmatter,因此这些编辑是可选的。参见 Agents。
命令文件
V1 命令文件可以使用 command/ 或 commands/。V2 会发现两者。首选位置是:
.opencode/commands/<name>.md将文件从 command/ 移到 commands/ 下的相同相对路径,以保留命令名称。Markdown 正文仍是命令模板,description 和 agent frontmatter 保持相同的名称。将 subtask 重命名为 subagent,以使用后台委托的原生名称。如果 frontmatter 有单独的 model 和 variant 字段,请将变体追加到模型并移除 variant:
# V1
model: anthropic/claude-sonnet-4-5
variant: high
subtask: true
# V2
model: anthropic/claude-sonnet-4-5#high
subagent: true参见 Commands。
Skill 文件
V2 会从 .opencode/skill/ 和 .opencode/skills/ 中发现 skill。首选布局是:
.opencode/skills/<skill-id>/SKILL.md移动整个 skill 目录,而不仅仅是 SKILL.md,以便相对脚本、引用和其他支持文件仍然可用。保持目录名称稳定,以保留 skill ID。现有的 skill frontmatter 和 Markdown 正文无需为 V2 重写。参见 Skills。
指令文件
现有的 AGENTS.md 文件保持原位。V2 会从当前目录向上到 home 发现全局 ~/.config/opencode/AGENTS.md 和环境 AGENTS.md 文件。对于 home 之外的项目,发现会在项目根目录停止。
如果 V1 设置依赖 CLAUDE.md 回退,请将该指引移入适用的 AGENTS.md。V2 目前仅发现 AGENTS.md;由于非 API 的 V1 行为旨在保持兼容,还请针对受影响的项目详情提交一个兼容性问题。参见说明。
终端客户端配置
V2 用单个全局终端客户端配置文件取代分层的 V1 tui.json(c) 文件:
~/.config/opencode/cli.json该文件由终端客户端持有;后台服务不会加载它。当 cli.json 不存在时,首次 V2 终端客户端启动会迁移受支持的全局 tui.json 设置和持久化偏好,同时保持 V1 文件不变。项目本地的客户端配置不会被迁移,因为 V2 客户端配置是全局的。当前客户端设置参考参见CLI 设置。
插件
将 plugin 重命名为 plugins。将包与选项元组替换为对象:
// V1
{
"plugin": ["opencode-example-plugin", ["./plugin/local.ts", { "enabled": true }]]
}
// V2
{
"plugins": [
"opencode-example-plugin",
{
"package": "./plugin/local.ts",
"options": { "enabled": true }
}
]
}V2 会从 .opencode/plugin/ 和 .opencode/plugins/ 中发现本地插件;V2 文件请使用 .opencode/plugins/。
使用专门的插件迁移指南移植入口点、钩子、工具、事件和包导出。
服务器 API 与客户端
OpenCode 2 拥有经过修订、更符合人体工学的服务器 API 以及一组新客户端。调用 V1 服务器 API 的集成必须迁移到 V2 API。
使用 @opencode/client 包访问已发布的 V2 API。其端点、请求类型和响应参见生成的API 参考。
验证你的设置
在依赖 V2 进行日常工作之前,先在项目中验证你的模型、提供商凭据、代理、权限、MCP 服务器和插件。在确认你所需的 V2 行为之前,请保留你的 V1 设置,并且不要将 V1 指向你已转换为原生 V2 形态的配置。
来源:Hacker News · opencode.ai