Why: The base must stay unmodified by downstream products, so the roadmap, compatibility window, and plugin contract need a written proposal before further forks. Evidence: Three proposals under docs/proposals and a short pointer in the plugin development guide. Impact: Downstream work follows extension-point requests and a published base version. Tag commands in the compat proposal are not executed. Refs: .task/feature-base-extension-roadmap.md Co-authored-by: Cursor <cursoragent@cursor.com>
4.0 KiB
4.0 KiB
插件契约 v2
状态:实施中(2026-10-01);owner:weichen;主任务:Dev 仓 feature/base-extension-roadmap。
撰写日期:2026-10-01。配套:路线图、兼容规则。
1. 四种形态
下游按需要选一种或组合。基座对四种形态使用同一份 manifest 与同一套 fail-closed:任一资产解析失败则整包拒绝,保留上一状态。
| 形态 | 下游交付 | 基座职责 |
|---|---|---|
| 声明 | plugin.json + 资产目录 |
注册 tools/skills/mcp/knowledge/parsers/agents/channels/pages/commands/… |
| 进程内编译 | 下游 main import pkg/extension 并 RegisterCompiled |
link + builder 模式产出下游自己的单二进制 |
| sidecar | 原生进程,manifest backend.mode=sidecar |
拉起、健康(进程存活)、停止;环境变量注入 Host API |
| 前端 ESM | pages/*.json 指向模块 |
UI SDK 只暴露命令、SSE 与 @ui 组件;页面不得另起设计令牌 |
进程内编译适合要事务一致性的领域逻辑。sidecar 适合异构语言或强隔离。声明式 HTTP 工具仍然只描述调用,不携带任意代码。
2. manifest 增补
apiVersion 用 2.0-experimental 才强制网络白名单。字段见 modules/core/plugin_v2.go。
provides.skills:skills/<name>.md,宿主写入<data>/skills/plugin-<id>/。provides.mcpServers:并入小队 MCP 配置(与tools.mcp.servers合并)。provides.knowledge|parsers|agents|channels|pages:声明登记,供清单 API 与前端加载。scope.tenants/projects:空 = 全部可见。backend.command:sidecar 可执行文件(相对插件目录或 PATH)。schemaVersion记入plugin_schema。
密钥查找顺序:secrets.<pluginId>.<authRef>(按工具执行时的 OwnerID),再回落同名全局设置。不再在绑定期固定读 OwnerDefault。
3. Host API(sidecar 与外部进程)
监听由基座在本机拉起,令牌随进程环境变量 AGENT_HOST_API、AGENT_HOST_TOKEN 下发。
| 方法 | 路径 | 作用 |
|---|---|---|
| PUT/GET | /host/v1/kv/{key} |
插件隔离 KV(带 owner 与 plugin id) |
| POST | /host/v1/audit |
写通用审计 |
| POST | /host/v1/commands/{id} |
走命令总线 |
| POST | /host/v1/llm |
代理聊天补全;未配置模型返回 503,不静默伪造 |
| GET | /host/v1/knowledge/search |
混合检索(引擎在基座) |
插件故障不拖垮基座:sidecar 退出只记日志与审计,不退出主进程。
4. 进程内注册
func init() {
extension.RegisterCompiled(extension.InProc{
ID: "rdm",
Register: func(reg extension.Registry) error {
return reg.AddSkill("rdm-note", "# …")
},
})
}
下游 main:
import _ "agent-system-backend/pkg/extension/link"
func main() { extension.Main(extension.Options{Addr: "127.0.0.1:8787"}) }
5. 前端
@ui 导出 createPluginBridge:execCommand 与 subscribeEvents。插件页面模块默认导出 mount(el, bridge) / unmount()。宿主按 pages[].module 动态 import()。插件 TSX 与基座一样受 design-guard 约束(令牌、无私人 keyframes、无行内字号)。
6. 生命周期
- 启用:
POST /api/plugins/{id}/enabled,按租户记plugin_grants。 - 升级:
backend.schemaVersion大于已记录版本时调用宿主迁移钩子(KV 命名空间不清空;钩子由进程内扩展提供)。版本只升不降,降级拒绝装载。 - 管理面:
GET /api/plugins返回 manifest(含 v2 provides)以及技能/页面/渠道声明。既有设置页「插件」分组继续承担热重载。
7. 开发体验
make plugin-new NAME=<id>在examples/plugins/<id>生成声明式骨架。examples/plugins/decl-demo、examples/downstream-rdm、examples/sidecar-demo、examples/page-demo各覆盖一种形态。- 验收:
examples/downstream-rdm的测试不导入internal,并由make test-boundaries卡住。