@vetta-org/plugin-sdk 0.3.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/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 +6 -3
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"source": "docs/plugin",
|
|
3
|
+
"files": [
|
|
4
|
+
"README.md",
|
|
5
|
+
"ability-details.md",
|
|
6
|
+
"ai.md",
|
|
7
|
+
"app-actions.md",
|
|
8
|
+
"browser.md",
|
|
9
|
+
"conversation-and-agent.md",
|
|
10
|
+
"file-explorer.md",
|
|
11
|
+
"getting-started.md",
|
|
12
|
+
"guiding-the-agent.md",
|
|
13
|
+
"manifest.md",
|
|
14
|
+
"mcp.md",
|
|
15
|
+
"media.md",
|
|
16
|
+
"message-cards.md",
|
|
17
|
+
"permissions.md",
|
|
18
|
+
"styling-and-pitfalls.md",
|
|
19
|
+
"system-plugins.md",
|
|
20
|
+
"ui-slots.md"
|
|
21
|
+
]
|
|
22
|
+
}
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Vetta 桌面插件开发手册
|
|
2
|
+
|
|
3
|
+
面向第三方开发者的 Vetta 桌面端插件**对接与开发**完整手册。读完本目录你应当能从零写出、打包、安装、调试一个插件,并用上所有可用扩展点。
|
|
4
|
+
|
|
5
|
+
> 插件运行在 Vetta 桌面 App(Electron)的 renderer 进程内,与宿主共享 JavaScript realm——**没有安全沙箱**。只安装并启用你信任的插件。`@vetta-org/plugin-sdk` 权限用于声明与门控宿主 API,不承诺隔离恶意代码(见 [信任模型](#信任模型))。
|
|
6
|
+
|
|
7
|
+
## 文档导航
|
|
8
|
+
|
|
9
|
+
| 文档 | 内容 |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| [getting-started.md](./getting-started.md) | 环境、脚手架、Vite/Module Federation、构建、安装(含本地路径)、调试闭环 |
|
|
12
|
+
| [ability-details.md](./ability-details.md) | **能力详情页**:`ability.json`、结构化区块、Markdown 文件引用、多语言、资源打包与限制 |
|
|
13
|
+
| [guiding-the-agent.md](./guiding-the-agent.md) | **引导模型用好你的扩展**:三层心智模型、name/description 正反触发段、返回值引导、skill 渐进披露、执行边界、反模式与自检清单 |
|
|
14
|
+
| [manifest.md](./manifest.md) | `plugin.json` 全字段、`commands`、`contributionMode`、`agent_mode`(已废弃)、`defaultLocale` / i18n、settings、guidingWords、agent 贡献、**贡献智能体与团队** |
|
|
15
|
+
| [mcp.md](./mcp.md) | **MCP 三源聚合**、插件内聚 MCP(`agent.mcpServers`)、命名、生命周期、打包 |
|
|
16
|
+
| [permissions.md](./permissions.md) | 权限完整清单、门控点、声明/授权流程 |
|
|
17
|
+
| [ai.md](./ai.md) | 调用用户已配置的文本模型,模型列表、完成请求与凭据边界 |
|
|
18
|
+
| [browser.md](./browser.md) | 宿主管理的浏览器 session、持久 profile、多账号隔离、域名范围与类型化动作 |
|
|
19
|
+
| [file-explorer.md](./file-explorer.md) | 文件列表右键菜单、工具栏、装饰、定位、刷新与事件 |
|
|
20
|
+
| [ui-slots.md](./ui-slots.md) | **notify 全局 Toast** / 文件预览(**含大文件 getUrl 规范**)/ 全局浮层 / **工作区视图(整页)** / 活动 Tab / 输入栏动作 / **新会话上下文区** / **Turn 卡** / **Tool-call 槽** |
|
|
21
|
+
| [message-cards.md](./message-cards.md) | 消息卡片:`details.cards`、`registerCardRenderer`、`pendingFor`、跨轮去重 |
|
|
22
|
+
| [conversation-and-agent.md](./conversation-and-agent.md) | 对话、registerTool、**registerHook**、command.run、fs、network、storage、settings、i18n、工作模式 getAgentMode |
|
|
23
|
+
| [app-actions.md](./app-actions.md) | 动态 App Action:JSON Schema、审批、生命周期、取消与独立发布 |
|
|
24
|
+
| [system-plugins.md](./system-plugins.md) | 系统插件(presets)、租户打包 |
|
|
25
|
+
| [styling-and-pitfalls.md](./styling-and-pitfalls.md) | 样式、MF 顶层 JSX 陷阱、缓存与 version bump |
|
|
26
|
+
|
|
27
|
+
## 插件能做什么
|
|
28
|
+
|
|
29
|
+
一个插件在 `activate(ctx)` 里通过 `ctx` 注册贡献、调用能力;也可在 `plugin.json` **声明式**贡献(skills / MCP / guidingWords / commands…)。
|
|
30
|
+
|
|
31
|
+
| 能力 | 入口 | 权限 | 文档 |
|
|
32
|
+
| --- | --- | --- | --- |
|
|
33
|
+
| **全局 Toast / 错误通知** | `ctx.ui.notify` | 无 | [ui-slots](./ui-slots.md#全局通知-notify) |
|
|
34
|
+
| 全局浮层 UI | `ctx.ui.registerGlobalSlot` | `ui.slot.global` | [ui-slots](./ui-slots.md#全局浮层-registerglobalslot) |
|
|
35
|
+
| **工作区视图**(整页 + 侧边栏入口) | `ctx.ui.registerWorkspaceView` | `ui.slot.workspace-view` | [ui-slots](./ui-slots.md#工作区视图-registerworkspaceview) |
|
|
36
|
+
| 文件预览 | `ctx.ui.registerFilePreview` | `ui.slot.file-preview` | [ui-slots](./ui-slots.md#文件预览-registerfilepreview) |
|
|
37
|
+
| 文件列表扩展 | `ctx.fileExplorer.*` | `ui.file-explorer.*` / `workspace.read` | [file-explorer](./file-explorer.md) |
|
|
38
|
+
| 活动面板 Tab | `ctx.ui.registerActivityTab` / `openActivityTab` | `ui.slot.activity-tab` | [ui-slots](./ui-slots.md#活动面板-tab-registeractivitytab) |
|
|
39
|
+
| 输入栏动作(toggle) | `ctx.ui.registerInputAction` | `ui.slot.input-action` | [ui-slots](./ui-slots.md#输入栏动作-registerinputaction) |
|
|
40
|
+
| **新会话上下文区**(输入框下方的素材区) | `ctx.ui.registerNewSessionContext` | `ui.slot.new-session-context` | [ui-slots](./ui-slots.md#新会话上下文区-registernewsessioncontext) |
|
|
41
|
+
| 消息卡片渲染器 | `ctx.ui.registerCardRenderer` | `ui.slot.message` | [message-cards](./message-cards.md) |
|
|
42
|
+
| 工具行内渲染替换 | `ctx.ui.registerToolCallSlot` | `ui.slot.tool-call` | [ui-slots](./ui-slots.md#工具行内渲染-registertoolcallslot) |
|
|
43
|
+
| 本轮 Turn 卡 | `ctx.ui.registerTurnCard` | `ui.slot.turn-card` | [ui-slots](./ui-slots.md#本轮-turn-卡-registerturncard) |
|
|
44
|
+
| **键盘快捷键(宿主 scope 栈)** | `ctx.ui.registerShortcutScope` / `usePluginShortcutScope` | `ui.shortcuts.register` | [ui-slots](./ui-slots.md#键盘快捷键-registershortcutscope) |
|
|
45
|
+
| 读对话 / 事件 | hooks + `ctx.conversation.on` | `agent.session.read` | [conversation-and-agent](./conversation-and-agent.md#对话读状态) |
|
|
46
|
+
| 驾驶对话 | `ctx.conversation.sendPrompt/insertText/abort` | `agent.session.write` | [conversation-and-agent](./conversation-and-agent.md#对话驾驶) |
|
|
47
|
+
| 注册 Agent 工具 | `ctx.agent.registerTool` | `agent.tools.register` + `execute` | [conversation-and-agent](./conversation-and-agent.md#注册-agent-工具) |
|
|
48
|
+
| 注册 Coding Agent Hook | `ctx.agent.registerHook` | `agent.hooks.register` + `agent.hookHandler.execute` | [conversation-and-agent](./conversation-and-agent.md#注册-coding-agent-hook) |
|
|
49
|
+
| 注册 App Action | `ctx.appActions.register` | `app.actions.register` + `app.actionHandler.execute` | [app-actions](./app-actions.md) |
|
|
50
|
+
| 跑宿主命令 | `ctx.command.run` + 清单 `commands` | `agent.command.run` | [conversation-and-agent](./conversation-and-agent.md#命令执行-command) |
|
|
51
|
+
| 长驻进程(dev server 等) | `ctx.command.spawn` + 清单 `commands` | `agent.command.spawn` | [conversation-and-agent](./conversation-and-agent.md#长驻进程-commandspawn) |
|
|
52
|
+
| 离屏窗口截图(真实渲染管线) | `ctx.capture.offscreen` | `capture.offscreen` | [conversation-and-agent](./conversation-and-agent.md#离屏截图-captureoffscreen) |
|
|
53
|
+
| 读写文件 | `ctx.fs.*` | `fs.read` / `fs.write` | [conversation-and-agent](./conversation-and-agent.md#文件-api) |
|
|
54
|
+
| 宿主代理网络请求 | `ctx.network.request` | `network.fetch` | [conversation-and-agent](./conversation-and-agent.md#网络-api) |
|
|
55
|
+
| 宿主管理的浏览器自动化 | `ctx.browser.*` | `browser.*` | [browser](./browser.md) |
|
|
56
|
+
| 插件私有持久化 | `ctx.storage.*` | `storage.read` / `storage.write` | [conversation-and-agent](./conversation-and-agent.md#插件私有存储-api) |
|
|
57
|
+
| 调用用户 AI 模型(单轮/多轮+插件内部工具) | `ctx.ai.listModels/complete/chat` | `ai.models.list` / `ai.complete` | [ai](./ai.md) |
|
|
58
|
+
| 读写自身密钥 | `ctx.secrets.*` | `secrets.read` / `secrets.write` | [conversation-and-agent](./conversation-and-agent.md#密钥-api) |
|
|
59
|
+
| 插件 i18n | `ctx.i18n` / `useTranslation` + `locales/` | 无(catalog 随包) | [conversation-and-agent](./conversation-and-agent.md#插件-i18n) / [manifest](./manifest.md#i18n) |
|
|
60
|
+
| 新会话引导词 | `plugin.json` `guidingWords` | 无 | [manifest](./manifest.md#guidingwords引导词) |
|
|
61
|
+
| 打包 skill | `agent.skillPaths` | `agent.skills.control` | [manifest](./manifest.md#agent-agent-侧贡献) |
|
|
62
|
+
| **贡献智能体 / 团队** | `plugin.json` `agent.agents` / `agent.teams` | 无 | [manifest](./manifest.md#贡献智能体与团队) |
|
|
63
|
+
| **插件内聚 MCP(三源聚合之一)** | `agent.mcpServers` | `agent.mcp.control` | [mcp](./mcp.md) |
|
|
64
|
+
| 动态 system prompt | `registerSystemPromptProvider` | `agent.systemPrompt.*` | [conversation-and-agent](./conversation-and-agent.md#注册动态系统提示词-provider) |
|
|
65
|
+
| 自动续跑 | `registerContinuationProvider` | `agent.continuation.register` | [conversation-and-agent](./conversation-and-agent.md#注册-agent-自动续跑策略) |
|
|
66
|
+
| 贡献硬隔离模式 | `hardIsolation` / `contributionMode` | — | [ui-slots](./ui-slots.md#插件贡献硬隔离-hardisolation) / [manifest](./manifest.md#contributionmode) |
|
|
67
|
+
| **工作模式鉴别**(展示层定制;`agent_mode` 声明已废弃) | `ctx.getAgentMode` / `onAgentModeChanged` | 无 | [manifest](./manifest.md#agent_mode已废弃) / [conversation-and-agent](./conversation-and-agent.md#工作模式agent_mode) |
|
|
68
|
+
|
|
69
|
+
## 信任模型
|
|
70
|
+
|
|
71
|
+
- 插件按用户明确选择的**可信代码**处理,可以来自官方、市场或本地安装;宿主不把未知第三方代码自动提升为可信。
|
|
72
|
+
- 插件跑在 renderer 进程内,经 Module Federation 与宿主**共享同一份 React / React DOM / `@vetta-org/plugin-sdk` 单例**;可选再共享 **`@vetta/ui`** 设计系统 primitives(见 [styling-and-pitfalls](./styling-and-pitfalls.md#可选vettaui-宿主-primitives))。
|
|
73
|
+
- SDK 提供宿主能力出口与权限门控,可同步传递 React 组件并读取宿主公开状态,**刻意不做** iframe/worker 沙箱与异步消息桥。
|
|
74
|
+
- 每项公开能力由 `plugin.json` 声明权限、宿主单独授权、运行时校验;缺权限会抛 `Plugin permission denied: <permission>` 或 warn+noop(见 [permissions.md](./permissions.md))。这套机制服务于知情同意、治理和误用防护,不阻止同 realm 插件绕过 SDK 使用浏览器原生能力。
|
|
75
|
+
|
|
76
|
+
## 5 分钟速览
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { definePlugin } from "@vetta-org/plugin-sdk";
|
|
80
|
+
|
|
81
|
+
export default definePlugin({
|
|
82
|
+
activate(ctx) {
|
|
83
|
+
ctx.ui.registerGlobalSlot({ id: "root", component: MyPanel });
|
|
84
|
+
},
|
|
85
|
+
deactivate() {
|
|
86
|
+
// 可选:清理副作用。注册返回的 Disposable 已由宿主在卸载时统一处置
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"id": "my-plugin",
|
|
94
|
+
"name": "我的插件",
|
|
95
|
+
"version": "0.1.0",
|
|
96
|
+
"pluginApiVersion": "^2.0.0",
|
|
97
|
+
"entry": "dist/mf-manifest.json",
|
|
98
|
+
"moduleFederation": { "remoteName": "my_plugin", "expose": "./plugin" },
|
|
99
|
+
"styles": ["dist/style.css"],
|
|
100
|
+
"permissions": ["ui.slot.global"]
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
接下来从 [getting-started.md](./getting-started.md) 开始。
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
# 能力详情页(ability.json)
|
|
2
|
+
|
|
3
|
+
`ability.json` 是插件在 Desktop「能力」页里的**可选展示描述**。它不参与插件加载,也不授予权限:
|
|
4
|
+
|
|
5
|
+
- `plugin.json` 决定插件身份、入口、权限、命令和 Agent 贡献;
|
|
6
|
+
- `ability.json` 只决定能力详情中的介绍内容;
|
|
7
|
+
- 安装、启停、权限、命令、版本和贡献项仍由宿主固定渲染,详情文件不能覆盖这些交互。
|
|
8
|
+
|
|
9
|
+
没有 `ability.json` 的插件仍能正常安装和运行。能力页会显示 `plugin.json` 提供的名称、简介、图标、作者,
|
|
10
|
+
以及宿主自动生成的权限和贡献信息,但不会出现 showcase、功能网格或长篇说明。
|
|
11
|
+
|
|
12
|
+
## 推荐目录
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
my-plugin/
|
|
16
|
+
├── plugin.json
|
|
17
|
+
├── ability.json
|
|
18
|
+
├── presentation/
|
|
19
|
+
│ ├── README.md
|
|
20
|
+
│ ├── README.en.md
|
|
21
|
+
│ └── preview.webp
|
|
22
|
+
├── src/
|
|
23
|
+
└── dist/
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`@vetta-org/plugin-vite` 发现根目录的 `ability.json` 后,会把它和整个 `presentation/` 目录打进插件 zip。
|
|
27
|
+
因此,插件的 Markdown 与图片展示资源应放在 `presentation/` 下。
|
|
28
|
+
|
|
29
|
+
## 最小详情
|
|
30
|
+
|
|
31
|
+
身份字段必须和 `plugin.json` 保持一致:`slug = plugin.json.id`,`version = plugin.json.version`。
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"schemaVersion": 1,
|
|
36
|
+
"type": "plugin",
|
|
37
|
+
"slug": "my-plugin",
|
|
38
|
+
"version": "0.1.0",
|
|
39
|
+
"detail": {
|
|
40
|
+
"blocks": [
|
|
41
|
+
{
|
|
42
|
+
"type": "markdown",
|
|
43
|
+
"path": "presentation/README.md"
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Markdown 区块可以二选一:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{ "type": "markdown", "content": "## 使用方法\n\n直接写短内容。" }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{ "type": "markdown", "path": "presentation/README.md" }
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`content` 与 `path` 必须且只能提供一个。长正文优先使用 `path`,避免在 JSON 字符串里手工转义换行、引号和代码块。
|
|
61
|
+
路径相对插件根目录解析,不能是绝对路径,也不能用 `..` 越出插件目录。
|
|
62
|
+
|
|
63
|
+
## 推荐的丰富详情
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"schemaVersion": 1,
|
|
68
|
+
"type": "plugin",
|
|
69
|
+
"slug": "my-plugin",
|
|
70
|
+
"version": "0.1.0",
|
|
71
|
+
"detail": {
|
|
72
|
+
"blocks": [
|
|
73
|
+
{
|
|
74
|
+
"type": "showcase",
|
|
75
|
+
"showcase": {
|
|
76
|
+
"template": "workbench",
|
|
77
|
+
"canvas": "code",
|
|
78
|
+
"user_prompt": "检查这个页面在手机上的效果。",
|
|
79
|
+
"assistant_reply": "我会打开预览并检查响应式布局。"
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"type": "feature-grid",
|
|
84
|
+
"title": "主要能力",
|
|
85
|
+
"items": [
|
|
86
|
+
{
|
|
87
|
+
"title": "实时预览",
|
|
88
|
+
"description": "在工作区中直接查看页面。",
|
|
89
|
+
"icon": "solar:monitor-smartphone-linear"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"title": "继续交给 Agent",
|
|
93
|
+
"description": "把检查结果带回当前任务继续修改。",
|
|
94
|
+
"icon": "solar:magic-stick-3-linear"
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"type": "markdown",
|
|
100
|
+
"path": "presentation/README.md"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"type": "links",
|
|
104
|
+
"title": "相关资源",
|
|
105
|
+
"items": [
|
|
106
|
+
{ "label": "使用文档", "href": "https://example.com/docs" }
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
],
|
|
110
|
+
"meta": [
|
|
111
|
+
{ "key": "homepage", "value": "https://example.com" },
|
|
112
|
+
{ "key": "repository", "value": "https://github.com/example/my-plugin" }
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## 区块参考
|
|
119
|
+
|
|
120
|
+
区块按数组顺序渲染。宿主只接受下列白名单类型,不执行包内 HTML、JavaScript、CSS、iframe 或自定义操作。
|
|
121
|
+
如果需要兼容尚未支持新区块的旧版客户端,请在整页 `format: "blocks"` 声明中提供 `fallback` Markdown;旧版校验失败时会回退到该文件。
|
|
122
|
+
|
|
123
|
+
### hero
|
|
124
|
+
|
|
125
|
+
封面承诺,适合详情页开头。它只描述一句主张、徽章和可选配图;宿主把它画成带侧线的引言,而不是能力清单或右侧 Logo 栏。
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"type": "hero",
|
|
130
|
+
"eyebrow": "REAL BROWSER AUTOMATION",
|
|
131
|
+
"title": "让 Agent 在你看得见的浏览器里工作",
|
|
132
|
+
"description": "复用登录态,完成多步骤网页任务;提交前始终确认。",
|
|
133
|
+
"image": "presentation/preview.webp",
|
|
134
|
+
"image_alt": "在可见窗口中完成登录",
|
|
135
|
+
"layout": "stacked",
|
|
136
|
+
"badges": ["可见窗口", "会话隔离"]
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`layout` 可选 `stacked`(默认:主张在上、配图在下)或 `split`(有配图时文案与图片左右分栏)。没有 `image` 时不会留下空栏。配图必须是场景静帧;`icon.png` / `logo.svg` 以及与能力图标同一张图会被宿主忽略,避免再画一遍页头 Logo。
|
|
141
|
+
|
|
142
|
+
### stats
|
|
143
|
+
|
|
144
|
+
用少量数字或短词概括适用范围、规模和关键约束。宿主把 `value` 放进色块,右边跟标签和说明,排成度量行;不拉成通栏 KPI,也不需要填写列数。
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"type": "stats",
|
|
149
|
+
"title": "适合哪些任务",
|
|
150
|
+
"items": [
|
|
151
|
+
{ "value": "真实", "label": "页面环境", "description": "不是静态 HTML" },
|
|
152
|
+
{ "value": "多步", "label": "任务流程" },
|
|
153
|
+
{ "value": "可控", "label": "关键动作", "description": "提交前人工确认" }
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
最多 6 项。`value` 和 `label` 必填,`description` 可省略。
|
|
159
|
+
|
|
160
|
+
### gallery
|
|
161
|
+
|
|
162
|
+
展示多张界面截图或流程图。图片可以是插件包内的 `presentation/**` 文件,也可以是 HTTPS 地址;不接受 HTML、iframe 或脚本。
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"type": "gallery",
|
|
167
|
+
"title": "工作流预览",
|
|
168
|
+
"items": [
|
|
169
|
+
{
|
|
170
|
+
"src": "presentation/step-1.webp",
|
|
171
|
+
"alt": "打开网站并等待登录",
|
|
172
|
+
"caption": "1. 在可见窗口中完成登录"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"src": "presentation/step-2.webp",
|
|
176
|
+
"alt": "读取页面快照",
|
|
177
|
+
"caption": "2. Agent 根据页面结构定位内容"
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
图片按自适应网格排列,最多 8 张;`alt` 和 `caption` 可省略,但建议为截图提供有意义的 `alt`。
|
|
184
|
+
|
|
185
|
+
### comparison
|
|
186
|
+
|
|
187
|
+
解释两种方式、适用边界或「之前 / 之后」。左右是两份独立论点,宿主不会按行一一对应。`tone` 决定这一列是不是选中面板:中性列是带减号的未选列表,强调列是带勾选和主色描边的选中面板。默认左列 `neutral`、右列 `accent`。
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"type": "comparison",
|
|
192
|
+
"title": "从查资料到完成任务",
|
|
193
|
+
"left": {
|
|
194
|
+
"title": "只做网页搜索",
|
|
195
|
+
"items": ["返回搜索结果", "遇到登录态就中断"]
|
|
196
|
+
},
|
|
197
|
+
"right": {
|
|
198
|
+
"title": "使用 Browser",
|
|
199
|
+
"tone": "accent",
|
|
200
|
+
"items": ["打开真实网站", "提交前交还人工确认"]
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`tone` 可选 `neutral` 或 `accent`;默认左列为 `neutral`、右列为 `accent`。每列最多 8 条。
|
|
206
|
+
|
|
207
|
+
### feature-grid
|
|
208
|
+
|
|
209
|
+
能力清单:并列主张,不是先后步骤。宿主按短条目自动并排,不需要填写列数。`items` 至少一项,图标可省略;图标支持 `solar:` 或包内/HTTPS 图片。
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"type": "feature-grid",
|
|
214
|
+
"title": "主要能力",
|
|
215
|
+
"items": [
|
|
216
|
+
{ "title": "读取页面", "description": "提取页面结构与文字。", "icon": "solar:document-text-linear" }
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### steps
|
|
222
|
+
|
|
223
|
+
有先后顺序的流程。宿主画成带序号和连线的步骤轨,文案一次全部可见。
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"type": "steps",
|
|
228
|
+
"title": "开始使用",
|
|
229
|
+
"items": [
|
|
230
|
+
{ "title": "安装插件" },
|
|
231
|
+
{ "title": "授予权限", "description": "只开启任务实际需要的权限。" }
|
|
232
|
+
]
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### showcase
|
|
237
|
+
|
|
238
|
+
宿主生成的场景头图,不是真实截图,也不能由插件注入 CSS。插件只选择 `template`、`canvas` 和文案;
|
|
239
|
+
窗体外形、舞台和对话样式全部由 Desktop 绘制。
|
|
240
|
+
|
|
241
|
+
`template` 决定构图,不只是「一问一答」:
|
|
242
|
+
|
|
243
|
+
| template | 构图 |
|
|
244
|
+
| --- | --- |
|
|
245
|
+
| `canvas-hero` | 大号产品窗口 + 一句说明;提示词收成角标 |
|
|
246
|
+
| `prompt-result` | 左侧提示词卡片,右侧变成产物窗口 |
|
|
247
|
+
| `spotlight` | 居中命令面板:检索条 + 高亮结果 |
|
|
248
|
+
| `workbench` | 迷你工作台:活动栏 + 窗口 + 助手批注 |
|
|
249
|
+
| `chat-over-canvas` | 产品窗口为主角,对话作为附注 |
|
|
250
|
+
| `chat-thread` | 完整会话窗口(顶栏、消息、输入条) |
|
|
251
|
+
|
|
252
|
+
需要产品窗口的模板再选 `canvas`。每种 canvas 是可辨认的窗体外形,不是同一外壳里换几根色条:
|
|
253
|
+
|
|
254
|
+
| canvas | 窗体 |
|
|
255
|
+
| --- | --- |
|
|
256
|
+
| `design` | 点状画板 + 带控制点的 Frame |
|
|
257
|
+
| `code` | 编辑器:文件页签、行号、状态栏 |
|
|
258
|
+
| `docs` | 纸页文档 + 清单 |
|
|
259
|
+
| `browser` | 浏览器:标签、地址栏、页面列表 |
|
|
260
|
+
| `terminal` | 深色终端与提示符 |
|
|
261
|
+
| `board` | 三列看板 |
|
|
262
|
+
| `generic` | 指标卡 + 趋势图的仪表盘 |
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"type": "showcase",
|
|
267
|
+
"showcase": {
|
|
268
|
+
"template": "canvas-hero",
|
|
269
|
+
"canvas": "browser",
|
|
270
|
+
"brand_name": "Orders",
|
|
271
|
+
"user_prompt": "打开后台订单页。",
|
|
272
|
+
"assistant_reply": "我会在真实浏览器里读取页面,提交前先停下来确认。"
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
`user_prompt` 与 `assistant_reply` 在非对话模板里也会用到:分别作为提示词/检索条和结果说明。
|
|
278
|
+
可选 `brand_name`、`brand_icon_url` 会出现在窗体标题或页签上。
|
|
279
|
+
|
|
280
|
+
### image
|
|
281
|
+
|
|
282
|
+
展示真实图片。包内资源建议放进 `presentation/`;也可使用 HTTPS 图片。
|
|
283
|
+
|
|
284
|
+
```json
|
|
285
|
+
{
|
|
286
|
+
"type": "image",
|
|
287
|
+
"src": "presentation/preview.webp",
|
|
288
|
+
"alt": "插件界面预览",
|
|
289
|
+
"caption": "工作区主界面"
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### callout
|
|
294
|
+
|
|
295
|
+
提示块;`tone` 支持 `info`、`success`、`warning`。
|
|
296
|
+
|
|
297
|
+
```json
|
|
298
|
+
{
|
|
299
|
+
"type": "callout",
|
|
300
|
+
"tone": "info",
|
|
301
|
+
"title": "首次使用",
|
|
302
|
+
"content": "启用前需要完成本地运行时安装。"
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### markdown
|
|
307
|
+
|
|
308
|
+
Markdown 正文,支持内联 `content` 或包内文件 `path`。代码块由宿主统一高亮。
|
|
309
|
+
|
|
310
|
+
```json
|
|
311
|
+
{ "type": "markdown", "path": "presentation/README.md" }
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### links
|
|
315
|
+
|
|
316
|
+
HTTP(S) 外链按钮。
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{
|
|
320
|
+
"type": "links",
|
|
321
|
+
"title": "继续阅读",
|
|
322
|
+
"items": [{ "label": "文档", "href": "https://example.com/docs" }]
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## 整页引用文件
|
|
327
|
+
|
|
328
|
+
如果详情只有 Markdown,不需要 `blocks`:
|
|
329
|
+
|
|
330
|
+
```json
|
|
331
|
+
{
|
|
332
|
+
"schemaVersion": 1,
|
|
333
|
+
"type": "plugin",
|
|
334
|
+
"slug": "my-plugin",
|
|
335
|
+
"version": "0.1.0",
|
|
336
|
+
"detail": {
|
|
337
|
+
"format": "markdown",
|
|
338
|
+
"path": "presentation/README.md"
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
也可以把全部结构化区块放进独立 JSON:
|
|
344
|
+
|
|
345
|
+
```json
|
|
346
|
+
{
|
|
347
|
+
"detail": {
|
|
348
|
+
"format": "blocks",
|
|
349
|
+
"path": "presentation/detail.json",
|
|
350
|
+
"fallback": "presentation/README.md"
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
此时 `presentation/detail.json` 的格式为:
|
|
356
|
+
|
|
357
|
+
```json
|
|
358
|
+
{
|
|
359
|
+
"schemaVersion": 1,
|
|
360
|
+
"blocks": [
|
|
361
|
+
{ "type": "markdown", "path": "presentation/README.md" }
|
|
362
|
+
]
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
`fallback` 只在结构化详情文件无法读取或校验失败时生效。
|
|
367
|
+
|
|
368
|
+
## 多语言
|
|
369
|
+
|
|
370
|
+
详情页的 `i18n` 与 `plugin.json` 的 `%catalogKey%`/`locales/*.json` 是两套合同。详情文件不解析
|
|
371
|
+
`%catalogKey%`;应在 `ability.json#detail.i18n` 中提供本地化内容或文件路径。
|
|
372
|
+
|
|
373
|
+
```json
|
|
374
|
+
{
|
|
375
|
+
"detail": {
|
|
376
|
+
"blocks": [
|
|
377
|
+
{ "type": "markdown", "path": "presentation/README.md" }
|
|
378
|
+
],
|
|
379
|
+
"i18n": {
|
|
380
|
+
"en": {
|
|
381
|
+
"blocks": [
|
|
382
|
+
{ "type": "markdown", "path": "presentation/README.en.md" }
|
|
383
|
+
]
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
本地化字段采用**整体覆盖**:一旦 `i18n.en.blocks` 存在,它会替换默认的整个 `blocks` 数组,不做逐项合并。
|
|
391
|
+
因此,多语言丰富详情需要在每个 locale 中给出完整区块序列。
|
|
392
|
+
|
|
393
|
+
## 元信息
|
|
394
|
+
|
|
395
|
+
`detail.meta` 是有序数组。预置 `key` 支持 `homepage`、`repository`、`docs`、`license`;也可使用
|
|
396
|
+
`label` 创建自定义文本项。以 `http://` 或 `https://` 开头的值会渲染成链接。
|
|
397
|
+
|
|
398
|
+
```json
|
|
399
|
+
{
|
|
400
|
+
"meta": [
|
|
401
|
+
{ "key": "docs", "value": "https://example.com/docs" },
|
|
402
|
+
{ "label": "维护团队", "value": "Example Team" }
|
|
403
|
+
]
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## 安全与大小限制
|
|
408
|
+
|
|
409
|
+
- `ability.json` 最大 64 KiB;
|
|
410
|
+
- 单个 Markdown/结构化详情文件最大 512 KiB;
|
|
411
|
+
- 单张本地图片最大 8 MiB;
|
|
412
|
+
- 包内引用必须留在插件根目录;
|
|
413
|
+
- 图片只接受 AVIF、GIF、ICO、JPEG、JPG、PNG、SVG、WebP;
|
|
414
|
+
- 外部图片只接受 HTTPS;链接按钮接受 HTTP(S);
|
|
415
|
+
- 详情损坏不会阻断插件或能力页启动,宿主会忽略该插件的自定义介绍并记录诊断日志。
|
|
416
|
+
|
|
417
|
+
## 发布前检查
|
|
418
|
+
|
|
419
|
+
- `ability.json` 的 `slug`、`version` 与 `plugin.json` 完全一致;
|
|
420
|
+
- 长 Markdown 使用 `path`,文件位于 `presentation/`;
|
|
421
|
+
- `i18n` 中的 `blocks` 是完整数组;
|
|
422
|
+
- 图片路径大小写与归档中的真实文件一致;
|
|
423
|
+
- `bunx vite build` 生成的 zip 包含 `ability.json` 和 `presentation/**`;
|
|
424
|
+
- 安装后在「能力 → 我的」打开插件详情,核对宿主自动生成的权限和贡献项是否符合预期。
|
package/docs/ai.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# AI 文本能力
|
|
2
|
+
|
|
3
|
+
`ctx.ai` 让插件调用用户已经在 Vetta 中配置的文本模型。模型发现、默认模型解析、API Key 与登录凭据注入、实际请求都由 Desktop 主进程负责;插件只能看到脱敏后的模型描述与完成结果。
|
|
4
|
+
|
|
5
|
+
## 权限
|
|
6
|
+
|
|
7
|
+
- `ai.models.list`:调用 `ctx.ai.listModels()`。
|
|
8
|
+
- `ai.complete`:调用 `ctx.ai.complete()`、`ctx.ai.stream()` 或 `ctx.ai.chat()`,可能产生模型费用或消耗用户额度。
|
|
9
|
+
|
|
10
|
+
两项权限独立。只知道固定模型标识的插件可以仅声明 `ai.complete`;需要展示模型选择器时再同时声明 `ai.models.list`。
|
|
11
|
+
|
|
12
|
+
## 列出模型
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
const { defaultModel, models } = await ctx.ai.listModels();
|
|
16
|
+
|
|
17
|
+
const options = models.map((model) => ({
|
|
18
|
+
value: model.modelKey,
|
|
19
|
+
label: model.name,
|
|
20
|
+
provider: model.provider,
|
|
21
|
+
}));
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
列表只包含当前可用且支持文本输入的模型。`modelKey` 使用 `provider/model` 格式;`defaultModel` 只有在用户设置的默认模型当前可用时才返回,否则为 `null`。
|
|
25
|
+
|
|
26
|
+
## 完成文本
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
const result = await ctx.ai.complete({
|
|
30
|
+
modelKey: selectedModelKey,
|
|
31
|
+
systemPrompt: "在不改变含义的前提下优化用户提示词。只返回优化结果。",
|
|
32
|
+
prompt: userPrompt,
|
|
33
|
+
temperature: 0.3,
|
|
34
|
+
maxTokens: 1200,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
console.log(result.text, result.usage.totalTokens);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`modelKey` 可省略,此时宿主使用用户明确设置且当前可用的默认模型;没有可用默认模型时调用会失败。`reasoning` 只会传给声明支持推理的模型,`maxTokens` 不会超过该模型自身的输出上限。
|
|
41
|
+
|
|
42
|
+
`complete` 是单轮契约(`systemPrompt + prompt`),不接受工具或图片。多轮对话使用下方的 `chat`;插件提供 API Key 仍然不被接受——凭据永远由宿主注入。
|
|
43
|
+
|
|
44
|
+
## 流式完成
|
|
45
|
+
|
|
46
|
+
`ctx.ai.stream()` 与 `complete()` 接受相同请求并返回相同的最终结果,但会在生成期间通过
|
|
47
|
+
`onTextDelta` 交付增量文本。`delta` 是本次新增片段,`text` 是截至当前事件的完整文本;UI 通常直接使用
|
|
48
|
+
`text` 更新同一条消息,完成后再使用 Promise 返回值保存最终结果。
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
const controller = new AbortController();
|
|
52
|
+
|
|
53
|
+
const result = await ctx.ai.stream(
|
|
54
|
+
{
|
|
55
|
+
modelKey: selectedModelKey,
|
|
56
|
+
systemPrompt: "使用 Markdown 回答;公式使用 LaTeX。",
|
|
57
|
+
prompt: userPrompt,
|
|
58
|
+
maxTokens: 1600,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
signal: controller.signal,
|
|
62
|
+
onTextDelta: ({ text }) => updatePreview(text),
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
await saveAnswer(result.text);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
传入的 `AbortSignal` 取消时,宿主会中止主进程中的 Provider 请求,而不只是停止 UI 更新。事件与最终结果
|
|
70
|
+
仍经过 Capability Schema 校验;模型选择、凭据、额度、错误与 usage 语义均和 `complete()` 相同。
|
|
71
|
+
|
|
72
|
+
## 多轮对话 chat
|
|
73
|
+
|
|
74
|
+
`ctx.ai.chat()` 是**无状态**的多轮文本完成:宿主不保存任何会话状态,插件自己持有完整消息转写(需要跨重启保留时配合 `ctx.storage` 持久化),每次调用都发送全量 `messages`。权限沿用 `ai.complete`。当前 `chat()` 只返回完整结果;需要边生成边展示的单轮文本使用 `stream()`。
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
const messages: PluginAiChatMessage[] = [
|
|
78
|
+
{ role: "user", content: "轮到你走棋了。当前局面:…" },
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
const result = await ctx.ai.chat({
|
|
82
|
+
modelKey: selectedModelKey, // 可省略,同 complete 的默认模型解析
|
|
83
|
+
systemPrompt: "你是中国象棋棋手。",
|
|
84
|
+
messages,
|
|
85
|
+
tools: [
|
|
86
|
+
{
|
|
87
|
+
name: "make_move",
|
|
88
|
+
description: "落子。走法使用 ICCS 坐标,如 h2e2。",
|
|
89
|
+
parameters: {
|
|
90
|
+
type: "object",
|
|
91
|
+
properties: { move: { type: "string" } },
|
|
92
|
+
required: ["move"],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- `messages` 为全量转写,元素是 `user` / `assistant` / `toolResult` 三种角色;`assistant` 消息可携带其历史 `toolCalls`,`toolResult` 通过 `toolCallId` 与之对应。
|
|
100
|
+
- `tools` 是**插件内部工具**:只对本次请求可见,模型触发时宿主不执行任何东西,只把 `toolCalls` 原样返回(`stopReason: "toolUse"`)。插件自行执行,把结果作为 `toolResult` 消息追加进 `messages` 后再次调用 `chat`,形成插件内部 loop。这类工具**不会**注册进宿主 Agent,不影响正常会话。
|
|
101
|
+
- `temperature` / `maxTokens` / `reasoning` 语义与 `complete` 一致。
|
|
102
|
+
|
|
103
|
+
典型 loop:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
for (;;) {
|
|
107
|
+
const turn = await ctx.ai.chat({ systemPrompt, messages, tools });
|
|
108
|
+
messages.push({ role: "assistant", content: turn.text, toolCalls: turn.toolCalls });
|
|
109
|
+
if (turn.stopReason !== "toolUse") break;
|
|
110
|
+
for (const call of turn.toolCalls) {
|
|
111
|
+
const outcome = runLocalTool(call); // 插件内部执行,例如校验并落子
|
|
112
|
+
messages.push({
|
|
113
|
+
role: "toolResult",
|
|
114
|
+
toolCallId: call.id,
|
|
115
|
+
toolName: call.name,
|
|
116
|
+
content: outcome.text,
|
|
117
|
+
isError: outcome.isError,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|