| 1 | # 编写扩展工具、命令与 Native mod |
| 2 | |
| 3 | > 英文契约:[EXTENSIONS.md](../EXTENSIONS.md)。本指南描述当前已实现的宿主接口。 |
| 4 | |
| 5 | 实验性的 TypeScript 扩展宿主在共享 Node 进程中运行已审查的插件代码,Bun 可显式 |
| 6 | 选择。Rust Engine 仍负责会话、工具准入、审批、权限和执行。插件可以贡献工具、用户 |
| 7 | 命令、执行前提议、提示词片段及插件本地 JSON 状态;不能提供审批服务、替换核心 |
| 8 | 工具或命令,也不能运行另一套 agent loop。 |
| 9 | |
| 10 | ## 从可运行示例开始 |
| 11 | |
| 12 | [hello-extension](../examples/plugins/hello-extension/hello.mts) 演示工具和命令。 |
| 13 | [mod-extension](../examples/plugins/mod-extension/README.md) 进一步演示 scoped hook、 |
| 14 | 提示词片段、保存计数、结构化结果和清理。后者是普通 ESM,不需要编译器或安装依赖。 |
| 15 | |
| 16 | 1. 在配置中显式设置 `[features] extension_host = true`,或使用 |
| 17 | `codewhale --enable extension_host` 启动。功能开关放在子命令前。 |
| 18 | 2. 在 Codewhale 会话中运行 `/plugin install ./docs/examples/plugins/mod-extension`。 |
| 19 | 3. 运行 `/plugin validate mod-extension` 和 `/plugin show mod-extension`,审查 Native |
| 20 | 能力、源代码和权限。 |
| 21 | 4. `/plugin enable mod-extension` 会在缺少信任时显示审查流程。由本人执行审查给出的 |
| 22 | 精确信任命令,然后再次启用。安装后默认未启用、未信任;信任本身不执行代码。 |
| 23 | 5. 明确要求模型调用 `mod_counter`,参数为 `{"label":"first"}`。工具走普通审批流程。 |
| 24 | `{"increment":false}` 只读计数,`{"label":"blocked"}` 演示监听器拒绝。 |
| 25 | 6. 本人输入 `/mod-count` 可显示保存的计数;`/plugin disable mod-extension` 会移除工具、 |
| 26 | 命令、监听器和提示词片段,保留计数。 |
| 27 | |
| 28 | 协助编写插件的 agent 应在安装和验证后把审查交给本人,不应自动信任或启用插件。 |
| 29 | |
| 30 | ## 入口、审查与生命周期 |
| 31 | |
| 32 | 新插件包使用 Agent Plugins v1.0.0 的 `plugin.json`。在 |
| 33 | `extensions["net.codewhale"].native.path` 指定包内的普通 `.mjs`、`.js` 或 `.mts` |
| 34 | ES 模块文件;功能开启时目录及其他后缀会被拒绝。功能关闭时,Native 入口只列入 |
| 35 | 清单,不执行。用户目录是 `~/.codewhale/plugins/`,工作区目录是 |
| 36 | `<workspace>/.codewhale/plugins/`。 |
| 37 | |
| 38 | `native.paths` 可声明多个入口,最多 64 个。它们按声明顺序激活,共用一个所有者; |
| 39 | 某个入口激活失败时,只撤销该入口:已激活的入口保持注册,后续入口仍会激活;只有 |
| 40 | 没有任何入口激活成功时,整个插件才会失败。相同文件重复列出只算一个入口。源文件、权限或已审查内容变更需要重新审查。`/plugin reload` 是显式刷新, |
| 41 | 不提供自动监视或热重载。停用、撤销信任、所有者故障及宿主退出会撤销对应注册。 |
| 42 | |
| 43 | 注册返回可重复调用的 disposer,并归属于调用它的 Cordis fiber。可用 `ctx.effect` |
| 44 | 登记其他清理工作;异步清理会等待,但有两秒上限。不要在清理时继续使用 storage: |
| 45 | 所有者开始清理后该接口即不可用。详见示例的队列结束与注册撤销代码。 |
| 46 | |
| 47 | ## 运行时与依赖 |
| 48 | |
| 49 | Node 是默认运行时,要求 `^22.19 || >=24`。Bun 要求至少 1.4.0,目前只在 macOS |
| 50 | 完成资格验证,默认切换需要另行记录。用户可通过 `[extension_host] runtime` |
| 51 | 选择 `node`、`bun` 或 `auto`;显式配置的运行时路径失败时,不会默默搜索替代路径。 |
| 52 | `auto` 的回退原因会显示在 `/plugin` 和 doctor 诊断中。 |
| 53 | |
| 54 | 可在 `[extension_host]` 中设置 `mcp_backend = "host"`,不必启用 Native 扩展。 |
| 55 | `[features] extension_host` 只控制可选 Native 扩展;选择 Host MCP 不会激活它们。 |
| 56 | 这会让已有的 stdio、HTTP 和旧版 SSE MCP 服务器连接使用固定版本的 MCP SDK,运行于 |
| 57 | 独立的内置宿主进程。进程与网络权限、凭据解析、目录接纳、工具批准和取消仍由 Rust |
| 58 | 掌握。HTTP/SSE 请求通过 Rust 的受保护 HTTP 客户端发出;SDK 只收到不含真实地址 |
| 59 | 或凭据的会话标识,以及逐次授权的操作。适配器将 SDK 请求编号转换为 Rust 客户端 |
| 60 | 原有的字符串编号,并将其绑定到相应操作票据。显式选择 Host 后,失败不会回退到 |
| 61 | Rust,也不会重放结果不明的操作。传输行为完成资格验证前,默认仍为 |
| 62 | `mcp_backend = "rust"`。Host 路径与默认后端共用 Rust 的 GET 会话预检和有界 OAuth |
| 63 | 响应式刷新。只有 HTTP 明确拒绝当前传输协议后,Rust 才会签发新的、精确绑定的单次操作授权, |
| 64 | 允许切换到 SDK 的 SSE 传输;失效会话仍由 Rust 以类型化错误决定恢复方式。凭据、配置中的 URL、 |
| 65 | OAuth 浏览器登录和 Computer Use 决策密钥都保留在 Rust。两个后端接受同一套已记录的 MCP |
| 66 | 基准验证;切换默认后端前,必须通过这些基准和真实代理的验收测试,并满足 |
| 67 | Ops CURRENT_DECISIONS §26 的平台隔离与 Phase 3 实测门槛。正式切换默认后, |
| 68 | 原生 Rust 协议适配器再保留一个版本,然后删除;目录、会话、权限和凭据的权威 |
| 69 | 仍留在 Rust。缺少受支持的 Node 时,沿用 doctor 的运行时诊断;Host 失败不会 |
| 70 | 自动选择 Rust。 |
| 71 | |
| 72 | `.mts` 只支持 Node 能直接擦除的类型语法。enum、decorator、JSX 等需要转换的语法, |
| 73 | 应由作者先构建为 JavaScript。不要依赖 Bun 特有的 tsconfig 路径解析:Node 不读取 |
| 74 | 此配置。Bun 使用 `--no-install`,缺少依赖时会失败,不会下载。把运行时依赖和本地 |
| 75 | 导入打包在已审查包内;激活时不应安装包或获取代码。 |
| 76 | |
| 77 | 已审查的 DSH 组合在导入前会核对本地模块的路径和 SHA-256。Node 检查运行时解析; |
| 78 | Bun 解析已审查的 JavaScript 语法,并在执行计算式导入前核对对应凭据。未审查的文件 |
| 79 | 和环境中的包会被拒绝。Bun 目前无法保留查询参数或片段所区分的模块身份,动态 |
| 80 | CommonJS 解析也需要 Node;这些情况会明确提示使用 |
| 81 | `[extension_host] runtime = "node"`。普通 UTF-8 源码和已审查的计算式文件/JSON |
| 82 | 导入可在两种运行时中使用。 |
| 83 | |
| 84 | 宿主提供同一个 Cordis、schemastery、cosmokit 及有限的 DSH 兼容导出。支持的服务名为 |
| 85 | `tools`、`commands`、`prompt`、`storage`、`skills`、`shellHooks`、`mcp`、`logger`、`events`、`reflect`、`registry`。 |
| 86 | 需要未提供服务的插件会激活失败,并显示原因。目前没有已发布的插件编写 SDK; |
| 87 | 普通 ESM 示例使用这些文档规定的适配服务。 |
| 88 | |
| 89 | 共享进程内的插件并不相互隔离;所有者标签及 `dataDir` 不能构成彼此之间的安全边界。 |
| 90 | 宿主禁止进程内原生代码入口,包括 native addon、Worker 和相关 FFI/SQLite 入口。 |
| 91 | macOS Seatbelt 与 Linux bubblewrap 限制宿主进程,但不等于对每次插件文件访问执行 |
| 92 | 工具审批。不要在描述、日志、配置或结果中保存凭据。 |
| 93 | |
| 94 | Native 扩展启动必须通过操作系统隔离验证。Linux 缺少 bubblewrap 或命名空间探测失败 |
| 95 | 时会拒绝激活并显示具体原因;Windows 文件与网络隔离尚不可用时也会拒绝 Native。 |
| 96 | 固定摘要的 Builtin 宿主可使用明确标示的未隔离例外,但其所有效果仍须兑换 Rust |
| 97 | 签发的操作凭据。这不能证明 Native 隔离或默认运行时切换已完成。 |
| 98 | |
| 99 | 金融、数据与语音工具的实验性适配器分别使用 `[features] finance_host = true`、 |
| 100 | `data_host = true` 和 `speech_host = true`;三者默认使用 Rust。CLI `speech`/`tts` |
| 101 | 与模型工具共享语音准备逻辑,克隆样本、提供商请求和输出文件写入仍归 Rust。 |
| 102 | 选中 Host 后通过现有固定 Builtin harness 与 |
| 103 | 操作 broker 执行,即使 Native 扩展关闭也可独立运行。网络、文件、解析诊断、权限及凭据仍归 |
| 104 | Rust;选中的 Host 失败会明确报告,不会自动切回 Rust 适配器。 |
| 105 | |
| 106 | ## 工具和结构化结果 |
| 107 | |
| 108 | 用 `ctx.tools.register({name, description, parameters, execute})` 注册工具。名称最多 |
| 109 | 64 个字符,以字母开头,只使用字母、数字、`_`、`-`。使用插件专属前缀;核心名称、 |
| 110 | 审批名称族及 `mcp_`/`ext_` 前缀保留。核心及其他插件的工具不能被覆盖。 |
| 111 | |
| 112 | Rust 在审批前以及发送给宿主前检查 JSON Schema,包括类型、必填字段、大小限制和 |
| 113 | `additionalProperties: false`。外部 `$ref` 不会被获取,无法编译的 schema 会拒绝注册。 |
| 114 | 插件自行声明的只读属性不能取消审批:扩展工具始终是 `Required`。Full Access、Bypass |
| 115 | 或针对该已审查构建的精确会话授权可以满足外层要求。注册本身不授权执行。 |
| 116 | |
| 117 | `execute(input, exec)` 返回文本或 JSON。普通 JSON 会保留为工具结果的结构化数据, |
| 118 | 同时生成模型可读的文本输出。`mod_counter` 返回计数及标签。结构化结果不会执行 UI |
| 119 | 代码;自定义工具卡片和 Ratatui/GPUI 控件尚未提供。 |
| 120 | |
| 121 | 工具调用有 120 秒截止时间,取消通过 `exec.signal` 传递。插件应检查并向自己发起的 |
| 122 | 异步工作传递 signal;忽略取消的 JavaScript 仍可能在共享进程中继续运行,直到宿主 |
| 123 | 被终止。不要在共享进程中使用 `process.chdir`。 |
| 124 | |
| 125 | ## 用户命令与调用身份 |
| 126 | |
| 127 | `ctx.commands.register({name, description, argumentHint?, handler})` 注册用户输入的 |
| 128 | 斜杠命令。名称用小写字母、数字、`_`、`-`,以字母开头,最多 64 字符。命令不能覆盖 |
| 129 | 内置命令、别名或其他插件命令;同名 Markdown 用户/工作区/插件命令优先。 |
| 130 | |
| 131 | handler 只在用户调用时运行,可返回文本、`{kind:'success', text?}`、 |
| 132 | `{kind:'error', text}` 或 `{kind:'submit', prompt, text?}`。`submit` 会明显地把 prompt |
| 133 | 提交为用户下一条消息,后续模型工具仍经普通权限和审批流程。命令不能直接调用模型、 |
| 134 | 工具或审批,截止时间为 30 秒。输出去除终端转义序列并限制为 64 KiB,超过 128 KiB |
| 135 | 的提交 prompt 会被拒绝。 |
| 136 | |
| 137 | 工具上下文包含 `signal`、`callId`、`args`;命令包含 `signal`、`commandId`、 |
| 138 | `args`、`rawInput` 和空的 `attachments`。两者都可能获得本次调用的 `workspace`、 |
| 139 | 插件自己的 `dataDir` 及只读 `sessionId`、`agentId`、`originTurnId` 字符串。 |
| 140 | Rust 未提供的字段缺省;激活时不缓存会话身份。这些字符串只用于关联,不授予操作 |
| 141 | 会话或 agent 的权利。用户命令通常只有已知的 session 标签。 |
| 142 | |
| 143 | ## 执行前提议 |
| 144 | |
| 145 | ```js |
| 146 | ctx.on('tools/pre-execute', async (exec, next) => { |
| 147 | if (exec.name !== 'my_plugin_tool') return next() |
| 148 | if (exec.arguments.label === 'blocked') { |
| 149 | return { kind: 'deny', reason: 'This label is reserved.' } |
| 150 | } |
| 151 | return { kind: 'abstain' } |
| 152 | }) |
| 153 | ``` |
| 154 | |
| 155 | 监听器接收冻结的 `name`、`callId`、`arguments`、`signal`、`workspace`、`mode`、 |
| 156 | `model` 视图,没有 session、agent、tool 或 approval 句柄。`next()` 只表示不干预, |
| 157 | 并不执行工具。可返回: |
| 158 | |
| 159 | - `{kind:'abstain'}`、`undefined` 或 `null`:不干预。 |
| 160 | - `{kind:'deny', reason}`:拒绝调用。 |
| 161 | - `{kind:'ask', reason?}`:提出审批要求,由 Rust 的现有模式及权限规则处理。 |
| 162 | - `{kind:'revise', input}`:修订为 JSON 对象输入。 |
| 163 | - `{kind:'annotate', text}`:补充有边界的上下文。 |
| 164 | |
| 165 | DSH 的 `allow` 被视为不干预,每个所有者提示一次;不能预先批准。格式错误、异常、 |
| 166 | 超时及已撤销所有者使调用被拒绝。整个监听器批次有五秒截止时间。Native hooks 先执行, |
| 167 | 随后按核心注册顺序执行 mod 监听器;后续监听器会看到已接受的输入修订。拒绝优先, |
| 168 | 最后的有效修订决定输入。修订受现有 32 KiB 限制,Rust 重新准备调用并重复准入、策略 |
| 169 | 和审批检查。理由及上下文沿用现有清理与大小限制。 |
| 170 | |
| 171 | 该桥接作用于 Engine 会话的模型工具、受 gate 管理的 code-mode 调用及 `exec.core` |
| 172 | 调用。独立 ACP 执行路径没有 TypeScript 宿主 attachment。`prepend`/`global` 顺序 |
| 173 | 覆盖、包裹实际执行的中间件、DSH 运行时句柄和执行后结果改写均不支持。 |
| 174 | |
| 175 | ## 提示词片段 |
| 176 | |
| 177 | `ctx.prompt.registerSection({id, text})` 返回 disposer,贡献带来源的纯文本片段。 |
| 178 | Rust 只选择当前 Engine 所需、仍有效并具有已审查 Native 权限的所有者,按所有者/id |
| 179 | 稳定排序。完整当前快照通过 user-role 运行时消息交付,替换先前快照;没有片段时明确 |
| 180 | 撤回先前指令。它不替换系统提示词,也不能指定其他会话的片段。 |
| 181 | |
| 182 | 单个片段最多 4 KiB UTF-8 文本;每个所有者最多 32 KiB、128 个片段;宿主总计最多 |
| 183 | 128 KiB、1,024 个片段。带来源的最终块也限制为 128 KiB。id 必须以小写字母开头, |
| 184 | 只含 `a-z`、`0-9`、`_`、`-`,最多 64 字符。同一所有者重复使用 id 前,必须先撤销 |
| 185 | 旧注册。停用、撤销、注销或宿主退出都会移除相应片段。 |
| 186 | |
| 187 | 需要当前回合的模型及工作区时,可登记 `{id, text, interpolate: 'model-cwd'}`。 |
| 188 | 仅支持 `{{model}}` 和 `{{cwd}}`;Rust 在捕获回合提示词时展开一次,普通文本的 |
| 189 | 花括号保持不变。原始及展开文本均受片段与快照上限约束,捕获和撤回保留精确选定 |
| 190 | 入口。DSH persona 桥接只贡献 prefix/suffix;替换完整提示词及屏蔽运行时上下文 |
| 191 | 会被明确拒绝。 |
| 192 | |
| 193 | ## 插件本地状态 |
| 194 | |
| 195 | `ctx.storage.get(key)`、`set(key, json)`、`delete(key)` 操作 Rust 已指定的 `dataDir` |
| 196 | 内的普通 JSON。状态跨重启和代际保留;停用和卸载不会删除该目录。该服务不暴露 |
| 197 | 会话历史或凭据。所有者开始清理后拒绝访问,也拒绝链接文件、损坏记录和超限写入。 |
| 198 | |
| 199 | key 最多 128 UTF-8 字节,单个值最多 128 KiB;所有者当前可见记录上限为 4 MiB、 |
| 200 | 1,024 个 key。每个 key 原子替换;同一宿主内的目录队列串行处理操作。跨进程的同键 |
| 201 | 写入是最后写入生效,不是事务或跨进程原子增量计数。`mod_counter` 另外串行化自身的 |
| 202 | 读/改/写队列。损坏状态不会被自动覆盖,应明确选择恢复或删除。 |
| 203 | |
| 204 | ## 注册技能目录 |
| 205 | |
| 206 | `ctx.mcp.registerServer({serverName, server})` 提议字面量 MCP 定义并返回可重复调用的释放函数。 |
| 207 | 入口须声明 `inject = ['mcp']`。Rust 的现有目录仍拥有连接、工具准入、权限和认证; |
| 208 | 服务不向扩展暴露凭据,也不授予直接进程或网络访问。定义绑定精确的已审查、已选择 Native 入口。 |
| 209 | 每个 owner 最多 64 个服务器,宿主最多 256 个,每条定义最多 64 KiB。 |
| 210 | 桥接支持字面量 stdio、streamable HTTP 和 SSE;凭据值、URL 查询数据及不支持的启动/重连控制会被拒绝。 |
| 211 | 释放入口、变更调用方选择、停用插件或变更已审查字节会撤回定义并取消受影响的调用;不确定的写操作不会重放。 |
| 212 | |
| 213 | 原始 DSH skill-filesystem 桥接复用下述已审查技能根服务。配置必须明确设置 |
| 214 | `includeDefaultRoots: false`、`watch: false`,并列出包内相对路径 `customSkillDirs`; |
| 215 | 环境中的默认目录及独立 watcher 不受支持。 |
| 216 | |
| 217 | `ctx.skills.registerRoot({path: 'profiles/review-skills'})` 把已审查包内的技能 |
| 218 | 交给 Rust 现有的 skill catalog,并返回可重复调用的 disposer。入口的 `inject` |
| 219 | 列表需包含 `skills`: |
| 220 | |
| 221 | ```js |
| 222 | export const inject = ['skills'] |
| 223 | export function apply(ctx) { |
| 224 | ctx.skills.registerRoot({path: 'profiles/review-skills'}) |
| 225 | } |
| 226 | ``` |
| 227 | |
| 228 | 每个技能放在根目录下的独立子目录中,例如 |
| 229 | `profiles/review-skills/quick-check/SKILL.md`。路径相对于插件包,最多 512 个 |
| 230 | UTF-8 字节,只能使用正常的 `/` 分隔路径段;绝对路径、链接、空路径段、`.` 和 `..` |
| 231 | 会被拒绝。Rust 仅解析已审查字节清单覆盖的文件,复用现有的 |
| 232 | [frontmatter 与调用契约](./SKILLS.md#调用与别名元数据)。隐藏子目录会跳过, |
| 233 | 含 `SKILL.md` 的技能包会占有其嵌套示例;无效技能或空目录会导致注册被拒绝。 |
| 234 | 该接口归于已审查 Native 入口,无需额外声明 Skills 组件。 |
| 235 | |
| 236 | 等待注册及已注册目录均占用宿主配额:每个所有者最多 8 个目录,宿主最多 64 个。 |
| 237 | Rust 解析每次目录提议时最多读取 128 个候选 `SKILL.md`、4 MiB 原始输入;保留的 |
| 238 | 指令字段总量每个所有者最多 128 个技能、4 MiB,宿主最多 1,024 个技能、32 MiB。 |
| 239 | 这些是 catalog 的逻辑配额。相同所有者要重新注册同一路径,需先调用旧 disposer。 |
| 240 | |
| 241 | 清理、停用、撤销信任及宿主退出会从后续发现结果中移除目录。加载时会重新检查 |
| 242 | Native 审查凭据和当前注册;排队中的用户选择也会重新检查。进程或宿主重启后, |
| 243 | 先前保存的 Native 技能选择需重新选择。已完成的会话历史继续记录当时用过的指令。 |
| 244 | 该接口不监视文件,不提供配套文件访问,也不授予工具权限;核心审批及沙箱策略 |
| 245 | 继续生效。 |
| 246 | |
| 247 | ## 请求核心执行工具 |
| 248 | |
| 249 | 工具在直接模型调用、由共享 turn gate 管理本次执行时,可能获得 `exec.core`: |
| 250 | |
| 251 | ```js |
| 252 | async execute({ path }, exec) { |
| 253 | if (!exec.core) return { error: 'core handle unavailable' } |
| 254 | const result = await exec.core.call('read', { path }) |
| 255 | return { content: result.content, isError: result.isError } |
| 256 | } |
| 257 | ``` |
| 258 | |
| 259 | 核心调用沿用 allow/deny、hooks、Auto-Review、仓库规则、worker 权限和审批检查。 |
| 260 | 审批卡片及来源由 Rust 生成,插件不能批准或指定票据。该句柄不出现在激活、timer、 |
| 261 | 用户命令、sub-agent 调用或 `execute_tools` 中的嵌套扩展工具执行里;调用结束、取消、 |
| 262 | 插件停用或宿主退出后失效,等待中的审批以取消结果撤回。 |
| 263 | |
| 264 | 嵌套 shell 和网络调用强制要求用户审批,已记住的授权不满足它们;Ask 和 |
| 265 | Full Access 都会逐次询问用户,明确的会话拒绝仍然生效。Auto-Review 和 Never |
| 266 | 不会打开卡片,会直接拒绝。少数只读、工作区本地工具可免去新的卡片。外层插件工具的批准并不 |
| 267 | 批准它进一步发起的核心调用,授权按插件构建来源隔离。 |
| 268 | |
| 269 | 不允许递归调用扩展工具、MCP/Computer Use、解释器、agent/workflow/rlm、 |
| 270 | `request_user_input`、交互 shell、沙箱提权、会话权限/调度修改工具及其他明确拒绝的 |
| 271 | 目标。每次扩展工具调用最多发起 50 次核心调用,最多四个并发,一张等待审批卡片; |
| 272 | 宿主最多 256 个请求在途。审批等待期间,外层工具截止时钟暂停。完整的拒绝列表和 |
| 273 | `CoreCallError` 错误分类见[英文核心调用契约](../EXTENSIONS.md#asking-the-core-to-run-a-tool)。 |
| 274 | |
| 275 | ## 兼容边界 |
| 276 | |
| 277 | 原生 mod 入口、静态导入器及其他客户端的插件格式是不同入口。 |
| 278 | 兼容的 Claude 插件包仍通过[原生组件适配器](./PLUGINS.md)读取声明组件;这不加载 |
| 279 | Claude 的执行循环。Pi 扩展 API 并未被自动适配,需按当前契约手工移植。 |
| 280 | DSH 导入器在不执行插件的审查阶段读取 `dsh.bundle.patch`,封存入口、模块及资源的 |
| 281 | 路径与哈希。已审查的 Native 入口通过同一 Loader 加载选定的组合;`!!js` 表达式 |
| 282 | 仅在已审查 Native 激活时求值。Bun 不保留带 query/fragment 的模块身份,这类组合 |
| 283 | 会明确提示选择 Node。缺少 default 的预设目录保持未选中,不会自动选择第一个条目。 |
| 284 | 损坏或不支持的预设仍显示原因;这不加载 DSH 的 agent 执行循环或浏览器 UI。 |
| 285 | |
| 286 | 目前还没有作者可用的自定义 Ratatui/GPUI 控件及 UI slots。插件不能替换核心的 tools、commands、systemPrompt、审批、会话或凭据服务。 |
| 287 | 当前功能边界和安装流程见[插件编写指南](./PLUGIN_AUTHORING.md)及[插件包契约](./PLUGIN_BUNDLES.md)。 |
| 288 |