Files

10 KiB
Raw Permalink Blame History

LineUp UI Surface 与 App Capability 协议

版本: 1.0(提案) 状态: 已有 Web Chat Reference Host 日期: 2026-08-02

汇总入口: 本文保留 Surface 与 Capability 的协议和安全细节。后续 Runtime、SDK、App 管理及旧 app.id 字段的兼容迁移以 LineUp App 最终设计方案 为准。

1. 目标

LineUp 的 Agent 不应只能回一段文本,也不应获得在宿主 App 任意执行代码的权限。本协议定义一个类似“小程序表现层”的受控扩展模型:

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-originallow-top-navigationallow-popupsallow-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 使用解析器后仍须 sanitizerHTML Surface 只能放进 sandbox srcdoc
  7. 生产环境应只接受已签名或在 Agent allowlist 中的 bundle hashReference Host 先以严格 sandbox 保障隔离,并保留 app.integrity 字段用于升级。

3. 消息类型

所有消息均使用既有 lineup.v1 信封。conversation_ididsendertarget 的规则不变。

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 打开

{
  "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 为反向域名风格的稳定应用 IDversion 为 SemVer
  • htmlcssjs 是 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 注入唯一的全局对象:

LineUpSurface.event('refresh', { unit: 'c' })
LineUpSurface.onState((state) => render(state))
LineUpSurface.resize(document.documentElement.scrollHeight)

它只允许以下上行消息:

{
  "namespace": "lineup.surface.v1",
  "type": "event",
  "event": "refresh",
  "data": {"unit": "c"}
}

Host 将其转换为:

{
  "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 状态更新与关闭

{"type":"lineup.v1.ui.patch","payload":{"instance_id":"weather.dashboard.01","state":{"city":"上海","temperature":28}}}
{"type":"lineup.v1.ui.close","payload":{"instance_id":"weather.dashboard.01","reason":"completed"}}

用户主动关闭时 Client 应回送 ui.event,其中 eventclose

5. App Capability 合约

5.1 动态 Client Inventory

客户端在会话建立完成后,以及应用中心的 app 启用/停用/升级、Host policy 或 capability 可见性变化时,向对应 Agent 会话发送 client.inventory。它有单调递增或不可复用的 revisionAgent 必须用最新 revision 决定可请求的组件、Surface 和能力。收到撤销后的旧 app id、Surface 或 capability 引用时,Client 只返回确定的 unsupported / revoked 结果,绝不回退到执行或加载远端 bundle。

{
  "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 才能被请求;声明从不等同于授权。

{
  "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_onlyuser_confirmationsystem_permissionrestricted。后两者不能被记住为永久授权,且必须经过原生系统权限或额外身份校验。

5.3 调用与结果

{
  "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 回传:

{
  "type": "lineup.v1.app.result",
  "payload": {
    "call_id": "call_open_docs_01",
    "status": "completed",
    "result": {"opened": true}
  }
}

statuscompleted | rejected | cancelled | expired | unsupported | failed。同一 call_id 必须幂等;调用记录与授权决定应由 Gateway 审计。

6. Reference Host 当前实现与阶段边界

截至 2026-08-03Tauri Reference Host 已完成 M0 和 M1-01M1-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 段落是后续 M2M4 的正式契约,不是当前 Web Chat 已开放的功能。M2 建立应用中心本地启用注册表与隔离 Surface;M3 才生成/更新并通知 client.inventory,再实施 Capability policy;M4 负责下载、完整性校验、缓存、回滚与 Host 一致性。当前只以 Tauri Desktop Host 与同代码 Web Reference Host 实现这些边界;不规划独立 Android/iOS/Wails 客户端。