先选择最接近用户任务的扩展点,再在 activate(ctx) 中注册。一个插件可以组合多个扩展点,但每项都应有明确用途和独立权限。
清单声明面(技能、MCP、智能体、引导词)在插件安装时就生效,不依赖 activate() 执行;运行时注册面需要插件被启用并加载。
界面扩展点
ctx.ui 提供的注册面,每项对应一个独立权限:
| 扩展点 | API | 权限 |
|---|---|---|
| 全局通知 Toast | ctx.ui.notify | 无需权限 |
| 全局浮层 | registerGlobalSlot | ui.slot.global |
| 工作区视图(整页 + 侧边栏入口) | registerWorkspaceView、openWorkspaceView、setWorkspaceViewBadge、setWorkspaceViewHeader | ui.slot.workspace-view |
| 文件预览 | registerFilePreview | ui.slot.file-preview |
| 活动面板 Tab | registerActivityTab、openActivityTab、setActivityTabVisible | ui.slot.activity-tab |
| 输入栏动作与附件 | registerInputAction、setPromptAttachment | ui.slot.input-action |
| 新会话上下文区 | registerNewSessionContext | ui.slot.new-session-context |
| 消息卡片渲染器 | registerCardRenderer | ui.slot.message |
| 工具调用行内渲染 | registerToolCallSlot | ui.slot.tool-call |
| 本轮 Turn 卡 | registerTurnCard | ui.slot.turn-card |
| 能力详情页运行时区块 | registerAbilityDetailSlot | ui.slot.ability-detail |
| 键盘快捷键 scope | registerShortcutScope、usePluginShortcutScope | ui.shortcuts.register |
| 打开系统默认浏览器 | openExternal | shell.openExternal |
组件使用宿主共享的 React 与设计令牌。注册函数返回 Disposable,在停用阶段释放,避免重载后重复注册。
面板类槽位(文件预览、活动 Tab、工作区视图)禁止使用铺满视口的固定定位浮层,它们会盖住宿主自己的界面。
文件浏览器扩展点
ctx.fileExplorer 单独成组,读取工作区结构与写入界面装饰是两类权限:
| 扩展点 | API | 权限 |
|---|---|---|
| 右键菜单动作 | registerContextMenuAction | ui.file-explorer.context-menu |
| 工具栏动作 | registerToolbarAction | ui.file-explorer.toolbar |
| 文件状态装饰(角标、语义色、淡化、删除线、父目录聚合) | registerDecorationProvider | ui.file-explorer.decorations |
| 文件图标主题(文件名、扩展名、文件夹与明暗模式) | registerIconTheme | ui.file-explorer.decorations |
| 读取根目录、选择、定位与刷新 | getWorkspaceRoots、getSelection、reveal、refresh、onDidChangeSelection、onDidChangeFiles | workspace.read |
文件解析和界面渲染应分离,解析失败时返回可理解的错误状态,不要让单个文件使整个会话视图崩溃。
动态状态通过 onDidChangeDecorations 精确失效,不需要刷新文件系统。图标主题由用户在文件列表里选择;插件只能使用宿主语义色和声明式图标关联,不能改文件行的任意 CSS、布局或排序。新装饰能力要求 Plugin API ^2.7.0。
Agent 扩展点
运行时注册面(ctx.agent):
| 扩展点 | API | 权限 |
|---|---|---|
| 注册 Agent 工具 | registerTool | agent.tools.register + agent.toolHandler.execute |
| 注册 Coding Agent 生命周期 Hook | registerHook | agent.hooks.register + agent.hookHandler.execute |
| 动态系统提示词 Provider | registerSystemPromptProvider | agent.systemPrompt.write;操作非本插件的 block 另需 agent.systemPrompt.fullControl |
| 自动续跑策略 | registerContinuationProvider | agent.continuation.register |
| 动态开关工具 | Provider 的 actions.tools.setEnabled | agent.tools.control |
清单声明面(plugin.json):
| 扩展点 | 字段 | 权限 |
|---|---|---|
| 打包技能 | agent.skillPaths | agent.skills.control |
| 追加系统提示词文件 | agent.systemPrompt.promptPaths | agent.systemPrompt.write |
| 工具策略 | agent.toolPolicy | agent.tools.control |
| 插件内聚 MCP Server | agent.mcpServers | agent.mcp.control |
| 贡献智能体与团队 | agent.agents、agent.teams | 无需权限 |
| 新会话引导词 | guidingWords | 无需权限 |
工具输入使用结构化 schema,并把外部 I/O 错误映射为稳定结果。需要宿主文件、网络或模型能力时,先在清单中声明最小权限。
插件内聚 MCP 是第三配置源
会话的 MCP 工具面来自三源合并,互相并列而不是覆盖:用户全局 mcp.json、项目侧配置、以及插件清单贡献。插件源不回写用户文件,禁用或卸载插件只撤掉自己那部分。插件 Server 的运行时名统一为 plugin-<插件 id>-<本地 key>,与另外两源同时存在也不会撞名。
设置页的 MCP 编辑器只读写用户文件,不会列出也无法编辑插件内聚的 Server。
智能体与团队
插件在清单里贡献的智能体与团队会被铺成普通档案,与用户自己创建的并列。人设、头像和提示词由插件维护:停用插件时档案灰着留在原地,重新启用即恢复。
团队有两条硬约束:队长必须是本插件自己的智能体(否则被引用的插件一卸载,这支团队就成了打不开的壳),队长的任务书写在团队的 workflow 而不是成员条目上(两处都写就无法确定哪份生效)。其余成员可以用 <插件 id>/<智能体 id> 引用别的插件的智能体。
选中这类智能体后还可以用新会话上下文区在输入框下方摆出接下来要用的素材,激活条件只能写本插件自己的东西。
Provider 扩展点
插件不只消费宿主能力,也可以反过来给宿主提供能力:
App Action
App Action 让插件通过宿主动作系统暴露可发现命令,适合导航、打开面板或执行确定性操作。注册需要 app.actions.register,handler 被调用时需要 app.actionHandler.execute。
缺权限时会发生什么
不同注册点对「声明了但未授权」的处理不同,这个差异会直接影响你怎么组织 activate():
| 行为 | 涉及的注册点 |
|---|---|
抛出 Plugin permission denied 并中断调用 | registerInputAction、registerCardRenderer、registerToolCallSlot、registerTurnCard、registerShortcutScope、openActivityTab、setActivityTabVisible、setPromptAttachment、fileExplorer.*、registerSystemPromptProvider、registerContinuationProvider、conversation.*、fs.*、network.*、storage.*、media.*、ai.*、command.run |
| 跳过该贡献并打印警告,不影响其它能力 | registerGlobalSlot、registerFilePreview、registerActivityTab、registerNewSessionContext、agent.registerTool、agent.registerHook、appActions.register |
界面槽位的权限缺失不会在构建期暴露,装上去只是静默不显示,需要对照上表核对。