OpenCode
OpenCode 是一个编程 Agent,opencode serve 起的 server 在服务端保存会话、历史与执行状态。
这一页说明怎么接 OpenCode server:建连接、建会话、审批与追问卡片、会话级事件,以及这个协议要注意的地方。请求与事件的字段见 OpenCode 协议参考。
结论
- 适配器自带连接
createOpenCodeConnection:OpenCode server 是 REST 加一条全局 SSE(/global/event),发送立刻返回、回答从事件流来,审批与追问也经事件流到达,能力函数不用接入方写。地址与鉴权由连接选项决定。 - 整个应用一个连接、一个适配器实例;ChatKit 的 sessionId 就是 OpenCode 的会话 id。
- 审批与追问挂在工具片段上,用工具渲染器画卡片,按钮调
connection.respond。 - 只支持请求流模式;不支持插话与续接。
建连接
用的是 legacy 路由(/session/*、/permission/*、/question/*、/global/event),不用 v2 的 /api/*。浏览器直连时,OpenCode server 要允许跨域;生产环境建议走自己的服务转发并校验登录态,地址前缀写进 BASE。
会话与界面
会话目录由应用维护:connection.createSession(body) 把请求体原样发给 POST /session,返回 { sessionId, session };把 sessionId 存进自己的目录,交给 SessionList 与 useChatSessions。切到一个会话时,useChatSessions 经适配器取历史;往上翻时按页取更早的轮次。
只有权限为 ask 时才会有审批;要审批哪些工具,在这里或 OpenCode 的配置里定。
审批与追问
状态依次是 pending(等待回答)→ answering(已调用 respond,等后端确认)→ answered。回答没送达时 respond 抛错、状态退回 pending,可以重试;按钮在非 pending 时置灰即可。等待回答时这一轮的阶段是 awaiting-external。这一轮结束时仍在等待的请求标为 cancelled。
- 工具渲染器的参数里没有会话 id,卡片里用
useCurrentSession().session?.id取。 requestId只在pending/answering时有;没有时不能回答。- 审批里
patterns是这次要放行的模式,always是选「总是允许」时会放行的模式。 - 刷新后,已回答的追问从
question工具的输入与结果还原(没有requestId);审批不进历史,刷新后工具只有最终状态。
会话级事件
标题、待办、忙闲这类内容不在消息里。connection.listen(sessionId, listener) 收到这个会话的全部原始内容,用它驱动插槽:
连接断开时监听先收到 { kind: 'connection', status: 'disconnected' },连接自动重新订阅后收到 reconnected。断线期间的事件不补发,像上面这样在 reconnected 时补拉一次状态。
业务内容
本轮内的事件也可以在 transformOutput 里转成 data 片段显示在回答里。刷新加载历史时,适配器把历史回放成同形的会话事件逐条交给它,同一段逻辑在刷新后也能重建业务内容;增量与审批、追问的信令只在实时里有。哪些事件属于某一轮、怎么拿,见协议参考的没有内置处理的事件。
每一轮的额外参数(model、agent、system…)用 transformInput 加到 prompt_async 的请求体里,返回完整请求体,写法见上面的建连接。
输入怎么放进请求
输入里带了不支持的附件(不是图片,或需要后端取回的来源)时不发请求,这一轮以 UNSUPPORTED_ATTACHMENT 失败。
错误
已知限制
- 只支持请求流模式:同一会话里由其他客户端发起的轮次不实时显示,刷新后从历史出现。
- 不支持续接:断线后这一轮标记连接中断,由会话层经历史确认结果。
- 不支持插话:OpenCode 没有往进行中的一轮里追加输入的接口。
- 历史不保留审批记录:刷新后工具只有最终状态。
- 历史里的用户消息只还原文本,图片附件不还原。