Skip to content

5.10c SDK V2:下一代 API ​

一句话总结:V2 是 OpenCode SDK 的下一代入口,在保留 V1 的同时扩展了会话、问题、当前位置、事件流、历史分页、运行时操作和权限请求等 API。

⚠️ 版本边界

v1.18.22 同时导出 @opencode-ai/sdk 和 @opencode-ai/sdk/v2。V2 接口仍在演进,使用时应固定 SDK 版本;V1 在目标版本中没有移除,现有集成不必因为 V2 扩展而立即迁移。本章固定以 v1.18.22 tag 的 packages/sdk/js/src/v2/ 为准。


学完你能做什么 ​

  • 区分 V1 和 V2,知道什么时候用哪个
  • 用 @opencode-ai/sdk/v2 创建客户端
  • 理解 client.* 和 client.v2.* 两层访问路径
  • 使用 Permission、Question 独立模块
  • 调用 Session3 的增强方法(interrupt、wait、compact 等)
  • 从 V1 迁移到 V2

V2 是什么 ​

V1 和 V2 的定位 ​

OpenCode SDK 包(@opencode-ai/sdk)通过 package.json 的 exports 暴露两个入口:

入口路径版本状态适合谁
@opencode-ai/sdkV1保留现有集成、使用旧路由的代码
@opencode-ai/sdk/v2V2与 V1 并存、持续演进需要新能力、愿意固定版本验证的进阶用户

来源:packages/sdk/js/package.json:12-20

V2 重新设计了 API 结构,和 V1 不完全兼容。package.json 同时导出 . 和 ./v2,因此两个入口在 v1.18.22 中并存。

V2 的两层客户端结构(重要) ​

V2 客户端最大的特点是两层访问路径,理解这个就理解了 V2 的核心:

client                         OpencodeClient(27 个模块)
├── session  → Session2        旧路由 /session/*(基础方法)
├── permission → Permission    /permission/*(跨 session 权限)
├── question → Question        /question/*(跨 session 提问)
├── part     → Part            消息部件 CRUD
├── sync     → Sync            workspace 同步
├── worktree → Worktree        git worktree
├── experimental → Experimental 实验功能集合
├── ...
└── v2       → V2              新路由 /api/* 命名空间(17 个子模块)
    ├── session    → Session3  /api/session/*(增强方法)
    ├── permission → Permission3 /api/session/{}/permission
    ├── question   → Question3 /api/session/{}/question
    ├── health     → Health    /api/health
    ├── agent      → Agent     /api/agent
    ├── model      → Model     /api/model
    ├── fs         → Fs        /api/fs/*
    └── ...

关键区分:

访问路径类路由用途
client.sessionSession2/session/*基础方法(list/create/prompt)
client.v2.sessionSession3/api/session/*增强方法(interrupt/wait/compact/switchModel)
client.permissionPermission/permission/*跨 session 权限管理
client.v2.permissionPermission3/api/session/{}/permissionsession 级权限

来源:sdk.gen.ts:6990-7075(V2 类定义)、sdk.gen.ts:7077-7219(OpencodeClient 定义)

一句话记忆

增强的 session 方法在 client.v2.session,不是 client.session。client.session 是兼容旧路由的基础方法。

参数风格:以生成签名为准 ​

V2 多数方法不再使用 V1 通用的 { path: {...}, body: {...} } 结构,而是由 buildClientParams 把字段分发到 path、query 和 body。不过并非所有参数都平铺:部分端点仍使用 body 或 worktreeCreateInput 等请求体包装。调用时应以生成的 TypeScript 签名为准。

typescript
// ❌ V1 风格(V2 中不适用)
client.permission.reply({
  path: { requestID: "req-1" },
  body: { response: "always" },
})

// ✅ V2 风格(平铺)
client.permission.reply({
  requestID: "req-1",
  reply: "always",
})

来源:sdk.gen.ts:3121-3144(buildClientParams 自动分发)


开始使用 V2 ​

安装 ​

V2 和 V1 在同一个 npm 包里,不需要额外安装:

bash
npm install @opencode-ai/sdk

创建客户端 ​

typescript
import { createOpencodeClient } from "@opencode-ai/sdk/v2"

const client = createOpencodeClient({
  baseUrl: "http://localhost:4096",
  directory: "/path/to/my-project",        // 项目目录
  experimental_workspaceID: "ws-123",      // 可选:workspace 标识
})

客户端配置参数:

参数类型说明
baseUrlstring服务器 URL,默认 http://localhost:4096
directorystring项目目录,通过 X-Opencode-Directory header 传递
experimental_workspaceIDstringworkspace 标识,通过 X-Opencode-Workspace header 传递
fetchfunction自定义 fetch 实现
headersobject自定义请求头

来源:v2/client.ts:50-92

directory 和 workspaceID 怎么传递的?

directory 被编码后放入 X-Opencode-Directory header,experimental_workspaceID 放入 X-Opencode-Workspace header。对 GET / HEAD 请求,客户端拦截器还会把它们写入 query string;/api/* 会同时补普通键和 location[directory] / location[workspace],其他方法仍保留 header。


OpencodeClient 27 个模块总览 ​

V2 的 OpencodeClient 暴露 27 个模块属性。

来源:sdk.gen.ts:7077-7219

兼容 V1 的模块(19 个) ​

global project pty config tool instance path vcs command provider find file app mcp lsp formatter tui auth event

这些模块和 V1 基本一致,用法参考 5.10b API 参考。

V2 新增/增强的模块(8 个) ​

模块访问路径说明
sessionclient.session / client.v2.session两层:Session2 基础 + Session3 增强
permissionclient.permission跨 session 权限管理
questionclient.question跨 session 提问管理
partclient.part消息部件 CRUD
syncclient.syncworkspace 同步
worktreeclient.worktreegit worktree 管理
experimentalclient.experimental实验功能集合
v2client.v2/api/* 新路由命名空间(17 个子模块)

下面逐一详解。


V2 核心新能力详解 ​

1. Permission 独立模块 ​

V1 响应权限请求要用超长的 postSessionIdPermissionsPermissionId(),且只能响应特定 session 的请求。V2 把权限管理提升为独立模块,支持跨 session 查询和响应。

方法路由说明
permission.list()GET /permission列出所有 session 的待处理权限请求
permission.reply()POST /permission/{requestID}/reply响应权限请求
permission.respond()POST /session/{sessionID}/permissions/{permissionID}⚠️ 已废弃,用 reply 代替

来源:sdk.gen.ts:3085-3193

typescript
// 列出所有待处理的权限请求(跨 session)
const pending = await client.permission.list()
for (const request of pending.data ?? []) {
  console.log(`[${request.sessionID}] ${request.permission}: ${request.patterns.join(", ")}`)
}

// 响应某个权限请求(注意:字段名是 reply,不是 response)
await client.permission.reply({
  requestID: "req-123",
  reply: "always",     // "once" | "always" | "reject"
})

字段名注意

reply() 方法的 body 字段名是 reply(不是 response)。response 是已废弃的 respond() 方法的字段名。

和 V1 的对比:

操作V1V2
列出权限请求❌ 不支持✅ permission.list()
响应权限postSessionIdPermissionsPermissionId({path:{id,permissionID},body:{response}})permission.reply({requestID, reply})
跨 session 查询❌✅

2. Question 独立模块 ​

Agent 可以通过 question 工具向你提问。V1 没有统一管理接口,V2 新增独立模块。

方法路由说明
question.list()GET /question列出所有待回答的提问
question.reply()POST /question/{requestID}/reply回答提问
question.reject()POST /question/{requestID}/reject拒绝提问

来源:sdk.gen.ts:2982-3084

typescript
// 查看所有待回答的提问
const questions = await client.question.list()
for (const q of questions.data ?? []) {
  console.log(`[${q.sessionID}] ${q.questions[0]?.header ?? "(no question)"}`)
}

// 回答某个提问
await client.question.reply({
  requestID: "q-456",
  answers: [["选项 A"]],
})

// 拒绝提问
await client.question.reject({ requestID: "q-456" })

权限和提问的区别

  • Permission:Agent 要执行某个操作(如运行命令、编辑文件),请求你授权。
  • Question:Agent 需要信息(如选择方案、确认偏好),向你提问。

3. Session 增强(client.v2.session) ​

这是 V2 最容易踩坑的地方。增强的 session 方法在 client.v2.session(Session3 类),不是 client.session(Session2 类)。

Session2(client.session):兼容旧路由 /session/*,有 list/create/prompt/messages 等基础方法。

Session3(client.v2.session):新路由 /api/session/*。除控制方法外,目标版本还支持创建/获取会话、带游标的会话与消息列表、session 级问题和权限请求、历史分页及事件流。当前位置属于同一 V2 命名空间下的 client.v2.location,不是 Session3 方法。

来源:sdk.gen.ts:5038-5058(当前位置)、sdk.gen.ts:5171-5424(权限与问题)、sdk.gen.ts:5426-5873(Session3)

Session3 新增方法:

方法路由说明
interrupt()POST /api/session/{sessionID}/interrupt中断当前执行
wait()POST /api/session/{sessionID}/wait等待 session 空闲
compact()POST /api/session/{sessionID}/compact触发上下文压缩
context()GET /api/session/{sessionID}/context获取当前上下文
history()GET /api/session/{sessionID}/history获取历史记录
switchModel()POST /api/session/{sessionID}/model切换模型
switchAgent()POST /api/session/{sessionID}/agent切换 agent
events()GET /api/session/{sessionID}/eventsession 级事件流

其中 list() 和 messages() 支持游标分页,history({ sessionID, limit, after }) 返回指定聚合序号之后的有限事件页;events({ sessionID, after }) 会先回放再持续推送事件。

来源:sdk.gen.ts:5426-5517、sdk.gen.ts:5715-5793

typescript
const sessionID = "sess-abc"

// 注意:这些方法都在 client.v2.session(Session3)
await client.v2.session.interrupt({ sessionID })
await client.v2.session.wait({ sessionID })
await client.v2.session.compact({ sessionID })

// 切换模型
await client.v2.session.switchModel({
  sessionID,
  model: { providerID: "anthropic", id: "claude-sonnet-4-20250514" },
})

// 切换 agent
await client.v2.session.switchAgent({ sessionID, agent: "plan" })

// 获取上下文
const ctx = await client.v2.session.context({ sessionID })

最常见的坑

把增强方法写在 client.session 上会报错。记住:

  • client.session.prompt() ✅(基础方法在 Session2)
  • client.v2.session.interrupt() ✅(增强方法在 Session3)
  • client.session.interrupt() ❌(Session2 没有这个方法)

4. Part 模块(消息部件 CRUD) ​

V2 新增了对消息部件(Part)的细粒度操作。一条消息由多个 Part 组成,V2 支持删除和更新单个 Part。

方法路由说明
part.delete()DELETE /session/{sessionID}/message/{messageID}/part/{partID}删除部件
part.update()PATCH /session/{sessionID}/message/{messageID}/part/{partID}更新部件

来源:sdk.gen.ts:4330-4406

typescript
// 更新某个文本部件
await client.part.update({
  sessionID: "sess-abc",
  messageID: "msg-1",
  partID: "part-3",
  part: { type: "text", text: "修改后的内容" },
})

// 删除某个部件
await client.part.delete({
  sessionID: "sess-abc",
  messageID: "msg-1",
  partID: "part-3",
})

5. Sync 模块(workspace 同步) ​

Sync 是 V2 的多 workspace 事件同步机制。

方法路由说明
sync.start()POST /sync/start启动同步循环
sync.replay()POST /sync/replay回放同步事件
sync.steal()POST /sync/steal将 session 转移到当前 workspace
sync.history.list()POST /sync/history列出同步事件历史

来源:sdk.gen.ts:4448-4576(Sync 类)、sdk.gen.ts:4407-4446(History 类)

注意

sync.history 是一个 getter 属性(返回 History 类实例),不是方法。要调用 sync.history.list()。

typescript
// 启动同步
await client.sync.start()

// 查看同步事件历史(history 是 getter,再调 list)
const history = await client.sync.history.list({
  body: { "sess-abc": 10 },  // 返回 seq > 10 的事件
})
Sync 是什么场景用的?

当你同时在多个 workspace 运行 OpenCode,session 可能需要在不同 workspace 间迁移。Sync 提供事件日志机制,确保迁移可追溯、可回放。这是为未来的分布式/集群场景设计的,单机用户一般用不到。

目标版本的 workspace 由 adapter 负责创建与发现,内置 adapter 是 worktree;会话 warp 可用 copyChanges 复制当前补丁。v1.16.0 Release 曾加入保留脏文件和未跟踪文件的 managed clone,但该实现已被后续 adapter/worktree 路径取代,不能当作 v1.18.22 的当前行为。

当前实现:adapters/index.ts:5-18、workspace.ts:492-538、workspace.ts:559-620、workspace.ts:728-739。历史边界:v1.16.0 Release、commit 5661af203487b90cf9ee0844b198b03cce26c412。

6. Worktree 模块(git worktree 管理) ​

V2 新增了 git worktree 的完整管理接口。

方法路由说明
worktree.list()GET /experimental/worktree列出所有 worktree
worktree.create()POST /experimental/worktree创建 worktree
worktree.remove()DELETE /experimental/worktree删除 worktree 及分支
worktree.reset()POST /experimental/worktree/reset重置到默认分支

来源:sdk.gen.ts:1582-1723、types.gen.ts:2167-2187

typescript
// 列出所有 worktree
const worktrees = await client.worktree.list()

// 创建新 worktree
await client.worktree.create({
  worktreeCreateInput: { name: "feature-experiment" },
})

// 删除
await client.worktree.remove({
  worktreeRemoveInput: { directory: "/path/to/worktree" },
})

和 5.25 Git Worktree 课程的关系

本节是 SDK 编程接口。手动使用 worktree 请参考 5.25 Git Worktree 工作流。

7. Experimental 模块集合 ​

client.experimental 是聚合模块,包含 workspace、resource、capabilities、console、control-plane 等前沿功能。

子功能路由前缀说明
workspace/experimental/workspace多工作空间管理
resource/experimental/resourceMCP 资源查询
capabilities/experimental/capabilities能力声明
console/experimental/console控制台(组织切换)
controlPlane/experimental/control-plane控制平面(session 迁移)
session/experimental/session实验性会话(background 子代理)

来源:sdk.gen.ts:1243-1278

实验性 session 的 background 方法可以把阻塞的子代理转为后台执行:

typescript
// 把阻塞的子代理转到后台继续运行
await client.experimental.session.background({ sessionID: "sess-abc" })

该接口只会分离当前阻塞 session 的同步子代理。后台子代理能力仍受 OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true(或总开关 OPENCODE_EXPERIMENTAL=true)控制;未启用时接口返回 false。此外,subagent_depth 默认是 1,会阻止子代理继续启动子代理;只有明确调高后才允许更深嵌套。

来源:runtime-flags.ts:10-14,43、experimental handler:159-170、task.ts:96-115、config.ts:84-86、sdk.gen.ts:805-886


完整示例:用 V2 构建自动化助手 ​

typescript
import { createOpencodeClient } from "@opencode-ai/sdk/v2"

const client = createOpencodeClient({
  baseUrl: "http://localhost:4096",
  directory: "/path/to/my-project",
})

async function runTask(task: string) {
  // 1. 创建 session(client.session 是 Session2,基础方法)
  const session = await client.session.create({
    title: task.slice(0, 50),
    agent: "build",
  })
  const sessionID = session.data!.id

  // 2. 异步发送任务(平铺参数)
  await client.session.promptAsync({
    sessionID,
    parts: [{ type: "text", text: task }],
    model: { providerID: "anthropic", modelID: "claude-sonnet-4-20250514" },
  })

  // 3. 轮询权限请求,自动允许只读操作
  const poll = setInterval(async () => {
    const pending = await client.permission.list()
    for (const req of pending.data ?? []) {
      // permission 是操作类型(如 "read"、"grep"),patterns 是匹配模式
      if (req.permission === "read" || req.permission === "grep") {
        await client.permission.reply({
          requestID: req.id,
          reply: "always",   // 注意字段名是 reply
        })
      }
    }
  }, 1000)

  // 4. 等待 session 完成(增强方法在 client.v2.session)
  await client.v2.session.wait({ sessionID })
  clearInterval(poll)

  // 5. 获取结果(基础方法回到 client.session)
  const messages = await client.session.messages({ sessionID })
  const last = messages.data?.at(-1)

  // 6. 读取 Token 消耗和费用(V1/V2 都支持)
  if (last?.info.role === "assistant") {
    console.log(`费用: $${last.info.cost}`)
    console.log(`Token: 输入 ${last.info.tokens.input} / 输出 ${last.info.tokens.output}`)
  }

  return last
}

const result = await runTask("分析项目结构并生成 README")
console.log(result)

核心要点:基础方法(create/promptAsync/messages)在 client.session,增强方法(wait/interrupt/compact)在 client.v2.session。混用时注意切换路径。


V1 → V2 迁移指南 ​

导入路径 ​

typescript
// V1
import { createOpencodeClient } from "@opencode-ai/sdk"

// V2
import { createOpencodeClient } from "@opencode-ai/sdk/v2"

参数结构 ​

typescript
// V1:嵌套结构
await client.session.create({ body: { title: "xxx" } })
await client.session.prompt({ path: { id: "sess-1" }, body: { parts: [...] } })

// V2:平铺结构
await client.session.create({ title: "xxx" })
await client.session.prompt({ sessionID: "sess-1", parts: [...] })

权限响应 ​

typescript
// V1:超长方法名 + 嵌套参数
await client.postSessionIdPermissionsPermissionId({
  path: { id: sessionID, permissionID: "perm-1" },
  body: { response: "always" },
})

// V2:独立模块 + 平铺参数
await client.permission.reply({
  requestID: "req-1",
  reply: "always",   // 字段名变了:response → reply
})

会话控制 ​

typescript
// V1:已有中断和压缩能力,但方法名不同
await client.session.abort({ path: { id: sessionID } })
await client.session.summarize({ path: { id: sessionID } })
// V1 没有 wait,需要自行轮询

// V2:增强方法在 client.v2.session
await client.v2.session.interrupt({ sessionID })
await client.v2.session.wait({ sessionID })
await client.v2.session.compact({ sessionID })
await client.v2.session.switchModel({ sessionID, model: { ... } })
await client.v2.session.switchAgent({ sessionID, agent: "plan" })

能力对照表 ​

功能V1V2
权限响应postSessionIdPermissionsPermissionIdpermission.reply
权限列表❌permission.list
提问管理❌question.list/reply/reject
中断会话session.abortv2.session.interrupt
等待完成❌(自己轮询)v2.session.wait
触发压缩session.summarizev2.session.compact
切换模型❌v2.session.switchModel
切换 agent❌v2.session.switchAgent
消息部件 CRUD❌part.update/delete
worktree❌worktree.list/create/remove/reset
workspace 同步❌sync.start/replay/steal/history.list

踩坑提醒 ​

现象原因解决
createOpencodeClient is not exported导入路径错了V2 用 @opencode-ai/sdk/v2
client.session.interrupt is not a function用错了访问路径增强方法在 client.v2.session
参数报类型错误套用了另一端点的参数结构查看该方法的生成签名;多数参数平铺,部分仍有请求体包装
permission.reply 报字段错误字段名写成了 responseV2 字段名是 reply
sync.history is not a functionhistory 是 getter 不是方法调用 sync.history.list()
请求返回 HTML连的是 V1 服务器,不支持 /api/*确认服务器版本支持 V2
V2 方法签名和文档不一致V2 是实验性,API 会变以源码 sdk.gen.ts 为准
v2.session.wait 一直阻塞session 一直在运行先 v2.session.interrupt 或设超时

本课小结 ​

你学会了:

  1. V2 定位:持续演进的下一代 API,和 V1 并存于同一个 npm 包
  2. 两层结构:client.*(基础 + 新概念)和 client.v2.*(/api/* 新路由)
  3. 参数风格:多数方法改为端点字段参数,少数仍保留请求体包装,以生成签名为准
  4. 核心新能力:Permission/Question 独立模块、Session3 增强方法(在 client.v2.session)、Part CRUD、Sync、Worktree
  5. 迁移要点:导入路径、参数结构、权限字段名、session 访问路径的差异

相关资源 ​


附录:源码参考 ​

点击展开查看源码位置

目标版本:v1.18.22(2026-08-24)

功能文件路径行号
V1/V2 exports 配置packages/sdk/js/package.json12-20
V2 index(createOpencode)packages/sdk/js/src/v2/index.ts1-23
V2 客户端(createOpencodeClient)packages/sdk/js/src/v2/client.ts50-92
V2 服务器(ServerOptions)packages/sdk/js/src/v2/server.ts5-30
OpencodeClient 27 模块sdk.gen.ts7077-7219
V2 命名空间(17 子模块)sdk.gen.ts6990-7075
Permission 模块sdk.gen.ts3085-3193
Question 模块sdk.gen.ts2982-3084
Session3 模块sdk.gen.ts5426-5873
Session2 模块(基础方法)sdk.gen.ts3362-4329
Part 模块(update/delete)sdk.gen.ts4330-4406
Sync 模块 + History 类sdk.gen.ts4407-4576
Worktree 模块sdk.gen.ts1582-1723
Workspace 模块(experimental)sdk.gen.ts1006-1242
Experimental 模块(聚合)sdk.gen.ts1243-1278

关键类:

  • OpencodeClient:V2 客户端主类,27 个模块属性
  • V2:client.v2 命名空间,17 个 /api/* 子模块
  • Session2(client.session):旧路由基础方法
  • Session3(client.v2.session):新路由增强方法(interrupt/wait/compact/switchModel/switchAgent)
  • Permission(client.permission):跨 session 权限管理
  • Question(client.question):跨 session 提问管理

注意:packages/client/(@opencode-ai/client)是私有包,基于 Effect HttpApi 生成,非公开 SDK,不在本章范围。