@vetta-org/plugin-sdk 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.d.ts +0 -12
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js.map +1 -1
- package/dist/ai.d.ts +13 -0
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js.map +1 -1
- package/dist/app-actions.d.ts +0 -1
- package/dist/app-actions.d.ts.map +1 -1
- package/dist/app-actions.js.map +1 -1
- package/dist/cli-provider.d.ts +18 -0
- package/dist/cli-provider.d.ts.map +1 -0
- package/dist/cli-provider.js +2 -0
- package/dist/cli-provider.js.map +1 -0
- package/dist/context.d.ts +13 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/manifest-schema.d.ts +454 -51
- package/dist/manifest-schema.d.ts.map +1 -1
- package/dist/manifest-schema.js +139 -33
- package/dist/manifest-schema.js.map +1 -1
- package/dist/manifest.d.ts +2 -2
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +164 -38
- package/dist/manifest.js.map +1 -1
- package/dist/models.d.ts +35 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +2 -0
- package/dist/models.js.map +1 -0
- package/dist/ocr.d.ts +78 -0
- package/dist/ocr.d.ts.map +1 -0
- package/dist/ocr.js +2 -0
- package/dist/ocr.js.map +1 -0
- package/dist/official.d.ts +1 -0
- package/dist/official.d.ts.map +1 -1
- package/dist/official.js.map +1 -1
- package/dist/permissions.d.ts +1 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +8 -0
- package/dist/permissions.js.map +1 -1
- package/dist/secrets.d.ts +27 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +2 -0
- package/dist/secrets.js.map +1 -0
- package/dist/service-provider.d.ts +62 -0
- package/dist/service-provider.d.ts.map +1 -0
- package/dist/service-provider.js +2 -0
- package/dist/service-provider.js.map +1 -0
- package/dist/storage.d.ts +39 -6
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +8 -1
- package/dist/storage.js.map +1 -1
- package/dist/ui.d.ts +118 -7
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js.map +1 -1
- package/docs/.source.json +22 -0
- package/docs/README.md +104 -0
- package/docs/ability-details.md +424 -0
- package/docs/ai.md +121 -0
- package/docs/app-actions.md +111 -0
- package/docs/browser.md +80 -0
- package/docs/conversation-and-agent.md +619 -0
- package/docs/file-explorer.md +118 -0
- package/docs/getting-started.md +295 -0
- package/docs/guiding-the-agent.md +183 -0
- package/docs/manifest.md +296 -0
- package/docs/mcp.md +152 -0
- package/docs/media.md +141 -0
- package/docs/message-cards.md +188 -0
- package/docs/permissions.md +145 -0
- package/docs/styling-and-pitfalls.md +254 -0
- package/docs/system-plugins.md +58 -0
- package/docs/ui-slots.md +628 -0
- package/package.json +7 -3
- package/dist/settings.d.ts +0 -13
- package/dist/settings.d.ts.map +0 -1
- package/dist/settings.js +0 -2
- package/dist/settings.js.map +0 -1
package/docs/manifest.md
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# 清单参考(plugin.json)
|
|
2
|
+
|
|
3
|
+
`plugin.json` 是插件的唯一清单,位于 zip 归档根(或唯一顶层文件夹内)。
|
|
4
|
+
|
|
5
|
+
它描述插件的运行时合同。能力页的可选长详情使用独立的 `ability.json`,见
|
|
6
|
+
[能力详情页](./ability-details.md);不要把 showcase、长 Markdown 或展示图片塞进 `plugin.json`。
|
|
7
|
+
|
|
8
|
+
## 契约与校验
|
|
9
|
+
|
|
10
|
+
清单结构的唯一实现位于 `@vetta-org/plugin-sdk/manifest`:
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import {
|
|
14
|
+
PluginManifestSchema,
|
|
15
|
+
parsePluginManifest,
|
|
16
|
+
type PluginManifestInput,
|
|
17
|
+
} from "@vetta-org/plugin-sdk/manifest";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- `PluginManifestSchema` 是 TypeBox Schema,可直接序列化为 JSON Schema,供编辑器、CLI 或市场服务端使用。
|
|
21
|
+
- `PluginManifestInput` 与兼容类型 `PluginManifest` 均由 Schema 推导,不单独手写字段联合。
|
|
22
|
+
- `parsePluginManifest(value)` 先按 Schema 校验,再负责默认值、字符串归一化、去重、相对路径和跨字段约束。
|
|
23
|
+
- Schema 为向前兼容允许未知字段;发布工具可以对未知字段给警告,但宿主安装器不应因此拒绝更高版本清单。
|
|
24
|
+
|
|
25
|
+
Schema 只描述 `plugin.json` 数据本身;Plugin API 版本是否兼容、声明的文件是否存在等包级规则,仍由宿主和 `vetta-plugin pack` 校验。
|
|
26
|
+
|
|
27
|
+
## 完整示例
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"id": "my-plugin",
|
|
32
|
+
"name": "我的插件",
|
|
33
|
+
"version": "0.1.0",
|
|
34
|
+
"pluginApiVersion": "^2.0.0",
|
|
35
|
+
"entry": "dist/mf-manifest.json",
|
|
36
|
+
"moduleFederation": {
|
|
37
|
+
"remoteName": "my_plugin",
|
|
38
|
+
"expose": "./plugin"
|
|
39
|
+
},
|
|
40
|
+
"styles": ["dist/style.css"],
|
|
41
|
+
"permissions": ["ui.slot.global", "agent.session.read", "agent.command.run", "browser.read"],
|
|
42
|
+
"commands": ["git"],
|
|
43
|
+
"browser": { "allowedHosts": ["studio.example.com"] },
|
|
44
|
+
"defaultLocale": "zh",
|
|
45
|
+
"description": "一句话说明这个插件做什么",
|
|
46
|
+
"author": "你的名字",
|
|
47
|
+
"guidingWords": ["%guidingWords.summarize%", "把这段代码加上注释"],
|
|
48
|
+
"agent": {
|
|
49
|
+
"systemPrompt": { "promptPaths": ["prompts/extra.md"] },
|
|
50
|
+
"skillPaths": ["skills/"],
|
|
51
|
+
"mcpServers": "./.mcp.json",
|
|
52
|
+
"toolPolicy": { "allow": [], "deny": [] }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 字段
|
|
58
|
+
|
|
59
|
+
| 字段 | 必填 | 类型 | 说明 |
|
|
60
|
+
| --- | --- | --- | --- |
|
|
61
|
+
| `id` | ✅ | string | 全局唯一插件 id。决定安装目录、id 冲突时的去重,建议小写短横线。 |
|
|
62
|
+
| `name` | ✅ | string | 展示名。可用 `%key%`(见 [i18n](#i18n))。 |
|
|
63
|
+
| `version` | ✅ | string | 语义化版本。**bump 它可强制宿主重新拉取**绕过缓存(见 [styling-and-pitfalls.md](./styling-and-pitfalls.md))。 |
|
|
64
|
+
| `pluginApiVersion` | ✅ | string | 兼容的 SDK API 版本范围(当前为 `^2.0.0`)。 |
|
|
65
|
+
| `entry` | ✅ | string | Module Federation 清单路径,通常为 `dist/mf-manifest.json`。 |
|
|
66
|
+
| `moduleFederation` | ✅ | `{ remoteName, expose }` | `remoteName` 与 vite 配置 `name` 一致;`expose` 与 vite `expose` 一致(默认 `./plugin`)。 |
|
|
67
|
+
| `styles` | ❌ | string[] | 要注入的 CSS 文件路径(相对插件根)。 |
|
|
68
|
+
| `permissions` | ❌ | string[] | 声明需要的权限,见 [permissions.md](./permissions.md)。未声明即不可用。 |
|
|
69
|
+
| `commands` | ❌ | string[] | 允许 `ctx.command.run` 的**可执行文件名**(如 `["git","node"]`),见 [commands](#commands)。 |
|
|
70
|
+
| `browser` | 使用 `browser.*` 权限时必填 | `{ allowedHosts: string[] }` | 浏览器顶层导航的最大 host 授权;session 只能收窄,见 [browser.md](./browser.md)。 |
|
|
71
|
+
| `description` | ❌ | string | 简介。可用 `%key%`。 |
|
|
72
|
+
| `author` | ❌ | string | 作者。 |
|
|
73
|
+
| `icon` | ❌ | string | 能力页/插件列表展示的图标,也是[工作区视图](./ui-slots.md#工作区视图-registerworkspaceview)与活动 Tab 未声明图标时的回落。三态:省略(按类型落默认图)、Iconify 名(如 `solar:widget-add-bold`)、`http(s)://` 外链,或包内相对路径(如 `assets/icon.png`)。 |
|
|
74
|
+
| `defaultLocale` | ❌ | string | i18n 缺译回退 locale,默认 `"zh"`。见 [i18n](#i18n)。 |
|
|
75
|
+
| `guidingWords` | ❌ | string[] | 新会话引导词,见 [下文](#guidingwords引导词)。条目可用 `%key%`。 |
|
|
76
|
+
| `agent` | ❌ | object | Agent 侧贡献(prompt / skill / **MCP** / toolPolicy),见 [Agent 清单](#agent-agent-侧贡献)。 |
|
|
77
|
+
| `contributionMode` | ❌ | object | 贡献硬隔离,见 [contributionMode](#contributionmode)。 |
|
|
78
|
+
| `agent_mode` | ❌ | string \| string[] | **已废弃**(ADR-0071):无任何运行时语义,容忍存在但被忽略,见 [agent_mode](#agent_mode已废弃)。 |
|
|
79
|
+
|
|
80
|
+
## Module Federation 加载合同
|
|
81
|
+
|
|
82
|
+
插件只有一种加载方式:宿主用 `@module-federation/enhanced/runtime` 动态注册 remote 并加载 `expose`。
|
|
83
|
+
`entry` 指向 Federation 生成的 `dist/mf-manifest.json`,`moduleFederation` 必须声明与 Vite 配置一致的
|
|
84
|
+
`remoteName` 和 `expose`。React / React DOM / `@vetta-org/plugin-sdk` 由宿主作为共享单例提供。
|
|
85
|
+
|
|
86
|
+
清单不提供加载模式选择字段;声明 `runtime` 会被校验器拒绝,避免清单看似选择了一条宿主并不存在的加载路径。
|
|
87
|
+
|
|
88
|
+
## 安装目录与版本机制
|
|
89
|
+
|
|
90
|
+
用户插件按版本存放:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
~/.vetta/plugins/<id>/versions/<version>/
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- 安装一个**更新版本**只被记录为 **pending**;App 持续加载当前 `activeVersion`。
|
|
97
|
+
- 直到用户(或代码)触发 `window.vetta.plugins.reload(id)` 才切换到新版本 UI。
|
|
98
|
+
- 调试时改了代码要 bump `version` + reload 才稳妥生效(见 [styling-and-pitfalls.md](./styling-and-pitfalls.md#缓存刷新))。
|
|
99
|
+
- `listPlugins()` 会给出 **`rootPath`**:活动版本包在磁盘上的绝对根(用户插件 = 上表版本目录;系统插件 = `system-plugins/<id>`)。脚本、MCP 相对路径均相对此根解析。
|
|
100
|
+
|
|
101
|
+
系统插件不进 `~/.vetta/plugins`,见 [system-plugins.md](./system-plugins.md)。
|
|
102
|
+
|
|
103
|
+
## commands
|
|
104
|
+
|
|
105
|
+
`commands?: string[]`:**可执行文件名**粒度(如 `"git"`、`"node"`、`"npm"`),不是完整 argv。
|
|
106
|
+
|
|
107
|
+
- 未列入的二进制:`ctx.command.run` **硬拒绝**。
|
|
108
|
+
- 已声明:用户可在插件设置里**逐条开关**;关闭后调用拦截并提示用户。
|
|
109
|
+
- 需权限 `agent.command.run`。语义与 API 见 [conversation-and-agent.md 命令执行](./conversation-and-agent.md#命令执行-command)(ADR-0032)。
|
|
110
|
+
|
|
111
|
+
## contributionMode
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
"contributionMode": { "hardIsolation": true }
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- `hardIsolation: true`:该插件的 agent 贡献(tools / skills / MCP / systemPrompt)在 **mode 未打开**时不进入会话(冷启动即 gate,不必等 UI activate)。
|
|
118
|
+
- 通常配合 `registerInputAction({ hardIsolation: true })` 作为用户开关(ADR-0041)。
|
|
119
|
+
- **用户自建插件默认不要开**;模式型系统插件(如插件工作台)使用。
|
|
120
|
+
|
|
121
|
+
## agent_mode(已废弃)
|
|
122
|
+
|
|
123
|
+
> **Deprecated(ADR-0071,2026-08)**:本字段(插件级、tool / MCP server / skill 子资源级、`SKILL.md` frontmatter)**没有任何运行时语义**。宿主容忍它存在(既有 `plugin.json` 不会校验失败),但不解析、不排序、不展示。请不要在新插件里写它。
|
|
124
|
+
|
|
125
|
+
工作模式是**任务解释的先验**:Work/Coding 的差异完全由宿主的 mode 系统提示词、工作区事实注入与工具自描述承担,不影响任何插件能力的可用性与清单顺序。插件在所有模式下完整可用。
|
|
126
|
+
|
|
127
|
+
插件侧需要知道的只有两件事:
|
|
128
|
+
|
|
129
|
+
- **想收窄某个工具的使用场景**,写进该工具 **description 的反向触发段**(说明何时**不该**用它及替代做法)。那是模型真正阅读并据以选择的地方;`agent_mode` 从来做不到这一点。
|
|
130
|
+
- ⚠️ **`ctx.getAgentMode()` 读到的是「新会话默认模式」,不是当前会话正在用的模式**:模式于会话创建时固化、会话内不可变,用户改默认值不影响已存在的会话。它只适合展示层的软性定制,**不要在 tool / hook handler 里用它推断本次调用所属会话的模式**。详见 [conversation-and-agent](./conversation-and-agent.md#工作模式agent_mode)。
|
|
131
|
+
|
|
132
|
+
## i18n
|
|
133
|
+
|
|
134
|
+
插件 i18n(ADR-0033):
|
|
135
|
+
|
|
136
|
+
1. 包内 **`locales/<lang>.json`**:扁平 `key → 译文`(如 `zh.json` / `en.json`)。宿主 main 加载,随 `InstalledPlugin` 下发。
|
|
137
|
+
2. **`defaultLocale`**:缺译回退链 = 当前宿主语言 → defaultLocale → 裸 key。省略默认 `"zh"`。
|
|
138
|
+
3. **宿主渲染的字符串**(`name` / `description` / `register*` 的 `label` / `registerTool({ label })` / guidingWords 等):值为 **`%catalogKey%`** 时查 catalog;其它字符串当字面量(向后兼容)。
|
|
139
|
+
4. **插件自己的 React 组件内文案**:用 `useTranslation().t("catalogKey")` 或 `ctx.i18n.t`(裸 key,无 `%`),见 [conversation-and-agent 插件 i18n](./conversation-and-agent.md#插件-i18n)。
|
|
140
|
+
|
|
141
|
+
打包时 `locales/` 会打进 zip。
|
|
142
|
+
|
|
143
|
+
## guidingWords(引导词)
|
|
144
|
+
|
|
145
|
+
`guidingWords?: string[]` 是插件的**第一个声明式 UI 贡献**——与命令式 `ctx.ui.register*` 不同:**纯静态清单数据、无权限位、无运行时注册**。
|
|
146
|
+
|
|
147
|
+
- 唯一消费者是**新会话欢迎页**:在技能徽章下方按插件**分组**展示(组标题取插件 `name`),入选条件 = 插件已启用且 `guidingWords` 非空。
|
|
148
|
+
- 点击一条引导词=以其文本立即发起一轮对话(不填入输入框)。
|
|
149
|
+
- 展示限额(轮播,非数据截断):同时最多 3 组、每组最多 4 词;超出则组级 / 词级轮播。
|
|
150
|
+
|
|
151
|
+
## 插件配置放哪里
|
|
152
|
+
|
|
153
|
+
**宿主不再提供设置页配置槽**:`plugin.json#contributes.settings` 与只读的 `ctx.settings` 已在
|
|
154
|
+
Plugin API 1.6.0 移除(ADR-0105)。Plugin API 2.0 的存储改为文件合同(ADR-0107)。配置由插件自己渲染、自己持久化:
|
|
155
|
+
|
|
156
|
+
| 需求 | 用什么 |
|
|
157
|
+
| --- | --- |
|
|
158
|
+
| 完整配置界面(推荐) | `ctx.ui.registerWorkspaceView` 注册一个工作区配置页,配置与连通性检查、模型列表、预览放在同一屏 |
|
|
159
|
+
| 只有一两个开关 | 直接放进插件已有的活动 Tab、能力详情槽或全局槽 |
|
|
160
|
+
| 普通配置值 | `ctx.storage.readFile/writeFile("settings.json", ..., "utf8")`(插件私有存储,按 plugin id 隔离) |
|
|
161
|
+
| API Key 等密钥 | `ctx.secrets`(宿主加密凭据库),需要 `secrets.read` / `secrets.write` 权限 |
|
|
162
|
+
|
|
163
|
+
`settings.json` 是宿主迁移旧 `contributes.settings` 值时的落点:升级时宿主会把
|
|
164
|
+
`plugin-settings.json` 里该插件的非密钥字段一次性写进去,插件读回后自行归一化。密钥的凭据库命名空间
|
|
165
|
+
未变,无需迁移。
|
|
166
|
+
|
|
167
|
+
存储 API 不推断格式或扩展名:插件负责 `JSON.stringify/parse`,路径就是实际逻辑文件名。需要把多个
|
|
168
|
+
数据源拆成独立文件又保持一致时,用一次 `ctx.storage.commit()` 发布,并用 `readSnapshot()` 从同一
|
|
169
|
+
revision 读取;不要依次调用多次 `writeFile()` 冒充多文件事务。
|
|
170
|
+
|
|
171
|
+
带 `contributes` 字段的旧清单不会校验失败(顶层允许额外字段),但该字段不再有任何运行时语义。
|
|
172
|
+
|
|
173
|
+
## agent(Agent 侧贡献)
|
|
174
|
+
|
|
175
|
+
可选,向 agent 会话注入插件打包的资源(路径相对插件根,主进程聚合解析):
|
|
176
|
+
|
|
177
|
+
| 字段 | 说明 |
|
|
178
|
+
| --- | --- |
|
|
179
|
+
| `agent.systemPrompt.promptPaths` | 追加进系统提示词的提示片段文件路径。需 `agent.systemPrompt.write`(或 fullControl)。 |
|
|
180
|
+
| `agent.skillPaths` | 加入 agent 资源图的 skill 文件 / 目录。需 `agent.skills.control`。skill frontmatter 的 `agent_mode` 已废弃(ADR-0071),容忍存在但被忽略。 |
|
|
181
|
+
| `agent.skillPresentation` | 控制插件 Skill 在产品入口的可见性与展示文案。只影响界面呈现,不影响加载、调用或权限。 |
|
|
182
|
+
| `agent.mcpServers` | **插件内聚 MCP**(三源聚合之插件源):相对路径 `.mcp.json` 或内联 map。需 `agent.mcp.control`。内联 map 里的 `agent_mode` 已废弃(ADR-0071),容忍存在但被忽略。 |
|
|
183
|
+
| `agent.toolPolicy.allow` / `.deny` | 声明式工具可见性策略(注册后的工具 id)。需 `agent.tools.control`。 |
|
|
184
|
+
| `agent.agents` | **插件贡献的智能体**:人设、头像、系统提示词由插件提供,宿主把它们铺进用户的智能体库。**不需要权限**,见[贡献智能体与团队](#贡献智能体与团队)。 |
|
|
185
|
+
| `agent.teams` | **插件贡献的团队**:成员只能是本插件 `agent.agents` 里的智能体。同上。 |
|
|
186
|
+
|
|
187
|
+
> 在 JS 里**动态**注册 agent 工具走 `ctx.agent.registerTool`(见 [conversation-and-agent.md](./conversation-and-agent.md#注册-agent-工具)),与此处的**声明式**清单字段是两条不同路径。
|
|
188
|
+
>
|
|
189
|
+
> **插件 MCP** 与用户全局 / 项目 MCP **聚合**进同一会话,不写用户 mcp.json;启停与授权见 [mcp.md](./mcp.md)(ADR-0040)。
|
|
190
|
+
|
|
191
|
+
### Skill 展示策略
|
|
192
|
+
|
|
193
|
+
插件 Skill 默认作为内部实现隐藏。它仍会随 `agent.skillPaths` 加载并可被 Agent 调用,只是不作为独立选项出现在能力中心、智能体能力配置、命令菜单或 Skill 选择器。需要公开的 Skill 由插件显式声明:
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"agent": {
|
|
198
|
+
"skillPaths": ["agent/skills"],
|
|
199
|
+
"skillPresentation": {
|
|
200
|
+
"defaultVisibility": "hidden",
|
|
201
|
+
"skills": {
|
|
202
|
+
"vetta-ui-design": {
|
|
203
|
+
"defaultVisibility": "visible",
|
|
204
|
+
"displayName": "%plugin.name%"
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
- `defaultVisibility`:插件全部 Skill 的默认值,`visible` 或 `hidden`。
|
|
213
|
+
- `surfaces`:按入口覆盖默认值,当前支持 `abilityCatalog`、`agentConfiguration`、`commandPalette`、`skillPicker`、`pluginDetail`。
|
|
214
|
+
- `skills.<skill-name>`:按 `SKILL.md` 中的稳定 Skill 名覆盖插件默认值;可声明 `defaultVisibility`、`surfaces`、`displayName`、`displayDescription`。
|
|
215
|
+
- `displayName` / `displayDescription`:仅改变用户看到的文案,不改变 Skill 名、调用路由或已保存引用;支持插件 `%catalogKey%` 本地化占位符。
|
|
216
|
+
|
|
217
|
+
普通用户、项目、市场与 Vetta 内置 Skill 没有声明时继续默认可见。已安装的旧插件没有 `skillPresentation` 时按插件默认隐藏,避免把实现细节意外暴露为产品能力。
|
|
218
|
+
|
|
219
|
+
## 贡献智能体与团队
|
|
220
|
+
|
|
221
|
+
`agent.agents` / `agent.teams` 让插件把**自己的人设**带进产品:宿主在插件启用时把它们铺进用户的智能体库与团队列表,与用户自建的档案并列出现在智能体中心、新会话选择器和 `@` 提及里。
|
|
222
|
+
|
|
223
|
+
宿主**不再内置任何人设**——装机自带的那几位现在也由 `preset-agent` 这个预置插件提供,所以你写的插件与它们走的是同一条路径、同一套字段。
|
|
224
|
+
|
|
225
|
+
- **不需要权限**:这是清单声明面,不是运行时 API。用户对「装了什么插件」本身知情,因此没有单独的授权开关。
|
|
226
|
+
- 校验在构建期(`vetta-plugin validate` / `pack`)就做:id 格式、路径越界、头像格式都会直接失败,而不是等用户装上后发现智能体没出现。
|
|
227
|
+
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"agent": {
|
|
231
|
+
"agents": [
|
|
232
|
+
{
|
|
233
|
+
"id": "designer",
|
|
234
|
+
"name": "%agent.designer.name%",
|
|
235
|
+
"description": "%agent.designer.description%",
|
|
236
|
+
"mentionHandle": "designer",
|
|
237
|
+
"avatar": "agent/agents/designer.webp",
|
|
238
|
+
"systemPromptPath": "agent/agents/designer.md",
|
|
239
|
+
"abilities": "all"
|
|
240
|
+
}
|
|
241
|
+
],
|
|
242
|
+
"teams": [
|
|
243
|
+
{
|
|
244
|
+
"id": "design-team",
|
|
245
|
+
"name": "%team.design.name%",
|
|
246
|
+
"members": [
|
|
247
|
+
{ "agent": "designer", "responsibility": "Owns the visual result end to end." }
|
|
248
|
+
],
|
|
249
|
+
"workflowPath": "agent/workflows/design-team.md"
|
|
250
|
+
}
|
|
251
|
+
]
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### agents[] 字段
|
|
257
|
+
|
|
258
|
+
| 字段 | 必填 | 说明 |
|
|
259
|
+
| --- | --- | --- |
|
|
260
|
+
| `id` | ✅ | 插件内唯一,`^[a-z0-9][a-z0-9-]{0,63}$`。全局 id 由宿主拼成 `plugin:<pluginId>:<id>`,插件不要自己拼。 |
|
|
261
|
+
| `name` | ✅ | 支持 `%key%` 占位,按插件 `locales/` 解析;切语言即时跟随。 |
|
|
262
|
+
| `description` | ❌ | 同样支持 `%key%`,≤ 2000 字符。 |
|
|
263
|
+
| `mentionHandle` | ❌ | `@` 提及用的短名,缺省用 `id`。与用户已有 handle 冲突时宿主自动让号。 |
|
|
264
|
+
| `avatar` | ❌ | 插件包内相对路径,`.webp` / `.png` / `.jpg` / `.gif` / `.svg`,**单张 ≤ 512 KB**(要过一次 IPC)。 |
|
|
265
|
+
| `systemPromptPath` / `systemPrompt` | ✅(二选一) | 人设提示词。推荐用 `systemPromptPath` 指向 Markdown:提示词值得单独 diff。内联上限 64 000 字符。 |
|
|
266
|
+
| `abilities` | ❌ | `all`(默认)继承宿主全部已启用能力;`own` 只用本插件的能力。两种模式下**本插件的能力都强制激活,用户在能力面板里关不掉**——这个智能体存在的意义就是操作它自己的插件。 |
|
|
267
|
+
| `legacyIds` | ❌ | 本智能体**接管**的历史 blueprint id(≤ 16 个)。见下方「接管与升级」。 |
|
|
268
|
+
|
|
269
|
+
### teams[] 字段
|
|
270
|
+
|
|
271
|
+
| 字段 | 必填 | 说明 |
|
|
272
|
+
| --- | --- | --- |
|
|
273
|
+
| `id` | ✅ | 规则同 agents。 |
|
|
274
|
+
| `name` / `description` | `name` ✅ | 同样支持 `%key%` 占位。 |
|
|
275
|
+
| `members` | ✅ | 1–32 个 `{ agent, responsibility }`。**`agent` 只能写本插件 `agent.agents` 里的 id**:跨插件引用会让一个插件能否用取决于另一个插件装没装,宿主直接拒掉这一支团队并打 warn。 |
|
|
276
|
+
| `workflow` / `workflowPath` | ❌ | 队长的团队任务书,把这支团队的固定流水线写死。 |
|
|
277
|
+
| `legacyIds` | ❌ | 本团队接管的历史团队 id。 |
|
|
278
|
+
|
|
279
|
+
**第一个成员即队长**,也是用户在团队会话里唯一的对话入口。
|
|
280
|
+
|
|
281
|
+
### 生命周期
|
|
282
|
+
|
|
283
|
+
- **启用插件**:宿主把缺失的档案补齐——判据是「用户文档里现在有没有」,不是「历史上铺过没有」。因此用户误删、旧版本数据缺失都会被补回来。
|
|
284
|
+
- **升级插件**:没被用户手改过的档案跟着提供方走(铺档案时**不落 `systemPrompt`**,人设升级才能自动生效);用户改过的字段保留。
|
|
285
|
+
- **禁用插件**:档案**灰着留在原地**,既不隐藏也不从团队里摘掉,并标出「插件已禁用」。重新启用后一切原样回来——中途不动用户的档案。
|
|
286
|
+
- **贡献出错**:单个智能体/团队解析失败(提示词读不到、头像超限、成员引用非法)只跳过它自己并打 warn,不影响同插件的其它贡献。
|
|
287
|
+
|
|
288
|
+
### 接管与升级(legacyIds)
|
|
289
|
+
|
|
290
|
+
`legacyIds` 用于「人设从别处迁进插件」:宿主解析不到这些历史 id 时折算到本智能体,铺档案时也据此**认领**用户已有的同角色档案,而不是再铺一份新的。用户的 `@handle`、能力勾选与团队绑定因此不会被重置。
|
|
291
|
+
|
|
292
|
+
装机自带人设迁进 `preset-agent` 走的正是这条路径(`executor` → `developer` 等)。宿主自己不需要知道是哪个插件接管了哪个老角色。
|
|
293
|
+
|
|
294
|
+
### 配套:新会话上下文区
|
|
295
|
+
|
|
296
|
+
插件贡献的智能体被选中时,往往还想在新会话页摆出「接下来多半要用到的素材」(风格库、模板墙)。那是另一个扩展点:[ui-slots → 新会话上下文区](./ui-slots.md#新会话上下文区-registernewsessioncontext),`activateWhen.agents` 里写的就是这里的 `agents[].id`。
|
package/docs/mcp.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# 插件内聚 MCP 与三源聚合
|
|
2
|
+
|
|
3
|
+
插件可通过清单声明 **自带 MCP server**,随插件启用进入 agent 工具面,禁用/卸载即拆除。这是会话 MCP 的**第三配置源**,与用户全局 / 项目 MCP **聚合**后一起暴露给模型。
|
|
4
|
+
|
|
5
|
+
详见 ADR-0040。
|
|
6
|
+
|
|
7
|
+
## 三源聚合(必读)
|
|
8
|
+
|
|
9
|
+
每个 agent 会话的 MCP 工具来自 **三源合并**(`McpManager`):
|
|
10
|
+
|
|
11
|
+
| 源 | 配置位置 | 谁拥有 |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| **全局** | 用户 `~/.vetta/agent/mcp.json` | 用户设置页可编辑 |
|
|
14
|
+
| **项目** | 项目侧 MCP 配置(若有) | 项目 |
|
|
15
|
+
| **插件** | `plugin.json` → `agent.mcpServers` | 插件包;**不写**用户 mcp.json |
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
┌─ global mcp.json
|
|
19
|
+
McpManager ←────┼─ project mcp
|
|
20
|
+
└─ plugin contributions (mcpServerContributions)
|
|
21
|
+
│
|
|
22
|
+
▼
|
|
23
|
+
统一 tool 面:mcp_<server>_<tool>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
要点:
|
|
27
|
+
|
|
28
|
+
1. **并列聚合,不是覆盖**:三源 server 同时可运行;靠**运行时名**避免撞车。
|
|
29
|
+
2. **插件源不回写用户文件**:卸载/禁用插件只影响插件贡献,不会在用户 mcp.json 里留垃圾。
|
|
30
|
+
3. **物化路径**:主进程 `buildAgentPluginRuntimeConfig()` → `mcpServerContributions` → 会话 `setPluginServers`;启停插件走 `reconfigureAgentPlugins` 联动 reconcile。
|
|
31
|
+
4. **组合签名含 plugin fingerprint**:避免「只 reload 用户文件」时把插件 server 冲掉。
|
|
32
|
+
5. **硬隔离**(若插件声明 `contributionMode.hardIsolation` / input-action `hardIsolation`):mode 关时该插件的 MCP 贡献一并剥离,与 skills/tools 一致(ADR-0041)。
|
|
33
|
+
|
|
34
|
+
设置页的 MCP JSON 编辑器**只读写用户文件**,**不会**列出或编辑插件内聚 server。
|
|
35
|
+
|
|
36
|
+
## 清单
|
|
37
|
+
|
|
38
|
+
权限:`agent.mcp.control`。
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"permissions": ["agent.mcp.control"],
|
|
43
|
+
"agent": {
|
|
44
|
+
"mcpServers": "./.mcp.json"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
或内联:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
"agent": {
|
|
53
|
+
"mcpServers": {
|
|
54
|
+
"canvas": {
|
|
55
|
+
"command": "node",
|
|
56
|
+
"args": ["./scripts/start-mcp.mjs"],
|
|
57
|
+
"cwd": "."
|
|
58
|
+
},
|
|
59
|
+
"remote": {
|
|
60
|
+
"type": "http",
|
|
61
|
+
"url": "https://example.com/mcp"
|
|
62
|
+
},
|
|
63
|
+
"managed": {
|
|
64
|
+
"type": "service",
|
|
65
|
+
"serviceId": "gateway",
|
|
66
|
+
"path": "/mcp"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`type: "service"` 把 MCP 绑定到同一插件在 `providers.services` 中声明的受管本地服务。宿主只在服务
|
|
73
|
+
进入 `ready` 后物化当前动态回环 URL;服务停止或重启时自动撤下并重新连接。账号、登录路由和服务响应
|
|
74
|
+
语义仍由插件负责,宿主只处理所有插件都可复用的服务生命周期与 MCP 连接。
|
|
75
|
+
|
|
76
|
+
`.mcp.json` 形状与用户 MCP 相同:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"mcpServers": {
|
|
81
|
+
"canvas": {
|
|
82
|
+
"command": "node",
|
|
83
|
+
"args": ["./mcp/server.mjs"],
|
|
84
|
+
"cwd": "."
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
相对路径(`command` / `args` / `cwd`)相对**插件安装根**(`InstalledPlugin.rootPath`)解析;裸命令名(如 `node`)走 PATH(含托管运行时注入的 PATH)。
|
|
91
|
+
|
|
92
|
+
可选字段与用户 MCP 一致:`disabled`、`autoApprove`、`env`、`headers`、`displayName`、`description`、OAuth 相关等。
|
|
93
|
+
|
|
94
|
+
## 工作模式(agent_mode,已废弃)
|
|
95
|
+
|
|
96
|
+
> **Deprecated(ADR-0071)**:内联 map 里的 `agent_mode` 无任何运行时语义,容忍存在但被忽略。工具是否进入会话由可见性开关与 `scope_use` / `requires` 决定,与工作模式无关;清单顺序即注册序。想引导模型少用某个 server 的工具,把使用条件写进工具 description。
|
|
97
|
+
|
|
98
|
+
## 权限与生命周期
|
|
99
|
+
|
|
100
|
+
| 条件 | 行为 |
|
|
101
|
+
|------|------|
|
|
102
|
+
| 插件启用 + 已授 `agent.mcp.control` +(若硬隔离则 mode 开) | 贡献进入 `mcpServerContributions`,进程启动,工具进 agent |
|
|
103
|
+
| 未授权 / 禁用 / 硬隔离 mode 关 | 不贡献;已运行的 server 被 reconcile 关掉 |
|
|
104
|
+
| 系统插件 | 声明的权限自动全授(含本权限) |
|
|
105
|
+
| 启动失败 | 宿主会打错误日志(避免「无 tools」静默);排查 command/路径/依赖 |
|
|
106
|
+
|
|
107
|
+
## 运行时命名
|
|
108
|
+
|
|
109
|
+
- 本地 key(如 `canvas` / `cowart_mcp`)→ 全局名 **`plugin-<pluginId>-<normalizedLocal>`**
|
|
110
|
+
- `_` 与非法字符规范为 **kebab-case**(如 `cowart_mcp` → `cowart-mcp`);**禁止**在最终 runtimeName 中保留 `_`(兼容 `mcp_${server}_${tool}` 解析)
|
|
111
|
+
- Agent 可见工具名形如:`mcp_plugin-<id>-<local>_<toolName>`
|
|
112
|
+
|
|
113
|
+
命名后与全局/项目 server 并列,不因 local key 撞名而互相覆盖。
|
|
114
|
+
|
|
115
|
+
## 打包
|
|
116
|
+
|
|
117
|
+
`vettaPluginFederation` 打包时,若声明了 `agent.mcpServers`:
|
|
118
|
+
|
|
119
|
+
- 字符串路径:打入该 `.mcp.json`
|
|
120
|
+
- 约定目录 **`mcp/`** 一并打入
|
|
121
|
+
|
|
122
|
+
另外,构建工具也会把 **`scripts/`** 打进 zip(工作台脚本等同理),便于 `args: ["./scripts/start-mcp.mjs"]`。
|
|
123
|
+
|
|
124
|
+
`node_modules` 默认不进 zip;由预构建 bundle 或文档说明运行时安装。
|
|
125
|
+
|
|
126
|
+
## 与 registerTool
|
|
127
|
+
|
|
128
|
+
| | 插件 MCP(聚合第三源) | `ctx.agent.registerTool` |
|
|
129
|
+
|--|------------------------|---------------------------|
|
|
130
|
+
| 进程 | 独立 stdio / http | renderer handler |
|
|
131
|
+
| 适用 | 现成 MCP server、重逻辑、跨语言 | 轻逻辑、强绑 UI / 宿主状态 |
|
|
132
|
+
| 权限 | `agent.mcp.control` | `agent.tools.register` + `execute` |
|
|
133
|
+
| UI | 默认可走宿主工具渲染;可用 [registerToolCallSlot](./ui-slots.md#工具行内渲染-registertoolcallslot) 换皮 | 可返回 `cards` 进 [消息卡片](./message-cards.md) |
|
|
134
|
+
|
|
135
|
+
同一插件可同时使用两者。
|
|
136
|
+
|
|
137
|
+
## 非目标(当前)
|
|
138
|
+
|
|
139
|
+
- 设置页展示/编辑插件 MCP
|
|
140
|
+
- 安装插件时自动 `npm install` MCP 依赖
|
|
141
|
+
- per-server 用户开关 UI(清单 `disabled` 仍生效)
|
|
142
|
+
|
|
143
|
+
## MCP Apps
|
|
144
|
+
|
|
145
|
+
插件贡献的 MCP Server 与用户配置 Server 使用同一个 Desktop MCP Apps 宿主。Server 可在 Tool `_meta.ui.resourceUri`
|
|
146
|
+
中引用 `ui://` 资源,并返回 `text/html;profile=mcp-app`;Desktop 会使用双层 sandbox iframe、CSP 和 App Bridge
|
|
147
|
+
承载它。App 调用其它 Tool 时仍同时受 `_meta.ui.visibility` 与清单 `autoApprove` 约束,插件权限不会让不可信
|
|
148
|
+
App 继承可信 Renderer 能力或 MCP 凭据。
|
|
149
|
+
|
|
150
|
+
## 参考实现
|
|
151
|
+
|
|
152
|
+
- `packages/plugins/externals/cowart-vetta`:活动面板 + 插件内聚 MCP(画布工具)
|
package/docs/media.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# 媒体 Provider 协议
|
|
2
|
+
|
|
3
|
+
`ctx.media` 负责媒体能力发现与提交;通用任务生命周期在 `ctx.jobs`,临时产物生命周期在 `ctx.artifacts`。消费者不依赖具体模型、供应商、网关或本地渲染引擎。
|
|
4
|
+
|
|
5
|
+
## 消费媒体能力
|
|
6
|
+
|
|
7
|
+
需要 `media.generate` 权限。按 `operation` 和具体能力选择 Provider:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const provider = (await ctx.media.listProviders()).find((item) =>
|
|
11
|
+
item.capabilities.some(
|
|
12
|
+
(capability) =>
|
|
13
|
+
capability.operation === "generate" &&
|
|
14
|
+
capability.kind === "image" &&
|
|
15
|
+
capability.modes.includes("text-to-image"),
|
|
16
|
+
),
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
if (!provider) throw new Error("No image provider available");
|
|
20
|
+
|
|
21
|
+
const submitted = await ctx.media.submit({
|
|
22
|
+
operation: "generate",
|
|
23
|
+
providerId: provider.id,
|
|
24
|
+
kind: "image",
|
|
25
|
+
mode: "text-to-image",
|
|
26
|
+
prompt: "a red fox in snow",
|
|
27
|
+
dimensions: { width: 1024, height: 1024 },
|
|
28
|
+
inputs: [],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const job = await ctx.jobs.wait(submitted);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`generate` 用于提示词驱动的图片或视频生成。`compose` 接收工程文档与素材,`transcode` 接收一个媒体输入和目标输出:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
const submitted = await ctx.media.submit({
|
|
38
|
+
operation: "compose",
|
|
39
|
+
providerId: provider.id,
|
|
40
|
+
inputs: [{
|
|
41
|
+
kind: "document",
|
|
42
|
+
mimeType: "application/vnd.example.timeline+json",
|
|
43
|
+
source: { type: "plugin-blob", blobId: projectBlob.id },
|
|
44
|
+
}],
|
|
45
|
+
output: { kind: "video", mimeType: "video/mp4", fps: 30 },
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
工程 MIME 由 Provider 声明。Remotion、其他时间线引擎或模板渲染器都使用 `compose`,具体 composition、props 和 bundle 信息留在各自工程文档中。
|
|
50
|
+
|
|
51
|
+
任务状态为 `queued | running | succeeded | failed | cancelled`。`ctx.jobs.get()` 查询一次,`wait()` 轮询到终态,`cancel()` 请求取消。任务 ID 由宿主生成,不等于 Provider 的内部队列 ID。
|
|
52
|
+
|
|
53
|
+
输入素材通过句柄传递,不再嵌入 base64。插件私有素材使用 `plugin-blob`,工作区文件使用 `workspace-file`:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
inputs: [
|
|
57
|
+
{ kind: "image", source: { type: "plugin-blob", blobId: image.id } },
|
|
58
|
+
{ kind: "audio", source: { type: "workspace-file", path: audioPath } },
|
|
59
|
+
]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
成功结果的 `artifacts[]` 也是宿主管理的临时句柄,只包含 ID、MIME、大小和媒体元数据。插件必须明确选择保存位置,并在使用完成后释放临时产物:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
const artifact = job.artifacts[0];
|
|
66
|
+
if (!artifact) throw new Error("Media provider returned no artifact");
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const saved = await ctx.artifacts.persist(artifact, { type: "plugin-blob" });
|
|
70
|
+
// saved.blobId / saved.url can be stored in plugin state.
|
|
71
|
+
} finally {
|
|
72
|
+
await ctx.artifacts.release(artifact);
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`plugin-blob` 读写分别要求 `storage.read` / `storage.write`,`workspace-file` 读写分别要求 `fs.read` / `fs.write`。字节始终由宿主在主进程内按需读取或复制,不经过插件渲染进程;`release()` 只释放临时产物,不删除已经保存的文件或 Blob。
|
|
77
|
+
|
|
78
|
+
消费插件可用 `onProvidersChanged()` 监听 Provider 增删,并重新执行能力发现。插件并行激活时不能依赖固定加载顺序。
|
|
79
|
+
|
|
80
|
+
## 注册 Provider
|
|
81
|
+
|
|
82
|
+
Provider 插件需要 `media.provider.register`。只有远程传输才需要 `network.fetch`;本地渲染输出可使用插件 Blob 或工作区文件。
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
ctx.media.registerProvider({
|
|
86
|
+
id: "local-video",
|
|
87
|
+
displayName: "Local video",
|
|
88
|
+
capabilities: [{
|
|
89
|
+
operation: "generate",
|
|
90
|
+
kind: "video",
|
|
91
|
+
modes: ["image-to-video"],
|
|
92
|
+
aspectRatios: ["16:9", "9:16"],
|
|
93
|
+
resolutions: ["efficient", "balanced", "quality"],
|
|
94
|
+
defaultResolution: "balanced",
|
|
95
|
+
durationsSeconds: [5, 10],
|
|
96
|
+
}],
|
|
97
|
+
async submit(request, context) {
|
|
98
|
+
if (request.operation !== "generate") {
|
|
99
|
+
return {
|
|
100
|
+
id: crypto.randomUUID(),
|
|
101
|
+
status: "failed",
|
|
102
|
+
error: { code: "operation-unsupported", message: "Unsupported operation", retryable: false },
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const image = request.inputs.find((item) => item.kind === "image");
|
|
106
|
+
if (!image) throw new Error("An image is required");
|
|
107
|
+
await context.uploadInput(image.id, {
|
|
108
|
+
url: "http://127.0.0.1:8188/upload/image",
|
|
109
|
+
fieldName: "image",
|
|
110
|
+
});
|
|
111
|
+
// Adapt request.prompt / aspectRatio / durationSeconds inside the provider.
|
|
112
|
+
return { id: "provider-job-id", status: "queued" };
|
|
113
|
+
},
|
|
114
|
+
async getJob(jobId) {
|
|
115
|
+
return {
|
|
116
|
+
id: jobId,
|
|
117
|
+
status: "succeeded",
|
|
118
|
+
artifacts: [{
|
|
119
|
+
kind: "video",
|
|
120
|
+
source: { type: "remote-url", url: "http://127.0.0.1:8188/view?..." },
|
|
121
|
+
}],
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Provider 收到的 `inputs` 只有不透明 ID、媒体类型和 MIME,不包含插件 Blob 命名空间或工作区路径。只有当前任务上下文能用 `uploadInput()` 把对应文件流式上传到 HTTP(S) 服务。Provider 输出 source 支持 `remote-url`、`plugin-blob` 和 `workspace-file`,宿主会按 Provider 权限读取并导入为消费方临时产物。
|
|
128
|
+
|
|
129
|
+
`resolutions` 是 Provider 自定义的稳定选项 ID,不保证具有 `720p`、`2K` 等固定视频制式语义;例如本地模型可以用它表达像素预算档位,再在 Provider 内转换为最终宽高。若声明 `defaultResolution`,该值必须同时出现在 `resolutions` 中。消费者在没有已保存值或已有值不受当前模型支持时优先采用这个显式默认值。
|
|
130
|
+
|
|
131
|
+
## Provider SPI
|
|
132
|
+
|
|
133
|
+
通用媒体契约和 capability token 定义在 `@vetta-org/capability-sdk`,当前协议版本为 4。注册表、通用任务、临时产物存储、输入解析与网络传输位于 desktop 主进程。插件 Provider 通过受控 IPC 回调桥接到同一个 Registry,注销时会中止仍在执行的调用。
|
|
134
|
+
|
|
135
|
+
需要宿主凭据或其它主进程特权的实现仍应注册为宿主 Provider;普通远端服务、本地模型或 sidecar 可用 Provider 插件适配。两者对消费者暴露同一契约。
|
|
136
|
+
|
|
137
|
+
## Desktop 内置 Vetta Provider
|
|
138
|
+
|
|
139
|
+
desktop 默认注册 `desktop:vetta`,当前支持 `text-to-image` 与 `image-to-image`。它的实现位于主进程:renderer 只提交媒体协议请求,主进程固定选择 `images/generate` 或 `images/edit`,并负责注入 JWT 与刷新凭据。插件拿不到用户 token,也不能通过该接口传入任意网关路径。
|
|
140
|
+
|
|
141
|
+
该内置实现不是底层协议的前提。没有它的宿主构建仍可暴露一个空 Registry;消费者必须处理 `listProviders()` 为空和 `provider-unavailable`。
|