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.jsonexports 暴露两个入口:

入口路径版本状态适合谁
@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.sessionclient.session 是兼容旧路由的基础方法。

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

V2 多数方法不再使用 V1 通用的 { path: {...}, body: {...} } 结构,而是由 buildClientParams 把字段分发到 path、query 和 body。不过并非所有参数都平铺:部分端点仍使用 bodyworktreeCreateInput 等请求体包装。调用时应以生成的 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-3144buildClientParams 自动分发)


开始使用 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-5517sdk.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-18workspace.ts:492-538workspace.ts:559-620workspace.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-1723types.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,43experimental handler:159-170task.ts:96-115config.ts:84-86sdk.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
切换 agentv2.session.switchAgent
消息部件 CRUDpart.update/delete
worktreeworktree.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 个模块属性
  • V2client.v2 命名空间,17 个 /api/* 子模块
  • Session2client.session):旧路由基础方法
  • Session3client.v2.session):新路由增强方法(interrupt/wait/compact/switchModel/switchAgent)
  • Permissionclient.permission):跨 session 权限管理
  • Questionclient.question):跨 session 提问管理

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