Skip to content

插件 Slot 补齐计划 — 15 项中高优先级 #528

Description

@vastsa

插件插槽补齐计划(总纲 · 跟踪 issue)

状态: 需求已在子 issue 中定稿 / 部分待定;本 issue 只做总纲与进度跟踪,具体需求、示例、契约决策以子 issue 为准。
子 issue:

本次修订(2026-09-18): 依据两个子 issue 重写。原文的「15 项 / 5 阶段 / ADR 0278–0285 / 两段式权限名 / 文件浏览器 5 槽 / 底部状态栏」等内容已过时,全部以下文为准;差异见文末「与旧稿的差异」。


1. 目标与范围

补齐 PI-Desktop 插件系统的扩展面,分两条线:

线 回答的问题 子 issue 项数 权限前缀
UI 插槽 插件在界面上占哪个位置 #545 14 ui.<域>.<项>
运行时插槽 插件在运行时的哪个时刻被叫到、有没有决策权 #561 12 runtime.<域>.<项>

同一个功能经常两边都要:位置有了但数据拿不到是空壳,数据有了但界面没位置用户看不见。排期时按"一个用户可见能力"切 PR,而不是按线切。

不在范围内(已裁定,见 #545 / #561 附录):

  • 文件浏览器内的 5 个槽(右键菜单、工具栏、预览器、打开方式、装饰徽标)、工作面板 / 活动栏徽标 —— 内置面板只提供自身功能,不作为扩展点
  • 底部状态栏项 —— 宿主没有这个概念
  • 往会话历史写内容、替换工具实现、读凭证 / 数据目录、放松审批、改宿主自有 UI 控件、改已发生的事实
  • 签名 / 分发 / 市场(ADR-0276)

尚未拆子 issue、暂不排期: 旧稿的「Provider 三件套(OCR / CLI / 受管本地服务)」与「动态模型 Provider Snapshot + OAuth + Secret Store」。它们不是插槽,属于 provider / 凭证能力,需要单独立项;本 issue 不再跟踪。


2. 总体原则(两条线共用)

  • 向后兼容:只加不改。已发布的 ui.panel / ui.view / ui.theme / ui.settings / ui.microphone / ui.window.appearance 与全部旧 pi.* API 不动;manifest schemaVersion 保持 1
  • 一槽一权限(D1 / D11):三段式命名;安装页逐条展示,安装 = 同意全部;升级 / 热重载新增权限才再确认(沿用 addedPermissions / beyondApproval
  • 全部走运行时注册(D6):manifest 只保留 permissions;形状与数据由插件运行时一次推出;插件没启动 / 崩溃 / 禁用 / 卸载 → 界面上根本不占位置(D10);注册失败必须在插件行 / 诊断里可见
  • 宿主画外框,插件画内容(D4):弹窗 / 卡片 / 浮层的关闭、确认、取消、遮罩、层级由宿主拥有;插件最多"请求关闭";没有"撤销"概念
  • 状态归宿主(D5):用户选择由宿主按 全局 / 项目 / 会话 作用域保存,插件只读 + 订阅
  • 不设上限、不带版本号、通用错误(D7–D9):插件推几个显示几个;按到达顺序应用;只有成功 / 失败两态,不逐槽枚举错误码
  • 位置规则(D8):插件项永远排在宿主自有项之后;窗口变窄优先隐藏插件项
  • 风险分档只做展示(D2):不引入首次使用确认或默认拒绝;已有的运行时确认(剪贴板 / 出网 / 越界写 / 危险桌面操作)保持不变
  • Plan / Goal 统一(D3):拒绝码沿用 PLUGIN_DISABLED_IN_PLAN
  • capability 一致:主进程 capability bus 二次校验;注册 API 返回 dispose handle;Rust manifest validator 同步接受新权限名

3. UI 插槽清单(#545,D11 定稿)

# 插槽 区域 权限 风险 状态
1 消息卡渲染(Entry Renderer) 内容区 · 整条消息 ui.render.message
2 turn / tool 卡渲染 内容区 · 工具卡 ui.render.tool
3 块渲染器(围栏块) 内容区 · 代码块 ui.render.block
4 Markdown transformer(文字级) 内容区 · 句子内部 ui.render.text
5 Composer 声明式控件槽 输入区 · 左 / 右 ui.composer.control
6 附件源 输入区 · 「+」菜单 ui.composer.attachment
7 autocomplete provider 输入区 · 候选弹层 ui.composer.completion
8 改写草稿 输入区 · 输入框文字 ui.composer.draft
9 内联确认卡 输入框上方 ui.prompt.inline
10 全屏弹窗 浮层区 ui.prompt.modal
11 侧栏条目 外壳区 · 左侧栏 ui.shell.sidebar
12 全局浮层 外壳区 · 浮层 ui.shell.overlay
13 full-page workspace 外壳区 · 整页 ui.shell.page
14 插件文案多语种 全局 · 插件文案 (无权限)

关键前置(摘自 #545 汇总表):第 5 项需先定左右分区规则且「发送」永远最右;第 6 项需先把「+」改成菜单;第 8 项需差异对比 + 全局开关;第 11 项宿主左侧栏需先提供插件贡献位;第 13 项需路由命名空间 + 返回路径。


4. 运行时插槽清单(#561,权限名为建议,待 RD 定稿)

# 插槽 触发时机 权限(建议) 现状 状态
1 发送前(Before Send) 发送后、入队前 runtime.send.before ⚙️ 内核有、桌面端没 emit
2 运行中观测(Turn Watch) 本轮进行中 runtime.turn.watch 🔶 只给高信任;可靠性 ❌
3 中止本轮(Abort Turn) 本轮进行中 runtime.turn.abort ⚙️ Agent.abort() 未开放
4 工具闸门(Tool Gate) 工具调用前 / 后 runtime.tool.gate 🔶 拦 / 改结果只给高信任;改参数 ❌
5 工具能力扩展(Tool Extend) 工具执行期间 / 返回时 runtime.tool.extend ⚙️ 内核字段在、插件用不到
6 请求前改写(Before Request) 每次发模型前 runtime.request.before 🔶 / ⚙️
7 回合结束前(Turn Closing) 本轮即将结束 runtime.turn.closing ⚙️ 三个入口全在、全 0 命中
8 回合结束后读本轮(Turn Recap) 一轮结束后 runtime.turn.recap ❌ 桌面端明确禁了
9 本轮事实查询(Turn Facts) 一轮结束后或中途 runtime.turn.facts ❌ 原料在、不开放
10 宿主续跑(Turn Continue) 一轮结束后 runtime.turn.continue ⚙️ 宿主内部用了、没对外
11 会话生命周期(Session Lifecycle) 创建 / 切换 / 删除 / fork / 压缩前 runtime.session.lifecycle ⚙️ 上游事件够不到
12 审批前(Approval Before) 审批卡出现前 runtime.approval.before ❌ 两层都没有

标记:✅ 现在就能用 · 🔶 只开给高信任插件(contributes.agentExtensions)· ⚙️ 内核有、桌面端没接线 · ❌ 要新做。

运行时待定项 RD1–RD7(阻塞第 2 / 4 / 6 / 7 / 9 / 10 / 12 项的契约):

编号 待定 阻塞
RD1 插件续跑内容用户是否可见、进不进历史 7、10
RD2 续跑配额(每会话 / 每轮 N 次) 7、10
RD3 观测可靠性分级(尽力 vs 可确认送达) 2
RD4 "改写"类能力的审计粒度 4、6
RD5 闸门类是否先只做放行 / 拦下两态 4、12
RD6 第 9 项"产物文件"的定义(artifacts 表怎么改) 9
RD7 高信任插件(🔶)这条路长期是否保留 2、4、6

5. 推荐执行顺序

按"一个用户可见能力 = 一个 PR"排,优先做风险低、内核已就绪、只差接线的项:

批次 A(接线为主,⚙️ 项 + 低风险 UI)
  运行时 7 Turn Closing ⭐ · 运行时 1 Before Send · 运行时 3 Abort
  UI 5 Composer 控件槽 · UI 11 侧栏条目 · UI 14 文案多语种

批次 B(外壳 / 提示类 UI)
  UI 9 内联确认卡 · UI 10 全屏弹窗 · UI 12 全局浮层 · UI 13 整页工作区

批次 C(内容区渲染,触及 ChatTranscript.tsx hotspot)
  UI 3 块渲染器 · UI 2 工具卡 · UI 4 transformer · UI 1 消息卡

批次 D(输入区,触及 Composer.tsx hotspot)
  UI 6 附件源 · UI 7 补全 · UI 8 改写草稿

批次 E(需 RD 定稿后)
  运行时 2 / 4 / 5 / 6 / 8 / 9 / 10 / 11 / 12

批次 A 可与 RD1–RD7 讨论并行;批次 E 在 RD 定稿前不开工。


6. 每个 PR 的交付物

  1. ADR:编号在开 PR 时按当时序号分配,不预留(旧稿的 0278–0285 已被其他 ADR 占用)
  2. docs/spec/07-plugins/ 补章(UI 槽与运行时槽各一份索引章,逐槽契约按 plugin-ui-slot-contract-sample.md 格式)
  3. packages/plugin-sdkPLUGIN_PERMISSIONS 追加权限名 + pi.* 注册 API + schema 单测
  4. Rust host-core:manifest validator 接受新权限名(纯运行时注册,不新增 contributes.* 字段)
  5. 主进程:registry + capability bus 校验 + dispose
  6. Renderer:outlet 组件放 apps/desktop/src/plugins/renderer-slots/;hotspot 文件只加 outlet
  7. agent-runtime(运行时槽):接线到内核入口,审计落 host-core
  8. 至少 1 个 examples/plugins/ 示例插件,可 pnpm plugin:install 装入 dev 桌面
  9. 测试:SDK schema + plugin-runtime integration + renderer 组件 + Rust validator;verify:ui:* 不跑除非用户显式要求
  10. packages/plugin-sdk/README.md API 目录 + 07-plugins/14-plugin-roadmap.md 更新

通用验证:

pnpm build:js
pnpm --filter @pi-desktop/desktop typecheck
pnpm lint
pnpm -r --if-present test
cargo fmt --check && cargo test -p host-core --locked && cargo clippy -p host-core --all-targets
pnpm check:agent-policy

7. 风险与缓解

风险 缓解
ChatTranscript.tsx / Composer.tsx hotspot 只加 outlet,逻辑放 renderer-slots/;单 PR 净增 ≤ 150 行
数量不设上限(D8)导致界面拥挤 位置规则:插件项排在宿主项后,窄窗口优先隐藏;用户可禁用
高风险槽(消息卡 / 改写草稿 / 全屏弹窗)装上即生效 安装页显眼展示风险档;契约写死宿主强制边界;随时禁用
运行时"改写"类能力不可解释 RD4 定审计粒度前不做改写;先只做两态(RD5)
续跑无上限循环 RD2 定配额前不开放续跑
插件崩溃留下可点却必失败的控件 硬边界 2:插件消失后已渲染实例进入不可交互态
harness 层 11 个钩子自带持久化,与 host-core 独占持久化冲突 要用先出 ADR

8. 完成判据


与旧稿的差异(2026-09-18)

旧稿 现在
15 项、5 阶段、8 个 ADR(0278–0285) 14 项 UI + 12 项运行时;ADR 编号不预留(0278–0281 已被占用)
两段式权限名 ui.statusBar / ui.badge / ui.sidebar / ui.workspacePage / ui.overlay 三段式 ui.<域>.<项>(D11);状态栏、徽标已裁掉
contributes.* 声明 + pi.* 动态注册 三段式 只走运行时注册,manifest 只留 permissions(D6)
阶段 2 文件浏览器 5 槽 裁定不做
阶段 3 Provider 三件套、阶段 4 OAuth / Secrets 不属于插槽,移出本 issue,需单独立项
Sidebar 项目 / 会话列表条目、view badge 侧栏条目保留(第 11 项);badge 裁掉
overlay 并发 ≤ 3、badge 节流 数量不设上限(D8)
未提运行时时机 新增 #561 的 12 项运行时插槽与 RD1–RD7

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions