Reasonix 扩展开发实战:Extension Protocol v2 代码运行时插件完整指南
【免费下载链接】DeepSeek-ReasonixDeepSeek-native AI coding agent for your terminal. Engineered around prefix-cache stability — leave it running.项目地址: https://gitcode.com/GitHub_Trending/de/DeepSeek-Reasonix
Reasonix 通过扩展(Extensions)机制允许插件包在运行时改变 Agent 的行为——重写输入、拦截工具调用、替换系统提示词、贡献流式模型 Provider、发布结构化 UI,以及随包发布提示词模板和主题。本文以 docs/EXTENSIONS.md 为主线,结合 Extension Protocol v2 线协议文档、插件包文档 与 Go SDK 示例,完整讲解扩展的安装管理、能力模型、运行时重载、缓存性能、开发流程与安全边界,读完即可从零开发、安装并验证一个自己的 Reasonix 代码扩展。
两类插件能力:声明式与代码运行时
Reasonix 的扩展机制区分两种能力形态:
- 声明式能力(任何插件包都可以有):skills、agents、commands、prompts、hooks、MCP servers、themes。它们本质是文件与配置,以宿主(host)的正常权限运行,不引入独立进程。
- 代码运行时(Manifest v2
runtime块):一个以 sidecar 子进程形态存在、通过 Extension Protocol 与宿主对话的代码扩展。代码扩展是**完全信任(full trust)**的——安装前务必阅读下文安全模型一节。
正是这一"声明式 + 代码运行时"的双层设计,让 Reasonix 在不链接进宿主二进制的前提下,获得"改写运行时行为"的能力。
安装与管理
扩展与普通插件包的安装方式完全一致,走reasonix plugin命令族:
reasonix plugin install git:github.com/owner/extension --dry-run # 预览,不写文件 reasonix plugin install git:github.com/owner/extension --yes # 正式安装 reasonix plugin show <name> # 查看详情 reasonix plugin doctor <name> # 校验有效性install接受三类来源:GitHub 仓库(如git:github.com/owner/repo或完整 HTTPS URL)、GitHub 分支/子目录 URL、以及包含reasonix-plugin.json(或 Codex/Claude 兼容清单)的本地目录。本地开发推荐--link模式以目录链接代替复制:
reasonix plugin install /path/to/plugin --link --replace --yes关键标志:--dry-run只做计划与校验;--yes是任何写盘安装的必要条件;--replace允许替换同名插件;--name覆盖清单中的插件名;--link链接本地目录而非复制(移动或删除该目录会破坏链接)。
对于带runtime块的插件,--dry-run预览与show输出都会包含一个醒目的FULL TRUST块:列出运行时命令、它拦截的事件、拥有的替换槽(replacement slots),以及 Provider/UI 能力。安装、更新、替换或--link本身就是授权动作——没有第二次确认,且--link会持续信任后续被修改的内容。因此只应安装你完全信任的运行时。
已安装状态持久化在~/.reasonix/plugin-packages.json与~/.reasonix/plugins/<name>/(详见 插件包文档)。禁用/启用不卸载:reasonix plugin disable <name>/reasonix plugin enable <name>。
扩展能做什么
拦截器(Interceptors):17 个冻结的钩子点
扩展可以观测并对 17 个钩子点做出裁决(输入、工具调用、权限决策、Provider 请求/响应、压缩(compaction)、会话生命周期、前端事件)。每次拦截调用的裁决有四种:
continue:放行载荷;block:以用户可见的原因中止操作;replace:替换载荷——宿主会对每个替换结果按该钩子点的 DTO 与 schema重新校验;allow/deny:仅在permission.decision钩子点合法;来自完全信任扩展的allow会覆盖宿主自身的拒绝,并被审计。
拦截器按确定性顺序串行执行:清单priority升序(范围 -1000..1000,默认 0)→ 插件 ID → 注册顺序。超时方面:input/tool/permission 系列默认 5 秒;session/context/compaction/system-prompt 系列默认 30 秒;清单可按运行时调高,上限 60 秒。仅观测的扩展超时只警告一次并跳过;必需扩展与替换槽属主超时则直接导致操作失败。
替换策略槽(Replacement Strategies):单属主槽位
以下槽位在整个已安装插件集合中只有一个属主:system_prompt、context、provider_request、provider_response、compaction、session_policy、permission、frontend_events、tool:<name>、provider:<ref>。拦截器链先运行,槽属主拥有最终决定权;属主超时或报错必然使操作失败。如果两个插件声明了同一槽位,运行时构建会失败并同时点出两个来源——冲突在构建期暴露,而不是在运行期静默覆盖。
流式 Provider:plugin/<plugin>/<provider>/<model>命名空间
带providers能力的扩展通过extension/provider/catalog声明与宿主 Provider 等价的描述符(模型、上下文窗口、定价、视觉、推理、effort——但绝不涉及凭据)。新模型在模型选择器中以plugin/<plugin>/<provider>/<model>形式出现,并采用与内置 Provider 相同的 text/reasoning/tool-call/usage 流语义。该 ref 在一切内置 ref 可用的地方都可用:default_model、--model、CLI/Desktop/ACP 选择器、会话中途切模型——包括首次启动。
结构化 UI
带ui能力的扩展可发布status、card、form、notification载荷(host/ui/publish),并通过host/ui/request向用户提问(confirm、input、select、multiselect)。表面是纯结构化的:没有 HTML/CSS/JS、远程脚本、任意前端组件或不受控 URL,Markdown 走各前端已有的安全渲染器。每次表面更新都携带插件 ID、表面 ID、会话 ID 与运行时代数(generation);过期代数(stale-generation)的更新会被丢弃,因此标签切换或重载后的迟到结果永远无法覆盖当前状态。初始化时声明的动作被命名为/<plugin>:<action>,通过extension/ui/action调用,表单提交走extension/ui/submit;这些动作会出现在斜杠菜单、Desktop 命令面板与 ACP 的可发现命令中。
Prompts 与主题
/<plugin>:<name>提示词模板(与 commands 相同语义、支持参数替换,commands是兼容别名),以及 Desktop Settings 中只读的插件主题plugin:<plugin>:<theme>(.reasonix-theme文件,从不复制进用户主题库;插件被禁用/卸载时桌面回退到基础样式但保留 ID,重装同款插件即可恢复主题)。
运行时重载(Runtime Reload):一次失败原子的快照切换
修改已安装扩展(安装、更新、启用/禁用、或--link内容变化)绝不会改变正在运行的 turn。重载在所有交互前端都是一次失败原子的操作,入口包括:CLI/reload、Desktop/reload(composer 斜杠菜单)或命令面板的Reload Runtime、Serve/reload,以及 ACP 供应商方法_reasonix.io/session/reloadExtensions。流程共五步:
- 若正在运行 turn 或后台任务,CLI/Desktop/ACP 会只排队一次重载;Serve 则直接拒绝请求,由浏览器在空闲后重试。
- 空闲后,Reasonix 启动新的 sidecar 并构建新的运行时快照。
- 全部成功后原子切换,并携带会话路径、transcript、审批授权、目标/恢复状态。
- 若新构建失败,旧运行时原样继续工作。
- 只有切换完成后,旧的 sidecar 才被退役。
每个 turn 在整个 turn、工具批次与压缩期间固定(pin)一个运行时代数——扩展变更应用到下一个turn;无操作重载(no-op reload)会让 Provider 的提示词缓存前缀保持逐字节一致。
性能与提示词缓存(Prompt Cache)
- 零运行时路径:未安装任何代码运行时,Agent 走既有的 nil-dispatcher 路径,不涉及 sidecar 进程、JSON 编码、RPC 或事件队列。
- 启动预算:安装运行时后,Reasonix 在一个共享的 30 秒启动预算内至多并行初始化 4 个 sidecar。因此某个卡住的非必需运行时不会让启动/重载时间按已装插件数量翻倍。未在预算内启动的包,按其
runtime.required设置降级或失败。 - 热路径取舍:启用的同步拦截器刻意位于对应热路径上且串行执行,其 RPC 与 handler 延迟是累加的——input、tool、permission、provider 拦截器应保持短小且确定性。观测事件走有界非阻塞队列,背压时丢弃并告警,而不是拖住 turn。
- 缓存前缀影响:仅观测的扩展不改变 Provider 可见的缓存前缀;稳定的 system-prompt 或 tool 替换会在安装/重载后制造一次有意的冷前缀,此后保持可缓存。若在系统提示词、工具 schema、上下文前缀或 Provider 请求中注入时间戳、随机值、会话 ID 等逐 turn 数据,会摧毁缓存复用——动态数据应尽量留在当前 turn 的尾部(turn tail)。
- 宿主开销度量(仓库自带基准):
go test ./internal/extension/... -run '^$' -bench 'Extension|Dispatch' -benchmem对应的基准与分发实现位于 internal/extension/dispatch 与 internal/extension 目录 下的 benchmark 测试中。
开发一个扩展
官方建议从完整的starterextension示例包开始:它在同一目录里包含了 Manifest、Sidecar 源码、跨平台构建命令、链接安装方式与首个可观测拦截。典型开发循环四步:
- 在
reasonix-plugin.json中加入apiVersion: "reasonix.io/plugin/v2",声明contributes与(可选的)runtime,详见 插件包文档的 Manifest v2 一节。 - 实现 Sidecar。Go SDK(纯标准库、零依赖)处理传输、握手、时序、内容引用与关停;语言无关的参考是 线协议文档 与 生成的方法索引。
- 构建运行时二进制,用
reasonix plugin install /path/to/plugin --dry-run预览其信任与能力,再以--link --yes安装。 - 用
reasonix plugin doctor <name>校验,空闲时执行/reload,然后实际触发贡献的拦截、Provider、UI 动作或资源。
SDK 发布使用不可变标签sdk/go/vX.Y.Z,首个公开版本为sdk/go/v1.0.0;在该标签存在之前,请基于源码检出使用 starter,而不是依赖无版本号的模块。
最小可运行示例:starterextension
示例清单 sdk/go/examples/starterextension/reasonix-plugin.json:
{ "apiVersion": "reasonix.io/plugin/v2", "name": "starter-extension", "version": "0.1.0", "description": "Minimal Reasonix Extension Protocol sidecar", "provides": [ { "namespace": "plugin/starter-extension", "kind": "interceptors", "id": "default", "version": "1.0.0" } ], "contributes": {}, "runtime": { "command": "${REASONIX_PLUGIN_ROOT}/bin/starter-extension.exe", "args": [], "env": {}, "required": false, "priority": 0, "intercepts": ["input.receive"], "replaces": [], "capabilities": ["interceptors"] } }注意.exe后缀是刻意为之:固定运行时路径使同一份清单在所有平台一致(Unix 直接执行,Windows 需要可执行后缀)。${REASONIX_PLUGIN_ROOT}在启动时展开为已安装插件根目录。
Sidecar 本体 sdk/go/examples/starterextension/main.go 展示 SDK 的核心用法——实现Initialize声明订阅,注册拦截器,用extension.Continue()/extension.Replace(...)返回裁决:
func (starter) Initialize(context.Context, extension.InitializeParams) (*extension.InitializeResult, error) { return &extension.InitializeResult{ Subscriptions: []string{"input.receive"}, }, nil } func interceptInput(_ context.Context, _ string, payload json.RawMessage) (*extension.InterceptResult, error) { var input struct { Text string `json:"text"` } if err := json.Unmarshal(payload, &input); err != nil || !strings.HasPrefix(input.Text, inputPrefix) { return extension.Continue(), nil } return extension.Replace(map[string]string{ "text": strings.TrimPrefix(input.Text, inputPrefix) + " [rewritten by starter-extension]", }) } func main() { err := extension.Serve(context.Background(), starter{}, extension.Options{ Name: "starter-extension", Version: "0.1.0", Interceptors: map[string]extension.InterceptorFunc{ "input.receive": interceptInput, }, }) if err != nil { os.Exit(1) } }构建并安装(macOS/Linux):
go build -o bin/starter-extension.exe . plugin_root="$(pwd -P)" reasonix plugin install "$plugin_root" --dry-run reasonix plugin install "$plugin_root" --link --replace --yes先审阅 dry-run 输出中的FULL TRUST块再安装。然后新开会话(或在空闲会话中/reload),发送starter: explain what an Extension Protocol sidecar does——模型收到的将是重写后的文本(追加了[rewritten by starter-extension])。改main.go、重编、/reload,即可迭代。
更完整的参考实现是fullsidecar:输入重写、工具拦截(block + 参数重写)、system-prompt 策略替换、一个伪流式 Provider(文本块、工具调用、usage)、结构化 UI(会话启动时 status + card,demo动作背后的表单提示),以及有界的干净关停——全部在一个纯标准库小程序中。其清单 sdk/go/examples/fullsidecar/reasonix-plugin.json 同时声明了interceptors、strategies、providers、ui四种能力与system_prompt替换槽,可直接对照研究。该目录会被 internal/extension/conformance 以真实宿主端到端驱动。
SDK 的并发契约值得留意:Initialize先于一切回调且只运行一次;之后 SDK 可并发执行至多 32 个入站回调(拦截器、观测者、资源通知、Provider Catalog/Stream、UI 回调可重叠,多个 Provider 流可同时活跃),回调输入按调用本地对待,共享可变状态需自行加锁;SDK 自行串行化协议写入,扩展不得直接写 stdout,stderr 留给诊断。
Manifest v2 解析规则要点
v2 清单是严格的:必须声明精确的reasonix.io/plugin/v2(v1 与缺失版本被拒绝,无 v1 双读或自动迁移);任何未知字段——根级或contributes/runtime嵌套级——都是指明字段路径的报错;requires/provides声明依赖约束与能力上限,Sidecar 握手时按此校验,任何超出清单的声明都会以capability_not_declared失败。runtime块中command/args/env只支持exec form(命令不会被 shell 解释),intercepts/replaces/capabilities分别声明事件订阅、可拥有的替换槽与能力族(interceptors、strategies、providers、ui)。所有相对路径与 glob 必须留在插件根内:路径穿越、绝对路径、符号链接逃逸与非规则的 theme 文件都会被拒绝。完整字段语义见 插件包文档。
线协议:Extension Protocol v2 的关键契约
协议 ID 为reasonix.extension.v2,机器可读 schema 位于 internal/extension/protocol/schema.generated.json,方法/事件/限制/错误索引见 生成索引文档(由 cmd/extension-protocol-gen 生成,CI 校验漂移)。
- 传输:严格的 JSON-RPC 2.0 overNDJSON(stdin/stdout 每行一个完整 JSON 对象);stderr 归扩展用于诊断,宿主只捕获有界、凭据脱敏的尾部用于报错。帧在双向上限8 MiB,超限为连接致命错误
frame_too_large。请求 ID 为整数,params必须是对象;帧层面容忍未知成员,但 DTO 解码是严格的(未知字段被拒绝),拼写错误会立刻暴露。 - 生命周期:宿主以 exec form 拉起 sidecar,先发
extension/initialize(参数携带宿主将接受的清单预期;一代运行时的 4 个 sidecar 在共享 30 秒预算内并行初始化)→ sidecar 回声明,宿主校验(精确协议主版本、所有订阅/替换槽/Provider/UI 动作必须是清单子集)→ 宿主发extension/initialized;此前任何扩展→宿主流量都会毒化连接。关停有界:extension/shutdown带超时 → 关 stdin → 仍未退出则杀进程树。崩溃语义:sidecar 死亡会取消其全部挂起 RPC;若它正持有当前选中 Provider 或某个替换槽,当前操作显式失败——宿主从不静默回退到其他模型或策略;崩溃的 sidecar 只能由空闲时的运行时重载重启。 - 内容引用:可外部化的载荷字段超过64 KiB时被卸载进宿主内容存储——帧只带
ExternalizedField描述符(JSON 指针、内容 ref、字节数、SHA-256)与null占位;对端以256 KiB分块用host/content/read回读,校验字节数与哈希;单个内容对象上限8 MiB;未知或过期 ref 报content_ref_expired。 - 流式 Provider:
extension/provider/stream/open→stream/chunk→stream/end;块携带从 1 开始的连续序号,stream/end.lastSeq冻结终止边界;宿主缓冲乱序块、丢弃重复块,缺口持续存在则以缺失序号判定流为provider_interrupted。块类型含text、reasoning(带signature)、tool_call_start、tool_call_args_delta、tool_call、usage(含缓存 token)、done、error。取消流上下文发送stream/cancel。扩展读取自己的环境与凭据——宿主从不转发其他 Provider 的 API key 或 header。 - 错误:领域错误走 JSON-RPC 错误码
-32000并带结构化数据(reason、retryable、action);protocol_error、unknown_method、invalid_params、internal使用标准 JSON-RPC 码。冻结的 reason 表(capability_not_declared、content_ref_expired、frame_too_large、intercept_timeout、provider_failed、stale_generation、stream_gap等)在 生成索引 中可查。 - 稳定性契约:主版本 2 之内只允许三类演进——新增可选字段、新枚举值、新方法;既有必需字段、方向、限制、错误 reason 与语义永不改变。规范 schema 及其 SHA-256 由
cmd/extension-protocol-gen产出,CI 的go test ./...通过确定性生成测试(TestGeneratedArtifactsAreDeterministicAndCommitted)强制约束,任何漂移——包括无意的语义变更——都会让构建失败。
兼容性
- 原生
reasonix-plugin.json清单必须声明精确的reasonix.io/plugin/v2API 版本;没有 v1 双读或自动迁移(v1 上从未公开发布扩展清单)。次要别名(v2.0、v2.1…)与未知主版本同样被拒绝。 - 旧版 Reasonix 会忽略扩展专属状态:每会话的
<session>.extensions.jsonsidecar 文件、plugin/...模型 ref(直接解析为不可用模型)、extension_surface/extension_status事件种类(旧前端丢弃未知种类;无reasonix.extensionSurface的 ACP 客户端获得文本回退)。 plugin-packages.json保持既有 schema;一个已启用、已安装的运行时本身就是信任记录。
安全模型:完全信任的边界与宿主约束
代码扩展在 Reasonix 沙箱之外、以未过滤的继承环境运行:它可以读取完整会话与环境、绕过权限与工作区限制、直接操作机器;其permission.decision的allow会覆盖宿主拒绝。作为回报,宿主强制以下约束:
- 只有通过插件流(plugin flow)安装的插件才能启动运行时——项目配置永远不能声明运行时(这与"链接安装即授权"共同构成信任链);
- 握手拒绝任何超出清单的能力声明;
- 每次替换都针对该点的 DTO 与 schema 重新校验;
- sidecar 诊断、结构化 UI、拦截器 reason 与 Provider 错误在到达 UI、日志或错误面之前,都经过宿主的凭据脱敏(credential redaction)通道;普通 Provider/模型内容按产品数据原样保留;
- 崩溃的 sidecar 让自身操作显式失败——Reasonix 从不静默回退到另一模型或策略。
因此使用守则很简单:安装前审阅--dry-run与plugin show输出的FULL TRUST块(运行时命令、拦截事件、替换槽、Provider/UI 能力一览);只安装完全信任的运行时;--link会持续信任目录内后续的改动。
参考资源
- 扩展总览:重载、性能、缓存行为、兼容性与信任
- 线协议参考:传输、生命周期、内容引用、拦截、Provider、UI、错误、稳定性契约
- 生成的方法索引:17 个钩子点、全部方法方向、限制与错误表
- 插件包文档:Manifest v2 每个字段、CLI/Desktop 安装管理、Codex/Claude 兼容映射
- Go SDK 文档:SDK 回调与并发契约、最小示例
- starterextension 示例:首个可安装扩展
- fullsidecar 参考实现:Provider、UI、策略、内容引用的完整演示
- 宿主侧实现:internal/extension(sidecar 管理、dispatch、协议、uihub、conformance 等)
【免费下载链接】DeepSeek-ReasonixDeepSeek-native AI coding agent for your terminal. Engineered around prefix-cache stability — leave it running.项目地址: https://gitcode.com/GitHub_Trending/de/DeepSeek-Reasonix
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考