docs(lineup-app): organize current design documents
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
# LineUp UI Surface 与 App Capability 协议
|
||||
|
||||
**版本:** 1.0(提案)
|
||||
**状态:** 已有 Web Chat Reference Host
|
||||
**日期:** 2026-08-02
|
||||
|
||||
> **汇总入口:** 本文保留 Surface 与 Capability 的协议和安全细节。后续 Runtime、SDK、App 管理及旧 `app.id` 字段的兼容迁移以 [LineUp App 最终设计方案](app_final_design.md) 为准。
|
||||
|
||||
## 1. 目标
|
||||
|
||||
LineUp 的 Agent 不应只能回一段文本,也不应获得在宿主 App 任意执行代码的权限。本协议定义一个类似“小程序表现层”的受控扩展模型:
|
||||
|
||||
```text
|
||||
Agent / Adapter
|
||||
├─ lineup.v1.ui.open / patch / close ─────→ UI Surface Host
|
||||
│ └─ sandbox HTML + CSS + JS
|
||||
├─ lineup.v1.app.call ─────→ App Capability Registry
|
||||
│ └─ 用户确认 / 系统权限 / 本机执行
|
||||
←─ lineup.v1.ui.event / app.result ────── 用户动作或受控调用结果
|
||||
```
|
||||
|
||||
它有两层,不可混用:
|
||||
|
||||
| 层 | 作用 | 能做什么 | 不能做什么 |
|
||||
|---|---|---|---|
|
||||
| `UI Surface` | 呈现交互界面 | 显示 HTML/CSS/JS、收集用户事件、接收状态更新 | 读取宿主登录态、直接访问设备能力、直接调用 IM / 网络 |
|
||||
| `App Capability` | 调用宿主上层应用能力 | 在能力注册、权限和用户确认后打开链接、写剪贴板、选文件、调用原生模块等 | 由 Surface 脚本绕过权限直接调用 |
|
||||
|
||||
标准 Markdown、文本、状态、进度、choice、confirm、input 等仍应优先由宿主内置渲染器实现。Surface 适用于仪表盘、地图、复杂表单、图表、可视化编辑器等无法由内置组件良好表达的界面。
|
||||
|
||||
## 2. 安全模型(不可省略)
|
||||
|
||||
1. Agent 的 UI bundle 被视为**不可信内容**,不是 App 代码的一部分。
|
||||
2. Web Host 必须使用独立 origin 的 `iframe sandbox="allow-scripts"`。禁止 `allow-same-origin`、`allow-top-navigation`、`allow-popups`、`allow-forms`。
|
||||
3. Surface 的 CSP 至少为 `default-src 'none'; connect-src 'none'; img-src data: blob:; style-src 'unsafe-inline'; script-src 'unsafe-inline'`。默认不得联网、加载远程脚本、访问摄像头或地理位置。
|
||||
4. 宿主与 Surface 仅用 `postMessage` 通信;宿主必须同时验证 `event.source`、消息命名空间、`instance_id`、事件名、JSON 类型与大小。
|
||||
5. Surface 事件只是“用户意图”回传。任何上层 App / 原生能力都必须由 Agent 另行发送 `app.call`,再由宿主依据注册表、风险等级和用户授权执行。
|
||||
6. Content 不得写入宿主 DOM。Markdown 使用解析器后仍须 sanitizer;HTML Surface 只能放进 sandbox `srcdoc`。
|
||||
7. 生产环境应只接受已签名或在 Agent allowlist 中的 bundle hash;Reference Host 先以严格 sandbox 保障隔离,并保留 `app.integrity` 字段用于升级。
|
||||
|
||||
## 3. 消息类型
|
||||
|
||||
所有消息均使用既有 `lineup.v1` 信封。`conversation_id`、`id`、`sender`、`target` 的规则不变。
|
||||
|
||||
| Type | 方向 | 含义 |
|
||||
|---|---|---|
|
||||
| `lineup.v1.ui.open` | Agent → Client | 创建或替换一个 Surface 实例 |
|
||||
| `lineup.v1.ui.patch` | Agent → Client | 向已打开实例推送新的状态;不可注入或替换代码 |
|
||||
| `lineup.v1.ui.close` | Agent → Client | 关闭实例 |
|
||||
| `lineup.v1.ui.event` | Client → Agent | Surface 中的用户事件或用户关闭事件 |
|
||||
| `lineup.v1.client.inventory` | Client → Agent | 当前 Host 的 revisioned 最小接口清单:标准组件、应用中心已启用 app Surface 与 policy 筛选后的 capability |
|
||||
| `lineup.v1.app.list` | Client → Agent | 可选的兼容 capability-only 投影;不得替代完整 inventory 或宣称安装/授权 |
|
||||
| `lineup.v1.app.call` | Agent → Client | 请求调用一个声明过的 capability |
|
||||
| `lineup.v1.app.result` | Client → Agent | 调用完成、拒绝、失败或不支持的结果 |
|
||||
|
||||
未知的 `ui.*` 或 `app.*` type 只能安全显示或回复 `unsupported`,不得执行。
|
||||
|
||||
## 4. UI Surface 合约
|
||||
|
||||
### 4.1 打开
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"id": "msg_ui_01",
|
||||
"type": "lineup.v1.ui.open",
|
||||
"conversation_id": "conv_01",
|
||||
"sender": {"kind": "agent", "id": "agent_hermes_main"},
|
||||
"payload": {
|
||||
"instance_id": "weather.dashboard.01",
|
||||
"app": {
|
||||
"id": "com.lineup.weather.dashboard",
|
||||
"name": "天气面板",
|
||||
"version": "1.0.0",
|
||||
"integrity": "sha256-BASE64_DIGEST",
|
||||
"html": "<main><button id='refresh'>刷新</button></main>",
|
||||
"css": "main { padding: 16px }",
|
||||
"js": "document.querySelector('#refresh').onclick=()=>LineUpSurface.event('refresh',{unit:'c'})"
|
||||
},
|
||||
"state": {"city": "北京", "temperature": 22}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
- `instance_id` 在一个会话内唯一,格式为 `[A-Za-z0-9._:-]{1,128}`;相同 ID 的 `ui.open` 表示替换旧实例;
|
||||
- `app.id` 为反向域名风格的稳定应用 ID,`version` 为 SemVer;
|
||||
- `html`、`css`、`js` 是 Surface bundle;单个 bundle 的生产上限建议为 160 KiB,资源应使用经过 Artifact 管理和签名的本地引用,禁止任意公网 URL;
|
||||
- `state` 必须是 JSON object。UI bundle 将在 `ready` 后和每次 `ui.patch` 收到它;
|
||||
- **`ui.patch` 只能更新 `state`,绝不能更新 HTML/CSS/JS。** 若要升级 bundle,关闭旧实例并以新 `version` 打开新实例。
|
||||
|
||||
### 4.2 Surface bridge
|
||||
|
||||
Host 注入唯一的全局对象:
|
||||
|
||||
```js
|
||||
LineUpSurface.event('refresh', { unit: 'c' })
|
||||
LineUpSurface.onState((state) => render(state))
|
||||
LineUpSurface.resize(document.documentElement.scrollHeight)
|
||||
```
|
||||
|
||||
它只允许以下上行消息:
|
||||
|
||||
```json
|
||||
{
|
||||
"namespace": "lineup.surface.v1",
|
||||
"type": "event",
|
||||
"event": "refresh",
|
||||
"data": {"unit": "c"}
|
||||
}
|
||||
```
|
||||
|
||||
Host 将其转换为:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "lineup.v1.ui.event",
|
||||
"payload": {
|
||||
"instance_id": "weather.dashboard.01",
|
||||
"event": "refresh",
|
||||
"data": {"unit": "c"}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`event` 采用 `[A-Za-z][A-Za-z0-9._:-]{0,63}`,`data` 必须为小于 16 KiB 的 JSON object。Surface 不拥有 `app.call` bridge。
|
||||
|
||||
### 4.3 状态更新与关闭
|
||||
|
||||
```json
|
||||
{"type":"lineup.v1.ui.patch","payload":{"instance_id":"weather.dashboard.01","state":{"city":"上海","temperature":28}}}
|
||||
```
|
||||
|
||||
```json
|
||||
{"type":"lineup.v1.ui.close","payload":{"instance_id":"weather.dashboard.01","reason":"completed"}}
|
||||
```
|
||||
|
||||
用户主动关闭时 Client 应回送 `ui.event`,其中 `event` 为 `close`。
|
||||
|
||||
## 5. App Capability 合约
|
||||
|
||||
### 5.1 动态 Client Inventory
|
||||
|
||||
客户端在会话建立完成后,以及应用中心的 app 启用/停用/升级、Host policy 或 capability 可见性变化时,向**对应 Agent 会话**发送 `client.inventory`。它有单调递增或不可复用的 `revision`;Agent 必须用最新 revision 决定可请求的组件、Surface 和能力。收到撤销后的旧 app id、Surface 或 capability 引用时,Client 只返回确定的 `unsupported / revoked` 结果,绝不回退到执行或加载远端 bundle。
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "lineup.v1.client.inventory",
|
||||
"payload": {
|
||||
"revision": "catalog-42",
|
||||
"standard_components": [
|
||||
{"id":"choice","version":"1"},
|
||||
{"id":"confirm","version":"1"},
|
||||
{"id":"input","version":"1"}
|
||||
],
|
||||
"applications": [
|
||||
{
|
||||
"id":"com.lineup.svg-canvas",
|
||||
"version":"1.0.0",
|
||||
"surfaces":[{"id":"canvas","events":["draw","resize","snapshot"]}]
|
||||
}
|
||||
],
|
||||
"capabilities": [
|
||||
{"name":"artifact.save","version":"1.0","risk":"user_confirmation"}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`applications` 只能列出应用中心中**已启用且已在本地完成相应阶段验证**的应用,不包含 bundle 源码、安装来源、登录态、文件路径或用户内容。SVG 画板这样的 app 只允许 Agent 以 app id 创建受限 Surface,并通过用户触发的 `ui.event(draw / resize / snapshot)` 获取声明性画布事件;inventory 不授予远程脚本注入、读取宿主 DOM 或本机能力权限。
|
||||
|
||||
### 5.2 Capability 声明(`app.list` 兼容投影)
|
||||
|
||||
客户端可在 inventory 之后或 capability 变化时发送 `app.list` 作为仅包含 capability 的兼容投影。只有 inventory / app.list 中声明且仍未被撤销的 `capability` 才能被请求;声明从不等同于授权。
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "lineup.v1.app.list",
|
||||
"payload": {
|
||||
"capabilities": [
|
||||
{"name":"app.open_url","version":"1.0","risk":"user_confirmation","input_schema":{"type":"object","required":["url"]}},
|
||||
{"name":"clipboard.write","version":"1.0","risk":"user_confirmation","input_schema":{"type":"object","required":["text"]}},
|
||||
{"name":"device.pick_file","version":"1.0","risk":"system_permission","input_schema":{"type":"object"}}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
风险必须是下列之一:`display_only`、`user_confirmation`、`system_permission`、`restricted`。后两者不能被记住为永久授权,且必须经过原生系统权限或额外身份校验。
|
||||
|
||||
### 5.3 调用与结果
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "lineup.v1.app.call",
|
||||
"payload": {
|
||||
"call_id": "call_open_docs_01",
|
||||
"capability": "app.open_url",
|
||||
"reason": "打开部署文档供你核对",
|
||||
"expires_at": "2026-08-02T12:10:00Z",
|
||||
"arguments": {"url": "https://example.com/docs"}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Client 必须向用户说明 capability 和 `reason`,收到确认后再执行,随后使用相同 `call_id` 回传:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "lineup.v1.app.result",
|
||||
"payload": {
|
||||
"call_id": "call_open_docs_01",
|
||||
"status": "completed",
|
||||
"result": {"opened": true}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`status`:`completed | rejected | cancelled | expired | unsupported | failed`。同一 `call_id` 必须幂等;调用记录与授权决定应由 Gateway 审计。
|
||||
|
||||
## 6. Reference Host 当前实现与阶段边界
|
||||
|
||||
截至 2026-08-03,Tauri Reference Host 已完成 M0 和 M1-01~M1-03:严格 Envelope / Kernel / Store / Renderer 边界,Markdown、status/progress/error、choice、confirm、input 的可信原生渲染与本地状态恢复。它**尚未**实现 `client.inventory` 发送、应用中心、`ui.open / patch / close` 的 Surface Host、远端或本地 app bundle 加载、`app.call` 执行、Capability Registry 或 `app.result` 网络回传。
|
||||
|
||||
因此,这份协议中的 Surface、inventory 与 Capability 段落是后续 M2~M4 的正式契约,不是当前 Web Chat 已开放的功能。M2 建立应用中心本地启用注册表与隔离 Surface;M3 才生成/更新并通知 `client.inventory`,再实施 Capability policy;M4 负责下载、完整性校验、缓存、回滚与 Host 一致性。当前只以 Tauri Desktop Host 与同代码 Web Reference Host 实现这些边界;不规划独立 Android/iOS/Wails 客户端。
|
||||
Reference in New Issue
Block a user