docs: plugin host architecture design - #2222
Conversation
There was a problem hiding this comment.
建议暂缓合并。这个 PR 对进程启动、IPC 和关闭流程写得比较细,但几个更上层的边界还需要先定清楚,否则实现后很容易出现两套插件运行机制并存、OpenCode 接口反向侵入 BitFun 核心模块,以及插件权限和崩溃恢复无法闭环的问题。
以下意见只保留与当前设计直接相关的架构问题。
[P1] 不要绕过现有插件运行入口,另建第二套
问题
bitfun-opencode-ext-host-ipc-design.md:118-140,743-765 让 Core 和 Session 直接持有、调用 HostClient;opencode-plugin-client-route-adapter-design.md:192-209 又准备在 Core/Assembly 增加 PluginHostBackendBridge 和实例状态。
但仓库已经有统一的 PluginRuntimeClient。如果 Core 可以直接调用 Host,就会同时存在两条插件执行路径。
风险
两条路径会分别处理插件启停、超时、取消、崩溃恢复、权限检查和结果发布。Desktop、CLI、Remote 后续也可能各走各的入口,最终很难判断哪一套状态才是真的。
建议
产品模块只能调用 PluginRuntimeClient:
产品功能模块
-> PluginRuntimeClient
-> OpenCode 适配层
-> 进程服务
-> 一个 Plugin Host
-> 一个或多个插件执行进程
Assembly 只负责选择和注入实现;OpenCode 格式转换留在 OpenCode 适配层;Host 及其下属插件进程的启动、连接和回收留在 services。插件执行进程返回的工具、Hook 等内容还要交给真正的功能模块校验后再生效,不能由 Host 或 Assembly 直接注册。
完成标准
- Core/Assembly 不直接持有
HostClient或 Host 实例表。 - Desktop、CLI、Remote 都只能通过同一个插件运行入口调用。
- 插件启停、权限和恢复状态各自只有一个明确负责人。
[P1] 不要把 OpenCode 的整套 HTTP 接口变成 BitFun 内部总入口
问题
opencode-plugin-client-route-adapter-design.md:54-69,192-209,340-403 计划通过一个 backend.http.request,把 OpenCode SDK 请求分发到 Workspace、Session、文件、终端、Git、MCP、LSP、配置和模型供应商等大量 BitFun 模块。
文档虽然说这不是完整 OpenCode Server,但如果 Core 需要理解并分发这么多 OpenCode HTTP 路径,实际上仍然是在 Core 中建立一层“精简版 OpenCode Server”。
风险
OpenCode 一旦改接口,BitFun 多个核心模块都可能跟着改;其他插件生态也可能照此再建一套总入口。长期会出现多套外部协议直接穿透 Core,各功能模块原有的权限、取消和状态管理被绕开。
建议
只实现真实插件已经使用、并且有固定样例验证的 Client 接口。HTTP method、path 和 OpenCode DTO 都留在 OpenCode 适配层;适配层再调用各功能模块已有的窄接口,不要定义一个覆盖所有服务的通用 BackendCapability。
这里的 1.17.18 只需要被说明为“当前内嵌 SDK 和路由表的参考版本”。现有其他文档使用 1.18.4/1.18.9,应说明它们分别是历史审计快照还是当前实现依据,但没有必要为了尚未提出的多版本兼容设计复杂的版本协商。
完成标准
- OpenCode 路由变化不会迫使无关 Core 模块修改公共接口。
- 每条开放路由都有真实插件样例和端到端测试。
- 未验证路由明确返回不支持,而不是先铺完整 SDK 路由表。
- 文档明确
1.17.18的作用范围,并处理与其他参考版本的表述冲突。
[P1] 配置里写了插件,不等于已经允许执行
问题
bitfun-opencode-ext-host-ipc-design.md:145-164,497-523,676-739 把全局 app.json.plugin 非空作为启动 Host、导入插件和打开工作区插件实例的主要条件。
配置只能说明“用户或项目声明了这个插件”,不能直接证明“当前这份代码已经被允许在这个位置执行”。
风险
用户确认之后,本地文件或 npm 包内容可能已经变化;应用重启时也可能从同一路径加载到不同内容。远程工作区还可能错误沿用本机的确认结果。
建议
把两个步骤明确分开:
- 从配置中发现插件;
- 在执行前确认插件来源、当前实际内容、运行位置和权限仍然符合用户之前的决定。
Host 只能接收已经通过第二步检查的插件清单,不能自己根据原始 app.json 判断是否可以执行。重启、插件更新、切换到远程环境时都要重新检查当前内容和执行位置。
完成标准
- 没有有效执行许可时,不能启动 Host 或导入插件代码。
- 插件内容、运行位置或权限变化后,旧许可不能继续使用。
- 本地和远程环境分别判断,不允许远程失败后偷偷回到本机执行。
[P1] 明确 Host 和插件执行进程的关系,不要把两层混成一层
问题
一个 backend 对应一个 Host 是合理的:它可以统一管理连接、插件清单、请求转发、日志和关闭流程,也能避免每个工作区重复创建管理进程。
但当前文档没有把“管理 Host”和“真正运行插件代码的进程”分开。bitfun-opencode-ext-host-ipc-design.md:89-116,442-486,676-739 把一个 backend 对应到一个选定的 Node/Bun Host,并把多个插件声明直接交给这个 Host;现有通用设计也把 Plugin Host 定义成“直接加载并运行第三方插件代码的进程”。这与“一个 Host 管理多个插件执行进程”是两种不同的进程结构。
风险
如果按当前文字实现,所有插件仍会在唯一 Host 进程内执行。一个插件的死循环、内存耗尽或主动退出就会带走全部插件;Node/Bun 也只能在整个 backend 级统一选择。
反过来,如果实现人员自行在 Host 下增加多个插件进程,却没有文档规定谁启动、停止和回收它们,又会产生无人负责的子进程和不一致的恢复流程。
建议
明确采用下面的两层结构:
BitFun backend
-> 一个 Plugin Host:统一管理连接、插件清单、请求转发、日志和关闭
-> 多个插件执行进程:真正导入并运行第三方插件代码
一个 Host 管理多个执行进程即可,不必现在写死“一个插件一个进程”。运行环境相同、可以安全共享的插件可以共用执行进程;需要不同 Node/Bun 环境、远程位置或隔离条件时再拆开。
services 仍应掌握完整进程树,确保 backend 或 Host 退出时可以回收所有插件进程。Node/Bun 的选择如果是插件代码的运行要求,应放在插件执行进程这一层,而不是由唯一 Host 全局决定。
完成标准
- 文档明确 Host 是管理进程,插件代码由下层执行进程运行。
- 一个插件执行进程崩溃时,只撤下受影响插件,不必让所有插件失效。
- Host 崩溃时,Rust 将其管理的全部插件进程一起标记失效并统一恢复。
- backend 退出时,所有插件进程都能由受监督的进程树完整回收。
[P1] 如果要做“按插件授权”,请求里就必须知道是哪个插件发起的
问题
opencode-plugin-client-route-adapter-design.md:78-85 的请求只有 instance、请求编号、HTTP 方法和路径;:303-315 保存的也只是工作区和一份统一权限,没有插件身份。
但同一个工作区实例会加载多个插件。当前协议无法判断某个文件、终端或 Session 请求究竟来自哪一个插件。
风险
如果不同插件应有不同权限,一个插件就可能使用另一个插件获得的权限;日志也无法回答“是谁执行了这次操作”,撤销单个插件的权限同样无法立即生效。
建议
Host 给每个插件创建独立的 Client,请求由 Host 自动附带真实插件身份、当前插件内容版本和当前权限版本,不能让插件自己通过 header 声明身份。Rust 收到请求后,再根据该身份检查权限。
如果产品决定所有插件完全共享权限,也可以不做上述隔离,但文档就不应再承诺“按插件检查权限和审计”。两种模型需要明确选一个。
完成标准
- 能确认每个请求来自哪个插件。
- 两个插件可以拥有不同权限和独立审计记录。
- 禁用一个插件后,它不能继续借用旧请求或旧权限调用后端。
[P1] Host 崩溃后,旧能力必须整体撤下,再整体恢复
问题
bitfun-opencode-ext-host-ipc-design.md:813-820 规定 Host 丢失后实例失效,并在后续工作区操作时重新启动、重新打开实例,但没有说明旧的工具和 Hook 何时撤下、多个工作区是否一起恢复,以及恢复一半失败时产品应处于什么状态。
风险
可能出现旧工具仍显示可用但实际 Host 已经死亡,或者部分工作区使用新 Host、部分仍保留旧状态。对于删除、更新、执行工具等操作,如果请求已经送出但响应在崩溃时丢失,简单返回“连接断开”还可能诱导调用方重复执行。
建议
Host 崩溃后应按一个完整流程处理:先停止接收新调用,取消或等待旧调用,撤下该 Host 提供的全部工具和 Hook,启动新 Host,重新加载所有仍在使用的工作区,全部校验成功后再一次性恢复可用状态。中间任一步失败,就保持不可用,不能留下半新半旧状态。
对于已经发出但不知道是否执行成功的写操作,应返回清楚的“结果未知”,禁止自动重试,由真正保存状态的模块重新查询结果。
完成标准
- 单个插件执行进程崩溃时只影响对应插件;Host 崩溃时,其管理的全部插件一起失效、一起恢复。
- 恢复完成前旧工具和 Hook 不再可见,也不接收新调用。
- 任一工作区恢复失败时不会发布一半的新状态。
- 写操作丢失响应时不会自动重复执行。
以上问题都来自当前文档已经提出的生产路径,不依赖 OpenCode v2 或假设未来一定支持多版本。建议先把这些边界修正,再继续补充路由和进程实现细节。
Summary
Type and Areas
Type: docs
Areas: Rust core architecture, CLI/plugin host, OpenCode extension compatibility
Motivation / Impact
本 PR 将插件 Host 相关设计文档与运行时代码拆分,便于独立评审和后续维护。
文档覆盖以下内容:
input.client.*到backend.http.request的调用链路。当前文档基线以
@opencode-ai/sdk@1.17.18为准:pty.connect()、WebSocket、事件流、认证、TUI 等能力:不在本阶段适配范围内。对应运行时代码实现位于配套 PR:
Verification
git diff --check origin/main...HEADReviewer Notes
docs/architecture/extensions/bitfun-opencode-ext-host-ipc-design.mddocs/architecture/extensions/opencode-plugin-client-route-adapter-design.mdChecklist