Files
Agent-System-Documents/docs/proposals/2026-10-01-plugin-contract-v2.md
weichenandCursor a0da588f90 Document the base extension roadmap and plugin contract v2
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>
2026-10-01 10:34:30 +00:00

4.0 KiB
Raw Permalink Blame History

插件契约 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 卡住。