初始化 agent_ops 文档治理体系
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
# 设计整合说明与优先级
|
||||
|
||||
**版本:** 2026-08-07
|
||||
**范围:** 原 `lineup-app/设计/` 与原 `agent_ops/设计/`
|
||||
|
||||
## 1. 当前采用的整合规则
|
||||
|
||||
本目录下的“当前有效设计”统一采用以下优先级:
|
||||
|
||||
1. `01.当前有效设计/APP架构设计.md`
|
||||
2. `01.当前有效设计/02.正式方案/*.md`
|
||||
3. `90.历史设计归档/02.正式方案快照/*.md` 中与当前实现不冲突的补充内容
|
||||
4. `90.历史设计归档/01.前期分析与设计/*` 与 `00.阶段记录/*` 的历史背景
|
||||
|
||||
一句话概括就是:
|
||||
|
||||
**如有冲突,在结合项目当前实际实现的前提下,优先采用已经迁入 `01.当前有效设计/` 的原 `lineup-app/设计/` 文档。**
|
||||
|
||||
## 2. 为什么这样定
|
||||
|
||||
对比两边设计目录后,可以看到:
|
||||
|
||||
- 原 `lineup-app/设计/` 已经形成“权威入口 + 正式方案”的稳定结构;
|
||||
- `APP架构设计.md` 明确声明自己是当前权威设计基线,并声明替代关系;
|
||||
- 文档内容与当前 `lineup-app` 代码、当前 Host 基线、当前 Runtime 方向更一致;
|
||||
- 原 `agent_ops/设计/` 中的大量文档属于更早阶段的探索、协议草案或设计快照。
|
||||
|
||||
因此,本轮不再让两处旧目录平行竞争“谁代表当前设计”,而是让已迁入 `01.当前有效设计/` 的文档负责当前设计,让 `90.历史设计归档/` 负责背景、来源和历史演进。
|
||||
|
||||
## 3. 当前设计资料的角色划分
|
||||
|
||||
### 3.1 当前有效设计
|
||||
|
||||
主要回答“现在应该按什么实现”的问题:
|
||||
|
||||
- App、Runtime、Host 三者边界;
|
||||
- MiniApp、Tool、Surface、Capability 的正式约束;
|
||||
- 当前客户端基线;
|
||||
- 当前 Tool 和运行时语义。
|
||||
|
||||
### 3.2 历史设计来源
|
||||
|
||||
主要回答“之前为什么那样想、后来怎么演进到现在”的问题:
|
||||
|
||||
- 直连 WebSocket 方案;
|
||||
- 早期工具协议三分法;
|
||||
- 早期移动端 / Web 方向;
|
||||
- 阶段记录和早期架构草图。
|
||||
|
||||
## 4. 本轮之后应如何使用两套来源
|
||||
|
||||
### 4.1 看当前实现时
|
||||
|
||||
优先阅读本目录下的新整合文档,再回溯 `lineup-app/设计/` 的细节来源。
|
||||
|
||||
### 4.2 查历史背景时
|
||||
|
||||
回看 `90.历史设计归档/` 中的前期分析与阶段记录,但默认它们不再自动对当前实现生效。
|
||||
|
||||
## 5. 后续清理方向
|
||||
|
||||
本轮已经完成物理迁移;后续不再恢复旧的两处分散设计目录。
|
||||
@@ -0,0 +1,130 @@
|
||||
# 设计冲突与演进顺序
|
||||
|
||||
**版本:** 2026-08-07
|
||||
**用途:** 说明两处设计文档的先后关系、冲突点和整合后的取舍依据
|
||||
|
||||
## 1. 设计演进的三个阶段
|
||||
|
||||
当前已迁入 `02.架构设计/` 的材料,大致对应三个阶段。
|
||||
|
||||
### 第一阶段:早期探索阶段
|
||||
|
||||
主要来源:
|
||||
|
||||
- `90.历史设计归档/00.阶段记录/`
|
||||
- `90.历史设计归档/01.前期分析与设计/`
|
||||
|
||||
这一阶段的特征是:
|
||||
|
||||
- 仍以局域网直连 WebSocket、未来移动端等思路为主;
|
||||
- 工具协议与消息协议处于早期草案阶段;
|
||||
- 主要目标是确认产品定位和最小可行方向。
|
||||
|
||||
这一阶段保留了很多重要背景,但不再直接作为当前实现依据。
|
||||
|
||||
### 第二阶段:正式方案快照阶段
|
||||
|
||||
主要来源:
|
||||
|
||||
- `90.历史设计归档/02.正式方案快照/`
|
||||
|
||||
这一阶段已经开始形成 Runtime、App、SDK、Surface、Capability 的正式方案,但它仍然是一次较早的设计快照,并不完全等于今天的代码基线。
|
||||
|
||||
### 第三阶段:当前有效设计阶段
|
||||
|
||||
主要来源:
|
||||
|
||||
- `01.当前有效设计/README.md`
|
||||
- `01.当前有效设计/APP架构设计.md`
|
||||
- `01.当前有效设计/02.正式方案/`
|
||||
|
||||
这一阶段与当前 `lineup-app` 实现最贴近,也明确声明了权威入口和替代关系,因此作为今天的主设计基线。
|
||||
|
||||
## 2. 主要冲突点与取舍
|
||||
|
||||
### 2.1 连接主线
|
||||
|
||||
早期设计强调:
|
||||
|
||||
- 局域网 App 直连 Agent 插件 WebSocket
|
||||
|
||||
当前有效设计强调:
|
||||
|
||||
- `Tauri Desktop Host + Web Reference Host`
|
||||
- `AppServer / Runtime / Adapter` 的运行时模型
|
||||
|
||||
整合结论:
|
||||
|
||||
- 保留早期直连方案作为历史探索背景;
|
||||
- 当前实现和后续设计都以 Runtime 中心化的 Host / AppServer / Adapter 主线为准。
|
||||
|
||||
### 2.2 客户端形态
|
||||
|
||||
早期设计强调:
|
||||
|
||||
- Web / 未来移动端
|
||||
|
||||
当前有效设计强调:
|
||||
|
||||
- Tauri Desktop Host 与 Web Reference Host 的统一代码基线;
|
||||
- 不再以独立 Android/Kotlin 客户端作为当前主线。
|
||||
|
||||
整合结论:
|
||||
|
||||
- 早期移动端方向作为历史背景保留;
|
||||
- 当前产品和实现基线以 `lineup-app` 当前 Host 体系为准。
|
||||
|
||||
### 2.3 工具模型
|
||||
|
||||
早期设计强调:
|
||||
|
||||
- `app / toolset / action` 三分法;
|
||||
- 工具协议作为较独立的一层来描述。
|
||||
|
||||
当前有效设计强调:
|
||||
|
||||
- Agent Tool、Inventory、Tool Call、进度、结果、activation、operation;
|
||||
- Tool 是 Runtime 模型的一部分,而不是脱离 Runtime 的独立漂浮协议。
|
||||
|
||||
整合结论:
|
||||
|
||||
- 早期三分法保留为历史理解工具;
|
||||
- 当前实现与后续文档统一采用 Runtime 中心化 Tool 模型。
|
||||
|
||||
### 2.4 设计重心
|
||||
|
||||
早期设计更关注:
|
||||
|
||||
- 能否连通;
|
||||
- 消息长什么样;
|
||||
- 工具如何被粗粒度定义。
|
||||
|
||||
当前有效设计更关注:
|
||||
|
||||
- Runtime 的唯一所有权;
|
||||
- MiniApp、Surface、Capability 的边界;
|
||||
- 生命周期、恢复、子会话、工作区和 Agent 调用闭环。
|
||||
|
||||
整合结论:
|
||||
|
||||
- 现在的设计讨论必须站在第三阶段的视角进行;
|
||||
- 第一、二阶段只用于解释“为什么演进到这里”。
|
||||
|
||||
## 3. 当前采用的统一排序
|
||||
|
||||
当同一主题在多份文档中出现冲突时,当前采用的优先顺序是:
|
||||
|
||||
1. `01.当前有效设计/APP架构设计.md`
|
||||
2. `01.当前有效设计/02.正式方案/*.md`
|
||||
3. `02.整合结论/` 中已经写明的整合判断
|
||||
4. `90.历史设计归档/02.正式方案快照/`
|
||||
5. `90.历史设计归档/01.前期分析与设计/`
|
||||
6. `90.历史设计归档/00.阶段记录/`
|
||||
|
||||
## 4. 这份文档的作用
|
||||
|
||||
这份文档不是简单说明“哪个文件更新”,而是明确:
|
||||
|
||||
- 哪些冲突是设计演进带来的;
|
||||
- 现在为什么优先采用当前有效设计;
|
||||
- 早期文档应该怎样被使用,而不是继续和当前设计平级竞争。
|
||||
@@ -0,0 +1,878 @@
|
||||
# LineUp App 最终设计方案
|
||||
|
||||
**版本:** 1.0(当前开发基线)
|
||||
**状态:** 当前 App 设计的唯一汇总入口
|
||||
**日期:** 2026-08-06
|
||||
**来源:** [App 层架构方案](lineup-app-layer-architecture.md)、[Runtime 与 App SDK 架构方案](lineup-runtime-sdk-architecture.md)、[运行时与智能体工具](运行时与智能体工具.md)、[UI Surface 与 App Capability 协议](lineup-ui-surface-protocol.md)
|
||||
|
||||
**专题约束:** 远端 Agent 的角色、Agent Tool 与本地 UI Action / Host 生命周期意图的入口边界,以 [运行时与智能体工具](运行时与智能体工具.md) 为准。
|
||||
|
||||
---
|
||||
|
||||
## 0. 一句话定义
|
||||
|
||||
**LineUp 是运行在用户设备上的协作 Runtime,也就是整个产品的本地协调中心。**
|
||||
|
||||
Runtime 负责连接 AppServer 和 Remote Agent,保存会话与消息,管理应用、工具、权限和
|
||||
本地恢复。图文聊天、应用注册表、语音对话、画板和游戏则是在这个底座上运行的 App。
|
||||
用户使用 App 完成操作,Agent 发来消息或请求;两边都先交给 Runtime 处理,因此任何 App
|
||||
都不需要、也不能自己处理连接、登录态或系统权限。
|
||||
|
||||
```text
|
||||
Remote Agent
|
||||
│ 标准 LineUp 消息
|
||||
▼
|
||||
AppServer / IM
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ LineUp Runtime │
|
||||
│ │
|
||||
│ 连接 · 会话 · 消息筛选 · App 管理 · Tool 管理 │
|
||||
│ 权限 · Artifact · 存储 · Outbox · 恢复 · 审计 │
|
||||
└─────────────┬───────────────────────────┬───────────────┘
|
||||
│ Runtime SDK │ Host Provider
|
||||
▼ ▼
|
||||
Core / Installed App Tauri / OS
|
||||
chat · app-registry 文件 · 麦克风
|
||||
voice · whiteboard 通知 · 窗口
|
||||
game · … 安全存储
|
||||
```
|
||||
|
||||
## 1. 本方案的结论与优先级
|
||||
|
||||
本文件收敛正式方案目录下的三份已有文档,并在冲突处做出当前开发阶段的明确选择。
|
||||
|
||||
| 主题 | 最终决定 |
|
||||
|---|---|
|
||||
| App 的本体 | 整个产品是 `LineUpRuntime`;聊天不是 Runtime 本身。默认的 `Interaction App` 负责组织人与 Agent 的主交互,当前实现形态是 `chat`/图文 IM。 |
|
||||
| 默认入口 | `Interaction App` 是当前默认和 Recovery App;当前默认模式是 `im`(代码兼容作用域仍为 `chat`),以后可切换到实时音频或视频模式。 |
|
||||
| App 之间的关系 | App 只调用 Runtime SDK,不直接访问 AppServer、Agent、另一个 App 或 Tauri 特权 API。 |
|
||||
| Agent 交互 | Agent 只与 Runtime 通信;Runtime 按会话和应用作用域筛选并投递给 App。 |
|
||||
| 路由最小键 | 所有可路由消息必须有 `app_scope` 与 `conversation_id`;可选 `instance_id`、`operation_id`、`call_id`。 |
|
||||
| App 生态阶段 | 当前只使用本地短作用域,如 `chat`、`whiteboard`、`draw-and-guess`;暂不引入供应商身份、反向域名 App ID 或全局命名空间。 |
|
||||
| 丰富交互 | 标准内容先用可信内建组件;复杂互动用受限 Surface;设备/系统动作只能经 Capability Gateway。 |
|
||||
| Agent 工具 | App 在 SDK 源码中声明方法;构建生成 Manifest 的不可执行 Tool 描述,Runtime 汇总为动态 Inventory;Agent 只能调用当前 Inventory 中的方法。 |
|
||||
| Runtime SDK | SDK 提供状态订阅、Agent 消息订阅、Action、Tool、应用导航、Artifact、当前 App 子会话的通用业务数据、Capability 和生命周期接口。业务数据由 Runtime 持久化和隔离,App 不直接拥有存储。 |
|
||||
| Host | Tauri Desktop Host 与同代码的 Web Reference Host 是当前唯一实现/验收基线:前者是正式桌面外壳,后者是浏览器开发和验证入口;它们不是两个客户端。不规划独立 Android/Kotlin 客户端或 Wails Host。 |
|
||||
|
||||
本文件优先于来源文档中关于**后续 Runtime 重构**的结构描述。来源文档中的 M0~M4 完成记录、当前已实现字段和历史验收事实仍然有效;它们不自动成为新 Runtime 的长期模块边界。
|
||||
|
||||
## 2. 系统边界
|
||||
|
||||
### 2.1 Runtime、App 与 Host:先分清三者
|
||||
|
||||
这三个词经常同时出现,但职责不同:Runtime 是后台协调者,App 是用户看到的功能,Host
|
||||
是让 Runtime 运行在桌面窗口或浏览器中的外壳。Host 不能绕过 Runtime 直接把系统能力
|
||||
交给 App 或 Agent。
|
||||
|
||||
```text
|
||||
Runtime
|
||||
管理连接、状态、应用、工具、权限和副作用。
|
||||
|
||||
App
|
||||
面向用户或 Agent 的功能单元;只能通过 SDK 与 Runtime 协作。
|
||||
|
||||
Host
|
||||
提供操作系统或 WebView 能力;由 Runtime 使用,不直接暴露给 App/Agent。
|
||||
```
|
||||
|
||||
| 主体 | 必须负责 | 明确不负责 |
|
||||
|---|---|---|
|
||||
| Runtime | 连接 AppServer/Agent、管理会话与路由、安排 App 生命周期、Tool、Capability、持久化和审计 | 具体页面 DOM、任意 App 的业务 UI。 |
|
||||
| Chat App | 图文会话、时间线、输入、任务/交互卡片和 Artifact 基础展示 | `fetch` AppServer、同步 cursor、Agent 原始包解析、文件系统。 |
|
||||
| App Registry App | 让用户查看已安装 App,并请求安装/启用/禁用/移除或选择默认入口 | 直接写 Registry、删除 Bundle、绕过验签。 |
|
||||
| Installed App | 自己的交互体验、已声明 Tool、已声明事件和私有状态 | Tauri `invoke`、Host DOM、token、任意网络和其他 App 数据。 |
|
||||
| Tauri Host Provider | 在 Runtime 授权后实现文件、音频、通知、窗口和安全存储等系统操作 | 协议解释、应用策略和 Agent 路由。 |
|
||||
|
||||
### 2.2 关键禁止项
|
||||
|
||||
```text
|
||||
App → AppServer / Agent 直接网络连接 禁止
|
||||
App → 直接构造并发送未校验 Agent Envelope 禁止
|
||||
Surface → Tauri invoke / 父页面 DOM / 登录态 禁止
|
||||
Agent → 任意 App JavaScript 函数或 Host 特权 API 禁止
|
||||
Agent → 静默安装、启用、禁用或移除 App 禁止
|
||||
Surface Event → 直接执行系统能力 禁止
|
||||
```
|
||||
|
||||
## 3. Runtime 的内部模型
|
||||
|
||||
### 3.1 三类输入输出
|
||||
|
||||
Runtime 采用 Event / Command / Effect 分层。
|
||||
|
||||
```text
|
||||
Event:已经发生的事实
|
||||
Agent 文本、任务进度、用户点击、画板变更、录音完成、连接断开。
|
||||
|
||||
Command:希望 Runtime 处理的动作
|
||||
发送消息、调用 Tool、启动 App、启用 App、请求保存文件。
|
||||
|
||||
Effect:Runtime 决定执行的副作用
|
||||
网络发送、文件选择、录音、通知、创建 Surface、写存储。
|
||||
```
|
||||
|
||||
处理顺序固定为:
|
||||
|
||||
```text
|
||||
接收 Event / Command
|
||||
→ 验证与授权
|
||||
→ 更新 Runtime State
|
||||
→ 持久化事实 / Outbox
|
||||
→ 产生 Effect
|
||||
→ Effect 结果重新进入 Runtime,成为 Event
|
||||
→ 生成面向 App 的状态投影或消息投递
|
||||
```
|
||||
|
||||
第一版采用“事件驱动状态机 + 必要快照”,不实施完整 Event Sourcing。关键入站事件、用户决定、Tool 终态、outbox 和审计需持久化;纯渲染细节、焦点和滚动位置不必写入 Runtime 事实日志。
|
||||
|
||||
### 3.2 顶层状态
|
||||
|
||||
```text
|
||||
identity
|
||||
用户、设备、认证会话。
|
||||
|
||||
connection
|
||||
AppServer 连接、sync cursor、重试、实时状态、outbox。
|
||||
|
||||
conversations
|
||||
conversation、关联 Agent、消息引用、任务、交互、Artifact 元数据。
|
||||
|
||||
apps
|
||||
App Registry、默认 App、运行实例、每个 App 的可靠 Inbox、启动状态与按 App 子会话隔离的通用业务数据。
|
||||
|
||||
tools
|
||||
Tool 声明、Inventory revision、调用记录、operation 与进度。
|
||||
|
||||
capabilities
|
||||
权限请求、用户决定、系统权限、执行状态与最小审计。
|
||||
```
|
||||
|
||||
### 3.3 Runtime 生命周期
|
||||
|
||||
```text
|
||||
created
|
||||
→ restoring
|
||||
→ authenticating
|
||||
→ synchronizing
|
||||
→ online
|
||||
↘ reconnecting / offline_degraded
|
||||
→ stopping
|
||||
→ stopped
|
||||
```
|
||||
|
||||
启动时 Runtime 必须恢复 App Registry、默认入口、cursor、outbox、App Inbox、未完成 Tool 和可恢复实例。追平服务器历史后才开始正常实时投递;离线时可接受的用户/App Action 进入 outbox,重连后以幂等键和顺序发送。
|
||||
|
||||
## 4. 应用模型
|
||||
|
||||
### 4.1 本地应用作用域
|
||||
|
||||
当前阶段使用 Runtime 本地注册的 `app_scope`,而不是全球 App 身份:
|
||||
|
||||
```text
|
||||
chat
|
||||
app-registry
|
||||
settings
|
||||
voice
|
||||
whiteboard
|
||||
draw-and-guess
|
||||
runtime
|
||||
```
|
||||
|
||||
`app_scope` 的职责仅是本机启动、消息路由、Tool 路由、存储隔离和实例归属。它不是供应商身份,不要求跨市场唯一,也不承担开放生态的所有权模型。
|
||||
|
||||
未来如需市场、多供应商和跨设备分发,可以在 Manifest 中增加稳定身份映射;但不能改变本方案中的 `app_scope + conversation_id` 路由与隔离语义。
|
||||
|
||||
### 4.2 应用类型
|
||||
|
||||
| 类型 | 典型项 | 来源与执行方式 | 管理规则 |
|
||||
|---|---|---|---|
|
||||
| Core App | `chat`、`app-registry`、`settings` | 跟随 Runtime 发行的可信代码 | 不可由市场 Bundle 覆盖;`chat` 不可移除。 |
|
||||
| Installed App | `voice`、`whiteboard`、`draw-and-guess` | 经 Runtime 验证的 Bundle,在受限 Surface 中运行 | 可安装、启用、禁用、更新、回滚、移除。 |
|
||||
| Capability | 录音、选文件、保存、通知 | Tauri/OS 经 Runtime Host Provider 提供 | 不是 App,也不是自动授予的权限。 |
|
||||
|
||||
### 4.3 App Registry 与默认入口
|
||||
|
||||
Runtime App Manager 是安装和状态的唯一事实源:
|
||||
|
||||
```ts
|
||||
type AppRecord = {
|
||||
app_scope: AppScope;
|
||||
kind: "core" | "installed";
|
||||
installed_version: string;
|
||||
previous_version?: string;
|
||||
enabled: boolean;
|
||||
install_state: "installed" | "updating" | "failed";
|
||||
active_instance_count: number;
|
||||
installed_at: string;
|
||||
enabled_at?: string;
|
||||
};
|
||||
|
||||
interface RuntimeAppManager {
|
||||
listApps(filter?: AppListFilter): readonly AppRecord[];
|
||||
getApp(appScope: AppScope): AppRecord | undefined;
|
||||
install(request: InstallAppRequest): Promise<AppOperation>;
|
||||
enable(appScope: AppScope): Promise<AppOperation>;
|
||||
disable(appScope: AppScope, options?: DisableAppOptions): Promise<AppOperation>;
|
||||
update(appScope: AppScope, targetVersion?: string): Promise<AppOperation>;
|
||||
rollback(appScope: AppScope): Promise<AppOperation>;
|
||||
remove(appScope: AppScope, options?: RemoveAppOptions): Promise<AppOperation>;
|
||||
launch(request: LaunchAppRequest): Promise<AppInstanceHandle>;
|
||||
closeInstance(instanceID: AppInstanceID): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
App Registry App 只调用上述接口,不直接管理 Bundle 文件。安装、验证、启用、升级和移除是异步操作,必须有可观察的 `AppOperation`。
|
||||
|
||||
Runtime Shell 始终独立于上层 App,负责应用切换、全局连接状态、通知和安全恢复。其启动目标优先级:
|
||||
|
||||
```text
|
||||
显式目标(深链接 / 通知)
|
||||
→ 可安全恢复的前台实例
|
||||
→ 用户设定的 Default App
|
||||
→ chat Recovery App
|
||||
```
|
||||
|
||||
`chat` 是当前图文 IM 主入口,也是不可移除的 Recovery App。未来用户可将 `voice` 设为默认入口;这只改变主要交互方式,不改变会话、Agent、outbox 或 Artifact 的归属。
|
||||
|
||||
### 4.4 App 实例生命周期
|
||||
|
||||
```text
|
||||
not_running → launching → active → backgrounded → suspended
|
||||
↘ restoring → active
|
||||
active / suspended → closing → closed
|
||||
↘ failed
|
||||
```
|
||||
|
||||
- Chat 与 App Registry 默认是单一全局实例;
|
||||
- 工具型 App 默认按 `conversation_id` 创建实例,可用 `instance_id` 区分同会话的多个实例;
|
||||
- 禁用阻止新调用并收口现有实例;移除在实例关闭且用户确认后清理 Bundle/私有数据;
|
||||
- Surface 被回收时 App 通过 Runtime snapshot 恢复,不能重复发送先前的用户事件。
|
||||
|
||||
### 4.5 Runtime 编排、Interact 与扩展 App
|
||||
|
||||
`Interaction App` 是默认的人与 Agent 交互应用,但它不是整个 App 生态的调度器。应用
|
||||
启动、Tool 路由、前后台焦点、实例生命周期和恢复均由 Runtime 管理;Interact 只负责
|
||||
当前主会话的交互体验,以及把扩展 App 的结果投影回会话。
|
||||
|
||||
```text
|
||||
LineUp Runtime
|
||||
├── App Registry / App Instance Manager
|
||||
├── Tool Router / App Router
|
||||
├── Focus Manager / Lifecycle Manager
|
||||
└── Conversation / Store / Outbox
|
||||
│
|
||||
├── Interaction App(Core App)
|
||||
│ ├── IM mode:文字、图片、短消息
|
||||
│ ├── Audio mode:实时语音
|
||||
│ ├── Video mode:实时视频
|
||||
│ └── 标准交互原语:choice / confirm / input
|
||||
│
|
||||
└── Installed App
|
||||
├── whiteboard
|
||||
├── draw-and-guess
|
||||
└── task-dashboard
|
||||
```
|
||||
|
||||
Runtime 的职责是判断一个 Agent 请求应由哪个应用实例、交互模式或标准组件承接;Interact
|
||||
不直接执行任意 Tool,也不负责切换其他 App 的前台状态。它通过 SDK 接收 Runtime 已经
|
||||
校验过的交互事件,并提交声明性用户 Action。
|
||||
|
||||
应用焦点是 Runtime 状态的一部分:
|
||||
|
||||
```text
|
||||
interaction:audio-001 running / foreground
|
||||
↓ Agent 请求启动 draw-and-guess
|
||||
interaction:audio-001 background 或 suspended
|
||||
draw-and-guess:game-001 starting → running / foreground
|
||||
```
|
||||
|
||||
原来的实例不会因为切换前台就被删除。Runtime 保存焦点栈和实例状态,以便游戏结束、用户
|
||||
退出或新应用失败时恢复原来的 Interaction App 和会话模式。
|
||||
|
||||
一次“启动你画我猜”的完整流程是:
|
||||
|
||||
```text
|
||||
Agent launch(draw-and-guess)
|
||||
→ Runtime 校验 Envelope、Inventory、Manifest、参数和 App 状态
|
||||
→ 创建 draw-and-guess App Instance
|
||||
→ 保存当前 Interaction App/Audio Instance 的焦点位置
|
||||
→ 将旧实例切换为 background / suspended
|
||||
→ 将新实例切换为 foreground
|
||||
→ App 通过 SDK 创建自己的 Surface 并接收用户操作
|
||||
→ 结果经 Runtime 持久化、审计并回传 Agent
|
||||
→ 游戏结束或关闭后恢复焦点栈中的 Interaction App
|
||||
```
|
||||
|
||||
标准选择框、确认框和输入框属于 Interaction App 的内建交互原语,不需要作为独立安装包;
|
||||
画板、游戏和复杂任务面板属于可安装扩展 App,与 Interaction App 是 Runtime 上的平级应用。
|
||||
|
||||
因此,Runtime 负责“能否调用、调用到哪里、哪个实例在前台以及如何恢复”;Interact 负责
|
||||
“如何在主交互体验中呈现和协调结果”;扩展 App 负责“具体功能和自己的交互界面”。
|
||||
|
||||
## 5. 消息、筛选与实时订阅
|
||||
|
||||
### 5.1 统一路由 Envelope
|
||||
|
||||
Runtime 对跨边界、需要传输或审计的消息使用统一路由 Envelope;所有这类消息都必须带作用域。Envelope 统一的是消息承载、作用域与校验字段,不能抹平来源边界:远端 Agent Tool、本地 UI Action 与 Host 生命周期意图是三条独立入口,必须保留不同的 sender、来源校验与审计类型。用户 UI 不能因为复用了同一内部业务动作而被视为或包装成 Agent Tool:
|
||||
|
||||
```ts
|
||||
type RuntimeEnvelope = {
|
||||
v: 1;
|
||||
id: string;
|
||||
type: string;
|
||||
timestamp: string;
|
||||
|
||||
sender: {
|
||||
kind: "agent" | "runtime" | "app" | "user" | "host";
|
||||
id: string;
|
||||
};
|
||||
|
||||
target: {
|
||||
kind: "runtime" | "app";
|
||||
app_scope: AppScope;
|
||||
instance_id?: AppInstanceID;
|
||||
};
|
||||
|
||||
scope: {
|
||||
app_scope: AppScope;
|
||||
conversation_id: ConversationID;
|
||||
instance_id?: AppInstanceID;
|
||||
operation_id?: OperationID;
|
||||
};
|
||||
|
||||
correlation?: {
|
||||
reply_to?: string;
|
||||
call_id?: ToolCallID;
|
||||
inventory_revision?: string;
|
||||
sequence?: number;
|
||||
};
|
||||
|
||||
payload: JsonValue;
|
||||
};
|
||||
```
|
||||
|
||||
全局 Runtime 事件同样保留作用域:
|
||||
|
||||
```text
|
||||
target.app_scope = runtime
|
||||
scope.app_scope = runtime
|
||||
conversation_id = runtime:global
|
||||
```
|
||||
|
||||
### 5.2 现有协议兼容
|
||||
|
||||
当前 Tauri 实现与 M0~M4 golden fixture 使用 `lineup.v1.*` 类型,以及 Surface payload 内的旧 `app.id` 字段。这些是**当前实现兼容事实**,不能在没有版本迁移和 golden fixture 的情况下直接删除。
|
||||
|
||||
Runtime 重构的规则是:
|
||||
|
||||
```text
|
||||
旧 LineUp v1 Envelope
|
||||
→ Runtime Compatibility Adapter
|
||||
→ 补齐/映射 app_scope、conversation_id、instance_id
|
||||
→ RuntimeEnvelope
|
||||
→ App SDK
|
||||
```
|
||||
|
||||
新的 Runtime/App SDK 边界不得再依赖旧 `app.id` 的供应商命名语义。R0 负责冻结新旧字段的映射表、拒绝规则和双向 fixture;在映射完成前,旧协议继续只由 Runtime 适配层处理,App 永远不直接读取它。
|
||||
|
||||
### 5.3 Runtime 筛选链
|
||||
|
||||
```text
|
||||
Transport 原始输入
|
||||
→ 版本、type、app_scope/conversation_id、大小、schema 校验
|
||||
→ Agent 身份与会话关联校验
|
||||
→ id / sequence / cursor 去重与顺序处理
|
||||
→ App 安装、启用、版本、Host 兼容性校验
|
||||
→ Tool / Capability、Inventory、参数、策略、App 启动和前台条件校验
|
||||
→ conversation / instance / operation 所属关系校验
|
||||
→ Manifest 订阅声明与 SDK 订阅条件求交
|
||||
→ Runtime Event / App Inbox / cursor 持久化
|
||||
→ 向目标 App SDK 投递标准化消息
|
||||
```
|
||||
|
||||
Runtime 不广播原始 Agent 消息。相同 conversation 下,Chat 和 Voice 可以收到经 Runtime 投影的 Agent status;白板只收到本实例声明的 patch、Tool 调用和 lifecycle 事件,除非其 Manifest 明确申请并获准其他订阅。
|
||||
|
||||
### 5.4 App SDK 的实时 Agent 消息能力
|
||||
|
||||
SDK 同时提供实时订阅和可靠 Inbox 补读:
|
||||
|
||||
```ts
|
||||
interface AgentMessageAPI {
|
||||
list(request?: {
|
||||
conversation_id?: ConversationID;
|
||||
after?: AgentMessageCursor;
|
||||
limit?: number;
|
||||
}): Promise<AgentMessagePage>;
|
||||
|
||||
subscribe(
|
||||
options: {
|
||||
conversation_id?: ConversationID;
|
||||
types?: readonly AgentMessageType[];
|
||||
include_pending?: boolean;
|
||||
},
|
||||
handler: (message: AgentAppMessage) => Promise<void> | void,
|
||||
): Unsubscribe;
|
||||
|
||||
acknowledge(message_id: string): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
投递语义:
|
||||
|
||||
1. Runtime 先校验、标准化和持久化,后调用订阅者;
|
||||
2. 每条消息有唯一 `message_id`,App 必须幂等处理;
|
||||
3. 展示类消息可自动 ACK;Tool 调用、状态 patch 和需业务处理的消息要求 App 显式 ACK;
|
||||
4. App 未运行、暂停或崩溃时,消息进入该 App 的 Inbox;恢复后 `list()` 与 `subscribe()` 补齐;
|
||||
5. App 禁用、移除或不兼容时,Runtime 对关键 Agent 调用返回确定拒绝,不能静默丢弃。
|
||||
|
||||
实际投递集是:
|
||||
|
||||
```text
|
||||
Manifest agent_subscriptions
|
||||
∩ SDK subscribe filter
|
||||
∩ App 当前权限
|
||||
∩ conversation / instance scope
|
||||
∩ Agent target app_scope
|
||||
∩ Runtime Policy
|
||||
```
|
||||
|
||||
## 6. Runtime SDK v1
|
||||
|
||||
SDK 是上层 App 使用 Runtime 的唯一标准入口。Core App 拿到完整 App SDK;Installed App 只得到同一语义的受限 Surface Bridge SDK。
|
||||
|
||||
```ts
|
||||
interface LineUpAppRuntimeSDK {
|
||||
readonly app: AppContext;
|
||||
readonly lifecycle: AppLifecycleAPI;
|
||||
readonly state: AppStateAPI;
|
||||
readonly agentMessages: AgentMessageAPI;
|
||||
readonly actions: AppActionAPI;
|
||||
readonly tools: AppToolAPI;
|
||||
readonly apps: AppNavigationAPI;
|
||||
readonly artifacts: ArtifactAPI;
|
||||
readonly sessionData: MiniAppSessionDataAPI;
|
||||
readonly capabilities: CapabilityRequestAPI;
|
||||
readonly diagnostics: DiagnosticsAPI;
|
||||
}
|
||||
```
|
||||
|
||||
### 6.1 Context、状态和生命周期
|
||||
|
||||
```ts
|
||||
interface AppContext {
|
||||
app_scope: AppScope;
|
||||
app_version: string;
|
||||
instance_id: AppInstanceID;
|
||||
kind: "core" | "installed";
|
||||
entrypoint: string;
|
||||
scope: {
|
||||
conversation_id?: ConversationID;
|
||||
operation_id?: OperationID;
|
||||
parent_instance_id?: AppInstanceID;
|
||||
};
|
||||
granted_permissions: readonly AppPermission[];
|
||||
}
|
||||
|
||||
interface AppStateAPI<ViewModel = unknown> {
|
||||
snapshot(): ViewModel;
|
||||
subscribe(listener: (view: ViewModel, change: AppStateChange) => void): Unsubscribe;
|
||||
}
|
||||
```
|
||||
|
||||
App 只读取按其 Scope 裁剪的 View Model,不读取全量 Runtime State。生命周期包含 `start`、`foreground`、`background`、`suspend`、`restore` 与 `closing`;Runtime 保留禁用、移除和强制收口的最终权力。
|
||||
|
||||
### 6.2 Action、Tool 与跨 App 导航
|
||||
|
||||
```ts
|
||||
interface AppActionAPI {
|
||||
dispatch(action: AppAction): Promise<CommandReceipt>;
|
||||
}
|
||||
|
||||
interface AppToolAPI {
|
||||
onInvoke(listener: (call: AppToolInvocation) => Promise<AppToolOutcome>): Unsubscribe;
|
||||
progress(request: { call_id: ToolCallID; progress: ToolProgress }): Promise<void>;
|
||||
complete(request: { call_id: ToolCallID; result: JsonValue }): Promise<void>;
|
||||
fail(request: { call_id: ToolCallID; code: AppToolErrorCode; message?: string }): Promise<void>;
|
||||
}
|
||||
|
||||
interface AppNavigationAPI {
|
||||
listAvailable(): readonly AppSummary[];
|
||||
launch(request: LaunchAppRequest): Promise<AppInstanceHandle>;
|
||||
focus(instanceID: AppInstanceID): Promise<void>;
|
||||
close(instanceID: AppInstanceID): Promise<void>;
|
||||
openDefaultApp(): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
App 只能提交声明性 Action,不能构造原始 Agent Envelope。跨 App 打开、聚焦和关闭也必须经 Runtime 校验目标 App 的启用状态、入口、展示模式和会话作用域。
|
||||
|
||||
### 6.3 MiniApp session data、Artifact 与 Capability
|
||||
|
||||
Runtime 为当前 MiniApp App 子会话提供一份受限的通用 JSON 数据字典。MiniApp 定义其中的业务语义、嵌套
|
||||
结构、`current` / `history`、是否保存历史以及业务上是否允许修改;Runtime 不需要、也不得根据每个 App 的
|
||||
字段定义专属接口或业务 schema。Runtime 是隔离、并发裁决、持久化、恢复和技术性清理的唯一所有者。它不是
|
||||
供 App 跨 scope 查询或读写的数据库,也不等同于 Conversation Store、Tool/operation Store、outbox 或 Artifact Store。
|
||||
|
||||
```ts
|
||||
interface MiniAppSessionDataAPI<Data extends JsonObject = JsonObject> {
|
||||
get(): Promise<{ data: Data | null; revision: number }>;
|
||||
replace(request: { data: Data; expected_revision: number }): Promise<{ revision: number }>;
|
||||
subscribe(listener: (snapshot: { data: Data | null; revision: number }) => void): Unsubscribe;
|
||||
}
|
||||
```
|
||||
|
||||
`sessionData` 的同步语义是通用 SDK 契约:空数据固定为 `{ data: null, revision: 0 }`;订阅在 Runtime 的当前
|
||||
`app_scope + app_session_id` 串行顺序中注册,首次回调一定是注册时的完整快照,随后回调的 revision 严格递增。因而
|
||||
`get()` 与 `subscribe()` 之间的成功写入不会丢失:它要么已成为订阅首帧,要么作为紧随其后的更新到达。重复 / 旧
|
||||
revision 由 SDK 忽略;revision 跳跃或 Bridge 重连时,SDK 重新 `get()` 并建立新订阅。`expected_revision` 冲突只
|
||||
拒绝旧写入,不自动合并 MiniApp 业务数据;多入口协作写入是后续专项 Runtime 设计,不能由本通用 JSON 接口猜测。
|
||||
|
||||
每次调用的存储边界由不可伪造的 SDK context 推导:
|
||||
|
||||
```text
|
||||
app_scope + app_session_id
|
||||
```
|
||||
|
||||
App 不得指定、读取或写入其他 App、其他 App 子会话、Conversation Store 或 Runtime 内部记录。Runtime 只校验
|
||||
JSON 合法性、通用大小 / 解析深度配额与 `expected_revision`;旧 Surface、重复点击或并发写入不能覆盖较新的数据。
|
||||
刷新、Surface 重载和 Runtime 重启后,Runtime 向同一个 App 子会话恢复最后一个有效数据版本。App 禁用或移除时,
|
||||
Runtime 按用户确认和全局保留策略清理其数据;业务数据是否“历史只读”由 MiniApp 自己定义,不作为 Runtime 的
|
||||
统一业务语义。
|
||||
|
||||
MiniApp session data 不替代 Runtime operation。凡是会影响 Agent Tool 终态、deadline、outbox、焦点、权限或
|
||||
实例生命周期的动作,仍必须通过 Runtime Command / operation 原子裁决。例如 Pomodoro 可以将显示状态保存在
|
||||
自己的 session data,但 `ends_at` 到期、用户中断与唯一 Tool result 必须由 Runtime 的 deadline operation 负责,
|
||||
不能由 MiniApp 自行完成或直接发送结果。完成后写入 IM 的记录是独立、不可变的静态副本,不会随 MiniApp 后续
|
||||
修改 session data 而改变。
|
||||
|
||||
```text
|
||||
Artifact
|
||||
Runtime 管理元数据、受限读取、创建、会话引用、下载和用户保存。
|
||||
|
||||
Session Data
|
||||
Runtime 为每个 app_scope + app_session_id 提供隔离的通用 JSON 数据;管理 revision、配额、恢复与
|
||||
生命周期清理,不理解 MiniApp 的业务 schema。UI 动画、滚动位置等短暂渲染状态不必持久化。
|
||||
|
||||
Capability
|
||||
App 以 purpose + input 请求;Runtime 决定是否确认、调用 Host、审计并返回受控结果。
|
||||
```
|
||||
|
||||
Capability SDK 形状:
|
||||
|
||||
```ts
|
||||
interface CapabilityRequestAPI {
|
||||
request<T extends JsonValue>(request: {
|
||||
capability: CapabilityName;
|
||||
purpose: string;
|
||||
input: JsonValue;
|
||||
scope?: AppScope;
|
||||
}): Promise<CapabilityResult<T>>;
|
||||
}
|
||||
```
|
||||
|
||||
Runtime 必须检查:Manifest 声明、App 启用状态、Host 支持、前台要求、用户确认、系统权限、策略、速率限制和审计。结果只能是 `completed`、`cancelled_by_user`、`permission_denied`、`unsupported_on_host`、`policy_denied`、`expired` 或受控 `failed`,不暴露路径、token 或底层原生异常。
|
||||
|
||||
## 7. Tool、Inventory 与 Agent 调用
|
||||
|
||||
### 7.1 App Tool 声明
|
||||
|
||||
每个 App 可以声明 Agent 可调用 Tool。当前的完整定位是:
|
||||
|
||||
```text
|
||||
app_scope / method / contract_version
|
||||
|
||||
whiteboard / board.create / 1
|
||||
voice / voice.request_recording / 1
|
||||
draw-and-guess / game.start_round / 1
|
||||
```
|
||||
|
||||
由 SDK Tool 声明生成的 Manifest Tool 描述至少包括:标题、面向 Agent 的说明、输入/输出 JSON Schema、调用模型、超时、幂等性、前台要求、风险等级、App 启动策略和可能使用的 Capability;它是安装和运行时使用的产物,不是开发者再维护的一份 Tool 源文件。
|
||||
|
||||
调用模型固定为:
|
||||
|
||||
| 模型 | 用途 | 示例 |
|
||||
|---|---|---|
|
||||
| `query` | 只读、快速 | 查询画板摘要。 |
|
||||
| `command` | 确定性状态改变 | 创建白板、开始一局游戏。 |
|
||||
| `interactive` | 必须等待用户参与 | 录音、填写复杂表单。 |
|
||||
| `operation` | 长时间执行并有进度 | 导出画板、处理大文件。 |
|
||||
|
||||
### 7.2 动态 Runtime Inventory
|
||||
|
||||
Runtime 向每个活跃 Agent 会话发布 revisioned Inventory。它包含当前可用的标准组件、已启用 App、Surface、Tool 和 Capability;它不是安装命令,也不携带 Bundle 源码、token、文件路径或用户私有内容。
|
||||
|
||||
```text
|
||||
Agent 可见 Tool
|
||||
= 已验证 Manifest Tool
|
||||
∩ App enabled
|
||||
∩ Host supported
|
||||
∩ 用户 / 组织策略允许
|
||||
∩ Agent + conversation scope 允许
|
||||
∩ 所需前置条件满足
|
||||
```
|
||||
|
||||
App 启用、禁用、更新、撤销、Host 能力变化或策略收紧后,Runtime 增加 revision 并重新发布。Agent 的 Tool 调用必须携带 `inventory_revision`;旧 revision 的调用必须安全拒绝并提示 Agent 刷新清单。
|
||||
|
||||
### 7.3 Tool 调用闭环
|
||||
|
||||
```text
|
||||
Agent tool.invoke
|
||||
→ Runtime 校验 Envelope / Inventory / Tool / 参数 / Scope
|
||||
→ Tool Router 判断直接执行、启动 App、切换前台或等待用户交互
|
||||
→ App Instance / Focus Manager 创建或切换实例
|
||||
→ Interaction App、交互模式或扩展 App 承接请求
|
||||
→ Runtime / App SDK 发送 protocol receipt、progress / result / error
|
||||
→ Runtime 校验输出、持久化、更新焦点并回传 Agent
|
||||
```
|
||||
|
||||
长期 Tool 的协议状态与业务结果必须分开:`accepted` 表示 Runtime 已接收请求,`progress` 表示业务正在推进或刚刚
|
||||
真正开始;它们是按 `call_id` 持久化、可重放的协议事实,不受 Tool output schema 约束。只有最终 `result`(或
|
||||
受控 `error`)才必须通过 Tool 的 output schema,且同一 call_id 只能有一个最终业务结果。对于 Pomodoro,只有
|
||||
Host 已确认前台且 Runtime 创建 operation 后的 `started` progress,才允许 Agent 向用户说“已经开始计时”。
|
||||
|
||||
MiniApp 启动也使用同一条 protocol receipt / progress 通道,而不让 MiniApp 直接联系 Agent:Runtime 持久化
|
||||
`starting → ready | failed | cancelled` 后,向触发启动的 call_id 发送 `accepted / starting`、`activation_ready`、
|
||||
`activation_failed` 或 `activation_cancelled`。Agent 收到 ready 后再发送依赖调用是推荐方式,但 Runtime 必须持久化并
|
||||
等待提前到达的合法 `app_ready` / `foreground_required` 调用,ready 后按到达顺序执行;失败 / 取消时受控拒绝等待调用。
|
||||
`activation_not_required` 调用不等待,Pomodoro 的 `interrupt` 因此可以取消启动中的专注。Pomodoro 的 `started` 已同时
|
||||
表示 activation ready、已在前台且计时真实开始,不额外发送重复的 `activation_ready`。
|
||||
|
||||
至少支持以下确定错误码:
|
||||
|
||||
```text
|
||||
cancelled_by_user permission_denied app_disabled
|
||||
app_not_installed app_version_mismatch tool_not_visible
|
||||
invalid_arguments foreground_required operation_expired
|
||||
handler_failed
|
||||
```
|
||||
|
||||
Agent Tool 是 App 在 SDK 源码中声明、构建生成 Manifest Tool 描述后由 Runtime 发布给远端 Agent 的业务调用契约;它不是用户 UI 的公共操作入口。Capability 是 Runtime / Host 的系统能力。Agent Tool 可在 Runtime 裁决后请求 Capability,但不会因声明 Tool 自动获得麦克风、文件或剪贴板权限;本地 UI Action 也必须走自己的 SDK / Bridge 校验路径。
|
||||
|
||||
## 8. Surface 与安全边界
|
||||
|
||||
### 8.1 标准组件优先
|
||||
|
||||
文字、Markdown、图片、链接、状态、任务进度、choice、confirm、input、Artifact 基础预览优先采用 Runtime / Core App 的可信内建渲染器。它们必须可访问、可测试、可离线恢复,并保持 Markdown sanitizer、外链保护和严格 schema。
|
||||
|
||||
Surface 仅用于画板、地图、图表、复杂表单、游戏、专业编辑器等内建组件不足以表达的复杂交互。
|
||||
|
||||
### 8.2 Installed App Surface
|
||||
|
||||
下载型 App 运行在隔离容器中:
|
||||
|
||||
```text
|
||||
iframe sandbox="allow-scripts"
|
||||
- 不带 allow-same-origin
|
||||
- 不带 allow-popups / allow-top-navigation / allow-forms
|
||||
- 默认 CSP 禁止网络和外部脚本
|
||||
- 只经严格 postMessage / Surface Bridge SDK 通信
|
||||
```
|
||||
|
||||
Runtime/Host 必须验证 `event.source`、当前 `instance_id`、消息类型、事件名、JSON schema、消息大小和声明的投递策略。Surface 只能接收 state patch、触发已声明事件、处理已声明 Tool 调用、通过 Bridge 使用 Runtime 提供的当前 App 子会话 session data 和请求受控 Capability。
|
||||
|
||||
Surface 不能:
|
||||
|
||||
```text
|
||||
读取父页面 DOM、localStorage 或 token
|
||||
调用 Tauri invoke
|
||||
访问其他 App 的数据和实例
|
||||
直接访问 Agent / AppServer
|
||||
任意联网、导航、弹窗或加载远端脚本
|
||||
直接执行 app.call / 系统能力
|
||||
```
|
||||
|
||||
用户在 Surface 中的操作只是 App Event;需要系统副作用时,Runtime 仍按 Capability 规则确认、执行和审计。
|
||||
|
||||
### 8.3 Bundle 与发布
|
||||
|
||||
当前已验证的生产安全原则保持不变:Bundle 必须是不可变 artifact,执行前检查来源、大小、SHA-256、签名、Host 兼容性和回滚条件;验证失败不得进入 active cache。开发期 inline bundle 只能是受限开发 fixture,不是长期市场供应方式。
|
||||
|
||||
本阶段仅定义本地 `app_scope`,不定义开放市场发布者身份。Bundle 信任、App 管理和 Tool 可见性仍必须由 Runtime 控制;将来引入供应商身份时须以新 Manifest 版本扩展,不能放宽本节隔离规则。
|
||||
|
||||
## 9. App Manifest 最小契约
|
||||
|
||||
Manifest 的 tools 区块由 MiniApp SDK 的 `defineAgentTools(...)` 在构建时生成。开发者在代码中声明 Tool、schema、
|
||||
activation requirement 和本地 handler,不维护第二份手写描述;生成的 Manifest 只保存不可执行的公开契约,Bundle
|
||||
内部 SDK 自己保存 `method → handler` 映射。
|
||||
|
||||
```json
|
||||
{
|
||||
"format": "lineup.app.v1",
|
||||
"app_scope": "whiteboard",
|
||||
"version": "1.0.0",
|
||||
"runtime_sdk": {
|
||||
"api_version": "1",
|
||||
"required_features": [
|
||||
"app.lifecycle.v1",
|
||||
"app.tools.v1",
|
||||
"surface.state.v1",
|
||||
"app.session-data.v1"
|
||||
],
|
||||
"optional_features": ["artifact.create.v1"]
|
||||
},
|
||||
"entrypoints": [
|
||||
{"id": "canvas", "kind": "contextual", "default_eligible": false}
|
||||
],
|
||||
"agent_subscriptions": [
|
||||
{"type": "lineup.app.state.patch", "scope": "instance"},
|
||||
{"type": "lineup.tool.invoke", "scope": "conversation"}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"method": "board.create",
|
||||
"contract_version": "1",
|
||||
"handling": "direct",
|
||||
"delivery": "miniapp_sdk",
|
||||
"activation_requirement": "app_ready",
|
||||
"input_schema": {"type": "object", "additionalProperties": false},
|
||||
"output_schema": {"type": "object", "additionalProperties": false}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Runtime 在安装、启用和启动时验证 SDK API 版本与 feature 集;App 不兼容、未启用或当前 Host 不支持时,不得进入 Inventory、创建实例或接收 Tool 调用。
|
||||
|
||||
## 10. Tauri 参考实现的最终模块边界
|
||||
|
||||
```text
|
||||
lineup-app/
|
||||
├── runtime-core/
|
||||
│ ├── communication/ AppServer transport、sync、outbox
|
||||
│ ├── coordination/ event、state、router、policy、audit
|
||||
│ ├── apps/ app registry、instance manager、default app
|
||||
│ ├── tools/ tool registry、inventory、call lifecycle
|
||||
│ ├── capabilities/ capability gateway
|
||||
│ └── persistence/ runtime store、app inbox、instance state snapshot、artifact metadata
|
||||
├── runtime-sdk/
|
||||
│ ├── app.ts Core App SDK
|
||||
│ ├── manifest.ts App/Tool/Subscription 类型
|
||||
│ ├── protocol.ts RuntimeEnvelope / schema
|
||||
│ ├── errors.ts 稳定错误码
|
||||
│ └── testkit/
|
||||
├── surface-sdk/
|
||||
│ ├── bridge.ts sandbox message bridge
|
||||
│ └── surface.ts Installed App 最小 SDK
|
||||
└── tauri/
|
||||
├── src/
|
||||
│ ├── bootstrap/ 应用启动装配层
|
||||
│ ├── runtime-host/ Web/Tauri Host Provider adapter
|
||||
│ ├── shell/ switcher、default、recovery
|
||||
│ └── core-apps/ chat、app-registry、settings
|
||||
└── src-tauri/ Rust 文件、音频、通知、窗口 Provider
|
||||
```
|
||||
|
||||
`lineup-app/tauri/src/main.ts` 是当前 Tauri/Web Host 的启动装配入口:它创建 Host 侧适配、
|
||||
`LineUpRuntime` 与默认 Runtime App Host,并把它们接起来。它不创建或持有 Transport、
|
||||
Store、sync loop、outbox,也不解析原始 Agent payload。现阶段仍有可信 Renderer、Surface
|
||||
与 Capability 的 Host 集成代码;后续可以继续迁移这些 UI 适配,但不得把 Runtime 所有权
|
||||
重新放回 `main.ts`。
|
||||
|
||||
可直接迁移的现有基础:
|
||||
|
||||
| 当前模块 | 最终归属 |
|
||||
|---|---|
|
||||
| `transport-adapter.ts` | `runtime-core/communication/` |
|
||||
| `conversation-store.ts` | `runtime-core/persistence/` |
|
||||
| `interaction-kernel.ts` | `runtime-core/coordination/` 的协议入口/兼容适配基础 |
|
||||
| Tool/Task/Execution 状态机 | Chat App 的投影 + Runtime call state |
|
||||
| `surface-registry.ts` | `runtime-core/apps/` 的 App Registry 基础 |
|
||||
| `surface-instance-manager.ts` | `runtime-core/apps/` 的 Instance Manager 基础 |
|
||||
| Bundle manifest/cache/policy | `runtime-core/apps/` 与 Execution Plane |
|
||||
| `client-inventory.ts` | `runtime-core/tools/` 的动态 Inventory Publisher |
|
||||
| `capability-*` | `runtime-core/capabilities/` |
|
||||
| `trusted-dom-renderers.ts` | `tauri/src/core-apps/chat/` |
|
||||
|
||||
## 11. 实施顺序
|
||||
|
||||
### F0:冻结契约与兼容映射
|
||||
|
||||
- 定义 `RuntimeEnvelope`、`app_scope`、`conversation_id`、`instance_id`、`operation_id`、`call_id` 的类型和校验;
|
||||
- 定义旧 `lineup.v1.*` 到 RuntimeEnvelope 的兼容映射;
|
||||
- 定义 Runtime SDK v1、Surface Bridge SDK v1、App Manifest、Tool Descriptor 和错误码;
|
||||
- 为全部契约建立 golden fixture;不改变当前可验证的 M0~M4 行为。
|
||||
|
||||
### F1:Runtime Core 与 Interaction Core App(当前 `chat` 实现)
|
||||
|
||||
- 从 `main.ts` 提取单一 `LineUpRuntime`;
|
||||
- Runtime 独占 Transport、Store、outbox、原始消息校验和筛选;
|
||||
- 将当前图文 IM 迁为 Interaction Core App 的 `chat` 实现;
|
||||
- 当前 `chat` 用 `state.subscribe()` 和 `agentMessages.subscribe()` 获取已验证投影,不再依赖 Transport;
|
||||
- 保持登录、同步、本地回显、Markdown、Tool Call、Task、Artifact 的回归行为。
|
||||
|
||||
#### MVP-R1 实施状态(2026-08-04)
|
||||
|
||||
F0 与 F1 中支撑图文聊天迁移的最小闭环已落地并经自动化回归验证。此处的“完成”仅指下列 MVP 边界;Manifest、Installed App、完整 Tool Inventory 和应用市场仍属于后续阶段。
|
||||
|
||||
| MVP 项 | 当前实现 | 验证位置 |
|
||||
|---|---|---|
|
||||
| Runtime 单一所有权 | `LineUpRuntime` 唯一创建并持有 `TransportAdapter`、`ConversationStore`、`InteractionKernel`、同步循环与 outbox。 | `tauri/src/runtime/coordination/lineup-runtime.ts`、`lineup-runtime.test.ts` |
|
||||
| Core App 入口 | `CoreAppRegistry` 记录当前可作为默认入口的 Core App;`CoreAppHostRegistry` 再根据 `app_scope` 找到对应的可信装配器。当前默认实现仍是 `chat`,但 `main.ts` 不再直接依赖 Chat 内部 Renderer 和 Shell。 | `runtime/app-management/app-registry.ts`、`runtime-app-host.ts`、`core-apps/chat/chat-app-host.ts`、`runtime-app-host.test.ts` |
|
||||
| Chat SDK 边界 | Chat 仅以 `ChatRuntimeSDK` 订阅状态/Agent 消息、读取 Inbox、ACK,并以 Runtime Action 发送文本或交互动作。 | `runtime/app-management/app-sdk.ts`、`main.ts` |
|
||||
| 作用域与旧协议兼容 | 入站消息先经 `resolveIncomingScope`;旧 `lineup.v1` 自动映射为当前会话的 `chat` 作用域,显式非法或不匹配的 scope 在投递前拒绝。 | `runtime/coordination/runtime-envelope.ts`、`lineup-runtime.test.ts` |
|
||||
| 恢复与幂等 | `ConversationStore` 持久化 App Inbox;未 ACK 消息在 Runtime 重建后恢复,同一 `message_id` 不重复投递。 | `runtime/persistence/conversation-store.ts`、`conversation-store.test.ts`、`lineup-runtime.test.ts` |
|
||||
|
||||
本阶段不修改 AppServer 或 Hermes 的既有 `lineup.v1` 协议。兼容层只存在于 Runtime 内部,因此上层 Chat App 不读取旧协议字段,也不需要同步升级远端。
|
||||
|
||||
### F2:App Registry 与默认 App
|
||||
|
||||
- 将开发期 Surface Registry 迁为 Runtime App Registry;
|
||||
- 实现 App Instance Manager、App Focus Manager 和生命周期状态(foreground/background/suspended);
|
||||
- 实现 Runtime Tool Router / App Orchestrator:校验 Tool 后决定直接执行、启动 App、切换前台或等待用户交互;
|
||||
- 实现 App 启用、禁用、移除、版本记录、实例收口和默认入口选择;
|
||||
- 实现 `app-registry` Core App;
|
||||
- 将当前 `chat` 作为 Interaction App 的 IM 实现,为后续 Audio/Video 模式预留 entrypoint 和恢复策略。
|
||||
|
||||
### F3:Tool Registry、Inventory 与 SDK Inbox
|
||||
|
||||
- 解析 Manifest Tool/Subscription 并计算可见性;
|
||||
- 在 Runtime 连接与状态变化时同步 revisioned Inventory 给 Agent;Tool 可声明是否需要启动 App、是否要求前台以及结束后是否恢复原焦点;
|
||||
- 实现 Agent Tool → Runtime → App SDK → result/progress 的可靠闭环;
|
||||
- 将 MVP-R1 已实现的 Chat App Inbox、ACK、断线/崩溃恢复和拒绝码推广为所有受管理 App 的通用可靠投递机制。
|
||||
- 实现 `app.session-data.v1`:按 `app_scope + app_session_id` 隔离的通用 JSON 数据、revision 并发控制、
|
||||
通用配额、重启恢复与 lifecycle 清理;Runtime 不校验 MiniApp 业务 schema,且它不得替代 Tool/operation/outbox
|
||||
的终态裁决。
|
||||
|
||||
### F4:第一个 Installed App
|
||||
|
||||
- 选择画板或任务面板作为第一个完整 Installed App;
|
||||
- 验收 install → enable → Inventory → Tool 调用 → App 启动/前台切换 → Surface 事件 → Agent result → 原前台恢复 → disable/remove;
|
||||
- 语音/视频属于 Interaction App 的模式或受 Runtime 管理的扩展 App,必须复用 Runtime SDK、Tool、Artifact 和 Capability 通道,不建立独立 Agent 通信链路。
|
||||
|
||||
## 12. 完成准入条件
|
||||
|
||||
```text
|
||||
消息与隔离
|
||||
[ ] 缺少/非法 app_scope 或 conversation_id 的可路由消息被拒绝。
|
||||
[ ] App 不能收到其他 App 或其他 conversation 的 Agent 原始消息。
|
||||
[ ] App 未运行时关键消息可从 Inbox 有界恢复,重复投递不重复执行。
|
||||
|
||||
应用管理
|
||||
[ ] 启用/禁用/移除改变 Tool 可见性,并更新 Agent Inventory revision。
|
||||
[ ] 默认 App 失效时必定回退至 Interaction App 的 IM/`chat` Recovery 入口。
|
||||
[ ] 禁用/移除能收口活动实例、焦点栈、调用和私有数据清理流程。
|
||||
[ ] Agent 启动扩展 App 时,Runtime 能将当前前台实例切换到后台,并在扩展结束后恢复原焦点。
|
||||
|
||||
工具与能力
|
||||
[ ] Agent 只能调用当前 Inventory 中、参数 schema 合法的 Tool。
|
||||
[ ] Tool 的 result/progress/error 经 Runtime 校验、持久化和审计。
|
||||
[ ] MiniApp 只能读取和替换自身 App 子会话的合法 JSON session data;旧 revision、跨 App / 子会话、超配额
|
||||
和失效 SDK context 均被 Runtime 拒绝,刷新/重启后仅恢复最后一个有效版本。
|
||||
[ ] MiniApp session data 不能直接完成 Tool、写 outbox、改变焦点或绕过实例生命周期。
|
||||
[ ] App / Surface 无法绕过 Capability Gateway 获得系统权限。
|
||||
|
||||
安全
|
||||
[ ] Installed App 无法访问 Tauri invoke、Host DOM、token、任意网络或其他 App 数据。
|
||||
[ ] Bundle 只有在完整性和签名验证后才可运行;失败可回滚且不污染 active cache。
|
||||
```
|
||||
|
||||
## 13. 旧文档的后续定位
|
||||
|
||||
| 文档 | 保留价值 | 在本方案后的定位 |
|
||||
|---|---|---|
|
||||
| [App 层架构方案](lineup-app-layer-architecture.md) | M0~M4 已实现边界、测试和验收事实;Markdown、Surface、Capability 原则 | 迁移基础与历史实施记录。 |
|
||||
| [Runtime 与 App SDK 架构方案](lineup-runtime-sdk-architecture.md) | Runtime、App Manager、Tool Registry、SDK、消息筛选的完整初稿 | 被本文件收敛后的详细来源;以本文件的 `app_scope` 和实施顺序为准。 |
|
||||
| [UI Surface 与 App Capability 协议](lineup-ui-surface-protocol.md) | Surface sandbox、bridge、Capability 风险分级、inventory 和 Bundle 安全规则 | 协议细节来源;R0 需完成其旧字段到 RuntimeEnvelope 的版本化映射。 |
|
||||
|
||||
以后新增 App 设计、SDK 方法、Agent Tool、Surface 或 Capability 时,先修改本文件的边界/契约,再实施代码和细节协议,避免重新把功能堆回聊天页面或创建绕开 Runtime 的平行通道。
|
||||
@@ -0,0 +1,132 @@
|
||||
# LineUp App 层架构与迁移记录
|
||||
|
||||
**版本:** 2.0(Runtime 基线)
|
||||
**状态:** 当前实现边界与历史迁移记录
|
||||
**日期:** 2026-08-04
|
||||
**最终决策入口:** [LineUp App 最终设计方案](app_final_design.md)
|
||||
|
||||
## 1. 当前结论
|
||||
|
||||
LineUp 是运行在用户设备上的协作 Runtime,不是某个平台上的独立聊天客户端。可以把
|
||||
Runtime 理解为本地协调中心:它负责连接、消息、存储和恢复;`chat` 是它首先加载的可信
|
||||
内置功能。未来的语音、画板、游戏和应用中心也必须使用同一套 Runtime / SDK 边界,不能
|
||||
各自再建立一条到 Agent 的通信链路。
|
||||
|
||||
当前唯一实现与验收基线:
|
||||
|
||||
```text
|
||||
Tauri 2 Desktop Host + Web Reference Host
|
||||
```
|
||||
|
||||
这两个 Host 是同一套 TypeScript Runtime 的两种外壳:Tauri Desktop Host 用于正式桌面
|
||||
交付及受控系统能力;Web Reference Host 用于浏览器开发、Tailscale 联调和自动化测试。
|
||||
它们不是两套产品,Web 版也不能替代桌面版的系统能力。独立 Android/Kotlin 客户端与
|
||||
Wails Host 均不在当前或后续规划范围内;早期实验细节仅保留在 Git 历史,不构成架构或
|
||||
验收依据。
|
||||
|
||||
## 2. App 层边界
|
||||
|
||||
```text
|
||||
Remote Agent / AppServer
|
||||
│ lineup.v1 wire protocol
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ LineUpRuntime │
|
||||
│ Transport · sync loop · Store · Outbox · App Inbox │
|
||||
│ instance state · scope 路由 · Tool · Capability │
|
||||
└─────────────────────┬──────────────────────────────────┘
|
||||
│ Runtime SDK
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ Core / Installed App │
|
||||
│ chat · app-registry · voice · whiteboard │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
| 层 | 必须负责 | 不得负责 |
|
||||
|---|---|---|
|
||||
| `LineUpRuntime` | 处理 AppServer/Agent 通信、会话、原始协议兼容、scope 筛选、Store、sync、outbox、App Inbox、Tool 路由、App 实例和前台焦点、权限、按 App 子会话隔离的 MiniApp 通用 session data 与恢复。 | 具体业务页面 DOM 或任意 MiniApp 的业务语义。 |
|
||||
| Interaction Core App | 用户实际使用的主交互界面:当前是 Chat/IM,未来包含 Audio/Video 模式、标准交互原语和扩展结果投影。 | 直连 AppServer、维护 cursor、解析 Agent 原始包、调度其他 App 或直接写 Runtime Store。 |
|
||||
| Runtime SDK | App 与 Runtime 之间唯一的受限接口:提供 snapshot、订阅、Agent 消息、Inbox ACK、Runtime Action、App 导航、生命周期请求及当前 App 子会话的通用、revision 控制 session data。 | 把 Transport、token、Host 特权 API、Conversation Store 或其他 App/子会话数据暴露给 App。 |
|
||||
| Tauri/Web Host | 把 Runtime 放进桌面窗口或浏览器,并提供对应的 DOM/系统能力。 | 解释协议、决定 App 调度、绕过 Runtime 路由和策略。 |
|
||||
| Surface | 在隔离执行域呈现已验证 Bundle,并经受限 bridge 上报事件。 | 访问 Host DOM、登录态、Tauri API、任意网络。 |
|
||||
|
||||
## 3. MVP-R1:Runtime 托管 Chat
|
||||
|
||||
当前 MVP 已完成并以 Tauri/Web 自动化回归验证以下八项条件:
|
||||
|
||||
| 条件 | 当前事实 |
|
||||
|---|---|
|
||||
| MVP-01 | `LineUpRuntime` 唯一创建并持有 Transport、`ConversationStore`、sync loop 与 outbox。 |
|
||||
| MVP-02 | Runtime 从本地 `CoreAppRegistry` 解析默认 `chat`,`RuntimeAppHost` 负责挂载其 Shell;`main.ts` 不创建聊天页面。 |
|
||||
| MVP-03 | Chat 只经 `ChatRuntimeSDK` 订阅 Agent 消息、状态和 Chat-safe Runtime 事件,并以 Action 发送文本、交互、结果与取消。 |
|
||||
| MVP-04 | Runtime 向 Chat 投递前验证 `app_scope = chat` 与当前 `conversation_id`;非法/不匹配消息被拒绝。 |
|
||||
| MVP-05 | Compatibility Adapter 在 Runtime 内将旧 `lineup.v1` 映射为内部 `RuntimeEnvelope`;远端无须同步重写。 |
|
||||
| MVP-06 | 登录、同步、发送、本地回显、Markdown、Agent 状态、Tool/Task 与刷新恢复保持回归。 |
|
||||
| MVP-07 | Store 持久化每个 App 的 Inbox;运行时重建后未 ACK 消息可恢复,`message_id` 去重。 |
|
||||
| MVP-08 | `npm test -- --run` 通过 21 个文件 / 94 个测试;`npm run build` 通过。 |
|
||||
|
||||
源代码目录和依赖方向见 [Tauri/Web 源码导航](../../tauri/src/README.md)。
|
||||
|
||||
## 4. 当前源码归属
|
||||
|
||||
```text
|
||||
lineup-app/tauri/src/
|
||||
├── main.ts # 启动装配入口:连接 Tauri/Web Host、Runtime 与默认 App
|
||||
├── core-apps/chat/ # 当前 Interaction App 的 IM 实现、Renderer、样式
|
||||
└── runtime/
|
||||
├── app-management/ # Registry、App Instance、焦点、生命周期、Host
|
||||
├── communication/ # Transport Adapter
|
||||
├── coordination/ # Runtime、Kernel、Envelope、Tool/Task/交互编排
|
||||
├── persistence/ # Conversation Store、Outbox、App Inbox、instance state snapshot
|
||||
├── protocol/ # lineup.v1 解码与 golden fixture
|
||||
├── surfaces/ # Surface 生命周期、Bundle、隔离 Host
|
||||
├── capabilities/ # Registry、执行、审计
|
||||
├── artifacts/ # Artifact 元数据与缓存
|
||||
└── inventory/ # Agent 可见 Inventory
|
||||
```
|
||||
|
||||
依赖必须保持单向:
|
||||
|
||||
```text
|
||||
core-apps/interaction(当前 chat) → runtime/app-management SDK → runtime/coordination
|
||||
└→ communication / persistence / protocol
|
||||
Host adapters → runtime/coordination
|
||||
Surface → restricted bridge → Runtime Action / Capability Gateway
|
||||
```
|
||||
|
||||
## 5. 历史迁移的保留价值
|
||||
|
||||
早期 M0~M4 工作建立了当前 Runtime 的可复用安全基础。它们是迁移记录,不是新的功能
|
||||
排期或平行客户端路线:
|
||||
|
||||
| 历史阶段 | 保留成果 | 当前归属 |
|
||||
|---|---|---|
|
||||
| M0 | HTTP Transport、Conversation Store、协议解码、Markdown、基础回归 | `communication`、`persistence`、`protocol`、Chat Renderer |
|
||||
| M1 | Tool Call、Task、可靠交互结果、受限执行摘要 | `coordination`、Chat SDK 投影 |
|
||||
| M2 | 本地 Surface Registry、实例生命周期、隔离 iframe 与恢复 | `surfaces` |
|
||||
| M3 | Capability Registry、用户确认、审计与受限 Host 执行 | `capabilities` |
|
||||
| M4 | 签名 Manifest、Bundle 校验/缓存/回滚、golden fixture | `surfaces`、`protocol/golden` |
|
||||
|
||||
早期文档中的阶段性“尚未实现”或旧目录路径不得用来判断当前能力;需要审计细节时通过
|
||||
Git 历史检索。
|
||||
|
||||
## 6. 后续阶段
|
||||
|
||||
```text
|
||||
F2 持久化 App Registry、App Instance/Focus Manager、默认 App 切换
|
||||
F3 Runtime Tool Router、动态 Inventory、可靠 App SDK Tool 闭环
|
||||
F4 Interaction App 的 IM/Audio/Video 模式边界与第一个 Installed App
|
||||
```
|
||||
|
||||
未来 App 必须复用 `app_scope + conversation_id`、Runtime SDK、Tool/Capability、Artifact、
|
||||
Store、outbox 与恢复机制;禁止新建独立 Agent 通信通道。
|
||||
|
||||
## 7. 文档关系
|
||||
|
||||
| 文档 | 用途 |
|
||||
|---|---|
|
||||
| [app_final_design.md](app_final_design.md) | 当前产品、Runtime、App、SDK 与实施优先级的唯一汇总入口。 |
|
||||
| [lineup-runtime-sdk-architecture.md](lineup-runtime-sdk-architecture.md) | Runtime / SDK 的详细接口与未来 App Manager 契约。 |
|
||||
| [lineup-ui-surface-protocol.md](lineup-ui-surface-protocol.md) | Surface sandbox、bridge、Capability 和 Bundle 安全协议。 |
|
||||
| 本文件 | 当前实现边界、源码归属与 M0~M4 的迁移价值。 |
|
||||
@@ -0,0 +1,785 @@
|
||||
# LineUp Runtime 与 App SDK 架构方案
|
||||
|
||||
**版本:** 0.1(架构基线)
|
||||
**状态:** 已确认的 Runtime / SDK 方向;尚未完全落地到当前 Tauri 实现
|
||||
**日期:** 2026-08-06
|
||||
**相关专题:** [运行时与智能体工具](运行时与智能体工具.md)
|
||||
**关联方案:** [LineUp App 层架构设计方案](lineup-app-layer-architecture.md)、[LineUp UI Surface 与 App Capability 协议](lineup-ui-surface-protocol.md)
|
||||
|
||||
> **汇总入口:** 本文是 Runtime / SDK 初稿的详细来源。后续设计以 [LineUp App 最终设计方案](app_final_design.md) 为准;后者收敛了本地 `app_scope`、兼容迁移和实施顺序。
|
||||
|
||||
---
|
||||
|
||||
## 0. 结论与范围
|
||||
|
||||
LineUp 不是“聊天应用里附带一个交互 Runtime”。**LineUp App 本身就是运行在用户设备上的
|
||||
协作 Runtime**,可以把它理解为本地协调中心:它独占与 AppServer / Remote Agent 的连接,
|
||||
维护会话和本地应用生态,并为上层应用提供统一的消息、工具、存储、权限和生命周期接口。
|
||||
聊天只是第一个使用这些接口的内置 App。
|
||||
|
||||
```text
|
||||
Remote Agent
|
||||
│ LineUp Runtime Envelope
|
||||
▼
|
||||
AppServer / IM Transport
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────┐
|
||||
│ LineUp Runtime │
|
||||
│ │
|
||||
│ Connection · Session · Event Routing · App Manager │
|
||||
│ Tool Registry · Capability Gateway · Storage · Audit │
|
||||
└───────────────┬──────────────────────────────┬────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
Core App Installed App
|
||||
chat whiteboard
|
||||
app-registry voice
|
||||
settings draw-and-guess
|
||||
```
|
||||
|
||||
本方案定义:
|
||||
|
||||
- Runtime 与 Remote Agent、AppServer、上层 App、Tauri Host 的边界;
|
||||
- 动态应用列表、应用身份、应用安装/启用/禁用/移除与实例生命周期;
|
||||
- 应用向 Agent 公布工具方法的标准模型;
|
||||
- Runtime 对消息的统一 Envelope、筛选、可靠投递与 App SDK 订阅接口;
|
||||
- Core App、Installed App 和 Tauri Host 的信任分层;
|
||||
- SDK v1 的最小接口和工程模块边界。
|
||||
|
||||
本方案不在本次冻结:市场目录的商业模型、支付、公开开发者身份认证细节、多人/多 Agent 共享工作区、完整事件溯源和后台持续执行策略。它们必须建立在本方案的身份、路由和权限边界上。
|
||||
|
||||
## 1. 基本术语
|
||||
|
||||
| 术语 | 含义 |
|
||||
|---|---|
|
||||
| **Runtime** | 用户设备上的长期协作运行底座;唯一负责 AppServer/Agent 通信、应用管理和 Host 能力调度。 |
|
||||
| **App** | 用户实际使用的功能单元。Chat、应用注册表、语音、白板和游戏都是 App。 |
|
||||
| **Core App** | 跟随 LineUp Runtime 一起发布、默认受信任的内置 App,例如 `chat`。 |
|
||||
| **Installed App** | 从受信来源安装、验证后在受限执行环境中运行的 App。 |
|
||||
| **App Registry** | Runtime 在本机保存的应用清单,记录安装包、版本、启用状态、实例和回滚信息;它是这些状态的唯一依据。 |
|
||||
| **Tool** | App 先在 Manifest 中说明、Runtime 再公布给 Agent 的可调用方法。Agent 不能任意调用 App 内部函数。 |
|
||||
| **本地 UI Action** | 用户在 MiniApp 界面触发、经 SDK / Bridge 发给 Runtime 的本地请求;可以复用内部业务动作,但不进入 Agent Inventory,也不是 Agent Tool。 |
|
||||
| **Host 生命周期意图** | Host、工作区或系统发出的返回、切换、关闭、恢复等本地事件;Runtime 按生命周期和业务规则裁决,不能伪造成 Agent Tool。 |
|
||||
| **Capability** | Runtime / Host 保管的系统能力,例如录音、选择文件、保存 Artifact;App 必须请求,不能自动获得。 |
|
||||
| **Surface** | 一个受限的交互小界面,例如一块画板、一个语音会话面板或一局游戏。 |
|
||||
| **Conversation** | 用户与一个主 Agent 协作的一段会话范围;App 实例、工具调用和 Artifact 默认都归属这段会话。 |
|
||||
|
||||
## 2. 不可变架构原则
|
||||
|
||||
1. **Runtime 是唯一 Agent 通信入口。** App 不直接访问 AppServer、IM SDK、WebSocket、HTTP cursor 或 Agent endpoint。
|
||||
2. **App 只与 Runtime 通信。** Chat、Voice、Market、白板均通过 SDK 读状态、订阅消息、提交动作和请求能力。
|
||||
3. **每一条可路由 Runtime 消息必须具有 `app_scope` 与 `conversation_id`。** Runtime 以它们作为本地路由、授权、筛选、持久化和投递的首要键。
|
||||
4. **原始 Agent payload 不交给 App。** Runtime 完成版本、身份、schema、大小、去重和作用域校验后,才产生可订阅的标准化消息。
|
||||
5. **应用可调用方法必须先声明、后公布、再调用。** Agent 不执行 App 内任意函数,只能调用当前 Inventory 中精确声明的方法。
|
||||
6. **系统能力由 Runtime 独占。** Agent 和 App 只能请求 Capability;文件、麦克风、通知、剪贴板、窗口、密钥等均不得直接访问。
|
||||
7. **应用状态、消息和副作用分离。** Event 是已发生事实,Command 是请求动作,Effect 是 Runtime 执行的受控副作用。
|
||||
8. **UI 是 Runtime 状态的投影。** App 重启、断网或 Surface 回收后,Runtime 能依据持久化状态恢复到可解释的协作状态。
|
||||
9. **MiniApp 业务数据由 Runtime 通用托管。** App 只能通过 SDK 保存当前 App 子会话的 JSON 数据字典;Runtime
|
||||
不理解其业务 schema,但保证隔离、revision、持久化与恢复。App 不能直接访问浏览器/Host 存储、Conversation
|
||||
Store、Tool/operation Store 或其他 App 数据。
|
||||
|
||||
## 3. 运行时分层
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ Runtime Shell │
|
||||
│ 应用切换 · 默认入口 · 连接状态 · 通知 · 安全恢复 │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ Core App / Installed App │
|
||||
│ Interaction App(IM / Audio / Video) · App Registry │
|
||||
│ Settings · Whiteboard · Game · …(Installed App) │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ LineUp App Runtime SDK / Surface Bridge SDK │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ LineUp Runtime Core │
|
||||
│ Communication · Coordination · App Management · Tooling │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ Runtime Host Provider │
|
||||
│ Tauri Desktop / Web Reference Host 的文件、通知、窗口等 │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Runtime 内部有三个平面:
|
||||
|
||||
| 平面 | 职责 |
|
||||
|---|---|
|
||||
| Communication Plane | 登录、AppServer 连接、历史同步、实时入站、ACK、outbox、重连。 |
|
||||
| Coordination Plane | Envelope 校验、事件路由、会话/Agent 状态、App 生命周期、Tool Registry、Inventory、策略与审计。 |
|
||||
| Execution Plane | Tauri/Host Capability、Surface Sandbox、Bundle Cache、Artifact 内容和受控副作用。 |
|
||||
|
||||
## 4. 应用作用域
|
||||
|
||||
本阶段**不定义跨市场、跨供应商的 App 身份、反向域名命名空间或发布者归属模型**。Runtime 仅维护本机可用的应用作用域键(`app_scope`),用于启动、隔离存储、消息路由和 Tool 路由。
|
||||
|
||||
```text
|
||||
chat
|
||||
app-registry
|
||||
settings
|
||||
voice
|
||||
whiteboard
|
||||
draw-and-guess
|
||||
```
|
||||
|
||||
`app_scope` 是 Runtime 本地注册表中的短名称,不承诺在未来开放市场中全局唯一。开放生态时再通过版本化 Manifest 引入稳定 App ID、供应商身份和命名空间映射;届时不得破坏本节定义的 scope 路由语义。
|
||||
|
||||
### 4.1 作用域键
|
||||
|
||||
| 键 | 用途 |
|
||||
|---|---|
|
||||
| `conversation_id` | 协作会话边界;所有可路由消息均必填。 |
|
||||
| `app_scope` | 消息所属/目标 App 边界;所有可路由消息均必填。 |
|
||||
| `instance_id` | 可选;精确标识某块白板、语音会话或游戏实例。 |
|
||||
| `operation_id` | 可选;标识长任务、导出、执行进度或异步工具操作。 |
|
||||
| `call_id` | 可选;标识 Agent 发起的一次 Tool 调用。 |
|
||||
|
||||
Runtime 的全局管理事件也不省略路由键,而使用保留作用域:
|
||||
|
||||
```text
|
||||
app_scope: runtime 或具体 Core App
|
||||
conversation_id: runtime:global
|
||||
```
|
||||
|
||||
第三方 App 不得使用或伪造上述保留身份。
|
||||
|
||||
## 5. Runtime App Manager 与交互编排
|
||||
|
||||
### 5.1 应用目录与状态
|
||||
|
||||
Runtime 维护本地 `App Registry`,它是 App 安装、版本、信任和启用状态的唯一事实源;
|
||||
`App Instance Manager` 单独维护运行实例、前后台焦点和生命周期。App Catalog/市场只提供
|
||||
候选条目;市场 App 不直接写文件、删除 Bundle 或改写 Registry。
|
||||
|
||||
```text
|
||||
Catalog Entry → Downloading → Verifying → Installed → Enabled
|
||||
│ │
|
||||
│ ├── Active / Background / Suspended instances
|
||||
│ └── Disabled
|
||||
└── Update / Rollback / Remove
|
||||
```
|
||||
|
||||
建议的 App Record:
|
||||
|
||||
```ts
|
||||
type AppRecord = {
|
||||
app_scope: AppScope;
|
||||
kind: "core" | "installed";
|
||||
installed_version: string;
|
||||
previous_version?: string;
|
||||
enabled: boolean;
|
||||
install_state: "installed" | "updating" | "failed";
|
||||
active_instance_count: number;
|
||||
installed_at: string;
|
||||
enabled_at?: string;
|
||||
};
|
||||
|
||||
type AppInstanceRecord = {
|
||||
instance_id: AppInstanceID;
|
||||
app_scope: AppScope;
|
||||
conversation_id?: ConversationID;
|
||||
state: "starting" | "foreground" | "background" | "suspended" | "stopping" | "stopped" | "failed";
|
||||
parent_instance_id?: AppInstanceID;
|
||||
started_at: string;
|
||||
stopped_at?: string;
|
||||
error?: string;
|
||||
};
|
||||
```
|
||||
|
||||
### 5.2 Runtime 管理接口
|
||||
|
||||
```ts
|
||||
interface RuntimeAppManager {
|
||||
listApps(filter?: AppListFilter): readonly AppRecord[];
|
||||
getApp(appScope: AppScope): AppRecord | undefined;
|
||||
subscribe(listener: (change: AppRegistryChange) => void): Unsubscribe;
|
||||
|
||||
install(request: InstallAppRequest): Promise<AppOperation>;
|
||||
enable(appScope: AppScope): Promise<AppOperation>;
|
||||
disable(appScope: AppScope, options?: DisableAppOptions): Promise<AppOperation>;
|
||||
update(appScope: AppScope, targetVersion?: string): Promise<AppOperation>;
|
||||
rollback(appScope: AppScope): Promise<AppOperation>;
|
||||
remove(appScope: AppScope, options?: RemoveAppOptions): Promise<AppOperation>;
|
||||
|
||||
launch(request: LaunchAppRequest): Promise<AppInstanceHandle>;
|
||||
closeInstance(instanceID: AppInstanceID): Promise<void>;
|
||||
}
|
||||
|
||||
interface RuntimeAppOrchestrator {
|
||||
launch(request: LaunchAppRequest): Promise<AppInstanceHandle>;
|
||||
foreground(instanceID: AppInstanceID): Promise<AppInstanceHandle>;
|
||||
background(instanceID: AppInstanceID, reason?: string): Promise<AppInstanceHandle>;
|
||||
suspend(instanceID: AppInstanceID, reason?: string): Promise<AppInstanceHandle>;
|
||||
restore(instanceID: AppInstanceID): Promise<AppInstanceHandle>;
|
||||
closeInstance(instanceID: AppInstanceID): Promise<void>;
|
||||
getFocusStack(conversationID?: ConversationID): readonly AppInstanceID[];
|
||||
}
|
||||
```
|
||||
|
||||
安装、验签、升级、禁用和移除是异步操作,必须返回可观察的 `AppOperation`。禁用会阻止新实例和新 Tool 调用,并收口现有实例;移除会在实例关闭及用户确认后清理 Bundle 和 App 私有数据。
|
||||
|
||||
### 5.3 默认应用与安全回退
|
||||
|
||||
Runtime Shell 不属于任何 App。它负责应用切换、通知、连接状态和安全恢复。用户可选择一个具备 `default_eligible` entrypoint 的已启用 App 作为默认入口:
|
||||
|
||||
```text
|
||||
当前默认:chat → 图文 IM 为主
|
||||
未来默认:voice → 实时语音为主
|
||||
```
|
||||
|
||||
`chat` 是内建、不可移除的 Recovery App:默认 App 不兼容、被禁用、损坏或启动失败时,Runtime 必须回退到它。
|
||||
|
||||
启动优先级:
|
||||
|
||||
```text
|
||||
显式启动目标(深链接/通知)
|
||||
→ 可安全恢复的前台工作
|
||||
→ 用户 Default App
|
||||
→ chat Recovery App
|
||||
```
|
||||
|
||||
### 5.4 前台焦点与 App 间切换
|
||||
|
||||
`Interaction App` 是默认的人与 Agent 交互入口,但不是全局调度器。Runtime 的
|
||||
`App Orchestrator`、`Tool Router` 和 `Focus Manager` 负责决定 Agent 请求由哪个 App、哪个
|
||||
交互模式或哪个标准组件承接。Interact 只通过 SDK 呈现当前会话并提交用户 Action。
|
||||
|
||||
```text
|
||||
Interaction App / Audio Mode foreground
|
||||
→ Agent 请求 launch(draw-and-guess)
|
||||
Runtime 校验 Tool、Manifest、Inventory、参数和前台条件
|
||||
→ Audio Instance background / suspended
|
||||
→ Draw-and-Guess Instance starting → foreground
|
||||
→ 游戏结果经 Runtime 回传 Agent
|
||||
→ 关闭或结束后按 focus stack 恢复 Interaction App
|
||||
```
|
||||
|
||||
前后台切换必须保留原实例和 `conversation_id` 的关联,不能因为界面暂时不可见就删除
|
||||
未完成 Tool Call、Surface 或 App Inbox。若目标 App 启动失败,Runtime 应恢复原前台实例并
|
||||
向 Agent 返回受控的 `app_start_failed` 或 `handler_failed` 结果。
|
||||
|
||||
## 6. Runtime Tool Registry 与动态 Inventory
|
||||
|
||||
本章中的 Tool 专指远端 Agent Tool:它是 Runtime 对 Agent 发布的调用说明书,不是用户 UI 的公共操作入口。Agent Tool、本地 UI Action 与 Host 生命周期意图的完整入口边界,以 [运行时与智能体工具](运行时与智能体工具.md) 为准。
|
||||
|
||||
### 6.1 App Tool
|
||||
|
||||
App 可在 Manifest 中声明 Agent 可调用方法。当前 Tool 由 App Scope、局部方法和契约版本定位:
|
||||
|
||||
```text
|
||||
whiteboard/board.create@1
|
||||
whiteboard/board.apply_diagram@1
|
||||
voice/voice.request_recording@1
|
||||
```
|
||||
|
||||
Runtime 内部使用结构化键,而不依赖字符串拼接:
|
||||
|
||||
```ts
|
||||
type ToolKey = {
|
||||
app_scope: AppScope;
|
||||
method: string;
|
||||
contract_version: string;
|
||||
};
|
||||
```
|
||||
|
||||
每个 Tool 声明至少包含:
|
||||
|
||||
- 标题、面向 Agent 的说明、输入/输出 JSON Schema;
|
||||
- `query`、`command`、`interactive`、`operation` 四种调用模型之一;
|
||||
- 前台要求、超时、幂等规则和风险等级;
|
||||
- 是否需启动 App 实例、是否允许 Runtime 自动启动;
|
||||
- 该方法可能请求的 Capability。
|
||||
|
||||
### 6.2 可见性与 Inventory
|
||||
|
||||
Runtime 向 Agent 公布的工具不是安装包的全部声明,而是动态交集:
|
||||
|
||||
```text
|
||||
Agent 可见 Tool
|
||||
= 已验证 Manifest Tool
|
||||
∩ App 已启用
|
||||
∩ 当前 Host 支持
|
||||
∩ 用户/组织策略允许
|
||||
∩ 当前 Agent 和 conversation scope 允许
|
||||
∩ 所需前置权限满足
|
||||
```
|
||||
|
||||
Inventory 包含 App、Tool、Surface 和 Runtime Capability,并具有 revision。Runtime 在连接建立、App 启用/禁用/升级/撤销、Host 能力变化或策略收紧后重新发布。Agent 调用必须携带其看到的 `inventory_revision`;过期清单的调用应被安全拒绝并提示刷新。
|
||||
|
||||
### 6.3 Tool 调用流程
|
||||
|
||||
```text
|
||||
Agent lineup.tool.invoke
|
||||
→ Runtime 验证 Envelope、Agent、inventory revision、App 状态和参数 schema
|
||||
→ 创建 call record / 审计记录
|
||||
→ Tool Router 判断直接执行、启动 App、切换前台、等待用户交互,或创建异步 operation
|
||||
→ App Orchestrator / Interaction App / Installed App 承接调用
|
||||
→ Runtime / App 经 SDK 返回 protocol receipt、progress / result / error
|
||||
→ Runtime 验证输出 schema、持久化、更新焦点并回传 Agent
|
||||
```
|
||||
|
||||
若调用触发 App activation,Runtime 在写入 `starting → ready | failed | cancelled` 后,通过同一 call_id 的受控
|
||||
protocol receipt / progress 通知 Agent。`activation_ready` 表示 App 已能接收后续 `app_ready` Tool;Agent 可据此顺序
|
||||
编排调用,但 Runtime 仍须持久化并等待提前到达的合法调用,不能把时序正确性外包给 Agent。MiniApp、Surface 和 Host
|
||||
不直接向 Agent 发送 ready;`activation_not_required` Tool 不等待 activation。具体公开字段、等待队列和 Pomodoro
|
||||
`started` 覆盖 `activation_ready` 的规则,以 [运行时与智能体工具](运行时与智能体工具.md) 为准。
|
||||
|
||||
标准终态错误码至少包括:
|
||||
|
||||
```text
|
||||
cancelled_by_user
|
||||
permission_denied
|
||||
app_disabled
|
||||
app_not_installed
|
||||
app_version_mismatch
|
||||
tool_not_visible
|
||||
invalid_arguments
|
||||
foreground_required
|
||||
operation_expired
|
||||
handler_failed
|
||||
```
|
||||
|
||||
## 7. 统一消息 Envelope 与路由
|
||||
|
||||
### 7.1 Runtime Envelope
|
||||
|
||||
Runtime 对跨边界、需要传输或审计的消息使用统一基础 Envelope。该 Envelope 统一的是消息承载、作用域与校验字段,不把来源不同的入口合并成同一种调用:远端 Agent Tool、本地 UI Action 和 Host 生命周期意图必须保留不同的 sender、来源校验和审计类型。UI 点击不能仅因复用相同内部业务服务就被包装成 Agent Tool Call:
|
||||
|
||||
```ts
|
||||
type RuntimeEnvelope = {
|
||||
v: 1;
|
||||
id: string;
|
||||
type: string;
|
||||
timestamp: string;
|
||||
|
||||
sender: {
|
||||
kind: "agent" | "runtime" | "app" | "user" | "host";
|
||||
id: string;
|
||||
};
|
||||
|
||||
target: {
|
||||
kind: "runtime" | "app";
|
||||
app_scope: AppScope;
|
||||
instance_id?: AppInstanceID;
|
||||
};
|
||||
|
||||
scope: {
|
||||
app_scope: AppScope;
|
||||
conversation_id: ConversationID;
|
||||
instance_id?: AppInstanceID;
|
||||
operation_id?: OperationID;
|
||||
};
|
||||
|
||||
correlation?: {
|
||||
reply_to?: string;
|
||||
call_id?: string;
|
||||
inventory_revision?: string;
|
||||
sequence?: number;
|
||||
};
|
||||
|
||||
payload: JsonValue;
|
||||
};
|
||||
```
|
||||
|
||||
`scope.app_scope` 是消息所属的本地应用作用域,`target.app_scope` 是指定的消费 App。通常二者相同;Runtime 协调型消息可使用 `runtime` 作为 target,再由 Runtime 生成安全的 App 投影并分发。
|
||||
|
||||
### 7.2 消息分类
|
||||
|
||||
第一版类型分组:
|
||||
|
||||
```text
|
||||
lineup.content.* 文本、图片、音频、链接、Artifact 引用
|
||||
lineup.agent.* presence、status、task、progress
|
||||
lineup.interaction.* choice、confirm、input、form
|
||||
lineup.tool.* invoke、accepted、progress、result、error、cancel
|
||||
lineup.app.* state.patch、event、open、close、lifecycle
|
||||
lineup.capability.* request、result
|
||||
lineup.runtime.* inventory、connection、app registry、policy、error
|
||||
```
|
||||
|
||||
具体 schema 将在协议文档版本化;SDK 不暴露未经校验的原始 JSON。
|
||||
|
||||
### 7.3 Runtime 筛选链
|
||||
|
||||
```text
|
||||
Transport 收到原始消息
|
||||
→ 协议:版本、必填 app_scope/conversation_id、type、大小、schema
|
||||
→ 身份:Agent、用户、会话关联
|
||||
→ 去重/顺序:id、sequence、cursor
|
||||
→ App:安装、启用、版本、签名、Host 兼容性
|
||||
→ Tool/Capability:Inventory、方法、参数、策略、前台条件
|
||||
→ Scope:conversation、instance、operation 所属关系
|
||||
→ Subscription:App Manifest 声明与 SDK 订阅交集
|
||||
→ Persist:Runtime Event / App Inbox / cursor
|
||||
→ Deliver:投递给目标 App 的 SDK
|
||||
```
|
||||
|
||||
Runtime 不对所有 App 广播原始消息。一个白板 App 即使与 Chat 位于同一 conversation,也只能收到其 Manifest 声明且 Runtime 授权的实例 patch、Tool 调用或事件。
|
||||
|
||||
## 8. Runtime SDK v1
|
||||
|
||||
### 8.1 双通道模型
|
||||
|
||||
```text
|
||||
Inbound:Runtime → App
|
||||
- 已验证 Agent 消息
|
||||
- App 专属状态投影
|
||||
- Tool 调用
|
||||
- Runtime / App 生命周期
|
||||
|
||||
Outbound:App → Runtime
|
||||
- 用户与 App 声明性 Action
|
||||
- Tool result / progress / error
|
||||
- Surface Event
|
||||
- Capability request
|
||||
- 可恢复 State Snapshot
|
||||
```
|
||||
|
||||
顶层接口:
|
||||
|
||||
```ts
|
||||
interface LineUpAppRuntimeSDK {
|
||||
readonly app: AppContext;
|
||||
readonly lifecycle: AppLifecycleAPI;
|
||||
readonly state: AppStateAPI;
|
||||
readonly agentMessages: AgentMessageAPI;
|
||||
readonly actions: AppActionAPI;
|
||||
readonly tools: AppToolAPI;
|
||||
readonly apps: AppNavigationAPI;
|
||||
readonly artifacts: ArtifactAPI;
|
||||
readonly sessionData: MiniAppSessionDataAPI;
|
||||
readonly capabilities: CapabilityRequestAPI;
|
||||
readonly diagnostics: DiagnosticsAPI;
|
||||
}
|
||||
```
|
||||
|
||||
App 得到的是按 App Scope、conversation 和实例作用域裁剪后的接口和 View Model,而不是全量 Runtime State 或原始 Transport。
|
||||
|
||||
本地 UI Action 只经 SDK / Bridge 进入 Runtime;它不调用 Agent Tool、不会进入 Agent Inventory,也不带 Agent 身份。若业务需要,Runtime 可以在完成本地校验后让 UI Action 与 Agent Tool 调用同一个内部业务服务。
|
||||
|
||||
### 8.2 App Context、状态与生命周期
|
||||
|
||||
```ts
|
||||
interface AppContext {
|
||||
app_scope: AppScope;
|
||||
app_version: string;
|
||||
instance_id: AppInstanceID;
|
||||
kind: "core" | "installed";
|
||||
entrypoint: string;
|
||||
scope: {
|
||||
conversation_id?: ConversationID;
|
||||
operation_id?: OperationID;
|
||||
parent_instance_id?: AppInstanceID;
|
||||
};
|
||||
granted_permissions: readonly AppPermission[];
|
||||
}
|
||||
|
||||
interface AppStateAPI<ViewModel = unknown> {
|
||||
snapshot(): ViewModel;
|
||||
subscribe(listener: (view: ViewModel, change: AppStateChange) => void): Unsubscribe;
|
||||
}
|
||||
```
|
||||
|
||||
生命周期包含 `start`、`foreground`、`background`、`suspend`、`restore`、`closing`。App 可以保存快照并请求延迟关闭,但 Runtime 保留禁用、移除、内存回收和强制收口的最终权力。
|
||||
|
||||
### 8.3 Agent 消息订阅与可靠 Inbox
|
||||
|
||||
SDK 提供实时订阅,同时提供持久化 Inbox 补读:
|
||||
|
||||
```ts
|
||||
interface AgentMessageAPI {
|
||||
list(request?: {
|
||||
conversation_id?: ConversationID;
|
||||
after?: AgentMessageCursor;
|
||||
limit?: number;
|
||||
}): Promise<AgentMessagePage>;
|
||||
|
||||
subscribe(
|
||||
options: {
|
||||
conversation_id?: ConversationID;
|
||||
types?: readonly AgentMessageType[];
|
||||
include_pending?: boolean;
|
||||
},
|
||||
handler: (message: AgentAppMessage) => Promise<void> | void,
|
||||
): Unsubscribe;
|
||||
|
||||
acknowledge(message_id: string): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
投递语义:
|
||||
|
||||
1. Runtime 先验证并持久化,再回调 App;
|
||||
2. 每条消息有唯一 `message_id`,App 必须幂等处理;
|
||||
3. 展示类消息可自动确认;Tool 调用、状态 patch 等业务消息需 App 显式 ACK;
|
||||
4. App 未运行、暂停或崩溃时,Runtime 写入该 App Inbox;恢复后通过 `list()` 与 `subscribe()` 补齐;
|
||||
5. App 禁用、移除或不兼容时,Runtime 不静默投递或丢弃关键调用,而向 Agent 返回标准拒绝。
|
||||
|
||||
App 的订阅条件只是请求;实际投递集为:
|
||||
|
||||
```text
|
||||
Manifest agent_subscriptions
|
||||
∩ SDK subscribe filter
|
||||
∩ App 权限
|
||||
∩ conversation / instance scope
|
||||
∩ Agent target app_scope
|
||||
∩ Runtime Policy
|
||||
```
|
||||
|
||||
### 8.4 Action、Tool、App 导航与 Artifact
|
||||
|
||||
App 只提交声明性 Action;不得构造原始 Agent Envelope 或直接访问 Transport:
|
||||
|
||||
```ts
|
||||
interface AppActionAPI {
|
||||
dispatch(action: AppAction): Promise<CommandReceipt>;
|
||||
}
|
||||
|
||||
interface AppToolAPI {
|
||||
onInvoke(listener: (call: AppToolInvocation) => Promise<AppToolOutcome>): Unsubscribe;
|
||||
progress(request: { call_id: ToolCallID; progress: ToolProgress }): Promise<void>;
|
||||
complete(request: { call_id: ToolCallID; result: JsonValue }): Promise<void>;
|
||||
fail(request: { call_id: ToolCallID; code: AppToolErrorCode; message?: string }): Promise<void>;
|
||||
}
|
||||
|
||||
interface AppNavigationAPI {
|
||||
listAvailable(): readonly AppSummary[];
|
||||
launch(request: LaunchAppRequest): Promise<AppInstanceHandle>;
|
||||
focus(instanceID: AppInstanceID): Promise<void>;
|
||||
background(instanceID: AppInstanceID, reason?: string): Promise<void>;
|
||||
suspend(instanceID: AppInstanceID, reason?: string): Promise<void>;
|
||||
close(instanceID: AppInstanceID): Promise<void>;
|
||||
openDefaultApp(): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
`AppNavigationAPI` 只是 App 发给 Runtime 的受限请求接口。真正的 Tool Router、App Instance
|
||||
Manager 和 Focus Manager 位于 Runtime 内部:它们负责检查 App 是否已安装/启用、是否满足
|
||||
前台条件、是否需要用户确认,以及切换失败时恢复原前台实例。Interact 可以请求启动扩展
|
||||
App,但不能直接决定其他 App 的生命周期。
|
||||
|
||||
Artifact、媒体与文件通过 Runtime Artifact Store 暴露元数据、受限读取、创建、会话引用和用户保存;App 不取得任意路径或跨 App 文件访问权。
|
||||
|
||||
### 8.5 MiniApp session data 与 Capability
|
||||
|
||||
每个 MiniApp App 子会话使用 Runtime 管理的、通用的业务数据字典:
|
||||
|
||||
```text
|
||||
app_scope + app_session_id
|
||||
→ 一份 MiniApp 自己定义的 JSON 数据字典
|
||||
→ revisioned replace / subscribe
|
||||
→ 重启后向同一 App 子会话恢复
|
||||
```
|
||||
|
||||
这不是通用文件系统、键值数据库或跨子会话查询接口。MiniApp 负责定义数据的业务含义、嵌套结构、`current` /
|
||||
`history`、是否追加历史以及业务上是否可修改;Runtime 不声明或校验 MiniApp 业务 schema。Runtime 只负责从不可
|
||||
伪造的 SDK context 推导当前 `app_scope + app_session_id`,以及 JSON 合法性、通用配额、安全解析深度、revision
|
||||
比较、原子写入与重启恢复。关闭、禁用或移除对记录的技术性清理由 Runtime 的全局保留策略负责,但 Runtime 不把
|
||||
“关闭即业务数据只读”强加给每一个 MiniApp。
|
||||
|
||||
```ts
|
||||
interface MiniAppSessionDataAPI<Data extends JsonObject = JsonObject> {
|
||||
get(): Promise<{ data: Data | null; revision: number }>;
|
||||
replace(request: { data: Data; expected_revision: number }): Promise<{ revision: number }>;
|
||||
subscribe(listener: (snapshot: { data: Data | null; revision: number }) => void): Unsubscribe;
|
||||
}
|
||||
```
|
||||
|
||||
每个 `app_scope + app_session_id` 的 subscribe / replace 都由 Runtime 在同一串行顺序处理。没有已保存数据时,
|
||||
`get()` 和订阅首帧均为 `{ data: null, revision: 0 }`;订阅注册时 Runtime 捕获并先投递完整当前快照,之后才投递
|
||||
revision 严格递增的完整变化。于是读与订阅之间发生的 replace 不会漏掉:它要么在首帧中,要么紧随首帧出现。SDK
|
||||
忽略重复或旧 revision;发现跳跃或 Bridge 重连时取消旧订阅、重新 `get()` 并建立新订阅。revision CAS 只拒绝陈旧
|
||||
覆盖,不承担业务合并;多入口协作写入由后续独立的 mutation queue 方案解决。
|
||||
|
||||
`expected_revision` 不匹配时 Runtime 返回 `session_data_revision_conflict`;数据不是 JSON、SDK context 已失效或
|
||||
超出通用配额时分别返回 `session_data_invalid`、`session_data_unavailable`、`session_data_quota_exceeded`。App
|
||||
不得用 session data 直接完成 Agent Tool、写 outbox、改变焦点或执行 Capability。deadline、Tool 终态、outbox、
|
||||
焦点或生命周期仍走 Runtime 的 Command / operation 路径;Runtime 可以在同一原子事务中更新 operation 与
|
||||
session data,但二者保持不同的事实来源。
|
||||
|
||||
MiniApp 的短暂渲染状态(动画、滚动位置、临时 loading)不要求保存;Conversation / App 子会话保存人与
|
||||
Agent 可见的静态交互和结果,也不是 MiniApp 业务数据的事实来源。IM 中一旦写入的完成记录是已发生结果的
|
||||
不可变静态副本,不会随 MiniApp 之后修改 session data 而变化。Capability 必须通过统一请求接口:
|
||||
|
||||
```ts
|
||||
interface CapabilityRequestAPI {
|
||||
request<T extends JsonValue>(request: {
|
||||
capability: CapabilityName;
|
||||
purpose: string;
|
||||
input: JsonValue;
|
||||
scope?: AppScope;
|
||||
}): Promise<CapabilityResult<T>>;
|
||||
}
|
||||
```
|
||||
|
||||
Runtime 检查 Manifest 声明、App 启用状态、Host 支持、前台要求、用户确认、系统权限、策略、限流与审计;App 只能获得受控结果。
|
||||
|
||||
## 9. 信任分层与 Surface Bridge SDK
|
||||
|
||||
| 层级 | 例子 | 可用接口 | 明确禁止 |
|
||||
|---|---|---|---|
|
||||
| Core App | Chat、App Registry、Settings | 完整 App Runtime SDK(仍受 Scope 和 Capability 约束) | 直接绕过 Transport/Capability Gateway。 |
|
||||
| Installed App | 白板、语音、游戏 | 受限 Surface Bridge SDK:状态、已声明 Tool、事件、当前 App 子会话的 session data、Capability request | Tauri invoke、父 DOM、token、任意网络、其他 App 数据。 |
|
||||
| Host Provider | Tauri Desktop / Web Reference Host 实现 | Runtime 内部 Host Provider API | 向 Runtime 提供系统能力;不直接暴露给 Agent/Surface。 |
|
||||
|
||||
下载型 App 使用 `iframe sandbox="allow-scripts"` 或等价隔离容器;不包含 `allow-same-origin`。它经严格 CSP、来源验证和消息 schema 校验的桥接访问 Surface SDK。第一版不支持市场下载包执行本地 Rust、Node、Shell 或任意浏览器特权代码。
|
||||
|
||||
## 10. App Manifest 的 Runtime/SDK 声明
|
||||
|
||||
每个 App Manifest 除 Bundle、签名和 Surface 外,还应声明 Runtime SDK 与消息/工具契约。当前阶段只使用本地 `app_scope`,不在 Manifest 中固化供应商身份或全局命名空间:
|
||||
|
||||
Manifest 的 Tool 描述由 MiniApp SDK 的 `defineAgentTools(...)` 在构建期从源码生成。开发者只维护 SDK 中的 Tool
|
||||
声明和本地 handler;构建产物把不含函数、URL、回调或私有函数名的 schema / policy 描述写入 Manifest,供 Runtime
|
||||
安装、Registry 与 Agent Inventory 使用。Runtime 向 ready App session 的统一 SDK Tool 接收入口投递 method +
|
||||
params,SDK 再在 Bundle 内部按 method 分发 handler。
|
||||
|
||||
```json
|
||||
{
|
||||
"format": "lineup.app.v1",
|
||||
"app_scope": "whiteboard",
|
||||
"version": "1.2.0",
|
||||
"runtime_sdk": {
|
||||
"api_version": "1",
|
||||
"required_features": ["app.lifecycle.v1", "app.tools.v1", "surface.state.v1", "app.session-data.v1"],
|
||||
"optional_features": ["artifact.create.v1"]
|
||||
},
|
||||
"entrypoints": [
|
||||
{"id": "canvas", "kind": "contextual", "default_eligible": false}
|
||||
],
|
||||
"agent_subscriptions": [
|
||||
{"type": "lineup.app.state.patch", "scope": "instance"},
|
||||
{"type": "lineup.tool.invoke", "scope": "conversation"}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"method": "board.create",
|
||||
"contract_version": "1",
|
||||
"handling": "direct",
|
||||
"delivery": "miniapp_sdk",
|
||||
"activation_requirement": "app_ready",
|
||||
"input_schema": {"type": "object", "additionalProperties": false},
|
||||
"output_schema": {"type": "object", "additionalProperties": false}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Runtime 在安装、启用和启动时进行 SDK 版本及 feature 协商;不兼容 App 不进入可用 Inventory,也不得启动。
|
||||
|
||||
## 11. Runtime 状态与恢复
|
||||
|
||||
顶层 Runtime State 至少包含:
|
||||
|
||||
```text
|
||||
identity 用户、设备、认证会话
|
||||
connection AppServer 状态、cursor、重试、outbox
|
||||
conversations Agent 映射、消息引用、任务、交互、Artifact 元数据
|
||||
apps App Registry、默认 App、运行实例、App Inbox
|
||||
app_state 按 app_scope + app_session_id 隔离的 MiniApp session data、revision 与 activation
|
||||
tools 已声明 Tool、可见性、调用与 operation
|
||||
capabilities 授权、执行状态、最小审计
|
||||
```
|
||||
|
||||
第一版采用“事件驱动状态机 + 必要快照”,而非完整 Event Sourcing:关键入站事件、用户决定、Tool 结果、outbox 和审计记录持久化;渲染细节和短暂 UI 状态不必全部写入事件日志。
|
||||
|
||||
Runtime 生命周期:
|
||||
|
||||
```text
|
||||
created → restoring → authenticating → synchronizing → online
|
||||
↘ reconnecting / offline_degraded
|
||||
online / offline_degraded → stopping → stopped
|
||||
```
|
||||
|
||||
启动时恢复 App Registry、默认入口、会话 cursor、outbox、App Inbox、未完成 Tool 和可恢复实例;追平历史后再开始实时投递。离线时用户/App Action 进入 outbox,重连后按幂等键和顺序处理。
|
||||
|
||||
## 12. 推荐工程边界
|
||||
|
||||
```text
|
||||
lineup-app/
|
||||
├── runtime-core/
|
||||
│ ├── communication/ Transport、sync、outbox
|
||||
│ ├── coordination/ Event、state、router、policy、focus、audit
|
||||
│ ├── apps/ App Manager、Registry、Instance、Focus、Lifecycle Manager
|
||||
│ ├── tools/ Tool Registry、Inventory、Router、call lifecycle
|
||||
│ ├── capabilities/ Capability Gateway
|
||||
│ └── persistence/ Runtime Store、App Inbox、instance state snapshot、Artifact metadata
|
||||
├── runtime-sdk/
|
||||
│ ├── app.ts Core App SDK 类型
|
||||
│ ├── manifest.ts App/Tool/Subscription Manifest 类型
|
||||
│ ├── protocol.ts Runtime Envelope 与 schema
|
||||
│ ├── errors.ts 稳定错误码
|
||||
│ └── testkit/
|
||||
├── surface-sdk/
|
||||
│ ├── bridge.ts sandbox postMessage bridge
|
||||
│ └── surface.ts Installed App 最小接口
|
||||
└── tauri/
|
||||
├── src/
|
||||
│ ├── bootstrap/ Tauri/Web 的应用启动装配层
|
||||
│ ├── runtime-host/ Runtime Host Provider 适配
|
||||
│ ├── core-apps/ chat、app-registry、settings
|
||||
│ └── shell/ app switcher、default app、recovery
|
||||
└── src-tauri/ 文件、音频、通知、窗口等 Rust Provider
|
||||
```
|
||||
|
||||
当前 `lineup-app/tauri/src/main.ts` 同时承担 Runtime、Chat、Host Adapter 和开发 Fixture 的职责,是拆分的主要对象。现有 `InteractionKernel`、Transport、Conversation Store、Surface Registry、Instance Manager、Bundle Cache、Capability Registry 与 Inventory Publisher 可作为上述 Runtime Core 的迁移基础。
|
||||
|
||||
## 13. 第一阶段落地顺序与验收
|
||||
|
||||
### R0:冻结 Runtime/SDK 合约
|
||||
|
||||
- 固化 `RuntimeEnvelope`、app scope / conversation ID / instance ID 规则;
|
||||
- 固化 Event / Command / Effect 边界;
|
||||
- 定义 `LineUpAppRuntimeSDK`、`Surface Bridge SDK` 与 Manifest TypeScript 类型;
|
||||
- 为 Envelope、App Manifest、Tool Descriptor、SDK 错误码准备 golden fixtures。
|
||||
|
||||
### R1:建立 Runtime Core 和 Interaction Core App(当前 `chat` 实现)
|
||||
|
||||
- 将 Runtime 的创建与装配集中在单一启动入口,不让 `main.ts` 变成通信和业务逻辑的堆放处;
|
||||
- Runtime 独占 Transport、Store、outbox 和入站筛选;
|
||||
- 将当前图文 IM 重构为 Interaction Core App 的 `chat` 实现;
|
||||
- 当前 `chat` 通过 `agentMessages.subscribe()` 获取已验证的 Agent 消息,不再直接依赖 Transport;
|
||||
- 保持当前登录、同步、本地回显、Markdown、Tool Call、Task 与 Artifact 回归行为。
|
||||
|
||||
### R2:App Registry Core App
|
||||
|
||||
- 将现有开发期 `SurfaceRegistry` 升级为 Runtime App Registry;
|
||||
- 实现 Core App 记录、Installed App 记录、enable/disable/remove 和默认 App 选择;
|
||||
- 将 Registry UI 实现为 `app-registry`,仅调用 `RuntimeAppManager`;
|
||||
- App 状态变化后正确更新 Runtime Inventory。
|
||||
|
||||
### R3:Tool Registry 与 App SDK Inbox
|
||||
|
||||
- 实现 Manifest Tool / Subscription 解析与可见性筛选;
|
||||
- 将动态 Inventory 同步给 Agent;
|
||||
- 实现 Agent `tool.invoke` → Runtime → App SDK → `tool.result` 的可靠闭环;
|
||||
- App 未运行时持久化 Inbox,恢复后可幂等补读。
|
||||
- 实现 `app.session-data.v1`:按 `app_scope + app_session_id` 隔离的通用 JSON 数据、revisioned replace /
|
||||
subscribe、通用配额、刷新/重启恢复与关闭/禁用/移除清理;Runtime 不校验 MiniApp 业务 schema,且 Tool/
|
||||
operation/outbox 不得由该 API 直接改写。
|
||||
|
||||
### R4:第一个 Installed App 闭环
|
||||
|
||||
- 接入一个已签名的画板或任务面板 App;
|
||||
- 验收 install → enable → inventory 更新 → Agent 调用 Tool → App 启动 → Surface 事件 → Agent result → disable/remove;
|
||||
- 后续语音 App 复用同一 SDK、Tool、Capability 与 Artifact 通道,而不是再实现独立通信链路。
|
||||
|
||||
硬性验收:
|
||||
|
||||
```text
|
||||
1. 缺少或非法 app_scope / conversation_id 的可路由消息被 Runtime 拒绝;
|
||||
2. App 不会收到其他 App 或其他 conversation 的 Agent 原始消息;
|
||||
3. App 被禁用后,其 Tool 从 Inventory 移除且新调用被拒绝;
|
||||
4. 断线/重启后 App Inbox、outbox、Tool 调用和实例状态能有界恢复;
|
||||
5. 下载型 App 无法访问 Tauri invoke、Host DOM、token 或未授予 Capability;
|
||||
6. 默认 App 不可用时 Runtime 自动进入 chat Recovery App;
|
||||
7. 所有 Tool 调用、权限拒绝与终态结果均可按 app_scope、conversation_id、call_id 审计和测试。
|
||||
8. MiniApp 只能读写自身当前 App 子会话的 session data;非 JSON、旧 revision、跨 App / 子会话、超配额或
|
||||
失效 SDK context 均被稳定拒绝,重启只恢复最后一个有效数据版本。
|
||||
```
|
||||
|
||||
## 14. 与已有正式方案的关系
|
||||
|
||||
[运行时与智能体工具](运行时与智能体工具.md) 进一步冻结了 Agent 的角色、Agent Tool 的发布与调用模型,以及它和本地 UI Action、Host 生命周期意图的三条入口边界;本文的 Tool 与 Inventory 术语均按该专题理解。
|
||||
|
||||
- 本文将 [App 层架构方案](lineup-app-layer-architecture.md) 中的 Interaction Runtime 从“聊天 Host 内部模块”收敛为整个 App 的 Runtime,并补充了 App Manager、App Tool Registry、SDK 和消息路由模型。
|
||||
- 本文不废弃 [UI Surface 与 App Capability 协议](lineup-ui-surface-protocol.md) 中已完成的 Surface 隔离、Capability 确认、生产 Bundle 验签和动态 inventory 原则;后续应将其协议字段迁移/扩展为本文定义的 Runtime Envelope、App Manifest 和 Tool Registry,而不能引入绕开 Runtime 的平行通道。
|
||||
- 现有 M2/M4 文档和实现使用的 `app id` 是当前 Surface 协议的实现字段。本方案在 Runtime 重构阶段以 `app_scope` 作为本地路由字段;供应商身份和全局命名空间将在后续生态阶段通过独立、版本化的兼容设计重新引入。
|
||||
- 当前 M0~M4 实现可作为 Runtime 的迁移基础,但其现有文件边界不是最终 Runtime/App/SDK 边界。
|
||||
@@ -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 客户端。
|
||||
@@ -0,0 +1,310 @@
|
||||
# 运行时与智能体工具
|
||||
|
||||
**版本:** 0.1(当前 Runtime 契约)
|
||||
**状态:** 已确认
|
||||
**日期:** 2026-08-06
|
||||
**关联方案:** [LineUp App 最终设计方案](app_final_design.md)、[LineUp Runtime 与 App SDK 架构方案](lineup-runtime-sdk-architecture.md)、[第 04 次迭代:Runtime 工作区与 Pomodoro MiniApp](../../迭代/04.runtime_workspace/04.runtime_workspace.md)
|
||||
|
||||
---
|
||||
|
||||
## 0. 目的与权威范围
|
||||
|
||||
本文定义 Runtime 面向远端 Agent 的工具模型,以及它与本地 MiniApp 界面操作、Host 生命周期事件之间的边界。它是全局 Runtime 设计,Pomodoro 只是当前的具体例子,并不限制本文的适用范围。
|
||||
|
||||
本文确认的关键结论是:
|
||||
|
||||
> **Agent Tool 是 Runtime 发布给远端 Agent 的功能调用说明书。它不是用户点击界面的公共入口,也不是 MiniApp 内部函数的别名。**
|
||||
|
||||
用户通常通过 IM 表达自然语言意图,未来也可以通过 Voice 表达;远端 Agent 负责理解意图、选择可见 Tool 并向 Runtime 发起调用。Runtime 负责校验、路由、执行、持久化和回传结果。用户未来可以在 MiniApp 页面点击按钮,但那属于另一条本地 UI Action 入口,不能伪装成 Agent Tool 调用。
|
||||
|
||||
本文细化 [LineUp App 最终设计方案](app_final_design.md) 中 Runtime、Tool、状态和边界的结论。后续若修改本文已确认的语义,必须先更新正式方案,再调整迭代定义和技术规范。
|
||||
|
||||
## 1. 术语与对象
|
||||
|
||||
| 术语 | 定义 | 不是 |
|
||||
|---|---|---|
|
||||
| **远端 Agent** | 运行在 LineUp 设备外、通过 AppServer / IM Transport 与 Runtime 通信的智能体。它理解用户自然语言,并在可见能力范围内选择 Tool。 | 不是 Runtime 内的计时器、存储或 UI 控制器。 |
|
||||
| **Agent Tool** | MiniApp 在 Manifest 声明、Runtime 校验并发布给远端 Agent 的远程调用契约。它包含名称、说明、输入输出 schema、调用模型、可见性和策略。 | 不是用户按钮,也不是 App 任意 JavaScript 函数。 |
|
||||
| **Agent Inventory** | Runtime 根据 App、Host、权限、策略、会话和 Agent 范围计算出的 Agent Tool 清单及其 revision。 | 不是安装包内全部函数的列表。 |
|
||||
| **Tool Call** | Agent 对 Inventory 内某个 Agent Tool 发起的一次远端调用,带有 call_id、参数和 inventory revision。它可先收到协议回执和进度,最后才收到业务结果。 | 不等于业务 operation,也不等于 App instance。 |
|
||||
| **Tool 协议回执 / 进度** | Runtime 对一条已接受调用的可重放协议事实,例如 `accepted`、`starting`、`activation_ready`、`started`;用于告诉 Agent 调用是否已接收、MiniApp 是否已就绪、业务是否真正开始。 | 不是 Tool 的业务 result,不受业务 output schema 约束,也不等于一次完成记录。 |
|
||||
| **Tool 业务结果** | Tool 的最终业务结论;必须符合该 Tool Manifest 的 output schema,并且对同一 call_id 只持久化、回传一次。 | 不是“请求已收到”或“正在启动”的临时状态。 |
|
||||
| **Runtime 内部业务动作** | Runtime 为实现业务请求执行的受控命令,例如创建 deadline operation、改变焦点、写 outbox、关闭实例。多个入口可以复用同一个动作。 | 不是自动公开给 Agent 的 Tool。 |
|
||||
| **UI Action** | 用户在本地 MiniApp 页面点击、输入或拖动后,经 SDK / Bridge 发送给 Runtime 的本地请求。 | 不发布给 Agent,也不带 Agent 身份。 |
|
||||
| **Host 生命周期意图** | Host 或工作区产生的前后台切换、返回、关闭、恢复、锁屏等本地事实或请求。 | 不是 Agent Tool,也不应伪造成用户的 IM 指令。 |
|
||||
| **MiniApp instance** | 一个 MiniApp 的运行实例;Runtime 管理其生命周期、作用域和启动状态。 | 不必然对应一次业务 operation。 |
|
||||
| **App 子会话** | Interact 中围绕一个 MiniApp instance 保存的、面向人与 Agent 的交互记录。 | 不等于一次专注、计时或其他业务 operation。 |
|
||||
| **MiniApp activation** | Runtime 管理的一次 MiniApp 启动过程:`starting → ready | failed | cancelled`。每个 Agent Tool 声明业务执行前要求的就绪条件。 | 不是 MiniApp 的业务数据,也不是 operation。 |
|
||||
| **MiniApp session data** | Runtime 为当前 `app_scope + app_session_id` 持久化的通用 JSON 数据字典;内容和可变性由 MiniApp 自己定义。 | 不是 Runtime 可理解的 App 业务 schema,也不是 IM 记录或 operation Store。 |
|
||||
| **operation** | Runtime 管理的实际业务执行单元,例如一次有 deadline 的专注。它有独立的状态机和终态。 | 不等于 Tool Call、App 子会话或界面实例。 |
|
||||
|
||||
## 2. Agent 的角色、能力范围与边界
|
||||
|
||||
### 2.1 Agent 的职责
|
||||
|
||||
远端 Agent 是意图理解者和工具调用者,不是本地 Runtime 的替身。它的职责是:
|
||||
|
||||
1. 从用户 IM、Voice 或其他交互内容理解意图;
|
||||
2. 根据 Runtime 已发布的 Agent Inventory 选择合适的 Tool;
|
||||
3. 按 Tool schema 构造参数,并携带当前 inventory revision 发起调用;
|
||||
4. 接收 Runtime 回传的受控结果,在用户会话中解释、追问或发起下一步调用。
|
||||
|
||||
当用户说“现在开始 20 分钟的冥想,帮我计时”时,正确链路如下:
|
||||
|
||||
~~~text
|
||||
用户的 IM 消息
|
||||
→ 远端 Agent 理解为启动专注
|
||||
→ Agent 调用 pomodoro.start
|
||||
→ Runtime 请求 Pomodoro 进入前台
|
||||
→ Host 确认前台激活后,Runtime 才创建并管理专注 operation
|
||||
→ Pomodoro MiniApp 用自身 session data 显示界面
|
||||
~~~
|
||||
|
||||
Agent 不需要、也不能自己一秒一秒倒数。时间事实由 Runtime 持久化的 deadline 和本地时间裁决;MiniApp 仅根据 Runtime 投影显示剩余时间。
|
||||
|
||||
### 2.2 Agent 可做的事
|
||||
|
||||
- 调用当前 Agent Inventory 中、对当前 Agent 和 conversation 可见的 Agent Tool;
|
||||
- 接收 Runtime 回传的 Tool 结果、进度和受控交互结果;
|
||||
- 在会话范围内发送内容、交互或后续 Tool 请求;
|
||||
- 在 Runtime 允许的范围内请求启动 Tool 所需的 MiniApp instance。
|
||||
|
||||
### 2.3 Agent 明确不能做的事
|
||||
|
||||
- 不能调用未在当前 Inventory 中声明的 Tool,或绕过 inventory revision、参数 schema、会话和策略校验;
|
||||
- 不能直接读写 Runtime Store、MiniApp instance state、Conversation Store、operation Store、outbox 或审计记录;
|
||||
- 不能直接调用 Tauri、文件、通知、麦克风、窗口或其他 Host 特权能力;
|
||||
- 不能直接执行 MiniApp 的任意函数、修改其 DOM 或访问 Surface Bridge;
|
||||
- 不能指定、伪造或跨会话操控任意 instance_id、operation_id、app_scope;
|
||||
- 不能通过 Tool Call 绕过 App 安装、启用、权限确认、风险策略或 Host 能力限制。
|
||||
|
||||
Runtime 对 Agent 输入实行不信任原则:即使调用来自已认证 Agent,也必须重新验证可见性、作用域、幂等、生命周期和输出。
|
||||
|
||||
## 3. 三条独立入口
|
||||
|
||||
Runtime 可以接收不同来源的请求,但来源决定身份、校验、审计和可用能力。三者不是同一入口。
|
||||
|
||||
| 入口 | 发起者 | Runtime 接收的内容 | 是否进入 Agent Inventory | 主要校验 | 典型示例 |
|
||||
|---|---|---|---|---|---|
|
||||
| **Agent Tool** | 远端 Agent | 有 Agent 身份、call_id 和 inventory revision 的远端 Tool 调用 | 是 | Agent、Inventory、Tool schema、conversation、策略、幂等 | Agent 调用 pomodoro.start。 |
|
||||
| **MiniApp UI Action** | 用户在本地 MiniApp 页面操作 | SDK / Bridge 发送的本地 Action | 否 | 当前 instance、App 权限、UI schema、业务前置状态、用户确认 | 用户未来点击开始计时。 |
|
||||
| **Host 生命周期意图** | Runtime Shell、Host 或系统 | 前后台、返回、关闭、恢复、系统状态等本地事件 | 否 | instance 生命周期、焦点栈、系统策略、operation 收口规则 | 用户离开 Pomodoro 工作区。 |
|
||||
|
||||
三条入口的流程如下:
|
||||
|
||||
~~~text
|
||||
一、远端 Agent Tool
|
||||
用户 IM / Voice
|
||||
→ 远端 Agent 理解意图
|
||||
→ Agent Tool Call
|
||||
→ Runtime 校验、路由和持久化
|
||||
→ Runtime 内部业务动作
|
||||
→ MiniApp 状态投影 / Agent 结果 outbox
|
||||
|
||||
二、本地 UI Action(未来可选)
|
||||
用户点击 MiniApp 页面
|
||||
→ SDK / Surface Bridge 的本地 Action
|
||||
→ Runtime 校验和持久化
|
||||
→ Runtime 内部业务动作
|
||||
→ MiniApp 状态投影;必要时由 Runtime 产生 Agent 可见结果
|
||||
|
||||
三、Host 生命周期意图
|
||||
返回 / 切换工作区 / 关闭 / 恢复 / 系统事件
|
||||
→ Host Lifecycle Intent
|
||||
→ Runtime 生命周期与焦点裁决
|
||||
→ Runtime 内部业务动作 / operation 收口
|
||||
→ MiniApp 状态投影 / 必要的 Agent 结果 outbox
|
||||
~~~
|
||||
|
||||
后两条入口可以在 Runtime 内部复用与 Agent Tool 相同的业务服务。例如未来 UI 的开始按钮与 Agent Tool 的开始调用都可以请求同一个“创建专注 operation”动作。但复用内部动作不改变入口身份:UI Action 不会因此成为 Agent Tool,Host 生命周期也不会因此伪造成 Tool Call。
|
||||
|
||||
## 4. Agent Tool 的发布与调用
|
||||
|
||||
### 4.1 MiniApp 声明,Runtime 发布,Agent 调用
|
||||
|
||||
Agent Tool 的权威链条必须保持为:
|
||||
|
||||
~~~text
|
||||
MiniApp 源码中的 SDK Tool 声明
|
||||
→ 构建生成 Manifest 的不可执行 Tool 描述
|
||||
→ Runtime 验证 Manifest、App 与 Host 条件
|
||||
→ Runtime 计算并发布动态 Agent Inventory
|
||||
→ 远端 Agent 看到 Inventory 后选择 Tool
|
||||
→ Agent 发起 Tool Call
|
||||
→ Runtime 验证、路由、执行并回传
|
||||
~~~
|
||||
|
||||
MiniApp 只声明自己支持的候选能力,不能自行向 Agent 宣传、发送或执行 Tool Call。Runtime 是唯一的 Inventory 发布者、远端调用接收者和结果回传者。
|
||||
|
||||
Tool 声明的开发者入口是 MiniApp SDK,而不是第二份手写 Manifest:MiniApp 用 `defineAgentTools(...)` 同时声明
|
||||
公开 method、schema、activation requirement 和 `miniapp_sdk` Tool 的本地 handler。SDK 源码声明是唯一真相;构建阶段
|
||||
自动生成随 Bundle 安装的 Manifest Tool 描述。该描述供 Runtime 验证、Registry 和 Inventory 使用,但不包含 handler、
|
||||
私有函数名或其他可执行内容。Bundle 内 SDK 保留 `method → handler` 映射,收到 Runtime 的统一 Tool 调用后在 MiniApp
|
||||
内部分发。安装包仍可有 App 身份、版本、签名与 Host 能力等基础元数据,但开发者不再维护一份独立的 Tool 清单来重复
|
||||
描述源码已经定义的能力。`runtime` Tool 只能引用 Runtime 内置的受控执行类型。这样既有类似 MCP Tool 描述文件的稳定
|
||||
能力契约,也不让 Runtime 知道 MiniApp 的内部实现。
|
||||
|
||||
一个 Agent Tool Descriptor 至少应定义:
|
||||
|
||||
- 稳定的 app_scope、method、contract_version;
|
||||
- 面向 Agent 的业务说明、输入 schema 和输出 schema;
|
||||
- 调用模型,例如 query、command、interactive 或 operation;
|
||||
- 幂等键、超时或终态规则;
|
||||
- 启动就绪条件:`app_ready`(目标 MiniApp session 已 active、可接收方法调用)、`foreground_required`
|
||||
(在 app_ready 的基础上还必须已在前台),或 `activation_not_required`(只操作已有 Runtime 事实,不能因此
|
||||
启动或唤醒 MiniApp);
|
||||
- 所需权限、风险级别和对前台 / Host 的要求。
|
||||
|
||||
Runtime 实际发布给特定 Agent 的清单是动态交集:
|
||||
|
||||
~~~text
|
||||
已验证的 Manifest Tool
|
||||
∩ App 已安装并启用
|
||||
∩ Host 当前支持
|
||||
∩ 用户 / 组织策略允许
|
||||
∩ 当前 Agent 与 conversation 可见
|
||||
∩ 所需前置权限满足
|
||||
~~~
|
||||
|
||||
### 4.2 Runtime 收到 Tool Call 后的职责
|
||||
|
||||
Runtime 收到远端 Agent Tool Call 后,按以下顺序处理:
|
||||
|
||||
1. 验证远端 Envelope、Agent 身份、conversation 关联和 Inventory revision;
|
||||
2. 验证目标 Agent Tool 当前可见,且参数符合已发布 schema;
|
||||
3. 用 call_id 执行幂等去重,持久化调用记录和审计;
|
||||
4. 解析 Tool 所允许的作用域,不接受 Agent 任意指定跨会话目标;
|
||||
5. 创建或复用 MiniApp activation,并在 Tool 所需的 `app_ready` 或 `foreground_required` 条件满足后,选择内部
|
||||
执行路径:直接处理、将 method 与已验证参数投递 App SDK、创建 operation 或拒绝;Runtime 不解释参数的
|
||||
MiniApp 业务含义,也不直接写 MiniApp 的 session data;
|
||||
6. 原子持久化业务状态、operation 终态与需要回传的 outbox;
|
||||
7. 仅向相关 MiniApp 投递经过裁剪的状态或调用投影;
|
||||
8. 将协议回执 / 进度与最终业务结果分别持久化并经 outbox 回传:前者不使用业务 output schema;后者必须通过该 schema 校验。
|
||||
|
||||
Tool Call 的成功接收不等于业务已经完成。对于长期 operation,Runtime 可以先确认接受调用,业务真正开始时再报告
|
||||
进度,之后只在进入唯一终态时回传最终结果。相同 `call_id` 重放时,Runtime 返回已持久化的**最新协议回执或最终
|
||||
业务结果**,不重新执行业务,也不追加新的 outbox 消息。
|
||||
|
||||
### 4.3 activation 就绪通知与提前到达的后续调用
|
||||
|
||||
当 Agent 的一个 Tool 调用使 MiniApp 进入 `starting`,Runtime 必须把 activation 的受控状态作为与该 `call_id`
|
||||
关联的协议进度通知给发起 Agent:
|
||||
|
||||
```text
|
||||
accepted / starting
|
||||
= Runtime 已接受调用,MiniApp 正在准备
|
||||
|
||||
activation_ready
|
||||
= MiniApp 已 active / ready;后续要求 app_ready 的 Tool 现在可以接收
|
||||
|
||||
activation_failed / activation_cancelled
|
||||
= 本次启动不能继续;依赖它的等待中调用将得到受控失败结果
|
||||
```
|
||||
|
||||
这些通知是 Runtime 持久化 activation 后,经 Runtime outbox 发出的事实;MiniApp、Surface 和 Host 只能向 Runtime
|
||||
报告自己的就绪或失败,不能直接向 Agent 发送通知。公开通知只携带 `call_id`、`app_scope`、状态和受控 reason,不能
|
||||
把 `instance_id`、`app_session_id` 变成 Agent 可指定或跨会话操控的目标。
|
||||
|
||||
Agent 收到 `activation_ready` 后再发送有依赖关系的后续 Tool,是推荐的编排方式;但它不是 Runtime 接收调用的硬前提。
|
||||
若一条合法、同 conversation / app scope 的 `app_ready` 或 `foreground_required` Tool 在目标 activation 仍为
|
||||
`starting` 时提前到达,Runtime 必须持久化该调用并把它挂到同一 activation 的等待队列,在 ready 后按已持久化的到达
|
||||
顺序投递。activation 失败或取消时,Runtime 不执行这些 handler,而为各调用回传稳定的 `app_activation_failed` 或
|
||||
`app_activation_cancelled`。`activation_not_required` Tool 不进入等待队列,仍立即处理;因此 Pomodoro 在启动中收到
|
||||
`pomodoro.interrupt` 时可以立即取消启动。
|
||||
|
||||
某些 Tool 的业务进度比通用 activation 状态更强时,不应重复发两条没有新增信息的通知。第 04 次的
|
||||
`pomodoro.start` 在 Host 确认前台的同一提交中同时得到 `activation = ready`、创建 operation 并开始计时,因此只向
|
||||
Agent 发 `started`;它语义上已包含“Pomodoro ready”,且额外保证“前台已确认、计时已真实开始”。
|
||||
|
||||
## 5. 状态、身份与生命周期不得混同
|
||||
|
||||
本节是 Runtime 状态模型的一部分。下列对象可以互相关联,却不能共享语义或被一个 ID 替代。
|
||||
|
||||
~~~text
|
||||
Conversation(人与主 Agent 的协作范围)
|
||||
├── App 子会话(MiniApp 启动后可在 Interact 中保存的交互记录)
|
||||
├── MiniApp instance(实际运行的界面 / SDK 上下文)
|
||||
│ ├── activation(starting / ready / failed / cancelled)
|
||||
│ └── session data(该 App 子会话的通用、MiniApp 自解释 JSON 数据)
|
||||
├── Agent Tool Call(远端调用与其幂等、审计和回执)
|
||||
└── operation(实际业务执行;可无,也可独立于界面按规则收口)
|
||||
~~~
|
||||
|
||||
| 对象 | 最小职责 | 应有的状态 / 身份 | 不能承担的事实 |
|
||||
|---|---|---|---|
|
||||
| Conversation | 用户与主 Agent 的协作边界 | conversation_id | 不能表示某次业务执行。 |
|
||||
| App 子会话 | 保存围绕 MiniApp 的可见交互与历史 | app_session_id 或等价记录 | 不能替代 MiniApp instance 或 operation。 |
|
||||
| MiniApp instance | 管理运行、焦点、挂起、关闭和 SDK scope | instance_id、生命周期状态 | 不自动代表一次业务 operation。 |
|
||||
| MiniApp activation | 将 App 准备为 active / ready 的过程;某些 Tool 还要求成为前台 | instance_id、app_session_id、starting / ready / failed / cancelled | 不是业务 operation,不能自行产生 deadline 或终态。 |
|
||||
| MiniApp session data | MiniApp 自己的业务 / 展示数据 | app_scope 加 app_session_id 加 revision | Runtime 不理解内容;不能裁决 deadline、Tool 终态或 outbox。 |
|
||||
| Agent Tool Call | 一次远端请求、幂等与审计 | call_id、Tool key、inventory revision | 不能替代长期 operation。 |
|
||||
| operation | 实际业务过程、deadline、进度和唯一终态 | operation_id、业务状态机 | 不等于一次 MiniApp 启动或 App 子会话。 |
|
||||
|
||||
因此,一个 MiniApp 会话或 instance 启动时未必启动业务 operation;一个 operation 也不应靠 UI 是否存在来判断是否完成。Tool 可以声明其业务开始是否需要前台:例如 `pomodoro.start` 只有在用户确实进入前台专注界面后才创建 operation;未来 `todo.add` 则可以在后台数据上下文就绪后完成。具体 Tool 的就绪条件不能上升为 Runtime 的统一前台要求。
|
||||
|
||||
Runtime 可以在一个原子事务中同时更新 operation、activation、session data 和 outbox,但它们仍保留各自的事实来源和恢复规则。
|
||||
|
||||
## 6. 各层能力边界
|
||||
|
||||
| 主体 | 负责什么 | 可以请求什么 | 明确禁止 |
|
||||
|---|---|---|---|
|
||||
| **用户** | 通过 IM / Voice 表达意图;未来可直接操作 MiniApp UI。 | Agent 对话;本地 UI Action。 | 不直接调用 Agent Tool 协议或 Runtime 内部 Store。 |
|
||||
| **远端 Agent** | 意图理解、Tool 选择、调用参数和会话回复。 | 当前 Inventory 中的 Agent Tool。 | 直接访问 Host、Store、MiniApp DOM 或任意 App 函数。 |
|
||||
| **Runtime** | Agent 通信、Tool Registry、校验、路由、状态、operation、生命周期、持久化、outbox、审计。 | 通过 Host Provider 使用受控系统能力;向 App 投递受限 SDK 投影。 | 将 Transport、密钥或跨 App 数据直接暴露给 Agent / MiniApp。 |
|
||||
| **MiniApp** | 自己的界面、已声明业务处理和当前 App 子会话的 session data。 | SDK 提供的投影、声明性 Action、Capability request。 | 自行连接 Agent / AppServer、直接写 Runtime Store、直接使用 Tauri / Host。 |
|
||||
| **Surface / UI** | 渲染和收集本地用户事件。 | 受限 Surface Bridge。 | 假冒 Agent、构造 Agent Tool Call、直接操作 Host DOM 或特权 API。 |
|
||||
| **Host** | 提供窗口、通知、文件、音频等运行环境和系统事实。 | 向 Runtime 报告生命周期意图。 | 直接改变 MiniApp 业务状态、直接向 Agent 发布工具或交付特权能力。 |
|
||||
|
||||
特别地,Host 只报告本地事实或意图,最终如何改变业务状态由 Runtime 的业务规则裁决。例如“用户离开专注工作区”可触发 Runtime 中断当前专注,但 Host 不是在调用 pomodoro.interrupt,也不能假装成 Agent。
|
||||
|
||||
## 7. Pomodoro:第 04 次迭代的具体应用
|
||||
|
||||
第 04 次迭代用 Pomodoro 验证的是第一条入口,即 Agent Tool 到 Runtime 再到 MiniApp 投影。其冻结模型为:
|
||||
|
||||
~~~text
|
||||
用户在 IM 中说:现在开始 20 分钟的冥想,帮我计时
|
||||
→ 远端 Agent 调用 pomodoro.start
|
||||
→ Runtime 创建 Pomodoro instance、App 子会话和 activation(starting)
|
||||
→ Host 确认 Pomodoro 已成为当前前台 App
|
||||
→ Runtime 创建 deadline operation,Pomodoro 进入 focusing 并被动显示
|
||||
|
||||
用户在 IM 中说:停止或结束这次专注
|
||||
→ 远端 Agent 调用 pomodoro.interrupt
|
||||
→ 若仍在 starting,Runtime 取消启动;若已 focusing,Runtime 原子收口当前 operation
|
||||
→ Pomodoro 只接收通用 lifecycle 关闭通知
|
||||
~~~
|
||||
|
||||
本迭代的 pomodoro.start 把“Pomodoro 已成为用户当前前台界面”作为开始专注的前提:Runtime 接受调用并创建
|
||||
`starting` activation 后,先向 Agent 回传 `accepted / starting` 协议回执;这只能表示“正在进入专注模式”,不能说
|
||||
“已经开始计时”。Host 前台确认与 operation 创建成功后,Runtime 再回传 `started` 进度,此时 Agent 才能说“已开始
|
||||
为你计时”。确认之前不存在 operation、倒计时或专注 IM 记录;最终无法前台激活时,Runtime 回传符合 start output
|
||||
schema 的 `not_started` 业务结果并恢复 Interact。pomodoro.interrupt 由 Agent 调用,而不是 Runtime 私下调用 Pomodoro:
|
||||
它可取消当前启动尝试,或收口当前 focusing operation。Runtime 根据调用所在 conversation 定位目标,不接受 Agent 指定
|
||||
或伪造 instance_id、operation_id 等目标。
|
||||
|
||||
当前 Pomodoro Surface 不实现开始、暂停、继续、停止等业务按钮。未来如果引入“用户先让 Agent 设定时长、随后自己点击开始”之类的 UI,按钮的 onClick 应产生独立的本地 UI Action;Runtime 可以让它复用创建 operation 的内部动作,但不得改变当前 Agent Tool 的含义,也不要求为第 04 次迭代提前加入两阶段 Tool 模型。
|
||||
|
||||
用户切回 Interact、切至其他 MiniApp 或关闭 Pomodoro 属于 Host 生命周期意图。Runtime 按 Pomodoro 的业务规则将其收口为 interrupted,但不伪造一条 Agent Tool Call;自动暗屏、锁屏和 Surface 重载则不等于离开专注。
|
||||
|
||||
## 8. 对协议与实现的约束
|
||||
|
||||
1. Agent Inventory 只能包含 Agent Tool Descriptor;不得包含 UI Action、Host 生命周期事件或 MiniApp 任意内部方法。
|
||||
2. Runtime 必须分别记录 Agent Tool Call、UI Action 和 Host 生命周期意图的来源、身份、关联 ID 与审计类型;不得用一种来源伪造另一种。
|
||||
3. UI Action 的本地 Bridge 协议可在后续版本独立设计,但其入口、权限和审计不得依赖远端 Agent 身份或 inventory revision。
|
||||
4. Runtime 内部业务服务可由多个入口复用,但每个入口都必须先完成自己的授权、作用域和幂等校验。
|
||||
5. MiniApp session data 只能保存当前 App 子会话的 MiniApp 自定义 JSON 数据;不能借此完成 Tool、operation、outbox、焦点或 Host Capability 的特权动作。
|
||||
6. Agent Tool 的调用结果必须由 Runtime 校验 schema、持久化并经 outbox 回传;MiniApp 不得直接向 Agent 或 Transport 发送结果。
|
||||
7. 在没有明确产品决议前,不得为了未来 UI 的可能性改变已发布 Agent Tool 的语义,或向当前 Inventory 额外加入预设、打开、准备等 Tool。
|
||||
8. 对 operation、activation、session data、Tool receipt、outbox 和工作区目标的同一次业务变更,Runtime 必须先原子持久化事实,
|
||||
再让 Lifecycle、Host 和 Surface 对账;持久化失败不得改变任何层,提交后的 UI / Host 失败不得回滚已提交事实。
|
||||
|
||||
## 9. 非目标
|
||||
|
||||
本文不定义:
|
||||
|
||||
- Voice 的具体协议或语音识别实现;它未来只需复用“用户表达意图,再由 Agent 调用 Tool”的语义;
|
||||
- UI Action 的具体事件名称、Bridge 线协议或按钮设计;
|
||||
- 允许用户绕过 Agent 调用远端 Agent Tool;
|
||||
- 允许 MiniApp 直接与 Agent、AppServer、Tauri 或任意系统能力通信;
|
||||
- 将所有 MiniApp 都强制设计为有 operation 的应用;
|
||||
- 因未来 UI 交互扩展而修改第 04 次 Pomodoro 已冻结的直接开始模型。
|
||||
@@ -0,0 +1,593 @@
|
||||
# LineUp App 架构设计
|
||||
|
||||
**状态:** 当前权威设计基线
|
||||
**更新日期:** 2026-08-04
|
||||
**适用实现:** Tauri 2 Desktop Host 与同代码 Web Reference Host
|
||||
**本文取代:** `DESIGN.md`、`M4_COMPATIBILITY_AND_RELEASE.md`、`TECHNOLOGY_SELECTION.md`
|
||||
|
||||
## 1. 设计结论
|
||||
|
||||
**LineUp 是用户安装和使用的主应用;LineUp Runtime 是 LineUp App 内部提供给小程序的核心
|
||||
运行环境。** Runtime 不是与小程序平级的产品 App,也不是一个独立客户端。
|
||||
|
||||
```text
|
||||
LineUp App
|
||||
│
|
||||
├── App Shell / Host
|
||||
│ ├── 当前前台小程序的挂载与切换
|
||||
│ ├── 连接状态、通知和安全恢复入口
|
||||
│ └── Tauri / Web Host Provider
|
||||
│
|
||||
├── LineUp Runtime
|
||||
│ ├── AppServer / Remote Agent 通信
|
||||
│ ├── Conversation、Store、sync loop、outbox
|
||||
│ ├── MiniApp Registry、Instance、Focus、Lifecycle
|
||||
│ ├── Tool Router、Inbox、Inventory
|
||||
│ ├── Surface、Capability、Artifact、Audit
|
||||
│ └── LineUp MiniApp SDK
|
||||
│
|
||||
└── MiniApps
|
||||
├── Interact(系统内建:IM / Audio / Video)
|
||||
├── Task Dashboard(参考 / 已安装小程序)
|
||||
├── Whiteboard(参考 / 已安装小程序)
|
||||
├── Draw-and-Guess(参考 / 已安装小程序)
|
||||
└── 未来第三方小程序
|
||||
```
|
||||
|
||||
任何 MiniApp 都必须通过 Runtime SDK 使用通信、Tool、生命周期、Surface 和系统能力;不得
|
||||
建立独立 Agent 通信链路,或直接访问 Host 特权、Store、认证信息及其他 MiniApp 数据。
|
||||
|
||||
## 2. 产品边界与术语
|
||||
|
||||
| 术语 | 含义 | 不能做什么 |
|
||||
|---|---|---|
|
||||
| **LineUp App** | 用户实际使用的主应用、产品容器和交付单位。 | 不能把业务规则重新堆进 Shell。 |
|
||||
| **App Shell / Host** | Tauri 或 Web 中的挂载、切换、通知、恢复和受限 Host Provider。 | 不解释 Agent 协议,不决定 Tool/App 路由。 |
|
||||
| **LineUp Runtime** | App 内部的长期协调环境;唯一拥有通信、可靠性、MiniApp 调度与安全决策。 | 不负责任意 MiniApp 的具体 DOM 或业务 UX。 |
|
||||
| **MiniApp** | 运行在 Runtime 上的功能单元。 | 不直连 Agent/AppServer,不直写 Runtime 状态。 |
|
||||
| **System MiniApp** | 随 LineUp 发布、代码可信的内建 MiniApp,如 Interact。 | 仍不能绕过 Runtime 调度或 Capability Policy。 |
|
||||
| **Installed MiniApp** | 经验证后在受限 Surface 中运行的小程序。 | 不访问 Host DOM、Tauri invoke、token、任意网络或其他 MiniApp。 |
|
||||
| **MiniApp SDK** | Runtime 向 MiniApp 开放的受控能力集合。 | 不是通用 Web、Tauri 或 Agent SDK。 |
|
||||
| **Tool** | Agent 通过 Runtime 调用的、Manifest 已声明的 MiniApp 功能。 | 不是 App 内部任意函数。 |
|
||||
| **Capability** | Runtime/Host 保管的录音、文件、通知等系统能力。 | 不是 MiniApp 自动拥有的权限。 |
|
||||
| **Surface** | 复杂交互使用的受限 UI 容器。 | 不承载未验证远端脚本或 Host 特权。 |
|
||||
|
||||
### 2.1 Interact MiniApp
|
||||
|
||||
`Interact` 是第一个系统级 MiniApp,也是用户与 Agent 的默认交互入口:
|
||||
|
||||
```text
|
||||
Interact MiniApp
|
||||
├── IM Mode:文字、图片、短消息、任务和结果投影
|
||||
├── Audio Mode:实时语音(后续)
|
||||
├── Video Mode:实时视频(后续)
|
||||
└── IM 标准交互的默认 Renderer Provider:notice / choice / confirm / input
|
||||
```
|
||||
|
||||
当前实现仍保留兼容名称:
|
||||
|
||||
```text
|
||||
产品概念:Interact MiniApp
|
||||
当前 app_scope:chat
|
||||
当前目录:tauri/src/core-apps/chat/
|
||||
```
|
||||
|
||||
目录和作用域改名属于独立迁移任务,不能和 Runtime/MiniApp SDK 重构混在一起。
|
||||
|
||||
Interact 是可信内建代码,因此可以使用 LineUp 的可信 DOM 组件;但它和所有 MiniApp 一样,
|
||||
必须经 SDK 请求 Runtime 行为,不能直接访问 Transport、Conversation Store、焦点栈、App
|
||||
Registry、原始 Agent Envelope 或系统能力。
|
||||
|
||||
Interact 的 SDK 采用“通用 Runtime 操作 + 可信 UI 投影”的适配方式:Runtime 操作负责消息发送、
|
||||
标准交互提交/取消/过期、草稿和任务操作;UI 投影只负责 IM 视图、Agent 消息订阅和子会话展示。
|
||||
UI 投影不是另一套通信或存储协议,Interact 的可信 DOM 也不因此取得 Transport、Store、原始
|
||||
Agent Envelope、Host DOM 根节点或系统能力。当前 `chat` 兼容接口保留扁平方法,但新代码应按
|
||||
`sdk.runtime.*` 和 `sdk.ui.*` 使用。
|
||||
|
||||
## 3. 当前技术与 Host 基线
|
||||
|
||||
唯一客户端实现与验收基线是:
|
||||
|
||||
```text
|
||||
Tauri 2 Desktop Host + Web Reference Host
|
||||
```
|
||||
|
||||
它们运行同一份 TypeScript Runtime 与 MiniApp 代码,并不是两个客户端:
|
||||
|
||||
- **Web Reference Host**:浏览器开发、Tailscale 联调和自动化验证入口;只能提供可安全降级
|
||||
的浏览器能力。
|
||||
- **Tauri Desktop Host**:正式桌面交付;只在 Runtime 授权后提供文件、通知、媒体等 Host
|
||||
Provider 能力。
|
||||
|
||||
不规划独立 Android/Kotlin 客户端,也不规划 Wails Host。已退役的 Android/Wails Spike 只保留
|
||||
在 Git 历史中,不能作为架构、协议、目录或验收依据。
|
||||
|
||||
开发命令:
|
||||
|
||||
```bash
|
||||
cd lineup-app/tauri
|
||||
|
||||
npm run web:dev # Web / Tailscale Reference Host
|
||||
npm run desktop:dev # Tauri Desktop Host
|
||||
npm test -- --run # Runtime、MiniApp 与 Host 回归
|
||||
npm run build # TypeScript 检查与 Web production build
|
||||
```
|
||||
|
||||
Web Host 与 AppServer 的地址必须按访问位置匹配:同机浏览器使用 `127.0.0.1`,Tailscale 浏览器
|
||||
使用受信任的 Tailscale 地址。`0.0.0.0` 仅是服务器监听地址,不能作为浏览器 API 目标。
|
||||
|
||||
## 4. Runtime 的唯一所有权
|
||||
|
||||
Runtime 是以下对象的唯一所有者:
|
||||
|
||||
```text
|
||||
Transport
|
||||
Conversation Store
|
||||
sync loop
|
||||
outbox
|
||||
协议兼容与原始 Envelope 校验
|
||||
作用域筛选与 message_id 去重
|
||||
MiniApp Inbox 与 ACK
|
||||
MiniApp Registry、Instance、Focus、Lifecycle
|
||||
Tool Router、Inventory、Capability、Surface、审计与恢复
|
||||
```
|
||||
|
||||
当前消息路径必须保持单向:
|
||||
|
||||
```text
|
||||
AppServer / Remote Agent
|
||||
→ LineUp Runtime
|
||||
→ 协议、scope、权限、去重和持久化
|
||||
→ MiniApp SDK Inbox / Tool Call
|
||||
→ MiniApp UX
|
||||
→ SDK Result / Progress / Lifecycle Request
|
||||
→ Runtime 校验、审计、outbox
|
||||
→ AppServer / Remote Agent
|
||||
```
|
||||
|
||||
禁止项:
|
||||
|
||||
```text
|
||||
MiniApp 直接 fetch AppServer 或 Agent
|
||||
MiniApp 直接读写 Conversation Store、sync cursor 或 outbox
|
||||
MiniApp 构造或发送原始 Agent Envelope
|
||||
MiniApp 直接切换其他 MiniApp、改写 focus stack 或 Registry
|
||||
MiniApp 直接调用 Tauri invoke、Host DOM 根节点或系统能力
|
||||
Surface 从消息 payload 直接加载 URL、HTML、CSS、JS、Bundle 或 source
|
||||
```
|
||||
|
||||
## 5. MiniApp 信任模型与 SDK 权限视图
|
||||
|
||||
所有 MiniApp 共享 Runtime 的协议语义,但按来源获得不同的 SDK 视图。
|
||||
|
||||
| 能力 | Interact / System MiniApp | Installed MiniApp |
|
||||
|---|---:|---:|
|
||||
| 接收自身 Inbox、ACK | 可以 | 可以 |
|
||||
| 接收 Runtime Tool Call | 可以 | 可以 |
|
||||
| 上报 progress / result / error | 可以 | 可以 |
|
||||
| 请求前台、后台、关闭 | 可以,由 Runtime 决定 | 可以,由 Runtime 决定 |
|
||||
| 请求 Surface | 可以,经 Runtime Policy | 可以,经 Runtime Policy |
|
||||
| 请求 Capability | 可以,经 Runtime Policy | 可以,经 Runtime Policy |
|
||||
| 可信内建 DOM 组件 | 可以 | 不可以 |
|
||||
| 隔离 iframe / Surface Bridge | 可选 | 必须 |
|
||||
| 直连 Transport、Store、Agent | 不可以 | 不可以 |
|
||||
| 直接 Host / Tauri / OS 调用 | 不可以 | 不可以 |
|
||||
| 读取其他 MiniApp 数据 | 不可以 | 不可以 |
|
||||
|
||||
系统内建不等于绕过 Runtime:可信来源只决定代码的发布与 UI 执行方式,不改变 Runtime 的
|
||||
通信、焦点、Tool、权限和审计边界。
|
||||
|
||||
## 6. MiniApp 实例、焦点与恢复
|
||||
|
||||
MiniApp Registry 管理“有哪些小程序”,Instance Manager 管理“哪些实例正在运行”。二者不能
|
||||
混为一个状态。
|
||||
|
||||
```ts
|
||||
type MiniAppInstanceRecord = {
|
||||
instance_id: string;
|
||||
app_scope: string;
|
||||
conversation_id?: string;
|
||||
state:
|
||||
| "starting"
|
||||
| "foreground"
|
||||
| "background"
|
||||
| "suspended"
|
||||
| "stopping"
|
||||
| "stopped"
|
||||
| "failed";
|
||||
parent_instance_id?: string;
|
||||
started_at: string;
|
||||
stopped_at?: string;
|
||||
error?: string;
|
||||
};
|
||||
```
|
||||
|
||||
Runtime 保存焦点栈,不以单个 MiniApp 自己的布尔状态作为事实来源:
|
||||
|
||||
```text
|
||||
focus stack:
|
||||
chat:audio-001
|
||||
draw-and-guess:game-001
|
||||
|
||||
foreground:
|
||||
draw-and-guess:game-001
|
||||
|
||||
background:
|
||||
chat:audio-001
|
||||
```
|
||||
|
||||
当扩展 MiniApp 关闭或失败时,Runtime 必须恢复焦点栈中的前一有效实例。重启后,Runtime
|
||||
恢复有界的实例与焦点状态;`starting` 或 `stopping` 中断的实例必须安全降级为 `suspended`,
|
||||
不得伪装为已完成的 Host 操作。
|
||||
|
||||
## 7. MiniApp SDK v1
|
||||
|
||||
下一阶段的核心交付是 **LineUp MiniApp SDK v1**。SDK 不是万能 API,而是 Runtime 根据
|
||||
Manifest、实例状态、权限和 Policy 开放的最小请求集合。
|
||||
|
||||
```ts
|
||||
interface LineUpMiniAppSDK {
|
||||
readonly context: MiniAppContext;
|
||||
|
||||
inbox: MiniAppInboxAPI;
|
||||
tools: MiniAppToolAPI;
|
||||
lifecycle: MiniAppLifecycleAPI;
|
||||
surfaces: MiniAppSurfaceAPI;
|
||||
capabilities: MiniAppCapabilityRequestAPI;
|
||||
}
|
||||
|
||||
type MiniAppContext = {
|
||||
app_scope: string;
|
||||
instance_id: string;
|
||||
conversation_id: string;
|
||||
app_version: string;
|
||||
state: "starting" | "foreground" | "background" | "suspended";
|
||||
};
|
||||
```
|
||||
|
||||
### 7.1 Inbox
|
||||
|
||||
```ts
|
||||
interface MiniAppInboxAPI {
|
||||
list(after_message_id?: string): readonly MiniAppMessage[];
|
||||
subscribe(listener: (message: MiniAppMessage) => void): Unsubscribe;
|
||||
acknowledge(message_id: string): boolean;
|
||||
}
|
||||
```
|
||||
|
||||
Runtime 在投递前校验 Envelope、scope、目标 MiniApp、实例状态和去重;消息先持久化,再由
|
||||
MiniApp ACK。后台化、刷新、Surface 重载或 Runtime 重启不能丢失未处理消息。
|
||||
|
||||
### 7.2 Tool
|
||||
|
||||
```ts
|
||||
interface MiniAppToolAPI {
|
||||
subscribe(listener: (call: MiniAppToolCall) => void): Unsubscribe;
|
||||
get(call_id: string): MiniAppToolCall | undefined;
|
||||
reportProgress(call_id: string, progress: MiniAppToolProgress): Promise<CommandReceipt>;
|
||||
complete(call_id: string, result: JsonObject): Promise<CommandReceipt>;
|
||||
fail(call_id: string, failure: MiniAppToolFailure): Promise<CommandReceipt>;
|
||||
cancel(call_id: string, reason?: string): Promise<CommandReceipt>;
|
||||
}
|
||||
```
|
||||
|
||||
`complete`、`fail`、`cancel` 都是对 Runtime 的请求,不是直接网络发送。Runtime 必须验证
|
||||
`call_id` 的实例归属、合法状态迁移、输入/输出 schema、幂等性和权限,然后持久化、审计并
|
||||
写入 outbox。
|
||||
|
||||
一旦 Runtime 接受某个 App instance 的关闭,关闭表示释放该 instance,不是把它留在后台:Runtime 停止
|
||||
向它投递新普通 Tool,将仍为 `received / routing / waiting_for_app / running` 的绑定 Tool 原子收敛为
|
||||
`cancelled(app_closed)`,每条 Tool 只写一条 cancelled outbox;已 `submitted` 的结果不可改写,继续可靠发出。
|
||||
然后才关闭 Surface、停止 instance、结束 App 子会话并恢复焦点。普通 Tool 的完成或失败本身不关闭 App。
|
||||
标准交互是例外:App 关闭只通知 Agent,不自动取消仍 pending 的人与 Agent 交互。
|
||||
|
||||
### 7.3 人与 Agent 标准交互、Lifecycle、Surface 与 Capability
|
||||
|
||||
`notice`、`choice`、`confirm`、`input` 是 Interact 的人与 Agent 交互组件,与 IM 消息同属于
|
||||
当前会话;它们不是 MiniApp SDK,也不用于 MiniApp 自己的业务表单、确认或输入。Agent 通过
|
||||
标准 Tool 交互请求发起它们,Runtime 校验、持久化、恢复、审计并可靠回传结果,Interact 负责呈现。
|
||||
|
||||
这四种是普通的人与 Agent 会话交互,不在 SDK v1 中统一定义为密码或秘密输入。已提交内容遵循普通 IM
|
||||
的会话历史、保留和日志基线;尤其不能因为它们是普通输入,就突破全局“日志不记录会话正文、Tool 参数、
|
||||
token 等内容”的约束。未提交 `input` 草稿仅保留在当前运行期,重启清空。密码、卡密、私钥、临时 token
|
||||
等真正秘密输入留待未来作为独立的 `password-input` 原语设计;届时 Runtime 必须按类型强制其展示、记录、
|
||||
传递和清理规则,Agent 不能用普通 `input` 绕过这些保护。
|
||||
|
||||
每条人与 Agent 的标准交互都必须在 Interact / IM 中留下相应的卡片、气泡或结果记录。`notice` 是只读
|
||||
IM 卡片;当前界面可同时短暂 Toast 提醒,但 Toast 只是同一条记录的辅助呈现,不能作为唯一消息或用户
|
||||
已阅读的证明。Runtime 成功持久化 notice 卡片并交给 Interact 呈现后,即可向 Agent 返回 `accepted`。
|
||||
|
||||
MVP 中,一个 LineUp Runtime 只连接一个 Agent,当前登录会话的 `agent_uid` 作为每条交互和
|
||||
App 子会话的 `agent_id`。此处保存 Agent 身份是为了让答案按创建时的上下文回到正确对象;本阶段
|
||||
不实现多 Agent 连接、切换、会话列表、outbox 或跨 Agent 路由。
|
||||
|
||||
Agent 发 interactive Tool 时,未带经过 Runtime 验证的 `app_session_context.app_session_id`,一律归入主 IM;
|
||||
Runtime 绝不能按当前前台 App 猜测归属。若带有该上下文,Runtime 只接受同一 Agent、同一父会话的真实
|
||||
App 子会话;已经关闭的子会话可保留为历史上下文,但不复活旧 App。Runtime 创建子会话时会可靠告知 Agent
|
||||
其稳定 ID,非法或跨会话的引用直接拒绝且不创建交互。
|
||||
|
||||
当 Interact 位于前台时,标准交互显示在 IM 时间线/卡片中;当 bundled MiniApp 位于前台时,
|
||||
Interact / Shell 可以在当前 App 之上显示同一会话的交互层。视觉位置不改变归属:请求和结果始终
|
||||
属于当前 conversation、Agent call 与 Interact instance,结果经 Runtime 回传 Agent,而不交给
|
||||
前台 MiniApp。
|
||||
|
||||
用户作答时,Interact 只把“交互 ID + 用户动作/答案”交给 Runtime,不直接把答案发送给 Agent。Runtime
|
||||
从创建时保存的记录取得 Agent、主会话、App 子会话和 Tool 的归属,并核对当前 LineUp / Interact 会话、
|
||||
有效期、答案格式和是否已结束。只有第一次有效回答可以写入会话历史和可靠 outbox;后续重复或重放提交
|
||||
不得改变结果,也不得再次通知 Agent。展示位置不是交互的 owner 或提交权限:App 前后台切换、关闭或从
|
||||
覆盖层改在 IM 显示,都不改变交互归属。客户端不能提交或改写 Agent、会话、Tool 或 App 子会话的归属字段。
|
||||
|
||||
用户回答、Agent remote dismiss、到期和 Runtime 失败都只能竞争同一条交互的唯一终态。Runtime 以第一个
|
||||
成功完成的原子状态写入为准,并同时持久化唯一 Tool 结果/outbox;后来到达的动作不得覆盖结果或再次通知
|
||||
Agent。MVP 中 interactive Tool 的等待期限与交互的 `expires_at` 相同,Agent 主动停止等待必须走 Runtime
|
||||
私有的 `interaction.dismiss(control_id, call_id)`:Runtime 从受认证 Envelope 推导 Agent 和会话,以 `call_id`
|
||||
定位交互。重放或目标已终态只返回稳定幂等回执,绝不再写 outbox;该控制契约不属于 MiniApp SDK。
|
||||
|
||||
启动一个 App instance 会在主 IM 会话中创建或恢复与之关联的 App 子会话,并可靠向当前 Agent 发送
|
||||
`app_session.opened`,提供 `agent_id`、`conversation_id`、`app_session_id`、`app_scope` 与 `instance_id`。
|
||||
主 IM 将其显示为可展开的
|
||||
折叠组,记录 Agent 的提问、用户回答和 Agent 的简洁结果;它不记录 App 内部业务操作,例如用户在
|
||||
Task Dashboard 点击按钮或填写 App 自己的表单、在 Whiteboard 绘制和编辑内容。App 进入后台时,
|
||||
子会话仍继续;Agent Tool 完成也不自动结束子会话。只有 Runtime 的 `AppLifecycleManager.close` 使
|
||||
instance 进入 stopped 或 failed、并从 focus stack 移除时,子会话才结束但保留为主 IM 的历史。新的
|
||||
App instance 必须创建新的子会话,不接续旧实例记录。
|
||||
|
||||
已结束子会话可在主 IM 中只读展开,但不会重新启动 App、重新执行操作或复活未回答的问题。用户选择
|
||||
“继续处理”旧工作时,Runtime 启动新的 App instance 和新的子会话;新子会话可以引用旧会话、artifact
|
||||
或已保存的 App 状态作为上下文,但不能向旧子会话追加记录。
|
||||
|
||||
若 App 子会话关联等待回答的 Agent 问题,App 位于前台时 Interact 可在其上方显示提问层;用户切换到
|
||||
其他 App 或普通 IM 时,该层收起但问题继续等待,主 IM 的对应折叠组标记“等待你的回答”。用户既可
|
||||
展开该组直接回答,也可回到原 App 后回答。Shell 可避免用户看到重复的视觉卡片,但展示位置不改变交互
|
||||
归属或提交资格;Runtime 通过首次有效回答和终态检查防止重复结果。真正关闭 App 会按普通 Tool 的
|
||||
`app_closed` 规则先收敛未终态 Tool,随后移除该 App 上方的展示层并结束 App 子会话。Runtime 将关闭事件
|
||||
(含仍 pending 的 interaction call_id)可靠通知 Agent;问题本身仍属于 Interact / 主 IM,直到 Agent 远程
|
||||
dismiss、用户回答、到期或 Runtime 失败才结束。
|
||||
|
||||
因此 bundled MiniApp 不能发起、读取、提交或取消这类 Agent 交互,也不因其显示在自身上方而获得
|
||||
Host DOM 或会话数据。复杂、高频或私有业务 UI(例如 Task Dashboard 的“新建任务”表单、Whiteboard
|
||||
画布文字编辑、画笔、颜色选择、拖放和工具栏)必须留在 MiniApp 自己的 Surface 内。系统权限确认
|
||||
(文件、麦克风、通知等)则属于 Runtime/Host Capability Gateway,而不是普通 `confirm`。
|
||||
|
||||
```ts
|
||||
interface MiniAppLifecycleAPI {
|
||||
requestForeground(): Promise<CommandReceipt>;
|
||||
requestBackground(): Promise<CommandReceipt>;
|
||||
requestClose(reason?: string): Promise<CommandReceipt>;
|
||||
}
|
||||
```
|
||||
|
||||
MiniApp 只能请求生命周期变化;Runtime 决定是否允许。若接受关闭,则按上面的 `app_closed` 收敛普通 Tool、
|
||||
处理 Surface 并恢复焦点;若用户只想暂时离开,应请求后台化。
|
||||
|
||||
Surface 与 Capability 也只能请求:
|
||||
|
||||
```text
|
||||
MiniApp requestOpenSurface / requestCapability
|
||||
→ Runtime 校验 Manifest、Policy、实例状态和用户确认要求
|
||||
→ Host Provider 执行受限操作
|
||||
→ Runtime 记录审计并返回受控结果
|
||||
```
|
||||
|
||||
不应在 SDK v1 提供 `fetch`、任意网络、任意文件系统、剪贴板、麦克风、摄像头或 Tauri
|
||||
直接调用。高风险能力以后通过 Capability Request 按需开放。
|
||||
|
||||
## 8. Tool Contract 与 Inventory
|
||||
|
||||
MiniApp 只能在 Manifest 中声明 Tool;Agent 只能调用当前 Runtime 已公布 Inventory 中的 Tool。
|
||||
|
||||
```ts
|
||||
type ToolDescriptorBase = {
|
||||
id: string; // 例如 task-dashboard.open
|
||||
version: 1;
|
||||
input_schema: JsonSchema;
|
||||
output_schema: JsonSchema;
|
||||
permissions?: readonly string[];
|
||||
};
|
||||
|
||||
type ToolDescriptor = ToolDescriptorBase & (
|
||||
| { handling: "interactive"; target?: never }
|
||||
| {
|
||||
handling: "direct" | "launch" | "foreground" | "operation";
|
||||
target: {
|
||||
app_scope: string;
|
||||
requires_foreground: boolean;
|
||||
restore_previous_focus: boolean;
|
||||
};
|
||||
timeout_ms?: number;
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
统一调度路径:
|
||||
|
||||
```text
|
||||
Agent Tool Invoke
|
||||
→ Runtime 校验 Envelope、Inventory revision、Tool Descriptor、参数、scope、App 状态和权限
|
||||
→ 创建可持久化 Tool Call / 审计记录
|
||||
→ Tool Router 判定 direct / interactive / launch / foreground / operation
|
||||
├── interactive:不使用 target、不创建业务 instance,只由 Runtime 建立 Interact 会话交互
|
||||
└── 其他 handling:投递到目标 MiniApp instance
|
||||
→ MiniApp SDK 报告 progress / result / error
|
||||
→ Runtime 校验输出 schema、持久化、更新焦点、写 outbox
|
||||
→ Agent 收到可靠结果
|
||||
```
|
||||
|
||||
`notice`、`choice`、`confirm`、`input` 是 Agent 调起的 Runtime 统一 `interactive` Tool/交互记录
|
||||
能力。Interact 提供会话语义和呈现,Runtime 持有记录与可靠结果路径;其他 MiniApp 只能作为当前
|
||||
前台界面被 Interact 交互层覆盖,不能调用、实现或取得该交互的内容与结果。
|
||||
|
||||
可回答交互的请求以相对 `expires_in_ms` 指定期限;Runtime 计算和持久化 `expires_at`,缺省 15 分钟,
|
||||
仅接受 1 分钟至 24 小时。notice 不等待回答,也不接受期限。
|
||||
|
||||
建议稳定拒绝码至少包括:
|
||||
|
||||
```text
|
||||
inventory_revision_mismatch
|
||||
tool_not_advertised
|
||||
tool_input_invalid
|
||||
tool_output_invalid
|
||||
app_disabled
|
||||
app_instance_not_found
|
||||
app_scope_mismatch
|
||||
lifecycle_denied
|
||||
capability_denied
|
||||
call_already_final
|
||||
```
|
||||
|
||||
## 9. Surface、Bundle、Capability 与发布安全
|
||||
|
||||
复杂 UI 只能运行在受限 Surface 中。Installed MiniApp 的生产 Bundle 必须满足:
|
||||
|
||||
```text
|
||||
签名 Manifest
|
||||
→ 精确 byte size 校验
|
||||
→ SHA-256 校验
|
||||
→ 原子写入已验证 cache
|
||||
→ app_id + exact version 解析
|
||||
→ sandbox="allow-scripts" 的隔离 Surface
|
||||
```
|
||||
|
||||
### 9.1 Host 兼容矩阵
|
||||
|
||||
| 情况 | Host 必须行为 | 禁止行为 | 观测信号 |
|
||||
|---|---|---|---|
|
||||
| 旧 Client 收到未知 `lineup.v1.*` | 安全 fallback,不执行 | 将 payload 当 HTML/能力执行 | `unsupported_type` / `unsupported_version` |
|
||||
| 新 Client 收到旧 plain text | 仅作为 Markdown 显示 | 赋予交互或能力 | legacy markdown renderer |
|
||||
| 不支持目标 Surface version | 不挂载,显示安全提示 | 自动前移到相邻版本 | `surface_unavailable` |
|
||||
| 收到生产 Surface | 仅在签名、长度、SHA-256 成功后挂载已验证 bytes | 接收 URL、HTML、JS、Bundle/source | install disposition |
|
||||
| 候选 Bundle 验证失败 | 保留当前 active 已验证版本 | 覆盖 active bytes 或执行候选 | `signature_invalid` 等 |
|
||||
| active Bundle 被撤销 | 原子回滚到已验证 predecessor;没有则不可用 | 回退到未验证缓存/远端 URL | `rolled_back` / `unavailable` |
|
||||
| Bridge 收到非当前 iframe 消息 | 丢弃 | 仅相信 origin 字符串或调用 Host API | bridge rejection counter |
|
||||
| Capability Catalog 变化 | 发布最小新 Inventory | 用过期 Inventory 自动执行 | catalog revision |
|
||||
|
||||
### 9.2 生产发布序列
|
||||
|
||||
1. 发布方创建不可变 artifact 与生产 Manifest:`app_id`、精确版本、artifact ID、长度、
|
||||
SHA-256、最小 Host 版本、权限、可信 `key_id`、Ed25519 签名。
|
||||
2. Manifest 与 Bundle 部署在 Host 配置的 HTTPS artifact 服务;Manifest 不携带 URL、HTML、
|
||||
CSS、JS 或 source。
|
||||
3. Host 在临时内存中验证签名、精确长度与 SHA-256;失败不写 cache。
|
||||
4. 验证成功后原子安装,并只解析精确 `app_id + version`;不得自动前移版本。
|
||||
5. 仅向满足最小 Host 版本且已启用目标版本的 Client 灰度发布。
|
||||
6. 监控签名、摘要、下载、Bridge 与 Capability 状态;安全事件发生时只回滚到已验证的
|
||||
predecessor。
|
||||
7. 回滚后保留审计元数据,但不记录 artifact 内容、URL、token、文件路径或 Capability 参数;
|
||||
新 artifact 必须使用新版本号。
|
||||
|
||||
正式 HTTPS Host 才可被标记为 production-capable。HTTP LAN 开发 Host 在浏览器 WebCrypto
|
||||
受限时必须安全拒绝验签,不能为了联调放宽该规则。
|
||||
|
||||
## 10. 当前实现状态
|
||||
|
||||
### 10.1 已完成:历史安全基础
|
||||
|
||||
历史 M0~M4 已完成并沉淀在当前代码中;它们不是新的平行排期:
|
||||
|
||||
| 历史阶段 | 保留能力 |
|
||||
|---|---|
|
||||
| M0 | HTTP Transport、Conversation Store、协议解码、Markdown、安全 fallback、基础回归 |
|
||||
| M1 | Tool Call、Task、可靠交互结果、执行摘要、可信 choice/confirm/input |
|
||||
| M2 | Surface Registry、实例生命周期、隔离 iframe 与恢复 |
|
||||
| M3 | Capability Registry、用户确认、审计与受限 Host 执行 |
|
||||
| M4 | 签名 Manifest、Bundle 校验/缓存/回滚、golden fixture |
|
||||
|
||||
### 10.2 已完成:第二次升级
|
||||
|
||||
```text
|
||||
00.base
|
||||
→ Runtime 托管 Interact IM(当前兼容名 chat)的稳定基线
|
||||
|
||||
01.kernel
|
||||
→ MiniApp Instance / Focus / Lifecycle、Tool Router、App Orchestrator、
|
||||
Extension SDK 雏形、Workspace 持久化和恢复
|
||||
```
|
||||
|
||||
当前实现已验证登录、同步、本地回显、Markdown、安全渲染、Tool Call、Task、Surface、
|
||||
Capability、App Inbox、outbox、实例焦点恢复和生产构建。自动化回归当前为 23 个测试文件、
|
||||
99 个测试;`npm run build` 通过。
|
||||
|
||||
Runtime 的关键结构位于:
|
||||
|
||||
```text
|
||||
tauri/src/runtime/app-management/
|
||||
tauri/src/runtime/coordination/
|
||||
tauri/src/runtime/persistence/
|
||||
tauri/src/runtime/protocol/
|
||||
tauri/src/runtime/surfaces/
|
||||
tauri/src/runtime/capabilities/
|
||||
tauri/src/core-apps/chat/
|
||||
```
|
||||
|
||||
### 10.3 下一阶段:03.sdk_and_coreapp
|
||||
|
||||
下一阶段不是应用市场,也不是立即建设服务端下载目录。目标见
|
||||
[03.sdk_and_coreapp.md](../迭代/03.sdk_and_coreapp/03.sdk_and_coreapp.md):定义并验证 MiniApp SDK v1:
|
||||
|
||||
```text
|
||||
冻结 AppManifest、ToolDescriptor、Tool Call、App Context、Result/Progress/Error 和错误码
|
||||
→ Runtime 实现通用 SDK 请求路由、schema 校验、审计、恢复和 revisioned Inventory
|
||||
→ Interact 适配为第一个 System MiniApp SDK 样本
|
||||
→ Task Dashboard 作为第一个参考 MiniApp 验证 Tool、progress、result、focus 与恢复
|
||||
→ Whiteboard 验证受限 Surface、状态 patch、Artifact 与 Capability 请求
|
||||
```
|
||||
|
||||
参考 MiniApp 在此阶段使用 `kind = bundled`:随开发 Host 内置、有 Manifest 和 Runtime 注册记录,
|
||||
但不依赖服务端 Catalog、远程下载或第三方发布。`bundled` 不是系统级信任;参考只描述这些
|
||||
MiniApp 在当前阶段用于验证 SDK 的工作目的,不是 Manifest 类型名称。
|
||||
|
||||
### 10.4 后续阶段
|
||||
|
||||
MiniApp SDK 与参考实现稳定后,再按顺序推进:
|
||||
|
||||
```text
|
||||
04.app-delivery-registry
|
||||
→ Catalog Entry、Manifest/Bundle 下载、验签、安装、启用、禁用、更新、回滚、移除
|
||||
|
||||
05.catalog-and-market(按产品需要)
|
||||
→ 搜索、分类、发布者、安装 UX、组织分发与可能的商业能力
|
||||
```
|
||||
|
||||
`03.sdk_and_coreapp` 已经包含 Task Dashboard 和 Whiteboard 的端到端参考实现;不再另设
|
||||
`03.reference-miniapps`,以免把同一批工作拆成两个编号。
|
||||
|
||||
“市场”属于最后的产品分发层,不能反向决定 Runtime、SDK、Tool 或安全模型。
|
||||
|
||||
## 11. 验收与发布门槛
|
||||
|
||||
每次 Runtime、MiniApp SDK 或 Host 改动至少满足:
|
||||
|
||||
```text
|
||||
npm test -- --run
|
||||
npm run build
|
||||
```
|
||||
|
||||
对于 SDK/参考 MiniApp,还必须有以下证据:
|
||||
|
||||
1. 合法与非法 Manifest、Inventory revision、Tool 输入/输出 schema 的自动化测试;
|
||||
2. Tool 启动 MiniApp、前后台切换、结果回传、失败回焦和重启恢复的场景测试;接受关闭后,未终态普通 Tool
|
||||
仅产生一次 `app_closed` 取消、已 submitted 结果继续 outbox,随后才关闭 Surface / instance 并恢复焦点;
|
||||
3. Interact 的 `notice / choice / confirm / input` 会话呈现不退化,覆盖 IM 内联与前台 bundled
|
||||
MiniApp 上交互层的显示、conversation/call 绑定、结果可靠回传 Agent、默认/越界交互期限、非法
|
||||
`app_session_context` 拒绝、`interaction.dismiss` 幂等重放,以及 bundled MiniApp 无法访问或提交交互的测试;
|
||||
4. MVP 的单 Agent 身份随交互和 App 子会话持久化;不新增多 Agent 连接、切换、会话列表或路由;
|
||||
5. App instance 的子会话创建、后台/暂停保留、`AppLifecycleManager.close` 后结束折叠、重启恢复和新 instance 隔离可验证;创建时会可靠向 Agent
|
||||
提供 `app_session_id`,关闭时会发出包含仍 pending interaction call_id 的可靠 App 已关闭事件,但不会自动
|
||||
取消 Interact 交互;子会话只包含人与 Agent 的交互,不包含 MiniApp 的内部业务操作;
|
||||
6. 已结束子会话可只读查看;“继续处理”旧工作会创建新 instance / 新子会话,可引用旧上下文但不复活旧问题;
|
||||
7. App 切换时未回答问题从前台 App 上方收起并在对应子会话标记;用户可在 IM 或回到原 App 后回答,且同一问题不可重复提交;
|
||||
8. Installed MiniApp 无法取得 Host DOM、Tauri invoke、token、任意网络或其他 MiniApp 数据;
|
||||
9. 有头浏览器验证登录、同步、本地回显和代表性 MiniApp 路径;
|
||||
10. 对生产 Bundle,验证签名 `open → patch → close → rollback`、隔离 Bridge 和 Capability 拒绝路径;
|
||||
11. `git diff --check` 无格式错误,日志不得包含身份、会话、消息正文、Tool 参数、token 或
|
||||
artifact 内容。
|
||||
|
||||
## 12. 相关文档与源码导航
|
||||
|
||||
- [迭代/00.base/00.base.md](../迭代/00.base/00.base.md):Runtime 托管 Interact IM 的稳定基线。
|
||||
- [迭代/01.kernel/01.kernel.md](../迭代/01.kernel/01.kernel.md):Runtime Kernel 与 MiniApp 编排目标和验收。
|
||||
- [tauri/src/README.md](../tauri/src/README.md):当前源码目录和依赖方向。
|
||||
- [tauri/README.md](../tauri/README.md):Tauri/Web Host 的运行、回归与历史实施细节。
|
||||
- [程序文件清单与功能说明.md](../程序文件清单与功能说明.md):实现文件和功能说明。
|
||||
- [LineUp App 最终设计方案](02.正式方案/app_final_design.md):跨文档详细契约来源;
|
||||
若与本文的产品术语或当前迭代顺序冲突,以本文为准并同步更新上游方案。
|
||||
@@ -0,0 +1,21 @@
|
||||
# LineUp App 当前设计文档
|
||||
|
||||
这里集中放置 LineUp App 当前阶段仍在使用的设计文档。LineUp App 是用户使用的主应用,Runtime 是 App 内部提供给 MiniApp 的核心运行环境;当前设计以这组文档为准。
|
||||
|
||||
## 当前权威入口
|
||||
|
||||
- [APP架构设计.md](APP架构设计.md):当前产品边界、Runtime、MiniApp、SDK、Interact 以及发布安全约束的总入口。
|
||||
- [LineUp App 最终设计方案](02.正式方案/app_final_design.md):当前架构、契约和实施顺序的详细汇总。
|
||||
|
||||
## 详细方案
|
||||
|
||||
- [LineUp App 层架构与迁移记录](02.正式方案/lineup-app-layer-architecture.md):M0~M4 的实施边界、迁移记录和分层规则。
|
||||
- [LineUp Runtime 与 App SDK 架构方案](02.正式方案/lineup-runtime-sdk-architecture.md):Runtime、App Manager、Tool Registry、SDK 和消息路由的详细方案。
|
||||
- [运行时与智能体工具](02.正式方案/运行时与智能体工具.md):远端 Agent 的角色与能力边界,以及 Agent Tool、本地 UI Action、Host 生命周期意图三条入口的正式约束。
|
||||
- [LineUp UI Surface 与 App Capability 协议](02.正式方案/lineup-ui-surface-protocol.md):Surface、Capability、权限确认和 Bundle 安全规则。
|
||||
|
||||
## 迭代和评审
|
||||
|
||||
迭代定义以及每轮设计评审、验收记录保留在 [迭代/](../迭代/README.md)。设计文档描述长期架构和当前阶段的约束,迭代文档描述具体要交付的工作。
|
||||
|
||||
根目录下的 `设计/00.records` 和 `设计/01.前期分析与设计` 属于历史资料,不作为当前 LineUp App 设计的主入口。
|
||||
@@ -0,0 +1,153 @@
|
||||
# 当前权威架构基线
|
||||
|
||||
**版本:** 2026-08-07 整合版
|
||||
**主来源:** `01.当前有效设计/APP架构设计.md`、`01.当前有效设计/02.正式方案/*.md`
|
||||
**补充来源:** `90.历史设计归档/` 中与当前实现不冲突的背景材料
|
||||
|
||||
## 1. 一句话结论
|
||||
|
||||
当前 LineUp 的权威架构基线应理解为:
|
||||
|
||||
**LineUp 是用户设备上的主应用;Runtime 是 LineUp App 内部的核心协调环境;MiniApp、Tool、Surface 和 Capability 都必须通过 Runtime 被管理,而不是各自直连服务端或 Agent。**
|
||||
|
||||
这一定义以当前有效设计区为准,比历史归档中的直连插件、早期 WebSocket 草案更接近今天的实际实现。
|
||||
|
||||
## 2. 当前产品结构
|
||||
|
||||
当前主线结构可以收敛为:
|
||||
|
||||
```text
|
||||
LineUp App
|
||||
├── App Shell / Host
|
||||
│ ├── Tauri Desktop Host
|
||||
│ └── Web Reference Host
|
||||
├── LineUp Runtime
|
||||
│ ├── 通信、会话、同步和 outbox
|
||||
│ ├── App Registry、Instance、Focus、Lifecycle
|
||||
│ ├── Tool Router、Inventory、Inbox
|
||||
│ ├── Surface、Capability、Artifact、Audit
|
||||
│ └── MiniApp SDK
|
||||
└── MiniApps
|
||||
├── Interact
|
||||
├── Task Dashboard
|
||||
├── Whiteboard
|
||||
├── Pomodoro
|
||||
└── 后续其他 MiniApp
|
||||
```
|
||||
|
||||
## 3. 当前最重要的架构边界
|
||||
|
||||
### 3.1 App、Runtime、Host 三者分离
|
||||
|
||||
这是当前设计最关键的共识:
|
||||
|
||||
- `LineUp App` 是用户安装和使用的产品容器;
|
||||
- `Runtime` 是 App 内部唯一拥有通信、可靠性、调度和安全决策的协调中心;
|
||||
- `Host` 负责提供桌面或浏览器环境,但不能绕过 Runtime 直接把系统能力交给 App 或 Agent。
|
||||
|
||||
### 3.2 MiniApp 不能绕过 Runtime
|
||||
|
||||
所有 MiniApp 都必须通过 Runtime SDK 获得:
|
||||
|
||||
- 消息与 Inbox;
|
||||
- Tool 调用;
|
||||
- 生命周期;
|
||||
- Surface;
|
||||
- Capability;
|
||||
- 受控状态投影。
|
||||
|
||||
它们不能:
|
||||
|
||||
- 直接连接 AppServer 或 Agent;
|
||||
- 直接读写 Runtime Store、cursor、outbox;
|
||||
- 直接调用 Host 特权或 Tauri 能力;
|
||||
- 直接操控其他 MiniApp 或焦点栈。
|
||||
|
||||
### 3.3 Agent 只与 Runtime 通信
|
||||
|
||||
当前主线下,Agent 不应直接把某个 MiniApp 当作远端可执行对象,而是只能通过 Runtime 发布出来的受控 Tool / Inventory 与系统交互。
|
||||
|
||||
## 4. 当前 Host 基线
|
||||
|
||||
本轮整合后,当前唯一有效的客户端实现/验收基线是:
|
||||
|
||||
```text
|
||||
Tauri 2 Desktop Host + Web Reference Host
|
||||
```
|
||||
|
||||
这意味着:
|
||||
|
||||
- Web Host 是开发、联调和验证入口;
|
||||
- Tauri Host 是正式桌面交付入口;
|
||||
- 二者共享同一份 TypeScript Runtime 与 MiniApp 代码;
|
||||
- 它们不是两套客户端。
|
||||
|
||||
与此冲突的早期判断,例如“当前以纯 Web 或未来移动端为主线”“局域网 App 直连 Agent 插件”,本轮都不再作为当前架构主结论保留。
|
||||
|
||||
## 5. 当前应用模型
|
||||
|
||||
### 5.1 Interact 是默认系统级入口
|
||||
|
||||
当前设计已经把“聊天页面”提升并重述为更高层的 `Interact`:
|
||||
|
||||
- 它是系统级 MiniApp;
|
||||
- 当前默认模式是 IM;
|
||||
- 后续可扩展 Voice / Video;
|
||||
- 当前代码与作用域仍保留 `chat` 兼容命名,但概念上应理解为 `Interact`。
|
||||
|
||||
### 5.2 Runtime 管理实例、焦点和恢复
|
||||
|
||||
当前基线不再把 App 看成静态页面集合,而是 Runtime 管理的一组实例:
|
||||
|
||||
- 哪些 App 已注册;
|
||||
- 哪些实例正在运行;
|
||||
- 谁在前台;
|
||||
- 谁被挂起;
|
||||
- 如何恢复;
|
||||
- 关闭或失败后如何回退到前一有效实例。
|
||||
|
||||
这也是后续工作区、Pomodoro、子会话、恢复与验收闭环成立的基础。
|
||||
|
||||
## 6. 当前 Tool 与 Inventory 模型
|
||||
|
||||
早期文档把工具分成 `app / toolset / action` 三类,这是有历史价值的草案;但当前主线已经收敛为:
|
||||
|
||||
- MiniApp 在受控声明中暴露可发布能力;
|
||||
- Runtime 进行校验、裁剪和动态汇总;
|
||||
- Agent 只能调用当前可见 Inventory 中的方法;
|
||||
- Tool 的生命周期、可见性、风险和结果由 Runtime 裁决。
|
||||
|
||||
因此,今天更适合使用“Agent Tool / Inventory / Tool Call / 业务结果 / 协议回执”这套术语,而不是继续把早期三分法当作当前实现的主模型。
|
||||
|
||||
## 7. 当前 Surface 与 Capability 约束
|
||||
|
||||
这一层是从当前有效设计区的 `02.正式方案/` 中明显成熟起来的:
|
||||
|
||||
- 复杂交互不再靠不受控页面,而是放入受限 Surface;
|
||||
- 系统能力不属于 MiniApp 自动拥有的权限,而是 Runtime/Host 保管的 Capability;
|
||||
- Agent、MiniApp、Surface 都不能绕过 Runtime 直接获得系统动作执行权。
|
||||
|
||||
这比 `agent_ops/设计/` 早期协议文档中的能力模型更接近当前安全边界。
|
||||
|
||||
## 8. 本轮对旧设计的取舍
|
||||
|
||||
### 8.1 仍保留的背景价值
|
||||
|
||||
- 产品不是任务管理系统;
|
||||
- 需要结构化消息与工具调用;
|
||||
- Agent 与 UI 渲染要解耦;
|
||||
- 工具、传输、连接需要分层思考。
|
||||
|
||||
### 8.2 不再作为当前权威基线的旧结论
|
||||
|
||||
- 局域网 WebSocket 直连是当前主线;
|
||||
- 当前客户端基线是“Web / 未来移动端”;
|
||||
- 工具协议可以脱离 Runtime 架构单独成立;
|
||||
- 早期 `app / toolset / action` 三分法就是当前 Tool 模型本身。
|
||||
|
||||
## 9. 当前建议阅读顺序
|
||||
|
||||
1. 先看本文,建立当前架构全景;
|
||||
2. 再看 [Agent工具与运行时边界](./03.Agent工具与运行时边界.md);
|
||||
3. 如需原始来源,再回看 `01.当前有效设计/APP架构设计.md` 与 `01.当前有效设计/02.正式方案/` 各细化文档;
|
||||
4. `90.历史设计归档/` 仅用于追溯历史背景,不再作为当前设计入口。
|
||||
@@ -0,0 +1,116 @@
|
||||
# Agent 工具与运行时边界
|
||||
|
||||
**版本:** 2026-08-07 整合版
|
||||
**主来源:** `01.当前有效设计/02.正式方案/运行时与智能体工具.md`
|
||||
**辅助来源:** `01.当前有效设计/APP架构设计.md`、`90.历史设计归档/01.前期分析与设计/tool-action-design.md`
|
||||
|
||||
## 1. 当前权威结论
|
||||
|
||||
当前最重要的结论是:
|
||||
|
||||
**Agent Tool 是 Runtime 发布给远端 Agent 的受控调用契约,不是用户按钮,也不是 MiniApp 内部函数的别名。**
|
||||
|
||||
这是当前设计相对于早期草案最重要的收敛之一。
|
||||
|
||||
## 2. Agent 当前扮演什么角色
|
||||
|
||||
远端 Agent 当前应该被理解为:
|
||||
|
||||
- 用户意图的理解者;
|
||||
- Runtime 已发布能力的调用者;
|
||||
- 结果解释与后续编排者。
|
||||
|
||||
它不应该被理解为:
|
||||
|
||||
- 本地 Runtime 的替身;
|
||||
- MiniApp 内部状态的直接操作者;
|
||||
- Host 权限的直接调用者;
|
||||
- 任意前端函数的远程执行入口。
|
||||
|
||||
## 3. 三条入口必须分开
|
||||
|
||||
当前有效设计明确把三条入口区分开了:
|
||||
|
||||
### 3.1 Agent Tool
|
||||
|
||||
由远端 Agent 发起,经过 Runtime 校验、持久化、路由和回传。
|
||||
|
||||
### 3.2 本地 UI Action
|
||||
|
||||
由用户在 MiniApp 页面中的点击、输入、拖动等触发,经 SDK / Surface Bridge 进入 Runtime。
|
||||
|
||||
### 3.3 Host 生命周期意图
|
||||
|
||||
由前后台切换、返回、关闭、恢复、锁屏等本地环境事实触发,经 Runtime 的生命周期裁决进入状态机。
|
||||
|
||||
这三条入口可以复用内部业务动作,但不能混同身份。
|
||||
|
||||
## 4. 为什么早期工具三分法不能直接作为当前主模型
|
||||
|
||||
`agent_ops/设计/01.前期分析与设计/tool-action-design.md` 中的 `app / toolset / action` 三分法,在早期用于说明不同工具形态,很有价值;但它已经不能完整表达当前 Runtime 模型,原因是:
|
||||
|
||||
- 当前 Runtime 要管理 Inventory 可见性;
|
||||
- 要管理 Tool Call 的幂等、状态、回执和终态;
|
||||
- 要管理激活过程、App 就绪条件、前台要求和 operation;
|
||||
- 要把 Tool、MiniApp instance、子会话、业务 operation 区分开。
|
||||
|
||||
因此,当前主模型应优先使用:
|
||||
|
||||
- Agent Tool;
|
||||
- Agent Inventory;
|
||||
- Tool Call;
|
||||
- 协议回执 / 进度;
|
||||
- 业务结果;
|
||||
- MiniApp activation;
|
||||
- operation。
|
||||
|
||||
## 5. 当前 Runtime 对 Tool 的唯一所有权
|
||||
|
||||
Tool 相关的这些职责都必须由 Runtime 统一承担:
|
||||
|
||||
- 发布 Inventory;
|
||||
- 校验调用;
|
||||
- 做幂等去重;
|
||||
- 决定是否可见;
|
||||
- 决定是否允许启动或复用 MiniApp;
|
||||
- 持久化业务状态与 outbox;
|
||||
- 回传协议回执与最终结果。
|
||||
|
||||
这意味着:
|
||||
|
||||
- MiniApp 不能自己向 Agent 宣传 Tool;
|
||||
- Surface 不能直接把前端动作伪造成 Tool Call;
|
||||
- Agent 不能绕过 Inventory 直接指定任意 instance 或内部函数;
|
||||
- Host 事件也不能伪造成 Tool 调用。
|
||||
|
||||
## 6. 当前与项目实际情况一致的几个重点
|
||||
|
||||
结合当前 `lineup-app` 的实现方向,以下几个判断具有最高优先级:
|
||||
|
||||
- Tool 是 Runtime 语义的一部分,不是单独漂浮在外的协议层;
|
||||
- Interact / MiniApp / Workspace / Focus / Lifecycle 与 Tool 调用之间已经形成一套统一模型;
|
||||
- 长时业务行为需要区分“调用被接受”“激活已就绪”“业务已开始”“业务已完成”;
|
||||
- 用户本地操作、Agent 远程调用和 Host 生命周期变化必须各自可审计、可恢复。
|
||||
|
||||
## 7. 对旧设计中仍可保留的部分
|
||||
|
||||
早期协议文档中这些认识仍然有价值:
|
||||
|
||||
- 结构化调用比自然语言裸发更稳定;
|
||||
- 工具定义需要 schema 化;
|
||||
- 消息需要统一信封与版本化;
|
||||
- 调用与返回需要成对、可追踪。
|
||||
|
||||
但这些内容如今应作为当前 Runtime Tool 模型的背景,而不是替代它。
|
||||
|
||||
## 8. 本轮整合后的使用建议
|
||||
|
||||
如果后续在设计、实现或迭代文档里出现以下歧义,优先按本文判断:
|
||||
|
||||
- 某个按钮是不是 Agent Tool;
|
||||
- 某个本地事件能不能当成 Tool 调用;
|
||||
- 某个 MiniApp 能不能直接发消息给 Agent;
|
||||
- 某个 operation 是否等同于一次 Tool Call;
|
||||
- 某个实例 ID 是否能由 Agent 任意指定。
|
||||
|
||||
这些问题的默认答案,都应该先经过 Runtime 边界来裁决,而不是回退到早期直连协议思路。
|
||||
@@ -0,0 +1,60 @@
|
||||
# Stage 1:需求分析与架构设计
|
||||
|
||||
**状态:** ✅ 已完成
|
||||
**时间:** 2026-07-24 → 2026-07-27
|
||||
|
||||
---
|
||||
|
||||
## 目标
|
||||
|
||||
完成 LineUp Agents 的产品定位、架构设计、协议定义,输出第一版设计方案。
|
||||
|
||||
---
|
||||
|
||||
## 完成内容
|
||||
|
||||
### 产品定位
|
||||
|
||||
- 定义为"远程 Agent 操作交互端",不是任务管理系统
|
||||
- MVP 范围:局域网直连,不做中转,不做 MCP 封装,不做通知推送
|
||||
|
||||
### 架构设计
|
||||
|
||||
- 三层协议:通讯协议层、消息协议层、工具协议层
|
||||
- 局域网直连方案,Agent 插件内开 WebSocket 端口 9527
|
||||
- 预共享 Token 鉴权
|
||||
- 手动输入 IP:端口发现方式
|
||||
|
||||
### 消息协议
|
||||
|
||||
- 统一信封格式:`{ v, id, type, payload }`
|
||||
- 7 种消息类型:system.hello、system.ping/pong、text、image、tool.list、tool.call、tool.result
|
||||
|
||||
### 工具协议
|
||||
|
||||
- 三类工具:app(有状态应用)、toolset(无状态工具集)、action(单指令工具)
|
||||
- app 类型通过 instance_id 管理生命周期,open → use → close
|
||||
- tool.call / tool.result 标准化格式
|
||||
|
||||
### 技术选型
|
||||
|
||||
- App 端:Vite + React + Tailwind + shadcn/ui
|
||||
- 通信:裸 WebSocket,不引入 Socket.io
|
||||
- Agent 端:Hermes 平台插件优先
|
||||
|
||||
### 文档产出
|
||||
|
||||
| 文档 | 位置 |
|
||||
|------|------|
|
||||
| v1 设计方案 | LineUpAgents/设计/design-v1.md |
|
||||
| 早期分析与设计 | LineUpAgents/设计/早期分析与设计/ |
|
||||
| README 项目入口 | LineUpAgents/README.md |
|
||||
|
||||
---
|
||||
|
||||
## 关键决策
|
||||
|
||||
- 不做独立 Channel 进程,插件嵌入 Agent
|
||||
- 不做 MCP 封装,工具路由用字典映射
|
||||
- 先纯 Web,后续再考虑 Tauri 壳子
|
||||
- 结构化消息返回,不是自然语言裸发
|
||||
@@ -0,0 +1,44 @@
|
||||
# Stage 2:App 端交互原型
|
||||
|
||||
**状态:** 🟡 进行中
|
||||
**时间:** 2026-07-27 →
|
||||
|
||||
---
|
||||
|
||||
## 目标
|
||||
|
||||
搭建 LineUp App 端的 Vite + React 项目框架,完成核心页面的交互原型。不写业务逻辑(WebSocket 通信、工具调用),只搭 UI 和页面流。
|
||||
|
||||
---
|
||||
|
||||
## 要做的事
|
||||
|
||||
### 1. 搭建项目骨架
|
||||
|
||||
- 用 Vite + React + TypeScript 初始化项目
|
||||
- 安装 Tailwind CSS + shadcn/ui
|
||||
- 配置基础目录结构(pages / components / hooks / types)
|
||||
|
||||
### 2. 确定核心页面与交互流程
|
||||
|
||||
需要跟用户确认的界面:
|
||||
|
||||
- 首页(连接 Agent)—— 输入 IP:端口 + Token,点击连接
|
||||
- 连接状态指示 —— 已连接 / 连接中 / 断开
|
||||
- 会话主界面 —— 消息流展示(text、image、tool 卡片)
|
||||
- 消息输入区 —— 输入框 + 发送按钮
|
||||
- 工具渲染 —— choice、confirm、input 三种工具的 UI 原型
|
||||
|
||||
### 3. 输出静态原型
|
||||
|
||||
- 每个页面一个独立组件,不带状态管理
|
||||
- 用 mock 数据展示页面效果
|
||||
- 串联成可点击的页面流
|
||||
|
||||
---
|
||||
|
||||
## 待讨论
|
||||
|
||||
- 主界面布局:左侧会话列表 + 右侧对话区,还是单栏对话?
|
||||
- 工具卡片样式:choice 选择按钮的排列方式、confirm 确认/取消的视觉样式
|
||||
- Dark/Light 主题偏好
|
||||
@@ -0,0 +1,66 @@
|
||||
# Stage 2:中转服务器方案设计
|
||||
|
||||
**状态:** 🟡 进行中
|
||||
**最后更新:** 2026-07-30
|
||||
|
||||
---
|
||||
|
||||
## 目标
|
||||
|
||||
建立一套可在本机通过 Docker Compose 启动的 IM 服务环境,供唐僧叨叨客户端完成基础聊天验证,并作为 LineUp 远程 Agent 交互的后续通讯基础。
|
||||
|
||||
该环境是一套协作的服务集合,而非两个互相替代的 IM:WuKongIM 是通讯层,唐僧叨叨是业务层。
|
||||
|
||||
## 决策记录
|
||||
|
||||
| 决策 | 结论 |
|
||||
|------|------|
|
||||
| 通讯层 | **WuKongIM v2** |
|
||||
| 业务层 | **唐僧叨叨服务端 v1.5** |
|
||||
| 部署方式 | Docker Compose,本地单节点 |
|
||||
| 业务依赖 | MySQL 8、Redis 7、MinIO |
|
||||
| 前端入口 | 唐僧叨叨 Web 与 Manager,均仅绑定本机端口 |
|
||||
| 消息客户端 | 唐僧叨叨客户端或 WuKongIM 官方 SDK |
|
||||
| Agent 接入 | 后续基于 WuKongIM 官方 SDK 或已验证协议实现;不依赖其他平台的专用机器人网关 |
|
||||
| 数据策略 | Docker 命名卷持久化;本地环境禁止将密钥写入仓库 |
|
||||
|
||||
## 系统架构
|
||||
|
||||
```text
|
||||
唐僧叨叨 Web / 移动客户端
|
||||
├─ HTTP 业务请求 ───────────► 唐僧叨叨服务端 :8090
|
||||
└─ TCP / WebSocket 消息 ────► WuKongIM :5100 / :5200
|
||||
│
|
||||
HTTP API ◄────────────┤
|
||||
└─ Webhook gRPC ─► 唐僧叨叨服务端 :6979
|
||||
|
||||
唐僧叨叨服务端 ──► MySQL / Redis / MinIO
|
||||
```
|
||||
|
||||
## 本地端口边界
|
||||
|
||||
| 服务 | 容器端口 | 本机端口 | 作用 |
|
||||
|------|----------|----------|------|
|
||||
| WuKongIM HTTP API | 5001 | 15001 | 健康检查和本地调试 |
|
||||
| WuKongIM TCP | 5100 | 15100 | 官方 SDK 的 TCP 长连接 |
|
||||
| WuKongIM WebSocket | 5200 | 15200 | Web / WebSocket 客户端长连接 |
|
||||
| WuKongIM 监控 | 5300 | 15300 | 本地监控 |
|
||||
| 唐僧叨叨 API | 8090 | 18090 | 业务 API |
|
||||
| 唐僧叨叨 Web | 80 | 18082 | 用户聊天界面 |
|
||||
| 唐僧叨叨 Manager | 80 | 18083 | 后台管理 |
|
||||
| MinIO | 9000 / 9001 | 19000 / 19001 | 文件服务及其控制台 |
|
||||
|
||||
所有映射通过 `HOST_BIND_IP` 精确绑定到指定宿主机网卡。当前部署目标为 Tailscale 地址 `100.121.118.116`,同时 `EXTERNAL_IP` 设为该地址,以供客户端连接。数据库、Redis 和容器间 gRPC 不发布到宿主机;如需进一步收紧公开面,可取消 Manager、监控和 MinIO 的端口映射。
|
||||
|
||||
## LineUp 集成边界
|
||||
|
||||
基础 IM 环境与 LineUp Agent 集成分两个阶段验收:
|
||||
|
||||
1. 先验证唐僧叨叨用户注册、登录、单聊/群聊、文件上传和服务重启后的数据持久化。
|
||||
2. 再设计 LineUp 中转适配器。适配器需要处理 WuKongIM 的认证、频道/会话、收发消息及自定义消息载荷;其实现应基于官方 SDK 或严格按已验证协议开发。
|
||||
|
||||
`tool.call` 和 `tool.result` 仍由 LineUp 定义,但需要先确定与唐僧叨叨/WuKongIM 消息扩展机制的精确映射,不能把尚未验证的 JSON WebSocket 假设写入生产实现。
|
||||
|
||||
## 部署与验收
|
||||
|
||||
> 该阶段的唐僧叨叨 v1.5 + WuKongIM v2 Compose 套件已于 2026-08-03 退役并从仓库移除。本记录仅保留当时的方案历史;当前本地 IM 环境见 [`infra/wukongim-v3/`](../../infra/wukongim-v3/)。
|
||||
@@ -0,0 +1,169 @@
|
||||
# LineUp Agents — 架构设计文档
|
||||
|
||||
**状态:** 讨论稿,持续更新
|
||||
**最后更新:** 2026-07-25
|
||||
|
||||
---
|
||||
|
||||
## 一、产品定位
|
||||
|
||||
LineUp Agents 是一个**远程 Agent 操作交互端**。用户通过手机或 Web App,与运行在本地或服务器上的 AI Agent 进行顺畅的交互。
|
||||
|
||||
不是任务管理系统。Agent 的任务拆解、执行、管理,属于 Agent 自己的工作范畴,不属于 LineUp 的职责。
|
||||
|
||||
---
|
||||
|
||||
## 二、架构总览
|
||||
|
||||
```text
|
||||
┌─────────────────┐ ┌─────────────────────────┐
|
||||
│ 用户 App 端 │ │ Agent 机器 │
|
||||
│ (Web / 未来移动端)│ │ │
|
||||
│ │ │ ┌─────────────────────┐ │
|
||||
│ ┌─────────────┐ │ │ │ LineUp 插件 │ │
|
||||
│ │ 工具渲染层 │ │ WebSocket │ │(嵌入 Agent 进程内) │ │
|
||||
│ │ Confirm │ │◄────────►│ │ │ │
|
||||
│ │ Input │ │ │ │ WebSocket 端口 │ │
|
||||
│ │ SVG Renderer │ │ │ └─────────┬───────────┘ │
|
||||
│ │ ... │ │ │ │ │
|
||||
│ └─────────────┘ │ │ 进程内通信 │
|
||||
│ │ │ │ │
|
||||
│ ┌─────────────┐ │ │ ┌─────────▼───────────┐ │
|
||||
│ │ 消息协议层 │ │ │ │ Agent 核心 │ │
|
||||
│ │ WebSocket │ │ │ │ (Hermes / │ │
|
||||
│ │ 连接管理 │ │ │ │ OpenClaw / 其他) │ │
|
||||
│ └─────────────┘ │ │ └─────────────────────┘ │
|
||||
└─────────────────┘ └─────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、已敲定的设计决策
|
||||
|
||||
### 3.1 总体架构
|
||||
|
||||
| 决策 | 当期结论 | 后续扩展思路 |
|
||||
|------|---------|-------------|
|
||||
| 网络模式 | 局域网直连,App 直接连 Agent 插件暴露的 WebSocket 端口 | — |
|
||||
| 中转服务 | 不做中转 | 需要远程连接时引入中转服务 |
|
||||
| Agent 接入方式 | 每个 Agent 框架写一个插件,插件内开 WebSocket 端口 + 进程内通信 | — |
|
||||
| 端口配置 | 固定端口(默认 9527),支持环境变量覆盖 | — |
|
||||
| 发现方式 | 手动输入 IP:端口 | mDNS 广播自动发现 |
|
||||
| App 端技术方向 | 响应式 Web | 原生移动端 App |
|
||||
| 连接认证 | 预共享 Token,用户在插件配置中设定,App 连接时提供 | 端到端加密,密钥交换 |
|
||||
| 工具路由方式 | 字典路由,按工具名映射到对应的处理函数 | 可引入框架级路由 |
|
||||
|
||||
### 3.2 协议分层
|
||||
|
||||
三层结构,层间独立不耦合:
|
||||
|
||||
```
|
||||
第一层:通讯协议层
|
||||
解决的问题:管道怎么通
|
||||
包含:WebSocket 连接建立、身份标识交换、心跳保活、断线重连
|
||||
|
||||
第二层:消息协议层
|
||||
解决的问题:消息长什么样子
|
||||
包含:统一信封格式、消息路由、版本号、消息去重
|
||||
|
||||
第三层:工具协议层
|
||||
解决的问题:App 能干什么
|
||||
包含:工具发现(查询可用工具)、工具调用、返回结果
|
||||
注:工具定义格式复用 MCP 的 JSON Schema 方式
|
||||
```
|
||||
|
||||
### 3.3 核心数据对象
|
||||
|
||||
| 对象 | 含义 | 关键字段 |
|
||||
|------|------|---------|
|
||||
| Agent | 一个可连接的 Agent 实例 | id、name、runtime、status |
|
||||
| Session | 一条对话上下文 | id、agentId、title |
|
||||
| Message | 用户或 Agent 产生的内容 | id、sessionId、role、type、payload、timestamp |
|
||||
| Tool | App 端可被 Agent 调用的能力 | name、description、inputSchema、outputSchema |
|
||||
|
||||
### 3.4 与 MCP 的关系
|
||||
|
||||
- 工具协议层复用 MCP 的 `tools/list`、`tools/call` 定义方式
|
||||
- 工具 Schema 格式遵循 MCP 的 JSON Schema 标准
|
||||
- 通讯协议层和消息协议层是 MCP 未覆盖的部分——补充了远程安全双向传输的能力
|
||||
|
||||
### 3.5 工具分类
|
||||
|
||||
详见独立文档 [tool-action-design.md](tool-action-design.md)——第一节"工具协议定义"。
|
||||
|
||||
| 决策 | 当期结论 | 后续扩展思路 |
|
||||
|------|---------|-------------|
|
||||
| 工具结构 | `{ name, type, description, actions[] }`(app/toolset)或 `{ name, type, description, inputSchema }`(action) | — |
|
||||
| app 生命周期 | open → use → close,通过 instance_id 维护 | — |
|
||||
| toolset 调用 | 每次独立,无状态 | — |
|
||||
| action 调用 | 只有一个 action,最简 | — |
|
||||
| 状态跟踪 | 依赖 Agent 自身对话上下文 | — |
|
||||
| 与 MCP 对应关系 | Tool = MCP Server,Action = MCP Tool | — |
|
||||
|
||||
---
|
||||
|
||||
## 四、第一版技术栈
|
||||
|
||||
| 端 | 选型 |
|
||||
|----|------|
|
||||
| Agent 插件 | 按 Agent 框架选择语言(Hermes 用 Python,OpenClaw 待定) |
|
||||
| App 端(Web) | 待定(Next.js / Vite + React) |
|
||||
| 通信方式 | WebSocket(裸 ws,不引入 Socket.io) |
|
||||
| 消息格式 | JSON |
|
||||
|
||||
---
|
||||
|
||||
## 五、MVP 边界
|
||||
|
||||
| 做 | 不做 |
|
||||
|----|------|
|
||||
| 局域网内 App 直连 Agent | 远程广域网连接 |
|
||||
| App 收发消息、查看 Agent 状态 | 任务生命周期管理 |
|
||||
| Agent 调 App 的工具(confirm/input 等) | 审批卡片系统 |
|
||||
| 简单的工具发现协议 | 多 Agent 编排、调度 |
|
||||
| 文本 + 富内容消息展示 | 通知推送 |
|
||||
| | 中转服务 |
|
||||
| | MCP 封装(第一期不做) |
|
||||
|
||||
---
|
||||
|
||||
## 六、一次完整的交互流程(以 choice 工具为例)
|
||||
|
||||
```
|
||||
Agent 需要用户做选择
|
||||
│
|
||||
├── Agent 发起 { type: "tool.call", payload: { name: "choice", call_id: "c1", arguments: { question: "部署到哪个环境?", options: ["测试", "预发布", "生产"] } } }
|
||||
│
|
||||
├── Agent 插件收到 → 通过 WebSocket 推给 App
|
||||
│
|
||||
├── App 渲染选择界面,三个选项展示给用户
|
||||
│
|
||||
├── 用户点击"预发布"
|
||||
│
|
||||
├── App 通过 WebSocket 发回 { type: "tool.result", payload: { name: "choice", call_id: "c1", result: { message: "预发布" } } }
|
||||
│
|
||||
├── Agent 插件收到 → 交回给 Agent 核心
|
||||
│
|
||||
└── Agent 拿到结果,继续执行
|
||||
```
|
||||
|
||||
关键原则:
|
||||
|
||||
- App 端不关心 Agent 为什么问这个问题,只负责渲染和返回用户操作结果
|
||||
- Agent 端不关心 App 用什么 UI 渲染的,只负责发起工具调用和处理返回结果
|
||||
- 调用(Agent → App)和返回(App → Agent)都使用结构化消息,不是自然语言文本
|
||||
- 整个交互走一条 WebSocket 连接,双向实时,消息协议统一
|
||||
|
||||
### 6.1 消息方向与格式
|
||||
|
||||
所有消息使用统一信封:`{ v, id, type, payload }`。完整格式定义见 [tool-action-design.md](tool-action-design.md)——第二节"消息协议定义"。
|
||||
|
||||
---
|
||||
|
||||
## 七、后续可能的扩展
|
||||
|
||||
- 加入中转服务,支持广域网远程连接
|
||||
- 加入端到端加密,中转不可读消息内容
|
||||
- 加入 mDNS 局域网自动发现(类似 Bonjour)
|
||||
- 移动端原生 App
|
||||
- 支持流式数据传输(如实时 SVG 渲染)
|
||||
+577
@@ -0,0 +1,577 @@
|
||||
# LineUp IM Agent 交互平台:历史方案与实施计划(已归档)
|
||||
|
||||
**版本:** 0.2(提案)
|
||||
**状态:** 已归档;不作为当前技术路线
|
||||
**日期:** 2026-07-30
|
||||
**原决策范围:** LineUp 的 IM 基础、人与 Agent 的交互协议、首个 Hermes 接入,以及唐僧叨叨 / WuKongIM 在体系中的职责。
|
||||
|
||||
> 归档说明(2026-08-03):本方案依赖唐僧叨叨业务服务与 WuKongIM v2 运行栈;该路线已经退役。当前项目使用 WuKongIM 3.0 官方源码 + LineUp AppServer,客户端与 Mini Runtime 路线见 `lineup-app/` 和 `设计/02.正式方案/`。本文仅保留作为历史决策与兼容性分析记录。
|
||||
|
||||
---
|
||||
|
||||
## 1. 决策摘要
|
||||
|
||||
LineUp 的定位是一个连接**人、人与人协作、人与智能体**的交互平台。它以即时通讯的可靠连接和消息能力为基础,但不把产品限制为传统的聊天窗口。
|
||||
|
||||
LineUp 的核心能力是:用户与 Agent 可以在同一个会话中交换文本、媒体、状态、产物和结构化交互;Agent 可在用户授权边界内调用用户端工具,用户端将结构化结果可靠返回给 Agent。
|
||||
|
||||
因此,本方案作出以下正式决策:
|
||||
|
||||
1. **LineUp 以唐僧叨叨作为成熟 IM 业务层基础,以 WuKongIM 作为通讯内核。** 用户、联系人、群组、文件、工作台与现有多端客户端能力优先复用;仅在 LineUp 的产品需要超出现有能力时再扩展或替换。
|
||||
2. **LineUp 的正式 Agent 交互通道采用 `增强后的 LineUp Client + WuKongIM 官方 SDK + LineUp 自定义消息协议`。** 该协议在唐僧叨叨的用户与业务体系内运行,不把 LineUp 限制为机器人文本对话。
|
||||
3. **LineUp Agent Adapter / Gateway 作为外部常驻服务运行,负责连接 Agent Runtime 与 LineUp 协议。** Hermes 是第一个适配目标,后续可接入其他 Agent Runtime。
|
||||
4. **唐僧叨叨 Robot Events API 是可复用的机器人接入能力。** 它适合快速提供文本型 Hermes 入口、通知和兼容降级;结构化工具交互的正式主载体仍是 LineUp 自定义消息协议。
|
||||
5. **WuKongIM AI Plugin 不作为第一阶段核心依赖。** 它保留为后续的服务端路由、低延迟流式输出和自动化处理能力;待确认当前部署版本的插件机制与唐僧叨叨配套版本兼容后再评估。
|
||||
6. **LineUp Client 是对唐僧叨叨客户端能力的面向 Agent 扩展,而不是孤立重造一个聊天客户端。** choice、confirm、canvas、artifact 等由 LineUp 模块实现,同时保留已有 IM 业务体验。
|
||||
|
||||
---
|
||||
|
||||
## 2. 产品目标与边界
|
||||
|
||||
### 2.1 产品目标
|
||||
|
||||
LineUp 让人和 Agent 在远程、跨设备、可恢复的 IM 会话中协作。交互不局限于文本问答,而是包含:
|
||||
|
||||
- 人与人:文本、图片、语音、文件与群组协作;
|
||||
- 人与 Agent:自然语言、任务进度、状态、图像、文件和流式内容;
|
||||
- Agent 与用户端工具:选择、确认、表单、画板、文件预览、设备能力等;
|
||||
- Agent 与 Agent:在受控会话或频道中交换结构化协作消息;
|
||||
- 用户对 Agent 的控制:继续、取消、修改指令、批准、拒绝和恢复。
|
||||
|
||||
### 2.2 关键体验目标
|
||||
|
||||
用户面对的不是一个只会输出文本的机器人,而是一个可呈现和操作工作内容的 Agent 会话。例如:
|
||||
|
||||
```text
|
||||
Agent:需要确认部署目标
|
||||
└─ LineUp Client 渲染为 choice 卡片
|
||||
|
||||
用户:选择“预发布”
|
||||
└─ Client 返回结构化 tool.result
|
||||
|
||||
Agent:开始执行,持续更新进度
|
||||
└─ Client 渲染进度与状态,不强迫用户阅读多段文本
|
||||
|
||||
Agent:生成架构草图
|
||||
└─ Client 打开画板,持续接收 canvas.patch
|
||||
|
||||
用户:点击“暂停”,修改颜色后继续
|
||||
└─ Client 发送控制事件或 tool.result
|
||||
```
|
||||
|
||||
### 2.3 明确不承担的职责
|
||||
|
||||
- LineUp 不取代 Agent Runtime 的任务规划、工具调用引擎或记忆系统;
|
||||
- LineUp 不把 WuKongIM 替换为自研消息服务器;
|
||||
- LineUp 不在第一阶段实现多 Agent 编排平台;
|
||||
- LineUp 不默认授予 Agent 对用户设备、文件或系统操作的无限权限;
|
||||
- LineUp 不把所有消息都交给服务端 AI 插件处理。
|
||||
|
||||
---
|
||||
|
||||
## 3. 总体架构
|
||||
|
||||
```text
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ LineUp Client(唐僧客户端能力 + 扩展) │
|
||||
│ │
|
||||
│ 会话 / 聊天 / 媒体 / 文件 │
|
||||
│ Agent 状态 / 进度 / 产物 │
|
||||
│ Tool Registry / Choice / Confirm / Form / Canvas │
|
||||
│ LineUp 自定义消息渲染与本地授权 │
|
||||
└───────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
│ WuKongIM SDK 长连接
|
||||
│ 原生消息 + LineUp 自定义 Payload
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ WuKongIM │
|
||||
│ │
|
||||
│ 认证后连接、频道、可靠投递、离线同步、重连、顺序、流消息 │
|
||||
└───────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
│ 指定 Agent 频道 / 绑定路由
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ LineUp Agent Adapter / Gateway │
|
||||
│ │
|
||||
│ Actor / Device / Conversation 映射 │
|
||||
│ LineUp 信封编解码、去重、持久 inbox/outbox │
|
||||
│ call_id 生命周期、超时、取消、恢复、权限校验 │
|
||||
│ Hermes Adapter、未来其他 Runtime Adapter │
|
||||
└───────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
Hermes / 其他 Agent Runtime 与其本地工具
|
||||
|
||||
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 唐僧叨叨业务层(复用与扩展) │
|
||||
│ │
|
||||
│ 用户与认证 / 联系人与群组 / 文件与对象存储 / 工作台 / 管理端 │
|
||||
│ Android、Web、PC 基础 IM 能力 / Robot Events(可选接入) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.1 组件职责
|
||||
|
||||
| 组件 | 负责 | 不负责 |
|
||||
|---|---|---|
|
||||
| WuKongIM | 长连接、消息投递、离线同步、频道与消息流 | LineUp 工具语义、设备授权、Agent 会话管理 |
|
||||
| 唐僧叨叨 | LineUp 的 IM 业务基础:用户、认证、好友、群组、文件、工作台、管理端与既有多端客户端 | 替代 LineUp 的 Agent 交互协议与工具语义 |
|
||||
| LineUp Client | 在唐僧叨叨客户端基础能力上增加交互 UI、工具注册、本地授权、协议显示与回传 | Agent 任务规划与执行 |
|
||||
| LineUp Adapter / Gateway | 协议路由、可靠性、会话与调用映射、Agent 适配 | 直接替代 IM 服务 |
|
||||
| Hermes | 理解用户意图、执行任务、调用其工具、请求用户交互 | 负责移动端 UI 渲染 |
|
||||
| WuKongIM AI Plugin(后续可选) | 服务端高性能路由、流式和自动化 Hook | 定义 LineUp 协议或取代唐僧叨叨业务层与 Adapter |
|
||||
|
||||
---
|
||||
|
||||
## 4. 协议分层与消息模型
|
||||
|
||||
### 4.1 三层分工
|
||||
|
||||
```text
|
||||
LineUp 交互协议层
|
||||
└─ 会话语义、Actor、设备能力、工具、状态、产物、取消与确认
|
||||
|
||||
WuKongIM 消息传输层
|
||||
└─ 文本、媒体、文件、自定义 Payload 的可靠投递与同步
|
||||
|
||||
WuKongIM 连接层
|
||||
└─ 认证、长连接、心跳、重连、离线恢复
|
||||
```
|
||||
|
||||
LineUp 不重新实现下层 IM 可靠传输;WuKongIM 也不解释 LineUp 的工具和 UI 语义。
|
||||
|
||||
### 4.2 统一 LineUp 信封
|
||||
|
||||
所有自定义交互消息使用一个可演进的信封。基础媒体仍优先使用 IM 的原生媒体能力;信封保存其交互语义、引用关系或媒体元数据。
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"id": "msg_01J...",
|
||||
"type": "lineup.v1.tool.call",
|
||||
"conversation_id": "conv_01J...",
|
||||
"sender": {
|
||||
"kind": "agent",
|
||||
"id": "agent_hermes_main"
|
||||
},
|
||||
"target": {
|
||||
"kind": "device",
|
||||
"id": "device_phone_01"
|
||||
},
|
||||
"timestamp": "2026-07-30T14:00:00+08:00",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
|
||||
字段规则:
|
||||
|
||||
- `v`:协议主版本。未知主版本必须拒绝执行,只可按安全降级显示;
|
||||
- `id`:全局唯一的应用级消息 ID,用于幂等去重;
|
||||
- `type`:以 `lineup.v1.` 为命名空间,禁止与基础 IM 类型混淆;
|
||||
- `conversation_id`:LineUp 会话 ID,不以某一个 IM message ID 代替;
|
||||
- `sender` / `target`:显式描述人、Agent、设备或群组,而不只依赖底层频道;
|
||||
- `payload`:仅由对应 `type` 的 schema 定义。
|
||||
|
||||
### 4.3 第一批正式消息类型
|
||||
|
||||
| 类型 | 方向 | 目的 |
|
||||
|---|---|---|
|
||||
| `lineup.v1.text` | 双向 | 与 Agent 相关的结构化文本元数据;普通 IM 文本仍可原生发送 |
|
||||
| `lineup.v1.device.hello` | Client → Gateway | 设备注册、客户端版本、协议版本、能力摘要 |
|
||||
| `lineup.v1.tool.list` | 双向 | 声明或查询可用工具与 schema |
|
||||
| `lineup.v1.tool.call` | Agent → Client | 请求调用用户端工具 |
|
||||
| `lineup.v1.tool.result` | Client → Agent | 返回工具结果、拒绝、取消或错误 |
|
||||
| `lineup.v1.tool.cancel` | 双向 | 取消尚未完成的调用 |
|
||||
| `lineup.v1.agent.status` | Agent → Client | idle / thinking / waiting_input / running / interrupted / failed |
|
||||
| `lineup.v1.agent.progress` | Agent → Client | 可合并、可覆盖的进度状态 |
|
||||
| `lineup.v1.artifact.offer` | Agent → Client | 声明图片、文件、网页、报告、画板等可展示产物 |
|
||||
| `lineup.v1.canvas.open` | Agent → Client | 创建一个受控画板实例 |
|
||||
| `lineup.v1.canvas.patch` | Agent → Client | 增量更新画板内容 |
|
||||
| `lineup.v1.canvas.close` | Agent → Client | 关闭画板实例 |
|
||||
| `lineup.v1.stream.start/delta/end` | Agent → Client | 流式文本、代码、图形或状态输出 |
|
||||
| `lineup.v1.ui.open/patch/close/event` | 双向 | 受控 HTML/CSS/JS UI Surface 的生命周期与用户事件 |
|
||||
| `lineup.v1.app.list/call/result` | 双向 | Client 上层应用能力的声明、授权调用与结构化结果 |
|
||||
|
||||
不在第一阶段实现的消息类型可以预留命名空间,但不得先发布无 schema 的行为。
|
||||
|
||||
UI Surface 与 App Capability 的完整隔离、生命周期和权限约束见[《LineUp UI Surface 与 App Capability 协议》](lineup-ui-surface-protocol.md)。Surface 不是 Agent 在 Client 主进程执行任意代码的通道;它只能运行在受限容器中,并通过预定义 bridge 回传用户事件。任何上层 App / 原生能力必须经 `app.call`、用户确认和 capability registry 执行。
|
||||
|
||||
---
|
||||
|
||||
## 5. 用户端工具调用规范
|
||||
|
||||
### 5.1 工具注册
|
||||
|
||||
Client 在设备上线或能力变化后发布 `tool.list`。每个工具至少声明:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "choice",
|
||||
"version": "1.0",
|
||||
"type": "action",
|
||||
"description": "展示选项并返回用户选择",
|
||||
"inputSchema": {},
|
||||
"outputSchema": {},
|
||||
"risk": "user_confirmation"
|
||||
}
|
||||
```
|
||||
|
||||
工具分为三类:
|
||||
|
||||
- `action`:一次性操作,如 `choice`、`confirm`、`input`;
|
||||
- `toolset`:无状态工具集合,如计算器、文件选择器;
|
||||
- `app`:有生命周期的交互应用,如 `canvas`,通过 `instance_id` 管理 `open → use → close`。
|
||||
|
||||
### 5.2 调用与返回
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"id": "msg_01J_call",
|
||||
"type": "lineup.v1.tool.call",
|
||||
"conversation_id": "conv_01J",
|
||||
"sender": { "kind": "agent", "id": "agent_hermes_main" },
|
||||
"target": { "kind": "device", "id": "device_phone_01" },
|
||||
"payload": {
|
||||
"call_id": "call_01J",
|
||||
"name": "choice",
|
||||
"action": "select",
|
||||
"expires_at": "2026-07-30T14:10:00+08:00",
|
||||
"arguments": {
|
||||
"question": "部署到哪个环境?",
|
||||
"options": ["测试", "预发布", "生产"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
返回必须携带相同 `call_id`,并且结果为可扩展对象,不长期限制为纯字符串:
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"id": "msg_01J_result",
|
||||
"type": "lineup.v1.tool.result",
|
||||
"conversation_id": "conv_01J",
|
||||
"sender": { "kind": "human", "id": "user_01" },
|
||||
"target": { "kind": "agent", "id": "agent_hermes_main" },
|
||||
"payload": {
|
||||
"call_id": "call_01J",
|
||||
"status": "completed",
|
||||
"result": {
|
||||
"selected": "预发布"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`status` 的第一版枚举为:
|
||||
|
||||
```text
|
||||
completed | rejected | cancelled | expired | unsupported | failed
|
||||
```
|
||||
|
||||
### 5.3 调用状态机
|
||||
|
||||
```text
|
||||
created
|
||||
→ delivered
|
||||
→ acknowledged_by_client
|
||||
→ waiting_user
|
||||
→ completed | rejected | cancelled | expired | failed
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
1. `call_id` 在 `conversation_id` 范围内唯一;
|
||||
2. 同一个 `call_id` 的重复 `tool.call` 必须幂等显示,不能重复执行本地副作用;
|
||||
3. `tool.result` 必须幂等转交给 Agent,Gateway 需持久记录最终状态;
|
||||
4. Client 离线时调用可等待到 `expires_at`,不得无限期阻塞 Agent;
|
||||
5. 用户、Agent 或系统取消后,旧调用及旧流输出不可覆盖新一代会话状态;
|
||||
6. 高风险工具没有显式用户授权时,Client 必须返回 `rejected`,而不是静默执行。
|
||||
|
||||
---
|
||||
|
||||
## 6. 身份、会话、设备与权限
|
||||
|
||||
### 6.1 四种核心对象
|
||||
|
||||
| 对象 | 说明 | 示例 |
|
||||
|---|---|---|
|
||||
| Human | 一个经过 IM 认证的人类用户 | `user_01` |
|
||||
| Device | 某个具体 Client 实例 | `device_phone_01` |
|
||||
| Agent | 一个可接收和处理 LineUp 消息的 Agent 实例 | `agent_hermes_main` |
|
||||
| Conversation | 人、人群或 Agent 间的一段稳定上下文 | `conv_01J` |
|
||||
|
||||
底层 WuKongIM 频道负责投递,LineUp `conversation_id` 负责产品语义。一个 Conversation 可映射到单聊、群聊、主题或多个设备,但映射关系必须由 Gateway 显式维护。
|
||||
|
||||
### 6.2 权限原则
|
||||
|
||||
1. 默认最小权限:Agent 只看得到被明确路由给它的会话;
|
||||
2. 设备能力显式声明:没有在 `tool.list` 声明的工具不可调用;
|
||||
3. 风险分级:`display_only`、`user_confirmation`、`system_permission`、`restricted`;
|
||||
4. 人类可取消:用户在任一 Agent 会话中必须可发出取消/中断;
|
||||
5. 群聊默认只响应明确提及或授权的 Agent;
|
||||
6. Gateway 记录调用、确认、结果和取消的审计事件;
|
||||
7. Agent Runtime 的本地终端、文件和浏览器权限不因 IM 通道自动扩大。
|
||||
|
||||
---
|
||||
|
||||
## 7. 可靠性、重连与用户插入指令
|
||||
|
||||
### 7.1 双层可靠性
|
||||
|
||||
```text
|
||||
WuKongIM
|
||||
└─ 连接、顺序、离线消息、重连与传输级可靠性
|
||||
|
||||
LineUp Gateway
|
||||
└─ 应用级 idempotency、call_id 状态、inbox/outbox、Agent 执行恢复
|
||||
```
|
||||
|
||||
Gateway 维护持久 inbox/outbox。任何消息只有在成功落入 inbox 后才视为被应用层接收;任何对 Agent 或 Client 的关键发送都要记录投递状态和应用级消息 ID。
|
||||
|
||||
### 7.2 用户中断
|
||||
|
||||
用户的新指令不能因为旧 Agent 任务运行很久而被阻塞:
|
||||
|
||||
```text
|
||||
Client 新消息
|
||||
→ WuKongIM 实时送达 Gateway
|
||||
→ Gateway 按 conversation_id 分发
|
||||
→ Hermes Adapter 调用 Hermes 的会话中断/追加机制
|
||||
→ 旧 run 标记为过期 generation
|
||||
→ 旧 run 的迟到输出禁止回写 Client
|
||||
→ 新指令立即开始或进入指定队列
|
||||
```
|
||||
|
||||
第一版约定:
|
||||
|
||||
- 普通新文本:默认 `interrupt` 当前同会话 Agent run;
|
||||
- `/stop` 或显式“停止”:取消当前 run 并通知用户;
|
||||
- “完成后再……”:由 Client 或 Adapter 标记为 queued follow-up;
|
||||
- 不同 Conversation:相互独立,可并发处理。
|
||||
|
||||
---
|
||||
|
||||
## 8. 技术选型与当前环境定位
|
||||
|
||||
### 8.1 主通道选择
|
||||
|
||||
| 方案 | 结论 | 原因 |
|
||||
|---|---|---|
|
||||
| 唐僧叨叨业务层 | 正式业务基础 | 已提供用户、认证、联系人、群组、文件、工作台、管理端及多端客户端基础;LineUp 优先在其上生长,而非在第一阶段重新建设这些通用 IM 业务能力 |
|
||||
| 唐僧叨叨 Robot Events API | 机器人接入与兼容通道 | 具备事件队列、ACK、机器人身份,适合文本 Hermes 入口、通知和降级交互;当前发送能力不适合作为完整结构化工具协议的唯一主载体 |
|
||||
| WuKongIM AI Plugin | 后续可选 | 适合低延迟和服务端 Hook;当前部署为唐僧叨叨配套 WuKongIM v2,插件协议和管理能力需单独验证,且不替代 Client/Gateway 的产品语义 |
|
||||
| WuKongIM SDK + 外部 LineUp Gateway | 正式 Agent 交互通道 | 在唐僧叨叨业务体系内保留自定义消息、双向实时、客户端工具、多个 Agent Runtime 与独立演进能力 |
|
||||
|
||||
### 8.2 当前部署的使用方式
|
||||
|
||||
当前环境维持:
|
||||
|
||||
```text
|
||||
唐僧叨叨服务端 v1.5 + WuKongIM v2 + MySQL + Redis + MinIO
|
||||
监听:100.121.118.116(Tailscale)
|
||||
```
|
||||
|
||||
该环境在本方案中的职责:
|
||||
|
||||
- 以唐僧叨叨作为 LineUp 的既有业务基础,继续提供用户、认证、联系人、群组、文件、工作台和管理能力;
|
||||
- 以 WuKongIM 承担基础 IM 服务、长连接、离线同步和自定义消息传输;
|
||||
- 以现有唐僧 Android / Web 作为可复用的客户端基础,并逐步加入 LineUp 交互渲染与工具模块;
|
||||
- 用于验证 LineUp Client 的 SDK 连接、消息、离线和媒体行为;
|
||||
- 为 LineUp Gateway 提供受控网络内的消息服务;
|
||||
- 可选提供唐僧 Robot Events 文本机器人、通知和降级入口。
|
||||
|
||||
不得为了启用 AI Plugin 而直接将该稳定配套环境替换为 WuKongIM 主线版本;插件实验必须使用独立测试实例。
|
||||
|
||||
---
|
||||
|
||||
## 9. 分阶段实施计划
|
||||
|
||||
### Phase 0:协议与可行性基线
|
||||
|
||||
**目标:** 在不改变现有生产性 IM 数据的前提下,确认 WuKongIM v2 可以承载 LineUp 自定义 Payload。
|
||||
|
||||
**工作项:**
|
||||
|
||||
1. 定义 `lineup.v1` 自定义 Payload 的编码、消息类型编号和最大体积;
|
||||
2. 使用两个测试账户,通过官方 SDK 收发并解码一条 LineUp 信封;
|
||||
3. 验证离线消息、多设备同步、顺序、重复投递与撤回等边界;
|
||||
4. 验证图片、文件与自定义交互消息的引用关系;
|
||||
5. 定义测试频道/Agent 身份命名规范;
|
||||
6. 记录 WuKongIM v2 的精确 SDK 与协议限制。
|
||||
|
||||
**验收标准:**
|
||||
|
||||
- 任一端离线重连后,`lineup.v1.tool.call` 不丢失且不重复执行;
|
||||
- 自定义消息可以被测试 Client 正确识别;
|
||||
- 不改变现有唐僧叨叨用户、消息和 Docker 命名卷。
|
||||
|
||||
### Phase 1:LineUp 协议核心与最小 Gateway
|
||||
|
||||
**目标:** 打通 Client、Gateway 与一个模拟 Agent 的双向结构化交互。
|
||||
|
||||
**工作项:**
|
||||
|
||||
1. 实现 LineUp 信封编解码库与 JSON Schema;
|
||||
2. 实现 Gateway 的 Actor、Device、Conversation 映射;
|
||||
3. 实现 SQLite 持久 inbox/outbox 与消息去重;
|
||||
4. 实现 `device.hello`、`tool.list`、`tool.call`、`tool.result`、`tool.cancel`;
|
||||
5. 实现 `call_id` 状态机、超时和审计;
|
||||
6. 使用模拟 Agent 发起 `choice` 与 `confirm`。
|
||||
|
||||
**验收标准:**
|
||||
|
||||
- Client 断线、Gateway 重启后,待处理工具调用仍可恢复;
|
||||
- 重复投递不导致工具执行两次;
|
||||
- 不受支持工具返回 `unsupported`;
|
||||
- 用户拒绝、取消、超时能精确回到对应 Agent 调用。
|
||||
|
||||
### Phase 2:LineUp Client 最小交互面
|
||||
|
||||
**目标:** 从“聊天 UI”进入“Agent 交互 UI”。
|
||||
|
||||
**工作项:**
|
||||
|
||||
1. 基于现有唐僧叨叨客户端能力建立 LineUp Client 扩展层或受控分叉,保留登录、联系人、聊天、媒体与文件基础;
|
||||
2. 接入并验证 WuKongIM SDK 的 LineUp 自定义 Payload;
|
||||
3. 实现文本、Agent 状态、进度、choice、confirm、input;
|
||||
4. 实现工具注册、风险提示、用户授权与结果回传;
|
||||
5. 实现会话列表中的 Agent 标识、运行状态与取消入口;
|
||||
6. 实现最小 Artifact 展示:图片与文件;
|
||||
7. 记录客户端能力及版本兼容策略。
|
||||
|
||||
**验收标准:**
|
||||
|
||||
- 用户可在手机上完成 Agent 发起的选择和确认;
|
||||
- Agent 能得到结构化而非文本猜测式的结果;
|
||||
- 用户可在 Agent 工作期间中断并发送替代指令;
|
||||
- 图片、文件与进度不会破坏普通人与人聊天。
|
||||
|
||||
### Phase 3:Hermes 首个正式 Adapter
|
||||
|
||||
**目标:** Hermes 成为第一个完整支持 LineUp 协议的 Agent Runtime。
|
||||
|
||||
**工作项:**
|
||||
|
||||
1. 实现 Hermes ↔ Gateway Adapter;
|
||||
2. 将 LineUp Conversation 映射到 Hermes session;
|
||||
3. 将用户新消息、取消和排队指令映射到 Hermes 原生会话控制;
|
||||
4. 将 Hermes 的工具等待映射为 `tool.call` / `tool.result`;
|
||||
5. 将 Hermes 进度、状态和产物映射为 LineUp 消息;
|
||||
6. 实现按用户/群组/Agent 的访问控制与审计。
|
||||
|
||||
**验收标准:**
|
||||
|
||||
- Hermes 可以发起 `choice` / `confirm` 并等待手机端结果后继续;
|
||||
- 任务中途的新指令能取消旧 run,旧输出不会污染新会话;
|
||||
- Gateway 或 Hermes 重启时不重复执行不可逆工具调用;
|
||||
- 白名单外用户不能访问 Hermes 的本地执行能力。
|
||||
|
||||
### Phase 4:富交互与机器人接入
|
||||
|
||||
**目标:** 扩展产品表达能力,同时保持现有唐僧客户端可用。
|
||||
|
||||
**工作项:**
|
||||
|
||||
1. 实现 `canvas.open/patch/close` 与基础画板;
|
||||
2. 实现 Artifact 卡片、图片预览、文件产物和流式状态;
|
||||
3. 建立 `lineup_hermes` 唐僧机器人,提供文本入口、通知和不支持富交互时的安全降级;
|
||||
4. Robot Events Adapter 将文本对话接入 Hermes;
|
||||
5. 对尚未安装 LineUp 扩展能力的客户端,发送安全的降级文本与 LineUp Client 跳转提示;
|
||||
6. 建立跨客户端能力协商策略。
|
||||
|
||||
**验收标准:**
|
||||
|
||||
- LineUp Client 可以呈现至少一种非文本 Agent 交互(画板或结构化表单);
|
||||
- 唐僧客户端仍可完成文本对话和基础确认降级;
|
||||
- 两种入口不会对同一会话产生重复 Agent 回复。
|
||||
|
||||
### Phase 5:WuKongIM AI Plugin 评估与增强
|
||||
|
||||
**前置条件:** 仅在独立测试实例确认当前 WuKongIM 版本、插件协议、插件管理与唐僧叨叨兼容边界后启动。
|
||||
|
||||
**目标:** 评估服务端 Plugin 是否为 LineUp 带来明确收益。
|
||||
|
||||
**评估项:**
|
||||
|
||||
- 流式延迟与 Gateway 外部 SDK 通道的对比;
|
||||
- 插件绑定是否能精确限定 Agent 频道;
|
||||
- 插件崩溃、升级、滚动重启的影响;
|
||||
- Go Plugin 与 Hermes/Python 外部 Runtime 的超时、取消和恢复;
|
||||
- 与唐僧叨叨 webhook 的消息所有权和重复处理风险;
|
||||
- 安全审计、配置密钥和多租户隔离。
|
||||
|
||||
只有存在可量化收益时,才将 Plugin 用作 Gateway 的优化实现或服务端路由层。
|
||||
|
||||
---
|
||||
|
||||
## 10. 近期执行顺序
|
||||
|
||||
本方案确认后,近期工作的严格顺序如下:
|
||||
|
||||
```text
|
||||
1. Phase 0:验证 WuKongIM v2 自定义 Payload 与 SDK 行为
|
||||
2. 固化 LineUp v1 消息 schema 与 call 状态机
|
||||
3. 建立最小外部 LineUp Gateway(模拟 Agent)
|
||||
4. 在唐僧叨叨客户端基础上实现 LineUp 最小交互:choice / confirm / input
|
||||
5. 实现 Hermes 正式 Adapter
|
||||
6. 补唐僧机器人作为文本、通知和降级入口
|
||||
7. 最后才评估 WuKongIM AI Plugin
|
||||
```
|
||||
|
||||
### 10.1 各阶段目标概览
|
||||
|
||||
| 阶段 | 阶段目标 | 完成后得到什么 |
|
||||
|---|---|---|
|
||||
| 1. 验证 WuKongIM 自定义消息能力 | 证明当前唐僧叨叨配套的 WuKongIM v2 能可靠传输 LineUp 自定义 Payload | 确认 IM 基础能够承载 `tool.call`、`tool.result` 等协议,而不是只能传普通文本 |
|
||||
| 2. 固化 LineUp 协议 | 明确消息格式、类型、版本、会话、调用 ID、取消和错误语义 | Client、Gateway、Hermes 可遵循同一份可测试、可版本化的协议契约 |
|
||||
| 3. 建立最小 Gateway | 建立 IM 和 Agent 之间的常驻中枢,负责收发、去重、会话映射与持久化 | 得到可靠的 LineUp 交换站,Client 或 Agent 重启时关键交互仍可恢复 |
|
||||
| 4. 实现最小 LineUp Client | 在唐僧叨叨客户端基础上,让客户端从文本聊天界面进入可操作的 Agent 交互界面 | 用户可执行 choice、confirm、input 等结构化交互,而非回复编号或自然语言猜测 |
|
||||
| 5. 接入 Hermes | 将真实 Hermes 会话映射为 LineUp 会话和工具调用 | 用户可远程操控 Hermes,Hermes 可请求确认、获得结果、展示状态并被中断 |
|
||||
| 6. 提供唐僧叨叨机器人入口 | 在既有唐僧叨叨业务与客户端体系内提供 Hermes 文本、通知和降级交互 | 为尚未具备 LineUp 富交互能力的客户端提供可用入口;复杂操作安全降级为文本 |
|
||||
| 7. 评估 WuKongIM AI Plugin | 判断服务端插件是否能在当前兼容边界内带来可量化收益 | 在确认版本、稳定性和收益后,决定是否加入低延迟流式与服务端路由增强层 |
|
||||
|
||||
依赖关系如下:
|
||||
|
||||
```text
|
||||
先证明 IM 能传 LineUp 消息
|
||||
↓
|
||||
再规定 LineUp 消息、会话和工具调用的统一语义
|
||||
↓
|
||||
建立可靠的 Gateway
|
||||
↓
|
||||
让 Client 将结构化消息渲染为可操作 UI
|
||||
↓
|
||||
接入 Hermes,形成真实的人—Agent 协作闭环
|
||||
↓
|
||||
补充唐僧叨叨机器人文本、通知与降级入口
|
||||
↓
|
||||
最后按实际收益评估 WuKongIM AI Plugin
|
||||
```
|
||||
|
||||
这保证产品核心先围绕“人和 Agent 的结构化协作”落地,而不是被某个现有 IM 客户端的文本机器人能力限制。
|
||||
|
||||
---
|
||||
|
||||
## 11. 与既有文档的关系
|
||||
|
||||
本方案继承早期文档中以下原则:
|
||||
|
||||
- 三层分离:连接层、消息传输层、工具/交互层;
|
||||
- LineUp 协议只定义上层交互,不重复实现 IM 基础能力;
|
||||
- `tool.call` / `tool.result` 使用结构化数据与 `call_id` 配对;
|
||||
- Agent Runtime 插件化接入,避免将所有 Agent 逻辑写入客户端。
|
||||
- 唐僧叨叨作为成熟 IM 业务层和客户端基础,优先复用并按 LineUp 实际需求扩展。
|
||||
|
||||
本方案替换或细化以下早期假设:
|
||||
|
||||
- 不再将“官方唐僧叨叨客户端上的文本机器人”视为 LineUp 的唯一体验;机器人是完整业务体系中的一种 Agent 接入形式;
|
||||
- 不再将“基于某一个 Agent 的 WebSocket 插件”视为广域网交互的唯一架构;
|
||||
- 正式引入 Client、Device、Agent、Conversation 与持久化 Gateway 的边界;
|
||||
- 将自定义 IM Payload、客户端工具注册、调用状态机、取消与权限模型列为 MVP 基础,而不是后续附加能力;
|
||||
- LineUp 的新增能力以唐僧叨叨已有用户、群组、文件、工作台和多端客户端能力为基础生长,不预设重造整套 IM 业务系统。
|
||||
@@ -0,0 +1,441 @@
|
||||
# LineUp Agents — MVP 产品与技术规格
|
||||
|
||||
**状态:** v0.1 / 讨论稿
|
||||
**日期:** 2026-07-24
|
||||
**目标:** 用最小可行的服务端、Web 客户端和 Agent Adapter,验证“统一的人机协作交互层”是否成立。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话定义
|
||||
|
||||
**LineUp Agents 是一个面向个人与小团队的 Agent 协作入口:人通过统一的对话、任务、状态与确认组件,连接并管理自己已经拥有的 AI Agent。**
|
||||
|
||||
它类似于一个为 Agent 设计的 Channel:不是替换 OpenClaw、Codex、Hermes 或自定义运行时,而是把它们接入一个更适合持续协作的交互层。
|
||||
|
||||
## 2. 讨论结论与产品判断
|
||||
|
||||
| 已达成的判断 | 对首版的含义 |
|
||||
|---|---|
|
||||
| 先做好交互,不急着做万能 Agent 平台 | 任务、状态、确认和展示是核心,不把能力重心放在模型或编排 |
|
||||
| Agent 的长期运行需要服务端 | 服务端持久化事件、会话和任务;客户端可随时离开和回来 |
|
||||
| 主流 Agent 应能接入 | 定义轻量、事件驱动的 Adapter 协议,而非绑定单一框架 |
|
||||
| 配置必须简单通用 | 用户只需绑定 Agent、选择会话、开始协作;复杂部署隐藏在 Adapter 后 |
|
||||
| 自动拆分/分配可探索,但自动可靠完成仍很难 | v0.1 允许 Agent 报告子任务;不做自动抢占、调度或自治执行承诺 |
|
||||
|
||||
## 3. 问题与目标用户
|
||||
|
||||
### 3.1 要解决的问题
|
||||
|
||||
今天的人机协作通常被困在终端、网页聊天页或某个框架专属界面中。用户很难跨端了解:
|
||||
|
||||
- 我的 Agent 现在是否正在执行、卡在哪里、是否失联?
|
||||
- 它何时需要我确认、补充信息或授权?
|
||||
- 多个持续任务之间,哪个最重要、哪个已经完成?
|
||||
- 换一个 Agent 框架后,为什么交互与历史记录都断裂?
|
||||
|
||||
LineUp 的工作不是替 Agent 思考,而是把这些协作状态变得可见、可控、连续。
|
||||
|
||||
### 3.2 首批用户
|
||||
|
||||
1. **重度个人 Agent 用户**:在本机或 VPS 上长时间运行一个或多个 Agent,希望在手机和 Web 上随时查看和介入。
|
||||
2. **小型 AI 原生团队**:成员共享少量部署好的 Agent,需要看清谁在处理什么、何时要人类决策。
|
||||
3. **Agent/Runtimes 开发者**:希望用少量接入工作获得成熟交互界面,而不是重造消息、任务与通知系统。
|
||||
|
||||
### 3.3 北极星任务
|
||||
|
||||
> 我在外出时收到一条通知:我的代码 Agent 需要决定是否执行数据库迁移。我打开 LineUp,看见完整任务上下文、影响说明和两个清晰选项;批准后 Agent 继续执行,我随后能看到结果与相关产物。
|
||||
|
||||
## 4. 产品原则
|
||||
|
||||
1. **状态先于文本。** 聊天记录很重要,但“正在运行”“等待我”“已失败”必须一眼可见。
|
||||
2. **人保留关键控制权。** 有风险、费用、外部副作用的行动必须能由 Agent 发起清晰的确认请求。
|
||||
3. **Agent 无关,但不抹平差异。** 统一最小能力;框架特有能力可通过扩展卡片展示。
|
||||
4. **渐进披露。** 默认只展示当前决策所需的信息;详细日志、原始事件和调试字段按需展开。
|
||||
5. **每一件事都可追溯。** 任务、消息、审批和状态转换写入不可变事件流,便于同步与审计。
|
||||
6. **先轻后重。** 先交付一个连接可靠、体验细腻的 Channel,再逐步增加任务协作能力。
|
||||
|
||||
## 5. MVP 范围
|
||||
|
||||
### 5.1 必须交付
|
||||
|
||||
| 能力 | 用户价值 | v0.1 交付 |
|
||||
|---|---|---|
|
||||
| Agent 绑定 | 连接用户已有 Agent | 配对码/Token 绑定、Agent 在线状态、撤销绑定 |
|
||||
| 会话 | 保持对话连续 | 会话列表、消息流、流式文本、附件链接 |
|
||||
| 任务 | 看清长期工作 | 创建任务、状态、进度、父子任务展示、取消请求 |
|
||||
| 人类介入 | 不让 Agent 默默卡住 | 单选、多选、文本补充、批准/拒绝四类交互卡片 |
|
||||
| 实时同步 | 跨端了解当前状态 | WebSocket、断线重连、顺序事件、离线补偿 |
|
||||
| 通知 | 用户离开时仍可介入 | 浏览器通知/邮件二选一,优先通知“等待用户”和“失败” |
|
||||
| Adapter SDK | 接入至少一个实际 Agent | TypeScript SDK + 示例 Adapter + 事件协议文档 |
|
||||
|
||||
### 5.2 明确不做
|
||||
|
||||
- 自动从自然语言可靠拆解、排程并完成任意复杂任务。
|
||||
- Agent 之间的自动抢单、竞价、自治协调与长期记忆治理。
|
||||
- 模型 API 代理、模型计费、Sandbox 执行器或完整远程桌面。
|
||||
- 企业 SSO、复杂 RBAC、多租户计费与组织级合规功能。
|
||||
- 原生 iOS/Android App;首版先用响应式 Web 验证交互模型。
|
||||
|
||||
## 6. 信息架构
|
||||
|
||||
```text
|
||||
工作区
|
||||
├── 收件箱
|
||||
│ ├── 等待我处理
|
||||
│ ├── 失败 / 需要关注
|
||||
│ └── 最近完成
|
||||
├── 会话
|
||||
│ └── 一个会话对应人与一个或多个 Agent 的协作上下文
|
||||
├── 任务
|
||||
│ ├── 全部
|
||||
│ ├── 进行中
|
||||
│ ├── 等待我
|
||||
│ └── 已完成 / 已取消
|
||||
├── Agent
|
||||
│ ├── 在线状态与能力
|
||||
│ ├── 绑定 / 配置
|
||||
│ └── Adapter 诊断日志
|
||||
└── 设置
|
||||
└── 用户、通知、开发者 Token
|
||||
```
|
||||
|
||||
### 6.1 核心对象
|
||||
|
||||
| 对象 | 含义 | 关键字段 |
|
||||
|---|---|---|
|
||||
| Workspace | 数据与访问边界 | `id`, `name` |
|
||||
| User | 使用者 | `id`, `displayName` |
|
||||
| Agent | 一个可连接的 Agent 实例 | `id`, `name`, `runtime`, `status`, `capabilities` |
|
||||
| Conversation | 一个协作上下文 | `id`, `workspaceId`, `title`, `agentIds` |
|
||||
| Message | 用户、Agent 或系统产生的内容 | `id`, `conversationId`, `author`, `content`, `createdAt` |
|
||||
| Task | 可追踪的工作单元 | `id`, `conversationId`, `parentTaskId`, `title`, `status`, `progress` |
|
||||
| Interaction | 必须由用户处理的结构化请求 | `id`, `taskId`, `type`, `status`, `payload`, `expiresAt` |
|
||||
| Event | 所有状态变化的事实记录 | `id`, `sequence`, `type`, `subject`, `payload`, `occurredAt` |
|
||||
|
||||
### 6.2 任务状态机
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> queued
|
||||
queued --> running: Agent 接受 / 用户开始
|
||||
running --> waiting_for_human: 发起 Interaction
|
||||
waiting_for_human --> running: 用户响应
|
||||
running --> succeeded: 完成
|
||||
running --> failed: 失败
|
||||
queued --> cancelled: 取消
|
||||
running --> cancelled: 取消确认 / Agent 停止
|
||||
waiting_for_human --> cancelled: 用户取消
|
||||
succeeded --> [*]
|
||||
failed --> [*]
|
||||
cancelled --> [*]
|
||||
```
|
||||
|
||||
状态定义:
|
||||
|
||||
- `queued`:任务已创建、尚未由 Agent 接受。
|
||||
- `running`:Agent 正在执行,允许不断上报进度与日志摘要。
|
||||
- `waiting_for_human`:工作被一个未处理的 Interaction 阻塞,应进入收件箱并通知用户。
|
||||
- `succeeded` / `failed` / `cancelled`:终态;任务和完整事件历史可查。
|
||||
|
||||
## 7. 关键交互
|
||||
|
||||
### 7.1 首次连接 Agent
|
||||
|
||||
1. 用户在「Agent」页点击“连接我的 Agent”。
|
||||
2. 选择 Adapter 类型(首版:Generic SDK;后续可有 OpenClaw 预设)。
|
||||
3. 服务端生成一次性配对码和安装命令;配对码有效期 10 分钟。
|
||||
4. 用户在 Agent 所在机器运行 Adapter,Adapter 用配对码建立出站 WebSocket。
|
||||
5. 服务端显示 Agent 名称、版本、可用能力与在线状态;用户确认命名后完成绑定。
|
||||
|
||||
**体验要求:** 不要求用户开放入站端口;Agent 主动连接中转服务。连接失败时必须显示可复制的诊断码及下一步建议。
|
||||
|
||||
### 7.2 发起与跟进一个任务
|
||||
|
||||
1. 用户在会话底部输入自然语言需求,可勾选“作为任务跟踪”。
|
||||
2. 系统先创建 `task.created`,再把文本作为 `message.created` 发送给目标 Agent。
|
||||
3. Agent 接受后上报 `task.status_changed: running`,可持续上报 0–100 的进度和一句简短状态。
|
||||
4. 用户可在会话内看简要进度,也可打开任务详情查看子任务、产物和事件时间线。
|
||||
5. Agent 完成、失败或需要人类处理时,状态即时同步到任务列表和收件箱。
|
||||
|
||||
### 7.3 Agent 请求人类决策
|
||||
|
||||
交互卡片的结构为:**为什么需要你 → 影响是什么 → 你能做什么 → 详细信息(折叠)**。
|
||||
|
||||
| 卡片类型 | 示例 | 必填交互 |
|
||||
|---|---|---|
|
||||
| `approval` | “将执行生产数据库迁移” | 批准 / 拒绝;可选备注 |
|
||||
| `choice` | “部署区域选择” | 2–5 个选项;可标注推荐项 |
|
||||
| `input` | “请提供测试环境 URL” | 文本输入、格式校验 |
|
||||
| `review` | “请检查生成的变更计划” | 展示内容或附件;批准 / 要求修改 |
|
||||
|
||||
响应后,卡片变为只读并保留“谁在何时作出什么决定”;对应事件发送回 Agent,任务恢复 `running`。
|
||||
|
||||
### 7.4 Agent 失联与恢复
|
||||
|
||||
- WebSocket 心跳间隔 25 秒;连续 2 次未收到心跳标记为 `offline`。
|
||||
- 运行中任务不立刻标为失败,而显示“Agent 连接中断”,并保留最后心跳时间。
|
||||
- Adapter 重连后携带最后确认的 `sequence`;服务端补发缺失下行事件。
|
||||
- 若 Agent 在配置的宽限期(默认 15 分钟)后仍未恢复,任务改为 `failed`,原因是 `agent_unreachable`。
|
||||
|
||||
## 8. 页面与组件规格
|
||||
|
||||
### 8.1 桌面端主界面
|
||||
|
||||
```text
|
||||
┌──────────────┬───────────────────────────────────────┬──────────────────────┐
|
||||
│ 工作区 │ 会话:网站重构 │ 任务详情 │
|
||||
│ │ │ │
|
||||
│ 收件箱 (2) │ [用户] 分析现有页面并提交改进建议 │ ● 正在执行 60% │
|
||||
│ 会话 │ │ “整理组件依赖” │
|
||||
│ 任务 │ [Agent] 已建立任务计划,正在扫描仓库… │ │
|
||||
│ Agent │ │ 子任务 │
|
||||
│ │ ┌───────────────────────────────────┐ │ ✓ 扫描仓库 │
|
||||
│ │ │ 需要你的确认 │ │ ● 整理组件依赖 │
|
||||
│ │ │ 允许安装 3 个开发依赖? │ │ ○ 输出迁移方案 │
|
||||
│ │ │ [查看变更] [拒绝] [允许] │ │ │
|
||||
│ │ └───────────────────────────────────┘ │ 事件时间线 │
|
||||
│ │ │ │
|
||||
│ │ 输入消息… [发送] │ │
|
||||
└──────────────┴───────────────────────────────────────┴──────────────────────┘
|
||||
```
|
||||
|
||||
**响应式规则:** 宽度小于 900px 时隐藏右侧详情为抽屉;小于 640px 时侧栏收起,顶部保留收件箱未读数和 Agent 在线指示。
|
||||
|
||||
### 8.2 首版设计令牌
|
||||
|
||||
```css
|
||||
:root {
|
||||
--surface-canvas: #f7f8fa;
|
||||
--surface-default: #ffffff;
|
||||
--surface-subtle: #f1f3f5;
|
||||
--text-primary: #1b1f24;
|
||||
--text-secondary: #59636e;
|
||||
--border-subtle: #d8dee4;
|
||||
--brand: #356ae6;
|
||||
--status-running: #356ae6;
|
||||
--status-waiting: #b7791f;
|
||||
--status-success: #168a5b;
|
||||
--status-danger: #c93c37;
|
||||
--radius-card: 12px;
|
||||
--radius-control: 8px;
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-6: 24px;
|
||||
}
|
||||
```
|
||||
|
||||
可访问性底线:正文与背景对比度至少 4.5:1;不只依靠颜色表达状态;所有卡片操作支持键盘焦点、Enter/Space 触发及明确的加载/提交结果。
|
||||
|
||||
## 9. 系统架构
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Clients[用户客户端]
|
||||
WEB[响应式 Web]
|
||||
MOBILE[未来移动端]
|
||||
end
|
||||
|
||||
subgraph Cloud[LineUp 中转服务]
|
||||
API[HTTPS API]
|
||||
RT[Realtime Gateway\nWebSocket]
|
||||
AUTH[身份与设备绑定]
|
||||
APP[协作应用服务]
|
||||
DB[(PostgreSQL)]
|
||||
BUS[(Event / Job Queue)]
|
||||
PUSH[通知服务]
|
||||
end
|
||||
|
||||
subgraph UserEnv[用户的本机或服务器]
|
||||
ADAPTER[LineUp Adapter]
|
||||
RUNTIME[OpenClaw / Codex /\nHermes / 自定义 Agent]
|
||||
end
|
||||
|
||||
WEB <-- HTTPS + WS --> API
|
||||
WEB <-- HTTPS + WS --> RT
|
||||
MOBILE <-- HTTPS + WS --> API
|
||||
MOBILE <-- HTTPS + WS --> RT
|
||||
API --> AUTH
|
||||
API --> APP
|
||||
RT --> APP
|
||||
APP --> DB
|
||||
APP --> BUS
|
||||
BUS --> PUSH
|
||||
ADAPTER <-- 出站 WSS --> RT
|
||||
ADAPTER <--> RUNTIME
|
||||
```
|
||||
|
||||
### 9.1 推荐实现取舍
|
||||
|
||||
| 层 | 首版选择 | 原因 |
|
||||
|---|---|---|
|
||||
| Web | Next.js + TypeScript | 同时覆盖 Web、响应式移动体验与后续 BFF 需求 |
|
||||
| 服务端 | TypeScript(Fastify/NestJS 任选其一) | 与 SDK 共享类型和校验器,迭代快 |
|
||||
| 实时连接 | 标准 WebSocket | 对 Agent 和客户端都简单;协议可公开 |
|
||||
| 数据 | PostgreSQL | 可靠存储、查询任务,JSONB 容纳扩展事件 |
|
||||
| 临时事件/队列 | Redis Streams 或 BullMQ | 处理通知、投递重试和长连接广播 |
|
||||
| Adapter SDK | TypeScript 首发 | 覆盖主要 Node Agent;协议保持语言无关 |
|
||||
| 鉴权 | 短期 Access Token + Agent 配对 Token | 安全模型足够简单,后续可演进 OAuth/设备授权 |
|
||||
|
||||
**关键边界:** 业务服务不执行 Agent 的工具调用,也不存放模型密钥。它仅路由已认证事件、持久化协作状态,并向用户端可靠同步。
|
||||
|
||||
## 10. 最小事件协议(LAP/0.1)
|
||||
|
||||
所有 WebSocket 帧使用统一 envelope。`payload` 按 `type` 做 schema 校验;未知字段容忍但记录,未知事件类型忽略并保留兼容性告警。
|
||||
|
||||
```ts
|
||||
type Envelope<T = unknown> = {
|
||||
id: string; // UUID,幂等键
|
||||
type: string; // 如 task.status_changed
|
||||
version: '0.1';
|
||||
occurredAt: string; // ISO 8601 UTC
|
||||
workspaceId: string;
|
||||
conversationId?: string;
|
||||
taskId?: string;
|
||||
sequence?: number; // 服务端下行事件全局递增序号
|
||||
correlationId?: string; // 串联一次用户请求和后续事件
|
||||
payload: T;
|
||||
};
|
||||
```
|
||||
|
||||
### 10.1 用户端 → 服务端
|
||||
|
||||
| 事件 | 说明 | 关键 payload |
|
||||
|---|---|---|
|
||||
| `message.create` | 发消息 | `content`, `targetAgentId` |
|
||||
| `task.create` | 创建受跟踪任务 | `title`, `description`, `targetAgentId` |
|
||||
| `interaction.respond` | 回应卡片 | `interactionId`, `response` |
|
||||
| `task.cancel_requested` | 请求取消 | `reason?` |
|
||||
|
||||
### 10.2 Agent → 服务端
|
||||
|
||||
| 事件 | 说明 | 关键 payload |
|
||||
|---|---|---|
|
||||
| `agent.hello` | Adapter 握手与能力声明 | `agentVersion`, `runtime`, `capabilities`, `lastSequence?` |
|
||||
| `message.create` | Agent 文本或富内容 | `content`, `blocks?` |
|
||||
| `task.accepted` | 接受任务 | `taskId` |
|
||||
| `task.status_changed` | 上报状态或进度 | `status`, `progress?`, `summary?`, `error?` |
|
||||
| `task.child_created` | 仅用于展示的子任务 | `parentTaskId`, `title`, `status` |
|
||||
| `interaction.requested` | 请求用户输入或确认 | `interaction` |
|
||||
| `artifact.created` | 上报链接/文件/结果 | `name`, `url`, `mimeType?`, `summary?` |
|
||||
|
||||
### 10.3 服务端 → Agent
|
||||
|
||||
| 事件 | 说明 |
|
||||
|---|---|
|
||||
| `task.assigned` | 新任务及所属会话上下文 |
|
||||
| `message.delivered` | 用户追加消息 |
|
||||
| `interaction.resolved` | 用户的结构化回答 |
|
||||
| `task.cancel_requested` | 用户请求停止 |
|
||||
| `system.resync_required` | Adapter 无法从断点恢复,需拉取或重建状态 |
|
||||
|
||||
### 10.4 Interaction 示例
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "interaction.requested",
|
||||
"version": "0.1",
|
||||
"id": "evt_01J...",
|
||||
"occurredAt": "2026-07-24T12:00:00Z",
|
||||
"workspaceId": "ws_01J...",
|
||||
"conversationId": "conv_01J...",
|
||||
"taskId": "task_01J...",
|
||||
"payload": {
|
||||
"interaction": {
|
||||
"id": "int_01J...",
|
||||
"type": "approval",
|
||||
"title": "允许执行数据库迁移吗?",
|
||||
"description": "此操作会在 production 执行 3 个已审阅的迁移文件。",
|
||||
"risk": "high",
|
||||
"options": [
|
||||
{ "id": "approve", "label": "允许执行", "style": "primary" },
|
||||
{ "id": "reject", "label": "拒绝", "style": "danger" }
|
||||
],
|
||||
"details": { "migrationCount": 3, "environment": "production" },
|
||||
"expiresAt": "2026-07-25T12:00:00Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 11. 安全与可靠性基线
|
||||
|
||||
- Agent 仅建立**出站** TLS WebSocket;用户无需暴露本机端口。
|
||||
- 配对码为一次性、短有效期且仅用于兑换 Agent 凭据;凭据可单独撤销。
|
||||
- 每一个 Agent 凭据限定到单 Workspace 和 Agent 身份,禁止跨工作区投递。
|
||||
- 写入事件必须使用 envelope `id` 去重;客户端与 Adapter 应能安全重发。
|
||||
- 明确区分“取消请求”与“已取消”:只有 Agent 确认停止或超时策略生效后任务才进入终态。
|
||||
- 记录审批摘要、响应人、时间和关联任务;不要默认把原始敏感输入发到通知正文。
|
||||
- 默认保留最小化运行日志;敏感字段可在 Adapter 侧脱敏后再上报。
|
||||
|
||||
## 12. 验证指标与首轮测试
|
||||
|
||||
### 12.1 产品假设
|
||||
|
||||
如果用户能在离开 Agent 所在机器后,仍清楚掌握任务状态并在 30 秒内完成关键介入,那么他们会愿意把更多长期任务交给已连接的 Agent。
|
||||
|
||||
### 12.2 成功指标
|
||||
|
||||
| 指标 | 首轮目标 |
|
||||
|---|---|
|
||||
| 成功绑定一个 Agent 的比例 | ≥ 80% |
|
||||
| 从收到“等待用户”通知到完成处理的中位时间 | < 60 秒 |
|
||||
| 用户判断任务当前状态的正确率 | ≥ 90% |
|
||||
| 消息/状态在在线情况下端到端可见延迟 P95 | < 1 秒 |
|
||||
| Agent 重连后未丢失的待处理事件比例 | 100% |
|
||||
| 试用用户愿意在下一周继续连接 Agent 的比例 | ≥ 60% |
|
||||
|
||||
### 12.3 5 人可用性测试
|
||||
|
||||
找 3 位已有 Agent 使用经验的用户与 2 位刚接触长任务 Agent 的用户;每人完成以下任务:
|
||||
|
||||
1. 连接一个模拟 Agent,说明它是否在线。
|
||||
2. 创建“整理项目依赖并产出计划”的任务,定位它的当前进度。
|
||||
3. 在不打开日志的前提下处理一个高风险批准请求。
|
||||
4. 让 Agent 断线并恢复,判断任务是否需要重新开始。
|
||||
|
||||
记录完成率、完成时间、误操作、口头疑问和 SUS 问卷。任何导致用户错误批准高风险行为的问题均视为 P0,必须在公开测试前解决。
|
||||
|
||||
## 13. 实施顺序与分工建议
|
||||
|
||||
### 13.1 三个迭代
|
||||
|
||||
| 迭代 | 目标 | 可演示结果 |
|
||||
|---|---|---|
|
||||
| I1:连通性 | 账户/工作区、配对、WebSocket、事件存储 | 一个模拟 Agent 在线,能发收消息 |
|
||||
| I2:协作闭环 | 会话、任务状态、Interaction 卡片、收件箱 | 用户能创建任务、远程批准、看到 Agent 继续与完成 |
|
||||
| I3:可靠性 | 重连补偿、通知、审计、示例 Adapter | Agent 断线恢复后协作连续,能供首批用户试用 |
|
||||
|
||||
### 13.2 初始分工(两人)
|
||||
|
||||
| 负责人 | 主责 | I1–I3 输出 |
|
||||
|---|---|---|
|
||||
| Stephen | 中转服务与协议 | 数据模型、事件路由、WebSocket、鉴权、Adapter SDK、模拟 Agent |
|
||||
| 爱德姆 | 产品交互与客户端 | 信息架构、Web 交互、任务/确认卡片、连接引导、可用性测试与反馈整理 |
|
||||
|
||||
共同决策:协议版本、风险确认策略、首个真实 Agent Adapter 的接入优先级。每周用一段真实的 Agent 长任务进行端到端验收,而不是只测 API。
|
||||
|
||||
## 14. 待决策清单
|
||||
|
||||
这些决策会改变实现范围,应在开始 I1 前由两位创始人确认:
|
||||
|
||||
1. **首个真实接入对象:** OpenClaw 优先,还是先做 Generic SDK + 模拟 Agent?建议先后者,避免因特定框架阻塞核心验证。
|
||||
2. **首批部署模式:** 托管中转服务,还是可自托管?建议先托管开发环境,协议与部署保持可自托管。
|
||||
3. **账号范围:** 仅个人 Workspace,还是首版就允许邀请第二位成员?建议数据模型支持成员,界面先围绕个人。
|
||||
4. **通知通道:** 浏览器推送、邮件、Telegram/飞书任选一条。建议浏览器通知优先,集成型 Channel 放在 I3 后评估。
|
||||
5. **产品命名:** “LineUp Agents”暂作工作名;上线前需检查域名、商标及中文名称可用性。
|
||||
|
||||
## 15. Definition of Done:MVP 演示
|
||||
|
||||
一次合格的演示应从零完成以下闭环:
|
||||
|
||||
1. 用户注册并创建一个 Workspace。
|
||||
2. 用户通过一次性配对码连接运行在另一台机器上的模拟或真实 Agent。
|
||||
3. 用户创建一个任务,关闭浏览器后再打开,任务与会话仍完整存在。
|
||||
4. Agent 上报至少两个进度变化,并创建一个子任务。
|
||||
5. Agent 发起一个 `approval` 请求,用户在收件箱处理后,Agent 收到响应并继续执行。
|
||||
6. 断开 Adapter,界面在合理时间内显示失联;重连后不重复执行或丢失审批响应。
|
||||
7. Agent 产出一条结果消息和一个 Artifact,任务成为 `succeeded`,全部过程在事件时间线中可追溯。
|
||||
|
||||
完成这套闭环后,再根据试用反馈决定是优先扩充移动端、增加框架 Adapter,还是深化任务协作能力。
|
||||
@@ -0,0 +1,398 @@
|
||||
# LineUp Agents v1 — 设计方案
|
||||
|
||||
**版本:** v1.1(第二版)
|
||||
**最后更新:** 2026-07-27
|
||||
|
||||
---
|
||||
|
||||
## 一、产品定位
|
||||
|
||||
LineUp Agents 是一个**远程 Agent 操作交互端**。用户通过手机或 Web App,与运行在本地或服务器上的 AI Agent 进行顺畅的交互。不是任务管理系统,Agent 的任务拆解、执行、管理属于 Agent 自己的工作范畴。
|
||||
|
||||
### MVP 范围
|
||||
|
||||
| 做 | 不做 |
|
||||
|----|------|
|
||||
| App 直连 Agent(局域网 WebSocket)| 任务生命周期管理 |
|
||||
| 通过 IM 中转连接 Agent(远程)| 自动化编排、调度 |
|
||||
| App 收发消息、查看 Agent 状态 | 通知推送 |
|
||||
| Agent 调 App 的工具(choice / 画板 / 计算器 等)| MCP 封装 |
|
||||
| 工具发现与调用协议 | 多 Agent 工作流编排 |
|
||||
| 用户 ↔ 用户基础通信(由 IM 平台提供)| 端到端加密(后续加) |
|
||||
|
||||
---
|
||||
|
||||
## 二、核心设计原则
|
||||
|
||||
**LineUp 协议只定义工具交互,不定义基础消息。**
|
||||
|
||||
这条原则是整个设计的关键。工具相关的内容(`tool.call`、`tool.result`、`tool.list`)由 LineUp 协议定义。文本、图片、语音、文件等基础消息,由下层传输平台(IM 服务或 WebSocket)原生处理,LineUp 不重复定义。
|
||||
|
||||
这让协议非常薄,换传输平台时工具协议不受影响。
|
||||
|
||||
---
|
||||
|
||||
## 三、架构总览
|
||||
|
||||
### 3.1 两种网络模式
|
||||
|
||||
LineUp 支持两种连接模式,插件根据配置自动切换。
|
||||
|
||||
**直连模式(局域网)**
|
||||
|
||||
```
|
||||
Agent 插件 ── WebSocket 端口 :9527 ──→ App 端(Web)
|
||||
```
|
||||
|
||||
Agent 插件在本地开 WebSocket Server,App 端手动输入 IP:端口连接。适合局域网内无公网 IP 的场景。
|
||||
|
||||
**中转模式(广域网)**
|
||||
|
||||
```
|
||||
Agent 适配器 ──→ WuKongIM ←── 唐僧叨叨客户端 / LineUp App
|
||||
▲
|
||||
│ Webhook / HTTP API
|
||||
唐僧叨叨业务服务
|
||||
```
|
||||
|
||||
WuKongIM 负责客户端长连接、消息投递和消息存储;唐僧叨叨业务服务负责用户、好友、群组、文件等 IM 业务能力,并通过 Webhook 与 WuKongIM 协作。客户端以 WuKongIM 官方 SDK 建立长连接,以唐僧叨叨 API 处理业务操作。LineUp 的中转适配器需使用 WuKongIM 官方 SDK 或经验证的协议实现,不能复用其他 IM 平台的网关协议。
|
||||
|
||||
### 3.2 分层架构
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ 工具协议层(LineUp 定义) │
|
||||
│ 内容:工具定义、tool.call / tool.result │
|
||||
│ App 渲染 choice / canvas / calculator 等工具 │
|
||||
├──────────────────────────────────────────────┤
|
||||
│ 消息传输层(IM 平台或直连 WebSocket) │
|
||||
│ 内容:文本 / 图片 / 文件 / 语音的收发 │
|
||||
│ 直连模式:WebSocket 原生传输 │
|
||||
│ 中转模式:WuKongIM 消息通道传输 │
|
||||
├──────────────────────────────────────────────┤
|
||||
│ 连接层(IM 平台或 WebSocket Server) │
|
||||
│ 内容:连接建立、心跳保活、断线重连 │
|
||||
│ 直连模式:Agent 插件 WS Server │
|
||||
│ 中转模式:WuKongIM 连接管理 │
|
||||
└──────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
LineUp 协议只关心最上面一层。下面两层由传输平台处理。
|
||||
|
||||
### 3.3 设计决策
|
||||
|
||||
| 决策 | 当期结论 | 后续扩展思路 |
|
||||
|------|---------|-------------|
|
||||
| 网络模式 | 局域网直连 + WuKongIM 中转并存 | — |
|
||||
| 中转服务 | 唐僧叨叨业务层 + WuKongIM 通讯层 | — |
|
||||
| Agent 接入方式 | 直连模式用 LineUp 适配器;中转模式待基于 WuKongIM SDK 实现 | — |
|
||||
| IM 平台选型 | WuKongIM;唐僧叨叨提供配套业务层和客户端 | — |
|
||||
| App 端技术方向 | Vite + React(直连模式用 WebSocket;中转模式使用 WuKongIM JS SDK / 唐僧叨叨 API) | 原生移动端 App |
|
||||
| 连接认证 | 直连:预共享 Token;中转:唐僧叨叨用户系统与 WuKongIM Token | 端到端加密 |
|
||||
| 工具路由方式 | 字典路由 | 可引入框架级路由 |
|
||||
|
||||
---
|
||||
|
||||
## 四、工具协议定义(LineUp 协议的全部内容)
|
||||
|
||||
LineUp 协议只定义工具相关的三个消息类型。
|
||||
|
||||
### 4.1 工具定义结构
|
||||
|
||||
所有工具使用统一的结构模板,通过 `type` 区分行为差异。
|
||||
|
||||
| type | 含义 | 定义结构 |
|
||||
|------|------|---------|
|
||||
| `app` | 有状态应用,有生命周期 | `{ name, type, description, actions[] }` |
|
||||
| `toolset` | 无状态工具集,多个动作 | `{ name, type, description, actions[] }` |
|
||||
| `action` | 单指令工具,一个动作 | `{ name, type, description, inputSchema }` |
|
||||
|
||||
### 4.2 三类工具的完整定义
|
||||
|
||||
#### type: app — 有状态应用
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "canvas",
|
||||
"type": "app",
|
||||
"description": "画板应用,支持绘制、撤销、清空等操作",
|
||||
"actions": [
|
||||
{
|
||||
"name": "open",
|
||||
"description": "创建画板实例,返回 instance_id",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"width": { "type": "integer", "description": "画板宽度" },
|
||||
"height": { "type": "integer", "description": "画板高度" }
|
||||
},
|
||||
"required": ["width", "height"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "draw",
|
||||
"description": "在画板上绘制路径",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"instance_id": { "type": "string", "description": "画板实例 ID" },
|
||||
"path": { "type": "string", "description": "SVG 路径数据" },
|
||||
"color": { "type": "string", "description": "线条颜色" },
|
||||
"width": { "type": "integer", "description": "线条粗细" }
|
||||
},
|
||||
"required": ["instance_id", "path"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "undo",
|
||||
"description": "撤销上一步操作",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"instance_id": { "type": "string", "description": "画板实例 ID" }
|
||||
},
|
||||
"required": ["instance_id"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clear",
|
||||
"description": "清空画板内容",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"instance_id": { "type": "string", "description": "画板实例 ID" }
|
||||
},
|
||||
"required": ["instance_id"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "close",
|
||||
"description": "关闭画板释放资源",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"instance_id": { "type": "string", "description": "画板实例 ID" }
|
||||
},
|
||||
"required": ["instance_id"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### type: toolset — 无状态工具集
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "calculator",
|
||||
"type": "toolset",
|
||||
"description": "基础计算器,无状态,每次调用独立",
|
||||
"actions": [
|
||||
{
|
||||
"name": "add",
|
||||
"description": "两个数相加",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": { "type": "number" },
|
||||
"b": { "type": "number" }
|
||||
},
|
||||
"required": ["a", "b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "subtract",
|
||||
"description": "两个数相减",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": { "type": "number" },
|
||||
"b": { "type": "number" }
|
||||
},
|
||||
"required": ["a", "b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "multiply",
|
||||
"description": "两个数相乘",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": { "type": "number" },
|
||||
"b": { "type": "number" }
|
||||
},
|
||||
"required": ["a", "b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "divide",
|
||||
"description": "两个数相除",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": { "type": "number" },
|
||||
"b": { "type": "number" }
|
||||
},
|
||||
"required": ["a", "b"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### type: action — 单指令工具
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "choice",
|
||||
"type": "action",
|
||||
"description": "向用户提供一组选项让其选择",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"question": { "type": "string", "description": "问题描述" },
|
||||
"options": { "type": "array", "items": { "type": "string" }, "description": "可选列表" }
|
||||
},
|
||||
"required": ["question", "options"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 三类工具对比
|
||||
|
||||
| | app | toolset | action |
|
||||
|--|-----|---------|--------|
|
||||
| 定义结构 | name + type + description + actions[] | name + type + description + actions[] | name + type + description + inputSchema |
|
||||
| 适用场景 | 需要维护内部状态的应用 | 多个独立功能可归类 | 单个独立功能 |
|
||||
| 生命周期 | open → use → close | 无 | 无 |
|
||||
| instance_id | 需要 | 不需要 | 不需要 |
|
||||
| 每次调用需 action | 是 | 是 | 仍需 action 字段 |
|
||||
| 示例 | canvas(画板) | calculator(计算器) | choice / confirm / input |
|
||||
|
||||
### 4.4 tool.call 消息格式
|
||||
|
||||
```json
|
||||
{
|
||||
"call_id": "c1",
|
||||
"name": "canvas",
|
||||
"action": "open",
|
||||
"arguments": { "width": 800, "height": 600 }
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
|
||||
- `call_id` — 调用方生成,唯一标识一次调用,用于 tool.result 配对
|
||||
- `name` — 工具名
|
||||
- `action` — 动作名(app 和 toolset 类型必填,action 类型可省略)
|
||||
- `arguments` — 动作的参数,按 inputSchema 结构传入
|
||||
|
||||
### 4.5 tool.result 消息格式
|
||||
|
||||
```json
|
||||
{
|
||||
"call_id": "c1",
|
||||
"name": "canvas",
|
||||
"action": "open",
|
||||
"result": {
|
||||
"message": "instance_id: inst_001"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
|
||||
- `call_id` — 与 tool.call 中的 call_id 一致,用于配对
|
||||
- `name` — 工具名
|
||||
- `action` — 动作名
|
||||
- `result` — 返回数据,约定统一放在 result.message,后续按工具定义扩展
|
||||
|
||||
### 4.6 tool.list 消息
|
||||
|
||||
```json
|
||||
{
|
||||
"tools": [
|
||||
{ "name": "choice", "type": "action", "description": "...", "inputSchema": {} },
|
||||
{ "name": "calculator", "type": "toolset", "description": "...", "actions": [] },
|
||||
{ "name": "canvas", "type": "app", "description": "...", "actions": [] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
设备注册和查询工具列表用统一的数据结构。直连模式下双向可查,中转模式下 App 端通过自定义消息类型注册工具。
|
||||
|
||||
---
|
||||
|
||||
## 五、完整交互流程
|
||||
|
||||
### 5.1 choice 工具调用
|
||||
|
||||
```
|
||||
Agent 需要用户做选择
|
||||
│
|
||||
├── Agent 发 tool.call
|
||||
│ { call_id: "c1", name: "choice", action: "select",
|
||||
│ arguments: { question: "部署到哪个环境?", options: ["测试", "预发布", "生产"] } }
|
||||
│
|
||||
├── App 渲染选择界面,用户点击"预发布"
|
||||
│
|
||||
├── App 返回 tool.result
|
||||
│ { call_id: "c1", name: "choice", action: "select",
|
||||
│ result: { message: "预发布" } }
|
||||
│
|
||||
└── Agent 拿到结果,继续执行
|
||||
```
|
||||
|
||||
### 5.2 canvas 完整生命周期
|
||||
|
||||
Agent 调 `canvas` 的 `open` 动作:
|
||||
|
||||
```json
|
||||
{ "call_id": "c1", "name": "canvas", "action": "open", "arguments": { "width": 800, "height": 600 } }
|
||||
```
|
||||
|
||||
App 返回 instance_id:
|
||||
|
||||
```json
|
||||
{ "call_id": "c1", "name": "canvas", "action": "open", "result": { "message": "instance_id: inst_001" } }
|
||||
```
|
||||
|
||||
Agent 绘制路径:
|
||||
|
||||
```json
|
||||
{ "call_id": "c2", "name": "canvas", "action": "draw", "arguments": { "instance_id": "inst_001", "path": "M10 10 L50 50", "color": "red" } }
|
||||
```
|
||||
|
||||
Agent 关闭画板:
|
||||
|
||||
```json
|
||||
{ "call_id": "c3", "name": "canvas", "action": "close", "arguments": { "instance_id": "inst_001" } }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、与 MCP 的关系
|
||||
|
||||
```
|
||||
MCP LineUp
|
||||
───────────────────────────────────────
|
||||
MCP Server ──────────────── Tool(画板 / 计算器 / 选择框)
|
||||
│ │
|
||||
├─ MCP Tool A ├─ Action(open / draw / add / select)
|
||||
├─ MCP Tool B ├─ Action
|
||||
└─ MCP Tool C └─ Action
|
||||
```
|
||||
|
||||
Tool = MCP Server 层,Action = MCP Tool 层。Action 才是实际可调用的最小能力单元。
|
||||
|
||||
工具定义格式复用 MCP 的 JSON Schema 标准。
|
||||
|
||||
---
|
||||
|
||||
## 七、后续可能的扩展
|
||||
|
||||
- 端到端加密,中转不可读消息内容
|
||||
- 移动端原生 App(目前用响应式 Web)
|
||||
- 直连模式切换到中转模式时的无缝过渡
|
||||
- 更多 Agent 框架的插件支持(目前以 Hermes 为第一期)
|
||||
- 框架级工具路由替代字典路由
|
||||
@@ -0,0 +1,388 @@
|
||||
# LineUp — 协议定义
|
||||
|
||||
## 一、工具协议定义
|
||||
|
||||
工具协议定义 App 端可以向 Agent 暴露哪些能力,以及每个能力长什么样。
|
||||
|
||||
### 1.1 工具定义结构
|
||||
|
||||
所有工具使用统一的结构模板,通过 `type` 区分结构差异。
|
||||
|
||||
| type | 含义 | 定义结构 |
|
||||
|------|------|---------|
|
||||
| `app` | 有状态应用,有生命周期 | `{ name, type, description, actions[] }` |
|
||||
| `toolset` | 无状态工具集,多个动作 | `{ name, type, description, actions[] }` |
|
||||
| `action` | 单指令工具,一个动作 | `{ name, type, description, inputSchema }` |
|
||||
|
||||
app 和 toolset 包含多个动作,用 `actions` 数组描述。action 只有一个操作,直接用 `inputSchema`,不需要数组包一层。
|
||||
|
||||
### 1.2 三类工具的完整定义
|
||||
|
||||
#### type: app — 有状态应用
|
||||
|
||||
需要生命周期管理,先 open 创建实例,执行动作,最后 close 释放。
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "canvas",
|
||||
"type": "app",
|
||||
"description": "画板应用,支持绘制、撤销、清空等操作",
|
||||
"actions": [
|
||||
{
|
||||
"name": "open",
|
||||
"description": "创建画板实例,返回 instance_id",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"width": { "type": "integer", "description": "画板宽度" },
|
||||
"height": { "type": "integer", "description": "画板高度" }
|
||||
},
|
||||
"required": ["width", "height"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "draw",
|
||||
"description": "在画板上绘制路径",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"instance_id": { "type": "string", "description": "画板实例 ID" },
|
||||
"path": { "type": "string", "description": "SVG 路径数据" },
|
||||
"color": { "type": "string", "description": "线条颜色" },
|
||||
"width": { "type": "integer", "description": "线条粗细" }
|
||||
},
|
||||
"required": ["instance_id", "path"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "undo",
|
||||
"description": "撤销上一步操作",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"instance_id": { "type": "string", "description": "画板实例 ID" }
|
||||
},
|
||||
"required": ["instance_id"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clear",
|
||||
"description": "清空画板内容",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"instance_id": { "type": "string", "description": "画板实例 ID" }
|
||||
},
|
||||
"required": ["instance_id"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "close",
|
||||
"description": "关闭画板释放资源",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"instance_id": { "type": "string", "description": "画板实例 ID" }
|
||||
},
|
||||
"required": ["instance_id"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### type: toolset — 无状态工具集
|
||||
|
||||
多个动作,无生命周期。每次调用独立,动作间不共享状态。
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "calculator",
|
||||
"type": "toolset",
|
||||
"description": "基础计算器,无状态,每次调用独立",
|
||||
"actions": [
|
||||
{
|
||||
"name": "add",
|
||||
"description": "两个数相加",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": { "type": "number" },
|
||||
"b": { "type": "number" }
|
||||
},
|
||||
"required": ["a", "b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "subtract",
|
||||
"description": "两个数相减",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": { "type": "number" },
|
||||
"b": { "type": "number" }
|
||||
},
|
||||
"required": ["a", "b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "multiply",
|
||||
"description": "两个数相乘",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": { "type": "number" },
|
||||
"b": { "type": "number" }
|
||||
},
|
||||
"required": ["a", "b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "divide",
|
||||
"description": "两个数相除",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": { "type": "number" },
|
||||
"b": { "type": "number" }
|
||||
},
|
||||
"required": ["a", "b"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### type: action — 单指令工具
|
||||
|
||||
只有一个动作,最简结构。
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "choice",
|
||||
"type": "action",
|
||||
"description": "向用户提供一组选项让其选择",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"question": { "type": "string", "description": "问题描述" },
|
||||
"options": { "type": "array", "items": { "type": "string" }, "description": "可选列表" }
|
||||
},
|
||||
"required": ["question", "options"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 三类工具对比
|
||||
|
||||
| | app | toolset | action |
|
||||
|--|-----|---------|--------|
|
||||
| 定义字段 | name + type + description + actions[] | name + type + description + actions[] | name + type + description + inputSchema |
|
||||
| 适用场景 | 需要维护内部状态的应用 | 多个独立功能可归类 | 单个独立功能 |
|
||||
| 生命周期 | open → use → close | 无 | 无 |
|
||||
| instance_id | 需要 | 不需要 | 不需要 |
|
||||
| 调用指定动作 | 每次调用需 action | 每次调用需 action | action 字段仍需传 |
|
||||
| 示例 | canvas | calculator | choice / confirm / input |
|
||||
|
||||
---
|
||||
|
||||
## 二、消息协议定义
|
||||
|
||||
消息协议定义 Agent 和 App 之间交换的所有消息格式。
|
||||
|
||||
### 2.1 统一信封
|
||||
|
||||
所有消息使用同一个信封格式。
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"id": "消息唯一 ID,用于去重和配对",
|
||||
"type": "消息类型",
|
||||
"payload": { }
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| v | integer | 协议版本号 |
|
||||
| id | string | 消息唯一 ID,全局唯一 |
|
||||
| type | string | 消息类型 |
|
||||
| payload | object | 消息内容,按 type 决定结构 |
|
||||
|
||||
### 2.2 消息类型
|
||||
|
||||
| type | 方向 | 说明 |
|
||||
|------|------|------|
|
||||
| `system.hello` | 双向 | 连接握手,交换身份 |
|
||||
| `system.ping` / `system.pong` | 双向 | 心跳保活 |
|
||||
| `text` | 双向 | 普通文本消息 |
|
||||
| `image` | Agent → App | 展示图片 |
|
||||
| `tool.list` | 双向 | 查询或返回工具列表 |
|
||||
| `tool.call` | Agent → App | 调用 App 端工具 / 动作 |
|
||||
| `tool.result` | App → Agent | 返回工具执行结果 |
|
||||
|
||||
### 2.3 各类型消息格式
|
||||
|
||||
#### system.hello
|
||||
|
||||
连接建立后,双方通过 hello 交换身份。
|
||||
|
||||
Agent → App:
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"id": "msg_hello_agent",
|
||||
"type": "system.hello",
|
||||
"payload": {
|
||||
"name": "我的 AI 助手",
|
||||
"token": "预设的配对 Token",
|
||||
"agent": "Hermes",
|
||||
"version": "0.19.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
App → Agent:
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"id": "msg_hello_app",
|
||||
"type": "system.hello",
|
||||
"payload": {
|
||||
"name": "我的手机",
|
||||
"platform": "web",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### text
|
||||
|
||||
两边都可以发送。
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"id": "msg_001",
|
||||
"type": "text",
|
||||
"payload": {
|
||||
"content": "帮我查一下下周的天气"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### image
|
||||
|
||||
Agent 展示图片给用户。
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"id": "msg_002",
|
||||
"type": "image",
|
||||
"payload": {
|
||||
"url": "http://192.168.1.100:9527/files/screenshot.png",
|
||||
"alt": "系统架构图",
|
||||
"caption": "这是当前项目的架构图"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### tool.list
|
||||
|
||||
查询 App 端支持哪些工具。
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"id": "msg_003",
|
||||
"type": "tool.list",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
|
||||
App 返回含工具列表。
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"id": "msg_004",
|
||||
"type": "tool.list",
|
||||
"payload": {
|
||||
"tools": [ ]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### tool.call
|
||||
|
||||
Agent 触发 App 端的一个工具动作。
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"id": "msg_005",
|
||||
"type": "tool.call",
|
||||
"payload": {
|
||||
"name": "工具名",
|
||||
"action": "动作名",
|
||||
"call_id": "本次调用的唯一 ID",
|
||||
"arguments": { }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### tool.result
|
||||
|
||||
App 返回工具执行结果给 Agent。
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"id": "msg_006",
|
||||
"type": "tool.result",
|
||||
"payload": {
|
||||
"name": "工具名",
|
||||
"action": "动作名",
|
||||
"call_id": "对应的调用 ID",
|
||||
"result": {
|
||||
"message": "用户操作结果"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
V1 简化约定:App 返回的用户操作结果统一放在 `result.message` 字段。后续工具需要更丰富的返回格式时(如文件路径、选择详情),再按工具定义扩展 `result` 结构。
|
||||
|
||||
---
|
||||
|
||||
## 三、协议分层关系
|
||||
|
||||
```
|
||||
通讯协议层(WebSocket 连接、心跳、重连、鉴权)
|
||||
↓
|
||||
消息协议层(信封 + 消息类型)
|
||||
↓
|
||||
工具协议层(工具定义 + 工具调用流程)
|
||||
```
|
||||
|
||||
三层独立不耦合。通讯层换了,上面两层不用改。消息层升版本号,工具层不受影响。工具层加新工具,上面两层不需要动。
|
||||
|
||||
---
|
||||
|
||||
## 四、与 MCP 的对应关系
|
||||
|
||||
```
|
||||
MCP LineUp
|
||||
──────────────────────────────────────
|
||||
MCP Server ────────────── Tool(画板 / 计算器 / 选择框)
|
||||
│ │
|
||||
├─ MCP Tool A ├─ Action(open / draw / add / select)
|
||||
├─ MCP Tool B ├─ Action
|
||||
└─ MCP Tool C └─ Action
|
||||
```
|
||||
|
||||
MCP 里每一个 Tool 是无状态的一次性调用单元。LineUp 里 Tool 多了一层容器层。app 类型让有状态工具在逻辑上是一个整体,Agent 看到 canvas 就知道这是一个画板应用,再看 actions 就知道具体可以做什么。
|
||||
@@ -0,0 +1,813 @@
|
||||
# LineUp App 最终设计方案
|
||||
|
||||
**版本:** 1.0(当前开发基线)
|
||||
**状态:** 当前 App 设计的唯一汇总入口
|
||||
**日期:** 2026-08-04
|
||||
**来源:** [App 层架构方案](lineup-app-layer-architecture.md)、[Runtime 与 App SDK 架构方案](lineup-runtime-sdk-architecture.md)、[UI Surface 与 App Capability 协议](lineup-ui-surface-protocol.md)
|
||||
|
||||
---
|
||||
|
||||
## 0. 一句话定义
|
||||
|
||||
**LineUp 是运行在用户设备上的协作 Runtime,也就是整个产品的本地协调中心。**
|
||||
|
||||
Runtime 负责连接 AppServer 和 Remote Agent,保存会话与消息,管理应用、工具、权限和
|
||||
本地恢复。图文聊天、应用注册表、语音对话、画板和游戏则是在这个底座上运行的 App。
|
||||
用户使用 App 完成操作,Agent 发来消息或请求;两边都先交给 Runtime 处理,因此任何 App
|
||||
都不需要、也不能自己处理连接、登录态或系统权限。
|
||||
|
||||
```text
|
||||
Remote Agent
|
||||
│ 标准 LineUp 消息
|
||||
▼
|
||||
AppServer / IM
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ LineUp Runtime │
|
||||
│ │
|
||||
│ 连接 · 会话 · 消息筛选 · App 管理 · Tool 管理 │
|
||||
│ 权限 · Artifact · 存储 · Outbox · 恢复 · 审计 │
|
||||
└─────────────┬───────────────────────────┬───────────────┘
|
||||
│ Runtime SDK │ Host Provider
|
||||
▼ ▼
|
||||
Core / Installed App Tauri / OS
|
||||
chat · app-registry 文件 · 麦克风
|
||||
voice · whiteboard 通知 · 窗口
|
||||
game · … 安全存储
|
||||
```
|
||||
|
||||
## 1. 本方案的结论与优先级
|
||||
|
||||
本文件收敛正式方案目录下的三份已有文档,并在冲突处做出当前开发阶段的明确选择。
|
||||
|
||||
| 主题 | 最终决定 |
|
||||
|---|---|
|
||||
| App 的本体 | 整个产品是 `LineUpRuntime`;聊天不是 Runtime 本身。默认的 `Interaction App` 负责组织人与 Agent 的主交互,当前实现形态是 `chat`/图文 IM。 |
|
||||
| 默认入口 | `Interaction App` 是当前默认和 Recovery App;当前默认模式是 `im`(代码兼容作用域仍为 `chat`),以后可切换到实时音频或视频模式。 |
|
||||
| App 之间的关系 | App 只调用 Runtime SDK,不直接访问 AppServer、Agent、另一个 App 或 Tauri 特权 API。 |
|
||||
| Agent 交互 | Agent 只与 Runtime 通信;Runtime 按会话和应用作用域筛选并投递给 App。 |
|
||||
| 路由最小键 | 所有可路由消息必须有 `app_scope` 与 `conversation_id`;可选 `instance_id`、`operation_id`、`call_id`。 |
|
||||
| App 生态阶段 | 当前只使用本地短作用域,如 `chat`、`whiteboard`、`draw-and-guess`;暂不引入供应商身份、反向域名 App ID 或全局命名空间。 |
|
||||
| 丰富交互 | 标准内容先用可信内建组件;复杂互动用受限 Surface;设备/系统动作只能经 Capability Gateway。 |
|
||||
| Agent 工具 | App 在 Manifest 声明方法,Runtime 汇总为动态 Inventory;Agent 只能调用当前 Inventory 中的方法。 |
|
||||
| Runtime SDK | SDK 提供状态订阅、Agent 消息订阅、Action、Tool、应用导航、Artifact、存储、Capability 和生命周期接口。 |
|
||||
| Host | Tauri Desktop Host 与同代码的 Web Reference Host 是当前唯一实现/验收基线:前者是正式桌面外壳,后者是浏览器开发和验证入口;它们不是两个客户端。不规划独立 Android/Kotlin 客户端或 Wails Host。 |
|
||||
|
||||
本文件优先于来源文档中关于**后续 Runtime 重构**的结构描述。来源文档中的 M0~M4 完成记录、当前已实现字段和历史验收事实仍然有效;它们不自动成为新 Runtime 的长期模块边界。
|
||||
|
||||
## 2. 系统边界
|
||||
|
||||
### 2.1 Runtime、App 与 Host:先分清三者
|
||||
|
||||
这三个词经常同时出现,但职责不同:Runtime 是后台协调者,App 是用户看到的功能,Host
|
||||
是让 Runtime 运行在桌面窗口或浏览器中的外壳。Host 不能绕过 Runtime 直接把系统能力
|
||||
交给 App 或 Agent。
|
||||
|
||||
```text
|
||||
Runtime
|
||||
管理连接、状态、应用、工具、权限和副作用。
|
||||
|
||||
App
|
||||
面向用户或 Agent 的功能单元;只能通过 SDK 与 Runtime 协作。
|
||||
|
||||
Host
|
||||
提供操作系统或 WebView 能力;由 Runtime 使用,不直接暴露给 App/Agent。
|
||||
```
|
||||
|
||||
| 主体 | 必须负责 | 明确不负责 |
|
||||
|---|---|---|
|
||||
| Runtime | 连接 AppServer/Agent、管理会话与路由、安排 App 生命周期、Tool、Capability、持久化和审计 | 具体页面 DOM、任意 App 的业务 UI。 |
|
||||
| Chat App | 图文会话、时间线、输入、任务/交互卡片和 Artifact 基础展示 | `fetch` AppServer、同步 cursor、Agent 原始包解析、文件系统。 |
|
||||
| App Registry App | 让用户查看已安装 App,并请求安装/启用/禁用/移除或选择默认入口 | 直接写 Registry、删除 Bundle、绕过验签。 |
|
||||
| Installed App | 自己的交互体验、已声明 Tool、已声明事件和私有状态 | Tauri `invoke`、Host DOM、token、任意网络和其他 App 数据。 |
|
||||
| Tauri Host Provider | 在 Runtime 授权后实现文件、音频、通知、窗口和安全存储等系统操作 | 协议解释、应用策略和 Agent 路由。 |
|
||||
|
||||
### 2.2 关键禁止项
|
||||
|
||||
```text
|
||||
App → AppServer / Agent 直接网络连接 禁止
|
||||
App → 直接构造并发送未校验 Agent Envelope 禁止
|
||||
Surface → Tauri invoke / 父页面 DOM / 登录态 禁止
|
||||
Agent → 任意 App JavaScript 函数或 Host 特权 API 禁止
|
||||
Agent → 静默安装、启用、禁用或移除 App 禁止
|
||||
Surface Event → 直接执行系统能力 禁止
|
||||
```
|
||||
|
||||
## 3. Runtime 的内部模型
|
||||
|
||||
### 3.1 三类输入输出
|
||||
|
||||
Runtime 采用 Event / Command / Effect 分层。
|
||||
|
||||
```text
|
||||
Event:已经发生的事实
|
||||
Agent 文本、任务进度、用户点击、画板变更、录音完成、连接断开。
|
||||
|
||||
Command:希望 Runtime 处理的动作
|
||||
发送消息、调用 Tool、启动 App、启用 App、请求保存文件。
|
||||
|
||||
Effect:Runtime 决定执行的副作用
|
||||
网络发送、文件选择、录音、通知、创建 Surface、写存储。
|
||||
```
|
||||
|
||||
处理顺序固定为:
|
||||
|
||||
```text
|
||||
接收 Event / Command
|
||||
→ 验证与授权
|
||||
→ 更新 Runtime State
|
||||
→ 持久化事实 / Outbox
|
||||
→ 产生 Effect
|
||||
→ Effect 结果重新进入 Runtime,成为 Event
|
||||
→ 生成面向 App 的状态投影或消息投递
|
||||
```
|
||||
|
||||
第一版采用“事件驱动状态机 + 必要快照”,不实施完整 Event Sourcing。关键入站事件、用户决定、Tool 终态、outbox 和审计需持久化;纯渲染细节、焦点和滚动位置不必写入 Runtime 事实日志。
|
||||
|
||||
### 3.2 顶层状态
|
||||
|
||||
```text
|
||||
identity
|
||||
用户、设备、认证会话。
|
||||
|
||||
connection
|
||||
AppServer 连接、sync cursor、重试、实时状态、outbox。
|
||||
|
||||
conversations
|
||||
conversation、关联 Agent、消息引用、任务、交互、Artifact 元数据。
|
||||
|
||||
apps
|
||||
App Registry、默认 App、运行实例、每个 App 的可靠 Inbox。
|
||||
|
||||
tools
|
||||
Tool 声明、Inventory revision、调用记录、operation 与进度。
|
||||
|
||||
capabilities
|
||||
权限请求、用户决定、系统权限、执行状态与最小审计。
|
||||
```
|
||||
|
||||
### 3.3 Runtime 生命周期
|
||||
|
||||
```text
|
||||
created
|
||||
→ restoring
|
||||
→ authenticating
|
||||
→ synchronizing
|
||||
→ online
|
||||
↘ reconnecting / offline_degraded
|
||||
→ stopping
|
||||
→ stopped
|
||||
```
|
||||
|
||||
启动时 Runtime 必须恢复 App Registry、默认入口、cursor、outbox、App Inbox、未完成 Tool 和可恢复实例。追平服务器历史后才开始正常实时投递;离线时可接受的用户/App Action 进入 outbox,重连后以幂等键和顺序发送。
|
||||
|
||||
## 4. 应用模型
|
||||
|
||||
### 4.1 本地应用作用域
|
||||
|
||||
当前阶段使用 Runtime 本地注册的 `app_scope`,而不是全球 App 身份:
|
||||
|
||||
```text
|
||||
chat
|
||||
app-registry
|
||||
settings
|
||||
voice
|
||||
whiteboard
|
||||
draw-and-guess
|
||||
runtime
|
||||
```
|
||||
|
||||
`app_scope` 的职责仅是本机启动、消息路由、Tool 路由、存储隔离和实例归属。它不是供应商身份,不要求跨市场唯一,也不承担开放生态的所有权模型。
|
||||
|
||||
未来如需市场、多供应商和跨设备分发,可以在 Manifest 中增加稳定身份映射;但不能改变本方案中的 `app_scope + conversation_id` 路由与隔离语义。
|
||||
|
||||
### 4.2 应用类型
|
||||
|
||||
| 类型 | 典型项 | 来源与执行方式 | 管理规则 |
|
||||
|---|---|---|---|
|
||||
| Core App | `chat`、`app-registry`、`settings` | 跟随 Runtime 发行的可信代码 | 不可由市场 Bundle 覆盖;`chat` 不可移除。 |
|
||||
| Installed App | `voice`、`whiteboard`、`draw-and-guess` | 经 Runtime 验证的 Bundle,在受限 Surface 中运行 | 可安装、启用、禁用、更新、回滚、移除。 |
|
||||
| Capability | 录音、选文件、保存、通知 | Tauri/OS 经 Runtime Host Provider 提供 | 不是 App,也不是自动授予的权限。 |
|
||||
|
||||
### 4.3 App Registry 与默认入口
|
||||
|
||||
Runtime App Manager 是安装和状态的唯一事实源:
|
||||
|
||||
```ts
|
||||
type AppRecord = {
|
||||
app_scope: AppScope;
|
||||
kind: "core" | "installed";
|
||||
installed_version: string;
|
||||
previous_version?: string;
|
||||
enabled: boolean;
|
||||
install_state: "installed" | "updating" | "failed";
|
||||
active_instance_count: number;
|
||||
installed_at: string;
|
||||
enabled_at?: string;
|
||||
};
|
||||
|
||||
interface RuntimeAppManager {
|
||||
listApps(filter?: AppListFilter): readonly AppRecord[];
|
||||
getApp(appScope: AppScope): AppRecord | undefined;
|
||||
install(request: InstallAppRequest): Promise<AppOperation>;
|
||||
enable(appScope: AppScope): Promise<AppOperation>;
|
||||
disable(appScope: AppScope, options?: DisableAppOptions): Promise<AppOperation>;
|
||||
update(appScope: AppScope, targetVersion?: string): Promise<AppOperation>;
|
||||
rollback(appScope: AppScope): Promise<AppOperation>;
|
||||
remove(appScope: AppScope, options?: RemoveAppOptions): Promise<AppOperation>;
|
||||
launch(request: LaunchAppRequest): Promise<AppInstanceHandle>;
|
||||
closeInstance(instanceID: AppInstanceID): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
App Registry App 只调用上述接口,不直接管理 Bundle 文件。安装、验证、启用、升级和移除是异步操作,必须有可观察的 `AppOperation`。
|
||||
|
||||
Runtime Shell 始终独立于上层 App,负责应用切换、全局连接状态、通知和安全恢复。其启动目标优先级:
|
||||
|
||||
```text
|
||||
显式目标(深链接 / 通知)
|
||||
→ 可安全恢复的前台实例
|
||||
→ 用户设定的 Default App
|
||||
→ chat Recovery App
|
||||
```
|
||||
|
||||
`chat` 是当前图文 IM 主入口,也是不可移除的 Recovery App。未来用户可将 `voice` 设为默认入口;这只改变主要交互方式,不改变会话、Agent、outbox 或 Artifact 的归属。
|
||||
|
||||
### 4.4 App 实例生命周期
|
||||
|
||||
```text
|
||||
not_running → launching → active → backgrounded → suspended
|
||||
↘ restoring → active
|
||||
active / suspended → closing → closed
|
||||
↘ failed
|
||||
```
|
||||
|
||||
- Chat 与 App Registry 默认是单一全局实例;
|
||||
- 工具型 App 默认按 `conversation_id` 创建实例,可用 `instance_id` 区分同会话的多个实例;
|
||||
- 禁用阻止新调用并收口现有实例;移除在实例关闭且用户确认后清理 Bundle/私有数据;
|
||||
- Surface 被回收时 App 通过 Runtime snapshot 恢复,不能重复发送先前的用户事件。
|
||||
|
||||
### 4.5 Runtime 编排、Interact 与扩展 App
|
||||
|
||||
`Interaction App` 是默认的人与 Agent 交互应用,但它不是整个 App 生态的调度器。应用
|
||||
启动、Tool 路由、前后台焦点、实例生命周期和恢复均由 Runtime 管理;Interact 只负责
|
||||
当前主会话的交互体验,以及把扩展 App 的结果投影回会话。
|
||||
|
||||
```text
|
||||
LineUp Runtime
|
||||
├── App Registry / App Instance Manager
|
||||
├── Tool Router / App Router
|
||||
├── Focus Manager / Lifecycle Manager
|
||||
└── Conversation / Store / Outbox
|
||||
│
|
||||
├── Interaction App(Core App)
|
||||
│ ├── IM mode:文字、图片、短消息
|
||||
│ ├── Audio mode:实时语音
|
||||
│ ├── Video mode:实时视频
|
||||
│ └── 标准交互原语:choice / confirm / input
|
||||
│
|
||||
└── Installed App
|
||||
├── whiteboard
|
||||
├── draw-and-guess
|
||||
└── task-dashboard
|
||||
```
|
||||
|
||||
Runtime 的职责是判断一个 Agent 请求应由哪个应用实例、交互模式或标准组件承接;Interact
|
||||
不直接执行任意 Tool,也不负责切换其他 App 的前台状态。它通过 SDK 接收 Runtime 已经
|
||||
校验过的交互事件,并提交声明性用户 Action。
|
||||
|
||||
应用焦点是 Runtime 状态的一部分:
|
||||
|
||||
```text
|
||||
interaction:audio-001 running / foreground
|
||||
↓ Agent 请求启动 draw-and-guess
|
||||
interaction:audio-001 background 或 suspended
|
||||
draw-and-guess:game-001 starting → running / foreground
|
||||
```
|
||||
|
||||
原来的实例不会因为切换前台就被删除。Runtime 保存焦点栈和实例状态,以便游戏结束、用户
|
||||
退出或新应用失败时恢复原来的 Interaction App 和会话模式。
|
||||
|
||||
一次“启动你画我猜”的完整流程是:
|
||||
|
||||
```text
|
||||
Agent launch(draw-and-guess)
|
||||
→ Runtime 校验 Envelope、Inventory、Manifest、参数和 App 状态
|
||||
→ 创建 draw-and-guess App Instance
|
||||
→ 保存当前 Interaction App/Audio Instance 的焦点位置
|
||||
→ 将旧实例切换为 background / suspended
|
||||
→ 将新实例切换为 foreground
|
||||
→ App 通过 SDK 创建自己的 Surface 并接收用户操作
|
||||
→ 结果经 Runtime 持久化、审计并回传 Agent
|
||||
→ 游戏结束或关闭后恢复焦点栈中的 Interaction App
|
||||
```
|
||||
|
||||
标准选择框、确认框和输入框属于 Interaction App 的内建交互原语,不需要作为独立安装包;
|
||||
画板、游戏和复杂任务面板属于可安装扩展 App,与 Interaction App 是 Runtime 上的平级应用。
|
||||
|
||||
因此,Runtime 负责“能否调用、调用到哪里、哪个实例在前台以及如何恢复”;Interact 负责
|
||||
“如何在主交互体验中呈现和协调结果”;扩展 App 负责“具体功能和自己的交互界面”。
|
||||
|
||||
## 5. 消息、筛选与实时订阅
|
||||
|
||||
### 5.1 统一路由 Envelope
|
||||
|
||||
所有 Agent、Runtime、App、User 与 Host 之间的可路由消息都必须带作用域:
|
||||
|
||||
```ts
|
||||
type RuntimeEnvelope = {
|
||||
v: 1;
|
||||
id: string;
|
||||
type: string;
|
||||
timestamp: string;
|
||||
|
||||
sender: {
|
||||
kind: "agent" | "runtime" | "app" | "user" | "host";
|
||||
id: string;
|
||||
};
|
||||
|
||||
target: {
|
||||
kind: "runtime" | "app";
|
||||
app_scope: AppScope;
|
||||
instance_id?: AppInstanceID;
|
||||
};
|
||||
|
||||
scope: {
|
||||
app_scope: AppScope;
|
||||
conversation_id: ConversationID;
|
||||
instance_id?: AppInstanceID;
|
||||
operation_id?: OperationID;
|
||||
};
|
||||
|
||||
correlation?: {
|
||||
reply_to?: string;
|
||||
call_id?: ToolCallID;
|
||||
inventory_revision?: string;
|
||||
sequence?: number;
|
||||
};
|
||||
|
||||
payload: JsonValue;
|
||||
};
|
||||
```
|
||||
|
||||
全局 Runtime 事件同样保留作用域:
|
||||
|
||||
```text
|
||||
target.app_scope = runtime
|
||||
scope.app_scope = runtime
|
||||
conversation_id = runtime:global
|
||||
```
|
||||
|
||||
### 5.2 现有协议兼容
|
||||
|
||||
当前 Tauri 实现与 M0~M4 golden fixture 使用 `lineup.v1.*` 类型,以及 Surface payload 内的旧 `app.id` 字段。这些是**当前实现兼容事实**,不能在没有版本迁移和 golden fixture 的情况下直接删除。
|
||||
|
||||
Runtime 重构的规则是:
|
||||
|
||||
```text
|
||||
旧 LineUp v1 Envelope
|
||||
→ Runtime Compatibility Adapter
|
||||
→ 补齐/映射 app_scope、conversation_id、instance_id
|
||||
→ RuntimeEnvelope
|
||||
→ App SDK
|
||||
```
|
||||
|
||||
新的 Runtime/App SDK 边界不得再依赖旧 `app.id` 的供应商命名语义。R0 负责冻结新旧字段的映射表、拒绝规则和双向 fixture;在映射完成前,旧协议继续只由 Runtime 适配层处理,App 永远不直接读取它。
|
||||
|
||||
### 5.3 Runtime 筛选链
|
||||
|
||||
```text
|
||||
Transport 原始输入
|
||||
→ 版本、type、app_scope/conversation_id、大小、schema 校验
|
||||
→ Agent 身份与会话关联校验
|
||||
→ id / sequence / cursor 去重与顺序处理
|
||||
→ App 安装、启用、版本、Host 兼容性校验
|
||||
→ Tool / Capability、Inventory、参数、策略、App 启动和前台条件校验
|
||||
→ conversation / instance / operation 所属关系校验
|
||||
→ Manifest 订阅声明与 SDK 订阅条件求交
|
||||
→ Runtime Event / App Inbox / cursor 持久化
|
||||
→ 向目标 App SDK 投递标准化消息
|
||||
```
|
||||
|
||||
Runtime 不广播原始 Agent 消息。相同 conversation 下,Chat 和 Voice 可以收到经 Runtime 投影的 Agent status;白板只收到本实例声明的 patch、Tool 调用和 lifecycle 事件,除非其 Manifest 明确申请并获准其他订阅。
|
||||
|
||||
### 5.4 App SDK 的实时 Agent 消息能力
|
||||
|
||||
SDK 同时提供实时订阅和可靠 Inbox 补读:
|
||||
|
||||
```ts
|
||||
interface AgentMessageAPI {
|
||||
list(request?: {
|
||||
conversation_id?: ConversationID;
|
||||
after?: AgentMessageCursor;
|
||||
limit?: number;
|
||||
}): Promise<AgentMessagePage>;
|
||||
|
||||
subscribe(
|
||||
options: {
|
||||
conversation_id?: ConversationID;
|
||||
types?: readonly AgentMessageType[];
|
||||
include_pending?: boolean;
|
||||
},
|
||||
handler: (message: AgentAppMessage) => Promise<void> | void,
|
||||
): Unsubscribe;
|
||||
|
||||
acknowledge(message_id: string): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
投递语义:
|
||||
|
||||
1. Runtime 先校验、标准化和持久化,后调用订阅者;
|
||||
2. 每条消息有唯一 `message_id`,App 必须幂等处理;
|
||||
3. 展示类消息可自动 ACK;Tool 调用、状态 patch 和需业务处理的消息要求 App 显式 ACK;
|
||||
4. App 未运行、暂停或崩溃时,消息进入该 App 的 Inbox;恢复后 `list()` 与 `subscribe()` 补齐;
|
||||
5. App 禁用、移除或不兼容时,Runtime 对关键 Agent 调用返回确定拒绝,不能静默丢弃。
|
||||
|
||||
实际投递集是:
|
||||
|
||||
```text
|
||||
Manifest agent_subscriptions
|
||||
∩ SDK subscribe filter
|
||||
∩ App 当前权限
|
||||
∩ conversation / instance scope
|
||||
∩ Agent target app_scope
|
||||
∩ Runtime Policy
|
||||
```
|
||||
|
||||
## 6. Runtime SDK v1
|
||||
|
||||
SDK 是上层 App 使用 Runtime 的唯一标准入口。Core App 拿到完整 App SDK;Installed App 只得到同一语义的受限 Surface Bridge SDK。
|
||||
|
||||
```ts
|
||||
interface LineUpAppRuntimeSDK {
|
||||
readonly app: AppContext;
|
||||
readonly lifecycle: AppLifecycleAPI;
|
||||
readonly state: AppStateAPI;
|
||||
readonly agentMessages: AgentMessageAPI;
|
||||
readonly actions: AppActionAPI;
|
||||
readonly tools: AppToolAPI;
|
||||
readonly apps: AppNavigationAPI;
|
||||
readonly artifacts: ArtifactAPI;
|
||||
readonly storage: ScopedStorageAPI;
|
||||
readonly capabilities: CapabilityRequestAPI;
|
||||
readonly diagnostics: DiagnosticsAPI;
|
||||
}
|
||||
```
|
||||
|
||||
### 6.1 Context、状态和生命周期
|
||||
|
||||
```ts
|
||||
interface AppContext {
|
||||
app_scope: AppScope;
|
||||
app_version: string;
|
||||
instance_id: AppInstanceID;
|
||||
kind: "core" | "installed";
|
||||
entrypoint: string;
|
||||
scope: {
|
||||
conversation_id?: ConversationID;
|
||||
operation_id?: OperationID;
|
||||
parent_instance_id?: AppInstanceID;
|
||||
};
|
||||
granted_permissions: readonly AppPermission[];
|
||||
}
|
||||
|
||||
interface AppStateAPI<ViewModel = unknown> {
|
||||
snapshot(): ViewModel;
|
||||
subscribe(listener: (view: ViewModel, change: AppStateChange) => void): Unsubscribe;
|
||||
}
|
||||
```
|
||||
|
||||
App 只读取按其 Scope 裁剪的 View Model,不读取全量 Runtime State。生命周期包含 `start`、`foreground`、`background`、`suspend`、`restore` 与 `closing`;Runtime 保留禁用、移除和强制收口的最终权力。
|
||||
|
||||
### 6.2 Action、Tool 与跨 App 导航
|
||||
|
||||
```ts
|
||||
interface AppActionAPI {
|
||||
dispatch(action: AppAction): Promise<CommandReceipt>;
|
||||
}
|
||||
|
||||
interface AppToolAPI {
|
||||
onInvoke(listener: (call: AppToolInvocation) => Promise<AppToolOutcome>): Unsubscribe;
|
||||
progress(request: { call_id: ToolCallID; progress: ToolProgress }): Promise<void>;
|
||||
complete(request: { call_id: ToolCallID; result: JsonValue }): Promise<void>;
|
||||
fail(request: { call_id: ToolCallID; code: AppToolErrorCode; message?: string }): Promise<void>;
|
||||
}
|
||||
|
||||
interface AppNavigationAPI {
|
||||
listAvailable(): readonly AppSummary[];
|
||||
launch(request: LaunchAppRequest): Promise<AppInstanceHandle>;
|
||||
focus(instanceID: AppInstanceID): Promise<void>;
|
||||
close(instanceID: AppInstanceID): Promise<void>;
|
||||
openDefaultApp(): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
App 只能提交声明性 Action,不能构造原始 Agent Envelope。跨 App 打开、聚焦和关闭也必须经 Runtime 校验目标 App 的启用状态、入口、展示模式和会话作用域。
|
||||
|
||||
### 6.3 Artifact、存储与 Capability
|
||||
|
||||
```text
|
||||
Artifact
|
||||
Runtime 管理元数据、受限读取、创建、会话引用、下载和用户保存。
|
||||
|
||||
Storage
|
||||
Runtime 为每个 app_scope 提供隔离 JSON 数据与 snapshot;管理配额、迁移和清理。
|
||||
|
||||
Capability
|
||||
App 以 purpose + input 请求;Runtime 决定是否确认、调用 Host、审计并返回受控结果。
|
||||
```
|
||||
|
||||
Capability SDK 形状:
|
||||
|
||||
```ts
|
||||
interface CapabilityRequestAPI {
|
||||
request<T extends JsonValue>(request: {
|
||||
capability: CapabilityName;
|
||||
purpose: string;
|
||||
input: JsonValue;
|
||||
scope?: AppScope;
|
||||
}): Promise<CapabilityResult<T>>;
|
||||
}
|
||||
```
|
||||
|
||||
Runtime 必须检查:Manifest 声明、App 启用状态、Host 支持、前台要求、用户确认、系统权限、策略、速率限制和审计。结果只能是 `completed`、`cancelled_by_user`、`permission_denied`、`unsupported_on_host`、`policy_denied`、`expired` 或受控 `failed`,不暴露路径、token 或底层原生异常。
|
||||
|
||||
## 7. Tool、Inventory 与 Agent 调用
|
||||
|
||||
### 7.1 App Tool 声明
|
||||
|
||||
每个 App 可以声明 Agent 可调用 Tool。当前的完整定位是:
|
||||
|
||||
```text
|
||||
app_scope / method / contract_version
|
||||
|
||||
whiteboard / board.create / 1
|
||||
voice / voice.request_recording / 1
|
||||
draw-and-guess / game.start_round / 1
|
||||
```
|
||||
|
||||
Tool Manifest 至少包括:标题、面向 Agent 的说明、输入/输出 JSON Schema、调用模型、超时、幂等性、前台要求、风险等级、App 启动策略和可能使用的 Capability。
|
||||
|
||||
调用模型固定为:
|
||||
|
||||
| 模型 | 用途 | 示例 |
|
||||
|---|---|---|
|
||||
| `query` | 只读、快速 | 查询画板摘要。 |
|
||||
| `command` | 确定性状态改变 | 创建白板、开始一局游戏。 |
|
||||
| `interactive` | 必须等待用户参与 | 录音、填写复杂表单。 |
|
||||
| `operation` | 长时间执行并有进度 | 导出画板、处理大文件。 |
|
||||
|
||||
### 7.2 动态 Runtime Inventory
|
||||
|
||||
Runtime 向每个活跃 Agent 会话发布 revisioned Inventory。它包含当前可用的标准组件、已启用 App、Surface、Tool 和 Capability;它不是安装命令,也不携带 Bundle 源码、token、文件路径或用户私有内容。
|
||||
|
||||
```text
|
||||
Agent 可见 Tool
|
||||
= 已验证 Manifest Tool
|
||||
∩ App enabled
|
||||
∩ Host supported
|
||||
∩ 用户 / 组织策略允许
|
||||
∩ Agent + conversation scope 允许
|
||||
∩ 所需前置条件满足
|
||||
```
|
||||
|
||||
App 启用、禁用、更新、撤销、Host 能力变化或策略收紧后,Runtime 增加 revision 并重新发布。Agent 的 Tool 调用必须携带 `inventory_revision`;旧 revision 的调用必须安全拒绝并提示 Agent 刷新清单。
|
||||
|
||||
### 7.3 Tool 调用闭环
|
||||
|
||||
```text
|
||||
Agent tool.invoke
|
||||
→ Runtime 校验 Envelope / Inventory / Tool / 参数 / Scope
|
||||
→ Tool Router 判断直接执行、启动 App、切换前台或等待用户交互
|
||||
→ App Instance / Focus Manager 创建或切换实例
|
||||
→ Interaction App、交互模式或扩展 App 承接请求
|
||||
→ App SDK 发送 progress / result / error
|
||||
→ Runtime 校验输出、持久化、更新焦点并回传 Agent
|
||||
```
|
||||
|
||||
至少支持以下确定错误码:
|
||||
|
||||
```text
|
||||
cancelled_by_user permission_denied app_disabled
|
||||
app_not_installed app_version_mismatch tool_not_visible
|
||||
invalid_arguments foreground_required operation_expired
|
||||
handler_failed
|
||||
```
|
||||
|
||||
Tool 是 App 的业务方法;Capability 是 Runtime/Host 的系统能力。Tool 可请求 Capability,但不会因声明 Tool 自动获得麦克风、文件或剪贴板权限。
|
||||
|
||||
## 8. Surface 与安全边界
|
||||
|
||||
### 8.1 标准组件优先
|
||||
|
||||
文字、Markdown、图片、链接、状态、任务进度、choice、confirm、input、Artifact 基础预览优先采用 Runtime / Core App 的可信内建渲染器。它们必须可访问、可测试、可离线恢复,并保持 Markdown sanitizer、外链保护和严格 schema。
|
||||
|
||||
Surface 仅用于画板、地图、图表、复杂表单、游戏、专业编辑器等内建组件不足以表达的复杂交互。
|
||||
|
||||
### 8.2 Installed App Surface
|
||||
|
||||
下载型 App 运行在隔离容器中:
|
||||
|
||||
```text
|
||||
iframe sandbox="allow-scripts"
|
||||
- 不带 allow-same-origin
|
||||
- 不带 allow-popups / allow-top-navigation / allow-forms
|
||||
- 默认 CSP 禁止网络和外部脚本
|
||||
- 只经严格 postMessage / Surface Bridge SDK 通信
|
||||
```
|
||||
|
||||
Runtime/Host 必须验证 `event.source`、当前 `instance_id`、消息类型、事件名、JSON schema、消息大小和声明的投递策略。Surface 只能接收 state patch、触发已声明事件、处理已声明 Tool 调用、使用隔离存储和请求受控 Capability。
|
||||
|
||||
Surface 不能:
|
||||
|
||||
```text
|
||||
读取父页面 DOM、localStorage 或 token
|
||||
调用 Tauri invoke
|
||||
访问其他 App 的数据和实例
|
||||
直接访问 Agent / AppServer
|
||||
任意联网、导航、弹窗或加载远端脚本
|
||||
直接执行 app.call / 系统能力
|
||||
```
|
||||
|
||||
用户在 Surface 中的操作只是 App Event;需要系统副作用时,Runtime 仍按 Capability 规则确认、执行和审计。
|
||||
|
||||
### 8.3 Bundle 与发布
|
||||
|
||||
当前已验证的生产安全原则保持不变:Bundle 必须是不可变 artifact,执行前检查来源、大小、SHA-256、签名、Host 兼容性和回滚条件;验证失败不得进入 active cache。开发期 inline bundle 只能是受限开发 fixture,不是长期市场供应方式。
|
||||
|
||||
本阶段仅定义本地 `app_scope`,不定义开放市场发布者身份。Bundle 信任、App 管理和 Tool 可见性仍必须由 Runtime 控制;将来引入供应商身份时须以新 Manifest 版本扩展,不能放宽本节隔离规则。
|
||||
|
||||
## 9. App Manifest 最小契约
|
||||
|
||||
```json
|
||||
{
|
||||
"format": "lineup.app.v1",
|
||||
"app_scope": "whiteboard",
|
||||
"version": "1.0.0",
|
||||
"runtime_sdk": {
|
||||
"api_version": "1",
|
||||
"required_features": [
|
||||
"app.lifecycle.v1",
|
||||
"app.tools.v1",
|
||||
"surface.state.v1"
|
||||
],
|
||||
"optional_features": ["artifact.create.v1"]
|
||||
},
|
||||
"entrypoints": [
|
||||
{"id": "canvas", "kind": "contextual", "default_eligible": false}
|
||||
],
|
||||
"agent_subscriptions": [
|
||||
{"type": "lineup.app.state.patch", "scope": "instance"},
|
||||
{"type": "lineup.tool.invoke", "scope": "conversation"}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"method": "board.create",
|
||||
"contract_version": "1",
|
||||
"invocation": {"kind": "command", "activation": "launch_if_needed"},
|
||||
"input_schema": {"type": "object", "additionalProperties": false},
|
||||
"output_schema": {"type": "object", "additionalProperties": false}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Runtime 在安装、启用和启动时验证 SDK API 版本与 feature 集;App 不兼容、未启用或当前 Host 不支持时,不得进入 Inventory、创建实例或接收 Tool 调用。
|
||||
|
||||
## 10. Tauri 参考实现的最终模块边界
|
||||
|
||||
```text
|
||||
lineup-app/
|
||||
├── runtime-core/
|
||||
│ ├── communication/ AppServer transport、sync、outbox
|
||||
│ ├── coordination/ event、state、router、policy、audit
|
||||
│ ├── apps/ app registry、instance manager、default app
|
||||
│ ├── tools/ tool registry、inventory、call lifecycle
|
||||
│ ├── capabilities/ capability gateway
|
||||
│ └── persistence/ runtime store、app inbox、artifact metadata
|
||||
├── runtime-sdk/
|
||||
│ ├── app.ts Core App SDK
|
||||
│ ├── manifest.ts App/Tool/Subscription 类型
|
||||
│ ├── protocol.ts RuntimeEnvelope / schema
|
||||
│ ├── errors.ts 稳定错误码
|
||||
│ └── testkit/
|
||||
├── surface-sdk/
|
||||
│ ├── bridge.ts sandbox message bridge
|
||||
│ └── surface.ts Installed App 最小 SDK
|
||||
└── tauri/
|
||||
├── src/
|
||||
│ ├── bootstrap/ 应用启动装配层
|
||||
│ ├── runtime-host/ Web/Tauri Host Provider adapter
|
||||
│ ├── shell/ switcher、default、recovery
|
||||
│ └── core-apps/ chat、app-registry、settings
|
||||
└── src-tauri/ Rust 文件、音频、通知、窗口 Provider
|
||||
```
|
||||
|
||||
`lineup-app/tauri/src/main.ts` 是当前 Tauri/Web Host 的启动装配入口:它创建 Host 侧适配、
|
||||
`LineUpRuntime` 与默认 Runtime App Host,并把它们接起来。它不创建或持有 Transport、
|
||||
Store、sync loop、outbox,也不解析原始 Agent payload。现阶段仍有可信 Renderer、Surface
|
||||
与 Capability 的 Host 集成代码;后续可以继续迁移这些 UI 适配,但不得把 Runtime 所有权
|
||||
重新放回 `main.ts`。
|
||||
|
||||
可直接迁移的现有基础:
|
||||
|
||||
| 当前模块 | 最终归属 |
|
||||
|---|---|
|
||||
| `transport-adapter.ts` | `runtime-core/communication/` |
|
||||
| `conversation-store.ts` | `runtime-core/persistence/` |
|
||||
| `interaction-kernel.ts` | `runtime-core/coordination/` 的协议入口/兼容适配基础 |
|
||||
| Tool/Task/Execution 状态机 | Chat App 的投影 + Runtime call state |
|
||||
| `surface-registry.ts` | `runtime-core/apps/` 的 App Registry 基础 |
|
||||
| `surface-instance-manager.ts` | `runtime-core/apps/` 的 Instance Manager 基础 |
|
||||
| Bundle manifest/cache/policy | `runtime-core/apps/` 与 Execution Plane |
|
||||
| `client-inventory.ts` | `runtime-core/tools/` 的动态 Inventory Publisher |
|
||||
| `capability-*` | `runtime-core/capabilities/` |
|
||||
| `trusted-dom-renderers.ts` | `tauri/src/core-apps/chat/` |
|
||||
|
||||
## 11. 实施顺序
|
||||
|
||||
### F0:冻结契约与兼容映射
|
||||
|
||||
- 定义 `RuntimeEnvelope`、`app_scope`、`conversation_id`、`instance_id`、`operation_id`、`call_id` 的类型和校验;
|
||||
- 定义旧 `lineup.v1.*` 到 RuntimeEnvelope 的兼容映射;
|
||||
- 定义 Runtime SDK v1、Surface Bridge SDK v1、App Manifest、Tool Descriptor 和错误码;
|
||||
- 为全部契约建立 golden fixture;不改变当前可验证的 M0~M4 行为。
|
||||
|
||||
### F1:Runtime Core 与 Interaction Core App(当前 `chat` 实现)
|
||||
|
||||
- 从 `main.ts` 提取单一 `LineUpRuntime`;
|
||||
- Runtime 独占 Transport、Store、outbox、原始消息校验和筛选;
|
||||
- 将当前图文 IM 迁为 Interaction Core App 的 `chat` 实现;
|
||||
- 当前 `chat` 用 `state.subscribe()` 和 `agentMessages.subscribe()` 获取已验证投影,不再依赖 Transport;
|
||||
- 保持登录、同步、本地回显、Markdown、Tool Call、Task、Artifact 的回归行为。
|
||||
|
||||
#### MVP-R1 实施状态(2026-08-04)
|
||||
|
||||
F0 与 F1 中支撑图文聊天迁移的最小闭环已落地并经自动化回归验证。此处的“完成”仅指下列 MVP 边界;Manifest、Installed App、完整 Tool Inventory 和应用市场仍属于后续阶段。
|
||||
|
||||
| MVP 项 | 当前实现 | 验证位置 |
|
||||
|---|---|---|
|
||||
| Runtime 单一所有权 | `LineUpRuntime` 唯一创建并持有 `TransportAdapter`、`ConversationStore`、`InteractionKernel`、同步循环与 outbox。 | `tauri/src/runtime/coordination/lineup-runtime.ts`、`lineup-runtime.test.ts` |
|
||||
| Core App 入口 | `CoreAppRegistry` 记录当前可作为默认入口的 Core App;`CoreAppHostRegistry` 再根据 `app_scope` 找到对应的可信装配器。当前默认实现仍是 `chat`,但 `main.ts` 不再直接依赖 Chat 内部 Renderer 和 Shell。 | `runtime/app-management/app-registry.ts`、`runtime-app-host.ts`、`core-apps/chat/chat-app-host.ts`、`runtime-app-host.test.ts` |
|
||||
| Chat SDK 边界 | Chat 仅以 `ChatRuntimeSDK` 订阅状态/Agent 消息、读取 Inbox、ACK,并以 Runtime Action 发送文本或交互动作。 | `runtime/app-management/app-sdk.ts`、`main.ts` |
|
||||
| 作用域与旧协议兼容 | 入站消息先经 `resolveIncomingScope`;旧 `lineup.v1` 自动映射为当前会话的 `chat` 作用域,显式非法或不匹配的 scope 在投递前拒绝。 | `runtime/coordination/runtime-envelope.ts`、`lineup-runtime.test.ts` |
|
||||
| 恢复与幂等 | `ConversationStore` 持久化 App Inbox;未 ACK 消息在 Runtime 重建后恢复,同一 `message_id` 不重复投递。 | `runtime/persistence/conversation-store.ts`、`conversation-store.test.ts`、`lineup-runtime.test.ts` |
|
||||
|
||||
本阶段不修改 AppServer 或 Hermes 的既有 `lineup.v1` 协议。兼容层只存在于 Runtime 内部,因此上层 Chat App 不读取旧协议字段,也不需要同步升级远端。
|
||||
|
||||
### F2:App Registry 与默认 App
|
||||
|
||||
- 将开发期 Surface Registry 迁为 Runtime App Registry;
|
||||
- 实现 App Instance Manager、App Focus Manager 和生命周期状态(foreground/background/suspended);
|
||||
- 实现 Runtime Tool Router / App Orchestrator:校验 Tool 后决定直接执行、启动 App、切换前台或等待用户交互;
|
||||
- 实现 App 启用、禁用、移除、版本记录、实例收口和默认入口选择;
|
||||
- 实现 `app-registry` Core App;
|
||||
- 将当前 `chat` 作为 Interaction App 的 IM 实现,为后续 Audio/Video 模式预留 entrypoint 和恢复策略。
|
||||
|
||||
### F3:Tool Registry、Inventory 与 SDK Inbox
|
||||
|
||||
- 解析 Manifest Tool/Subscription 并计算可见性;
|
||||
- 在 Runtime 连接与状态变化时同步 revisioned Inventory 给 Agent;Tool 可声明是否需要启动 App、是否要求前台以及结束后是否恢复原焦点;
|
||||
- 实现 Agent Tool → Runtime → App SDK → result/progress 的可靠闭环;
|
||||
- 将 MVP-R1 已实现的 Chat App Inbox、ACK、断线/崩溃恢复和拒绝码推广为所有受管理 App 的通用可靠投递机制。
|
||||
|
||||
### F4:第一个 Installed App
|
||||
|
||||
- 选择画板或任务面板作为第一个完整 Installed App;
|
||||
- 验收 install → enable → Inventory → Tool 调用 → App 启动/前台切换 → Surface 事件 → Agent result → 原前台恢复 → disable/remove;
|
||||
- 语音/视频属于 Interaction App 的模式或受 Runtime 管理的扩展 App,必须复用 Runtime SDK、Tool、Artifact 和 Capability 通道,不建立独立 Agent 通信链路。
|
||||
|
||||
## 12. 完成准入条件
|
||||
|
||||
```text
|
||||
消息与隔离
|
||||
[ ] 缺少/非法 app_scope 或 conversation_id 的可路由消息被拒绝。
|
||||
[ ] App 不能收到其他 App 或其他 conversation 的 Agent 原始消息。
|
||||
[ ] App 未运行时关键消息可从 Inbox 有界恢复,重复投递不重复执行。
|
||||
|
||||
应用管理
|
||||
[ ] 启用/禁用/移除改变 Tool 可见性,并更新 Agent Inventory revision。
|
||||
[ ] 默认 App 失效时必定回退至 Interaction App 的 IM/`chat` Recovery 入口。
|
||||
[ ] 禁用/移除能收口活动实例、焦点栈、调用和私有数据清理流程。
|
||||
[ ] Agent 启动扩展 App 时,Runtime 能将当前前台实例切换到后台,并在扩展结束后恢复原焦点。
|
||||
|
||||
工具与能力
|
||||
[ ] Agent 只能调用当前 Inventory 中、参数 schema 合法的 Tool。
|
||||
[ ] Tool 的 result/progress/error 经 Runtime 校验、持久化和审计。
|
||||
[ ] App / Surface 无法绕过 Capability Gateway 获得系统权限。
|
||||
|
||||
安全
|
||||
[ ] Installed App 无法访问 Tauri invoke、Host DOM、token、任意网络或其他 App 数据。
|
||||
[ ] Bundle 只有在完整性和签名验证后才可运行;失败可回滚且不污染 active cache。
|
||||
```
|
||||
|
||||
## 13. 旧文档的后续定位
|
||||
|
||||
| 文档 | 保留价值 | 在本方案后的定位 |
|
||||
|---|---|---|
|
||||
| [App 层架构方案](lineup-app-layer-architecture.md) | M0~M4 已实现边界、测试和验收事实;Markdown、Surface、Capability 原则 | 迁移基础与历史实施记录。 |
|
||||
| [Runtime 与 App SDK 架构方案](lineup-runtime-sdk-architecture.md) | Runtime、App Manager、Tool Registry、SDK、消息筛选的完整初稿 | 被本文件收敛后的详细来源;以本文件的 `app_scope` 和实施顺序为准。 |
|
||||
| [UI Surface 与 App Capability 协议](lineup-ui-surface-protocol.md) | Surface sandbox、bridge、Capability 风险分级、inventory 和 Bundle 安全规则 | 协议细节来源;R0 需完成其旧字段到 RuntimeEnvelope 的版本化映射。 |
|
||||
|
||||
以后新增 App 设计、SDK 方法、Agent Tool、Surface 或 Capability 时,先修改本文件的边界/契约,再实施代码和细节协议,避免重新把功能堆回聊天页面或创建绕开 Runtime 的平行通道。
|
||||
@@ -0,0 +1,132 @@
|
||||
# LineUp App 层架构与迁移记录
|
||||
|
||||
**版本:** 2.0(Runtime 基线)
|
||||
**状态:** 当前实现边界与历史迁移记录
|
||||
**日期:** 2026-08-04
|
||||
**最终决策入口:** [LineUp App 最终设计方案](app_final_design.md)
|
||||
|
||||
## 1. 当前结论
|
||||
|
||||
LineUp 是运行在用户设备上的协作 Runtime,不是某个平台上的独立聊天客户端。可以把
|
||||
Runtime 理解为本地协调中心:它负责连接、消息、存储和恢复;`chat` 是它首先加载的可信
|
||||
内置功能。未来的语音、画板、游戏和应用中心也必须使用同一套 Runtime / SDK 边界,不能
|
||||
各自再建立一条到 Agent 的通信链路。
|
||||
|
||||
当前唯一实现与验收基线:
|
||||
|
||||
```text
|
||||
Tauri 2 Desktop Host + Web Reference Host
|
||||
```
|
||||
|
||||
这两个 Host 是同一套 TypeScript Runtime 的两种外壳:Tauri Desktop Host 用于正式桌面
|
||||
交付及受控系统能力;Web Reference Host 用于浏览器开发、Tailscale 联调和自动化测试。
|
||||
它们不是两套产品,Web 版也不能替代桌面版的系统能力。独立 Android/Kotlin 客户端与
|
||||
Wails Host 均不在当前或后续规划范围内;早期实验细节仅保留在 Git 历史,不构成架构或
|
||||
验收依据。
|
||||
|
||||
## 2. App 层边界
|
||||
|
||||
```text
|
||||
Remote Agent / AppServer
|
||||
│ lineup.v1 wire protocol
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ LineUpRuntime │
|
||||
│ Transport · sync loop · Store · Outbox · App Inbox │
|
||||
│ scope 路由 · Compatibility Adapter · Tool · Capability │
|
||||
└─────────────────────┬──────────────────────────────────┘
|
||||
│ Runtime SDK
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ Core / Installed App │
|
||||
│ chat · app-registry · voice · whiteboard │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
| 层 | 必须负责 | 不得负责 |
|
||||
|---|---|---|
|
||||
| `LineUpRuntime` | 处理 AppServer/Agent 通信、会话、原始协议兼容、scope 筛选、Store、sync、outbox、App Inbox、Tool 路由、App 实例和前台焦点、权限与恢复。 | 具体业务页面 DOM。 |
|
||||
| Interaction Core App | 用户实际使用的主交互界面:当前是 Chat/IM,未来包含 Audio/Video 模式、标准交互原语和扩展结果投影。 | 直连 AppServer、维护 cursor、解析 Agent 原始包、调度其他 App 或直接写 Runtime Store。 |
|
||||
| Runtime SDK | App 与 Runtime 之间唯一的受限接口:提供 snapshot、订阅、Agent 消息、Inbox ACK、Runtime Action、App 导航和生命周期请求。 | 把 Transport、token、Host 特权 API 暴露给 App。 |
|
||||
| Tauri/Web Host | 把 Runtime 放进桌面窗口或浏览器,并提供对应的 DOM/系统能力。 | 解释协议、决定 App 调度、绕过 Runtime 路由和策略。 |
|
||||
| Surface | 在隔离执行域呈现已验证 Bundle,并经受限 bridge 上报事件。 | 访问 Host DOM、登录态、Tauri API、任意网络。 |
|
||||
|
||||
## 3. MVP-R1:Runtime 托管 Chat
|
||||
|
||||
当前 MVP 已完成并以 Tauri/Web 自动化回归验证以下八项条件:
|
||||
|
||||
| 条件 | 当前事实 |
|
||||
|---|---|
|
||||
| MVP-01 | `LineUpRuntime` 唯一创建并持有 Transport、`ConversationStore`、sync loop 与 outbox。 |
|
||||
| MVP-02 | Runtime 从本地 `CoreAppRegistry` 解析默认 `chat`,`RuntimeAppHost` 负责挂载其 Shell;`main.ts` 不创建聊天页面。 |
|
||||
| MVP-03 | Chat 只经 `ChatRuntimeSDK` 订阅 Agent 消息、状态和 Chat-safe Runtime 事件,并以 Action 发送文本、交互、结果与取消。 |
|
||||
| MVP-04 | Runtime 向 Chat 投递前验证 `app_scope = chat` 与当前 `conversation_id`;非法/不匹配消息被拒绝。 |
|
||||
| MVP-05 | Compatibility Adapter 在 Runtime 内将旧 `lineup.v1` 映射为内部 `RuntimeEnvelope`;远端无须同步重写。 |
|
||||
| MVP-06 | 登录、同步、发送、本地回显、Markdown、Agent 状态、Tool/Task 与刷新恢复保持回归。 |
|
||||
| MVP-07 | Store 持久化每个 App 的 Inbox;运行时重建后未 ACK 消息可恢复,`message_id` 去重。 |
|
||||
| MVP-08 | `npm test -- --run` 通过 21 个文件 / 94 个测试;`npm run build` 通过。 |
|
||||
|
||||
源代码目录和依赖方向见 [Tauri/Web 源码导航](../../lineup-app/tauri/src/README.md)。
|
||||
|
||||
## 4. 当前源码归属
|
||||
|
||||
```text
|
||||
lineup-app/tauri/src/
|
||||
├── main.ts # 启动装配入口:连接 Tauri/Web Host、Runtime 与默认 App
|
||||
├── core-apps/chat/ # 当前 Interaction App 的 IM 实现、Renderer、样式
|
||||
└── runtime/
|
||||
├── app-management/ # Registry、App Instance、焦点、生命周期、Host
|
||||
├── communication/ # Transport Adapter
|
||||
├── coordination/ # Runtime、Kernel、Envelope、Tool/Task/交互编排
|
||||
├── persistence/ # Conversation Store、Outbox、App Inbox
|
||||
├── protocol/ # lineup.v1 解码与 golden fixture
|
||||
├── surfaces/ # Surface 生命周期、Bundle、隔离 Host
|
||||
├── capabilities/ # Registry、执行、审计
|
||||
├── artifacts/ # Artifact 元数据与缓存
|
||||
└── inventory/ # Agent 可见 Inventory
|
||||
```
|
||||
|
||||
依赖必须保持单向:
|
||||
|
||||
```text
|
||||
core-apps/interaction(当前 chat) → runtime/app-management SDK → runtime/coordination
|
||||
└→ communication / persistence / protocol
|
||||
Host adapters → runtime/coordination
|
||||
Surface → restricted bridge → Runtime Action / Capability Gateway
|
||||
```
|
||||
|
||||
## 5. 历史迁移的保留价值
|
||||
|
||||
早期 M0~M4 工作建立了当前 Runtime 的可复用安全基础。它们是迁移记录,不是新的功能
|
||||
排期或平行客户端路线:
|
||||
|
||||
| 历史阶段 | 保留成果 | 当前归属 |
|
||||
|---|---|---|
|
||||
| M0 | HTTP Transport、Conversation Store、协议解码、Markdown、基础回归 | `communication`、`persistence`、`protocol`、Chat Renderer |
|
||||
| M1 | Tool Call、Task、可靠交互结果、受限执行摘要 | `coordination`、Chat SDK 投影 |
|
||||
| M2 | 本地 Surface Registry、实例生命周期、隔离 iframe 与恢复 | `surfaces` |
|
||||
| M3 | Capability Registry、用户确认、审计与受限 Host 执行 | `capabilities` |
|
||||
| M4 | 签名 Manifest、Bundle 校验/缓存/回滚、golden fixture | `surfaces`、`protocol/golden` |
|
||||
|
||||
早期文档中的阶段性“尚未实现”或旧目录路径不得用来判断当前能力;需要审计细节时通过
|
||||
Git 历史检索。
|
||||
|
||||
## 6. 后续阶段
|
||||
|
||||
```text
|
||||
F2 持久化 App Registry、App Instance/Focus Manager、默认 App 切换
|
||||
F3 Runtime Tool Router、动态 Inventory、可靠 App SDK Tool 闭环
|
||||
F4 Interaction App 的 IM/Audio/Video 模式边界与第一个 Installed App
|
||||
```
|
||||
|
||||
未来 App 必须复用 `app_scope + conversation_id`、Runtime SDK、Tool/Capability、Artifact、
|
||||
Store、outbox 与恢复机制;禁止新建独立 Agent 通信通道。
|
||||
|
||||
## 7. 文档关系
|
||||
|
||||
| 文档 | 用途 |
|
||||
|---|---|
|
||||
| [app_final_design.md](app_final_design.md) | 当前产品、Runtime、App、SDK 与实施优先级的唯一汇总入口。 |
|
||||
| [lineup-runtime-sdk-architecture.md](lineup-runtime-sdk-architecture.md) | Runtime / SDK 的详细接口与未来 App Manager 契约。 |
|
||||
| [lineup-ui-surface-protocol.md](lineup-ui-surface-protocol.md) | Surface sandbox、bridge、Capability 和 Bundle 安全协议。 |
|
||||
| 本文件 | 当前实现边界、源码归属与 M0~M4 的迁移价值。 |
|
||||
@@ -0,0 +1,726 @@
|
||||
# LineUp Runtime 与 App SDK 架构方案
|
||||
|
||||
**版本:** 0.1(架构基线)
|
||||
**状态:** 已确认的 Runtime / SDK 方向;尚未完全落地到当前 Tauri 实现
|
||||
**日期:** 2026-08-04
|
||||
**关联方案:** [LineUp App 层架构设计方案](lineup-app-layer-architecture.md)、[LineUp UI Surface 与 App Capability 协议](lineup-ui-surface-protocol.md)
|
||||
|
||||
> **汇总入口:** 本文是 Runtime / SDK 初稿的详细来源。后续设计以 [LineUp App 最终设计方案](app_final_design.md) 为准;后者收敛了本地 `app_scope`、兼容迁移和实施顺序。
|
||||
|
||||
---
|
||||
|
||||
## 0. 结论与范围
|
||||
|
||||
LineUp 不是“聊天应用里附带一个交互 Runtime”。**LineUp App 本身就是运行在用户设备上的
|
||||
协作 Runtime**,可以把它理解为本地协调中心:它独占与 AppServer / Remote Agent 的连接,
|
||||
维护会话和本地应用生态,并为上层应用提供统一的消息、工具、存储、权限和生命周期接口。
|
||||
聊天只是第一个使用这些接口的内置 App。
|
||||
|
||||
```text
|
||||
Remote Agent
|
||||
│ LineUp Runtime Envelope
|
||||
▼
|
||||
AppServer / IM Transport
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────┐
|
||||
│ LineUp Runtime │
|
||||
│ │
|
||||
│ Connection · Session · Event Routing · App Manager │
|
||||
│ Tool Registry · Capability Gateway · Storage · Audit │
|
||||
└───────────────┬──────────────────────────────┬────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
Core App Installed App
|
||||
chat whiteboard
|
||||
app-registry voice
|
||||
settings draw-and-guess
|
||||
```
|
||||
|
||||
本方案定义:
|
||||
|
||||
- Runtime 与 Remote Agent、AppServer、上层 App、Tauri Host 的边界;
|
||||
- 动态应用列表、应用身份、应用安装/启用/禁用/移除与实例生命周期;
|
||||
- 应用向 Agent 公布工具方法的标准模型;
|
||||
- Runtime 对消息的统一 Envelope、筛选、可靠投递与 App SDK 订阅接口;
|
||||
- Core App、Installed App 和 Tauri Host 的信任分层;
|
||||
- SDK v1 的最小接口和工程模块边界。
|
||||
|
||||
本方案不在本次冻结:市场目录的商业模型、支付、公开开发者身份认证细节、多人/多 Agent 共享工作区、完整事件溯源和后台持续执行策略。它们必须建立在本方案的身份、路由和权限边界上。
|
||||
|
||||
## 1. 基本术语
|
||||
|
||||
| 术语 | 含义 |
|
||||
|---|---|
|
||||
| **Runtime** | 用户设备上的长期协作运行底座;唯一负责 AppServer/Agent 通信、应用管理和 Host 能力调度。 |
|
||||
| **App** | 用户实际使用的功能单元。Chat、应用注册表、语音、白板和游戏都是 App。 |
|
||||
| **Core App** | 跟随 LineUp Runtime 一起发布、默认受信任的内置 App,例如 `chat`。 |
|
||||
| **Installed App** | 从受信来源安装、验证后在受限执行环境中运行的 App。 |
|
||||
| **App Registry** | Runtime 在本机保存的应用清单,记录安装包、版本、启用状态、实例和回滚信息;它是这些状态的唯一依据。 |
|
||||
| **Tool** | App 先在 Manifest 中说明、Runtime 再公布给 Agent 的可调用方法。Agent 不能任意调用 App 内部函数。 |
|
||||
| **Capability** | Runtime / Host 保管的系统能力,例如录音、选择文件、保存 Artifact;App 必须请求,不能自动获得。 |
|
||||
| **Surface** | 一个受限的交互小界面,例如一块画板、一个语音会话面板或一局游戏。 |
|
||||
| **Conversation** | 用户与一个主 Agent 协作的一段会话范围;App 实例、工具调用和 Artifact 默认都归属这段会话。 |
|
||||
|
||||
## 2. 不可变架构原则
|
||||
|
||||
1. **Runtime 是唯一 Agent 通信入口。** App 不直接访问 AppServer、IM SDK、WebSocket、HTTP cursor 或 Agent endpoint。
|
||||
2. **App 只与 Runtime 通信。** Chat、Voice、Market、白板均通过 SDK 读状态、订阅消息、提交动作和请求能力。
|
||||
3. **每一条可路由 Runtime 消息必须具有 `app_scope` 与 `conversation_id`。** Runtime 以它们作为本地路由、授权、筛选、持久化和投递的首要键。
|
||||
4. **原始 Agent payload 不交给 App。** Runtime 完成版本、身份、schema、大小、去重和作用域校验后,才产生可订阅的标准化消息。
|
||||
5. **应用可调用方法必须先声明、后公布、再调用。** Agent 不执行 App 内任意函数,只能调用当前 Inventory 中精确声明的方法。
|
||||
6. **系统能力由 Runtime 独占。** Agent 和 App 只能请求 Capability;文件、麦克风、通知、剪贴板、窗口、密钥等均不得直接访问。
|
||||
7. **应用状态、消息和副作用分离。** Event 是已发生事实,Command 是请求动作,Effect 是 Runtime 执行的受控副作用。
|
||||
8. **UI 是 Runtime 状态的投影。** App 重启、断网或 Surface 回收后,Runtime 能依据持久化状态恢复到可解释的协作状态。
|
||||
|
||||
## 3. 运行时分层
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ Runtime Shell │
|
||||
│ 应用切换 · 默认入口 · 连接状态 · 通知 · 安全恢复 │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ Core App / Installed App │
|
||||
│ Interaction App(IM / Audio / Video) · App Registry │
|
||||
│ Settings · Whiteboard · Game · …(Installed App) │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ LineUp App Runtime SDK / Surface Bridge SDK │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ LineUp Runtime Core │
|
||||
│ Communication · Coordination · App Management · Tooling │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ Runtime Host Provider │
|
||||
│ Tauri Desktop / Web Reference Host 的文件、通知、窗口等 │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Runtime 内部有三个平面:
|
||||
|
||||
| 平面 | 职责 |
|
||||
|---|---|
|
||||
| Communication Plane | 登录、AppServer 连接、历史同步、实时入站、ACK、outbox、重连。 |
|
||||
| Coordination Plane | Envelope 校验、事件路由、会话/Agent 状态、App 生命周期、Tool Registry、Inventory、策略与审计。 |
|
||||
| Execution Plane | Tauri/Host Capability、Surface Sandbox、Bundle Cache、Artifact 内容和受控副作用。 |
|
||||
|
||||
## 4. 应用作用域
|
||||
|
||||
本阶段**不定义跨市场、跨供应商的 App 身份、反向域名命名空间或发布者归属模型**。Runtime 仅维护本机可用的应用作用域键(`app_scope`),用于启动、隔离存储、消息路由和 Tool 路由。
|
||||
|
||||
```text
|
||||
chat
|
||||
app-registry
|
||||
settings
|
||||
voice
|
||||
whiteboard
|
||||
draw-and-guess
|
||||
```
|
||||
|
||||
`app_scope` 是 Runtime 本地注册表中的短名称,不承诺在未来开放市场中全局唯一。开放生态时再通过版本化 Manifest 引入稳定 App ID、供应商身份和命名空间映射;届时不得破坏本节定义的 scope 路由语义。
|
||||
|
||||
### 4.1 作用域键
|
||||
|
||||
| 键 | 用途 |
|
||||
|---|---|
|
||||
| `conversation_id` | 协作会话边界;所有可路由消息均必填。 |
|
||||
| `app_scope` | 消息所属/目标 App 边界;所有可路由消息均必填。 |
|
||||
| `instance_id` | 可选;精确标识某块白板、语音会话或游戏实例。 |
|
||||
| `operation_id` | 可选;标识长任务、导出、执行进度或异步工具操作。 |
|
||||
| `call_id` | 可选;标识 Agent 发起的一次 Tool 调用。 |
|
||||
|
||||
Runtime 的全局管理事件也不省略路由键,而使用保留作用域:
|
||||
|
||||
```text
|
||||
app_scope: runtime 或具体 Core App
|
||||
conversation_id: runtime:global
|
||||
```
|
||||
|
||||
第三方 App 不得使用或伪造上述保留身份。
|
||||
|
||||
## 5. Runtime App Manager 与交互编排
|
||||
|
||||
### 5.1 应用目录与状态
|
||||
|
||||
Runtime 维护本地 `App Registry`,它是 App 安装、版本、信任和启用状态的唯一事实源;
|
||||
`App Instance Manager` 单独维护运行实例、前后台焦点和生命周期。App Catalog/市场只提供
|
||||
候选条目;市场 App 不直接写文件、删除 Bundle 或改写 Registry。
|
||||
|
||||
```text
|
||||
Catalog Entry → Downloading → Verifying → Installed → Enabled
|
||||
│ │
|
||||
│ ├── Active / Background / Suspended instances
|
||||
│ └── Disabled
|
||||
└── Update / Rollback / Remove
|
||||
```
|
||||
|
||||
建议的 App Record:
|
||||
|
||||
```ts
|
||||
type AppRecord = {
|
||||
app_scope: AppScope;
|
||||
kind: "core" | "installed";
|
||||
installed_version: string;
|
||||
previous_version?: string;
|
||||
enabled: boolean;
|
||||
install_state: "installed" | "updating" | "failed";
|
||||
active_instance_count: number;
|
||||
installed_at: string;
|
||||
enabled_at?: string;
|
||||
};
|
||||
|
||||
type AppInstanceRecord = {
|
||||
instance_id: AppInstanceID;
|
||||
app_scope: AppScope;
|
||||
conversation_id?: ConversationID;
|
||||
state: "starting" | "foreground" | "background" | "suspended" | "stopping" | "stopped" | "failed";
|
||||
parent_instance_id?: AppInstanceID;
|
||||
started_at: string;
|
||||
stopped_at?: string;
|
||||
error?: string;
|
||||
};
|
||||
```
|
||||
|
||||
### 5.2 Runtime 管理接口
|
||||
|
||||
```ts
|
||||
interface RuntimeAppManager {
|
||||
listApps(filter?: AppListFilter): readonly AppRecord[];
|
||||
getApp(appScope: AppScope): AppRecord | undefined;
|
||||
subscribe(listener: (change: AppRegistryChange) => void): Unsubscribe;
|
||||
|
||||
install(request: InstallAppRequest): Promise<AppOperation>;
|
||||
enable(appScope: AppScope): Promise<AppOperation>;
|
||||
disable(appScope: AppScope, options?: DisableAppOptions): Promise<AppOperation>;
|
||||
update(appScope: AppScope, targetVersion?: string): Promise<AppOperation>;
|
||||
rollback(appScope: AppScope): Promise<AppOperation>;
|
||||
remove(appScope: AppScope, options?: RemoveAppOptions): Promise<AppOperation>;
|
||||
|
||||
launch(request: LaunchAppRequest): Promise<AppInstanceHandle>;
|
||||
closeInstance(instanceID: AppInstanceID): Promise<void>;
|
||||
}
|
||||
|
||||
interface RuntimeAppOrchestrator {
|
||||
launch(request: LaunchAppRequest): Promise<AppInstanceHandle>;
|
||||
foreground(instanceID: AppInstanceID): Promise<AppInstanceHandle>;
|
||||
background(instanceID: AppInstanceID, reason?: string): Promise<AppInstanceHandle>;
|
||||
suspend(instanceID: AppInstanceID, reason?: string): Promise<AppInstanceHandle>;
|
||||
restore(instanceID: AppInstanceID): Promise<AppInstanceHandle>;
|
||||
closeInstance(instanceID: AppInstanceID): Promise<void>;
|
||||
getFocusStack(conversationID?: ConversationID): readonly AppInstanceID[];
|
||||
}
|
||||
```
|
||||
|
||||
安装、验签、升级、禁用和移除是异步操作,必须返回可观察的 `AppOperation`。禁用会阻止新实例和新 Tool 调用,并收口现有实例;移除会在实例关闭及用户确认后清理 Bundle 和 App 私有数据。
|
||||
|
||||
### 5.3 默认应用与安全回退
|
||||
|
||||
Runtime Shell 不属于任何 App。它负责应用切换、通知、连接状态和安全恢复。用户可选择一个具备 `default_eligible` entrypoint 的已启用 App 作为默认入口:
|
||||
|
||||
```text
|
||||
当前默认:chat → 图文 IM 为主
|
||||
未来默认:voice → 实时语音为主
|
||||
```
|
||||
|
||||
`chat` 是内建、不可移除的 Recovery App:默认 App 不兼容、被禁用、损坏或启动失败时,Runtime 必须回退到它。
|
||||
|
||||
启动优先级:
|
||||
|
||||
```text
|
||||
显式启动目标(深链接/通知)
|
||||
→ 可安全恢复的前台工作
|
||||
→ 用户 Default App
|
||||
→ chat Recovery App
|
||||
```
|
||||
|
||||
### 5.4 前台焦点与 App 间切换
|
||||
|
||||
`Interaction App` 是默认的人与 Agent 交互入口,但不是全局调度器。Runtime 的
|
||||
`App Orchestrator`、`Tool Router` 和 `Focus Manager` 负责决定 Agent 请求由哪个 App、哪个
|
||||
交互模式或哪个标准组件承接。Interact 只通过 SDK 呈现当前会话并提交用户 Action。
|
||||
|
||||
```text
|
||||
Interaction App / Audio Mode foreground
|
||||
→ Agent 请求 launch(draw-and-guess)
|
||||
Runtime 校验 Tool、Manifest、Inventory、参数和前台条件
|
||||
→ Audio Instance background / suspended
|
||||
→ Draw-and-Guess Instance starting → foreground
|
||||
→ 游戏结果经 Runtime 回传 Agent
|
||||
→ 关闭或结束后按 focus stack 恢复 Interaction App
|
||||
```
|
||||
|
||||
前后台切换必须保留原实例和 `conversation_id` 的关联,不能因为界面暂时不可见就删除
|
||||
未完成 Tool Call、Surface 或 App Inbox。若目标 App 启动失败,Runtime 应恢复原前台实例并
|
||||
向 Agent 返回受控的 `app_start_failed` 或 `handler_failed` 结果。
|
||||
|
||||
## 6. Runtime Tool Registry 与动态 Inventory
|
||||
|
||||
### 6.1 App Tool
|
||||
|
||||
App 可在 Manifest 中声明 Agent 可调用方法。当前 Tool 由 App Scope、局部方法和契约版本定位:
|
||||
|
||||
```text
|
||||
whiteboard/board.create@1
|
||||
whiteboard/board.apply_diagram@1
|
||||
voice/voice.request_recording@1
|
||||
```
|
||||
|
||||
Runtime 内部使用结构化键,而不依赖字符串拼接:
|
||||
|
||||
```ts
|
||||
type ToolKey = {
|
||||
app_scope: AppScope;
|
||||
method: string;
|
||||
contract_version: string;
|
||||
};
|
||||
```
|
||||
|
||||
每个 Tool 声明至少包含:
|
||||
|
||||
- 标题、面向 Agent 的说明、输入/输出 JSON Schema;
|
||||
- `query`、`command`、`interactive`、`operation` 四种调用模型之一;
|
||||
- 前台要求、超时、幂等规则和风险等级;
|
||||
- 是否需启动 App 实例、是否允许 Runtime 自动启动;
|
||||
- 该方法可能请求的 Capability。
|
||||
|
||||
### 6.2 可见性与 Inventory
|
||||
|
||||
Runtime 向 Agent 公布的工具不是安装包的全部声明,而是动态交集:
|
||||
|
||||
```text
|
||||
Agent 可见 Tool
|
||||
= 已验证 Manifest Tool
|
||||
∩ App 已启用
|
||||
∩ 当前 Host 支持
|
||||
∩ 用户/组织策略允许
|
||||
∩ 当前 Agent 和 conversation scope 允许
|
||||
∩ 所需前置权限满足
|
||||
```
|
||||
|
||||
Inventory 包含 App、Tool、Surface 和 Runtime Capability,并具有 revision。Runtime 在连接建立、App 启用/禁用/升级/撤销、Host 能力变化或策略收紧后重新发布。Agent 调用必须携带其看到的 `inventory_revision`;过期清单的调用应被安全拒绝并提示刷新。
|
||||
|
||||
### 6.3 Tool 调用流程
|
||||
|
||||
```text
|
||||
Agent lineup.tool.invoke
|
||||
→ Runtime 验证 Envelope、Agent、inventory revision、App 状态和参数 schema
|
||||
→ 创建 call record / 审计记录
|
||||
→ Tool Router 判断直接执行、启动 App、切换前台、等待用户交互,或创建异步 operation
|
||||
→ App Orchestrator / Interaction App / Installed App 承接调用
|
||||
→ App 经 SDK 返回 progress / result / error
|
||||
→ Runtime 验证输出 schema、持久化、更新焦点并回传 Agent
|
||||
```
|
||||
|
||||
标准终态错误码至少包括:
|
||||
|
||||
```text
|
||||
cancelled_by_user
|
||||
permission_denied
|
||||
app_disabled
|
||||
app_not_installed
|
||||
app_version_mismatch
|
||||
tool_not_visible
|
||||
invalid_arguments
|
||||
foreground_required
|
||||
operation_expired
|
||||
handler_failed
|
||||
```
|
||||
|
||||
## 7. 统一消息 Envelope 与路由
|
||||
|
||||
### 7.1 Runtime Envelope
|
||||
|
||||
所有 Agent、Runtime、App、User 和 Host 之间的可路由消息使用统一基础 Envelope:
|
||||
|
||||
```ts
|
||||
type RuntimeEnvelope = {
|
||||
v: 1;
|
||||
id: string;
|
||||
type: string;
|
||||
timestamp: string;
|
||||
|
||||
sender: {
|
||||
kind: "agent" | "runtime" | "app" | "user" | "host";
|
||||
id: string;
|
||||
};
|
||||
|
||||
target: {
|
||||
kind: "runtime" | "app";
|
||||
app_scope: AppScope;
|
||||
instance_id?: AppInstanceID;
|
||||
};
|
||||
|
||||
scope: {
|
||||
app_scope: AppScope;
|
||||
conversation_id: ConversationID;
|
||||
instance_id?: AppInstanceID;
|
||||
operation_id?: OperationID;
|
||||
};
|
||||
|
||||
correlation?: {
|
||||
reply_to?: string;
|
||||
call_id?: string;
|
||||
inventory_revision?: string;
|
||||
sequence?: number;
|
||||
};
|
||||
|
||||
payload: JsonValue;
|
||||
};
|
||||
```
|
||||
|
||||
`scope.app_scope` 是消息所属的本地应用作用域,`target.app_scope` 是指定的消费 App。通常二者相同;Runtime 协调型消息可使用 `runtime` 作为 target,再由 Runtime 生成安全的 App 投影并分发。
|
||||
|
||||
### 7.2 消息分类
|
||||
|
||||
第一版类型分组:
|
||||
|
||||
```text
|
||||
lineup.content.* 文本、图片、音频、链接、Artifact 引用
|
||||
lineup.agent.* presence、status、task、progress
|
||||
lineup.interaction.* choice、confirm、input、form
|
||||
lineup.tool.* invoke、accepted、progress、result、error、cancel
|
||||
lineup.app.* state.patch、event、open、close、lifecycle
|
||||
lineup.capability.* request、result
|
||||
lineup.runtime.* inventory、connection、app registry、policy、error
|
||||
```
|
||||
|
||||
具体 schema 将在协议文档版本化;SDK 不暴露未经校验的原始 JSON。
|
||||
|
||||
### 7.3 Runtime 筛选链
|
||||
|
||||
```text
|
||||
Transport 收到原始消息
|
||||
→ 协议:版本、必填 app_scope/conversation_id、type、大小、schema
|
||||
→ 身份:Agent、用户、会话关联
|
||||
→ 去重/顺序:id、sequence、cursor
|
||||
→ App:安装、启用、版本、签名、Host 兼容性
|
||||
→ Tool/Capability:Inventory、方法、参数、策略、前台条件
|
||||
→ Scope:conversation、instance、operation 所属关系
|
||||
→ Subscription:App Manifest 声明与 SDK 订阅交集
|
||||
→ Persist:Runtime Event / App Inbox / cursor
|
||||
→ Deliver:投递给目标 App 的 SDK
|
||||
```
|
||||
|
||||
Runtime 不对所有 App 广播原始消息。一个白板 App 即使与 Chat 位于同一 conversation,也只能收到其 Manifest 声明且 Runtime 授权的实例 patch、Tool 调用或事件。
|
||||
|
||||
## 8. Runtime SDK v1
|
||||
|
||||
### 8.1 双通道模型
|
||||
|
||||
```text
|
||||
Inbound:Runtime → App
|
||||
- 已验证 Agent 消息
|
||||
- App 专属状态投影
|
||||
- Tool 调用
|
||||
- Runtime / App 生命周期
|
||||
|
||||
Outbound:App → Runtime
|
||||
- 用户与 App 声明性 Action
|
||||
- Tool result / progress / error
|
||||
- Surface Event
|
||||
- Capability request
|
||||
- 可恢复 State Snapshot
|
||||
```
|
||||
|
||||
顶层接口:
|
||||
|
||||
```ts
|
||||
interface LineUpAppRuntimeSDK {
|
||||
readonly app: AppContext;
|
||||
readonly lifecycle: AppLifecycleAPI;
|
||||
readonly state: AppStateAPI;
|
||||
readonly agentMessages: AgentMessageAPI;
|
||||
readonly actions: AppActionAPI;
|
||||
readonly tools: AppToolAPI;
|
||||
readonly apps: AppNavigationAPI;
|
||||
readonly artifacts: ArtifactAPI;
|
||||
readonly storage: ScopedStorageAPI;
|
||||
readonly capabilities: CapabilityRequestAPI;
|
||||
readonly diagnostics: DiagnosticsAPI;
|
||||
}
|
||||
```
|
||||
|
||||
App 得到的是按 App Scope、conversation 和实例作用域裁剪后的接口和 View Model,而不是全量 Runtime State 或原始 Transport。
|
||||
|
||||
### 8.2 App Context、状态与生命周期
|
||||
|
||||
```ts
|
||||
interface AppContext {
|
||||
app_scope: AppScope;
|
||||
app_version: string;
|
||||
instance_id: AppInstanceID;
|
||||
kind: "core" | "installed";
|
||||
entrypoint: string;
|
||||
scope: {
|
||||
conversation_id?: ConversationID;
|
||||
operation_id?: OperationID;
|
||||
parent_instance_id?: AppInstanceID;
|
||||
};
|
||||
granted_permissions: readonly AppPermission[];
|
||||
}
|
||||
|
||||
interface AppStateAPI<ViewModel = unknown> {
|
||||
snapshot(): ViewModel;
|
||||
subscribe(listener: (view: ViewModel, change: AppStateChange) => void): Unsubscribe;
|
||||
}
|
||||
```
|
||||
|
||||
生命周期包含 `start`、`foreground`、`background`、`suspend`、`restore`、`closing`。App 可以保存快照并请求延迟关闭,但 Runtime 保留禁用、移除、内存回收和强制收口的最终权力。
|
||||
|
||||
### 8.3 Agent 消息订阅与可靠 Inbox
|
||||
|
||||
SDK 提供实时订阅,同时提供持久化 Inbox 补读:
|
||||
|
||||
```ts
|
||||
interface AgentMessageAPI {
|
||||
list(request?: {
|
||||
conversation_id?: ConversationID;
|
||||
after?: AgentMessageCursor;
|
||||
limit?: number;
|
||||
}): Promise<AgentMessagePage>;
|
||||
|
||||
subscribe(
|
||||
options: {
|
||||
conversation_id?: ConversationID;
|
||||
types?: readonly AgentMessageType[];
|
||||
include_pending?: boolean;
|
||||
},
|
||||
handler: (message: AgentAppMessage) => Promise<void> | void,
|
||||
): Unsubscribe;
|
||||
|
||||
acknowledge(message_id: string): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
投递语义:
|
||||
|
||||
1. Runtime 先验证并持久化,再回调 App;
|
||||
2. 每条消息有唯一 `message_id`,App 必须幂等处理;
|
||||
3. 展示类消息可自动确认;Tool 调用、状态 patch 等业务消息需 App 显式 ACK;
|
||||
4. App 未运行、暂停或崩溃时,Runtime 写入该 App Inbox;恢复后通过 `list()` 与 `subscribe()` 补齐;
|
||||
5. App 禁用、移除或不兼容时,Runtime 不静默投递或丢弃关键调用,而向 Agent 返回标准拒绝。
|
||||
|
||||
App 的订阅条件只是请求;实际投递集为:
|
||||
|
||||
```text
|
||||
Manifest agent_subscriptions
|
||||
∩ SDK subscribe filter
|
||||
∩ App 权限
|
||||
∩ conversation / instance scope
|
||||
∩ Agent target app_scope
|
||||
∩ Runtime Policy
|
||||
```
|
||||
|
||||
### 8.4 Action、Tool、App 导航与 Artifact
|
||||
|
||||
App 只提交声明性 Action;不得构造原始 Agent Envelope 或直接访问 Transport:
|
||||
|
||||
```ts
|
||||
interface AppActionAPI {
|
||||
dispatch(action: AppAction): Promise<CommandReceipt>;
|
||||
}
|
||||
|
||||
interface AppToolAPI {
|
||||
onInvoke(listener: (call: AppToolInvocation) => Promise<AppToolOutcome>): Unsubscribe;
|
||||
progress(request: { call_id: ToolCallID; progress: ToolProgress }): Promise<void>;
|
||||
complete(request: { call_id: ToolCallID; result: JsonValue }): Promise<void>;
|
||||
fail(request: { call_id: ToolCallID; code: AppToolErrorCode; message?: string }): Promise<void>;
|
||||
}
|
||||
|
||||
interface AppNavigationAPI {
|
||||
listAvailable(): readonly AppSummary[];
|
||||
launch(request: LaunchAppRequest): Promise<AppInstanceHandle>;
|
||||
focus(instanceID: AppInstanceID): Promise<void>;
|
||||
background(instanceID: AppInstanceID, reason?: string): Promise<void>;
|
||||
suspend(instanceID: AppInstanceID, reason?: string): Promise<void>;
|
||||
close(instanceID: AppInstanceID): Promise<void>;
|
||||
openDefaultApp(): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
`AppNavigationAPI` 只是 App 发给 Runtime 的受限请求接口。真正的 Tool Router、App Instance
|
||||
Manager 和 Focus Manager 位于 Runtime 内部:它们负责检查 App 是否已安装/启用、是否满足
|
||||
前台条件、是否需要用户确认,以及切换失败时恢复原前台实例。Interact 可以请求启动扩展
|
||||
App,但不能直接决定其他 App 的生命周期。
|
||||
|
||||
Artifact、媒体与文件通过 Runtime Artifact Store 暴露元数据、受限读取、创建、会话引用和用户保存;App 不取得任意路径或跨 App 文件访问权。
|
||||
|
||||
### 8.5 存储与 Capability
|
||||
|
||||
每个 App 使用 Runtime 管理的作用域隔离存储:
|
||||
|
||||
```text
|
||||
app-data/chat/
|
||||
app-data/app-registry/
|
||||
app-data/voice/
|
||||
app-data/whiteboard/
|
||||
```
|
||||
|
||||
SDK 中的 `storage` 提供 JSON 数据和可恢复 snapshot,Runtime 负责配额、升级迁移、禁用和移除时的清理。Capability 必须通过统一请求接口:
|
||||
|
||||
```ts
|
||||
interface CapabilityRequestAPI {
|
||||
request<T extends JsonValue>(request: {
|
||||
capability: CapabilityName;
|
||||
purpose: string;
|
||||
input: JsonValue;
|
||||
scope?: AppScope;
|
||||
}): Promise<CapabilityResult<T>>;
|
||||
}
|
||||
```
|
||||
|
||||
Runtime 检查 Manifest 声明、App 启用状态、Host 支持、前台要求、用户确认、系统权限、策略、限流与审计;App 只能获得受控结果。
|
||||
|
||||
## 9. 信任分层与 Surface Bridge SDK
|
||||
|
||||
| 层级 | 例子 | 可用接口 | 明确禁止 |
|
||||
|---|---|---|---|
|
||||
| Core App | Chat、App Registry、Settings | 完整 App Runtime SDK(仍受 Scope 和 Capability 约束) | 直接绕过 Transport/Capability Gateway。 |
|
||||
| Installed App | 白板、语音、游戏 | 受限 Surface Bridge SDK:状态、已声明 Tool、事件、隔离存储、Capability request | Tauri invoke、父 DOM、token、任意网络、其他 App 数据。 |
|
||||
| Host Provider | Tauri Desktop / Web Reference Host 实现 | Runtime 内部 Host Provider API | 向 Runtime 提供系统能力;不直接暴露给 Agent/Surface。 |
|
||||
|
||||
下载型 App 使用 `iframe sandbox="allow-scripts"` 或等价隔离容器;不包含 `allow-same-origin`。它经严格 CSP、来源验证和消息 schema 校验的桥接访问 Surface SDK。第一版不支持市场下载包执行本地 Rust、Node、Shell 或任意浏览器特权代码。
|
||||
|
||||
## 10. App Manifest 的 Runtime/SDK 声明
|
||||
|
||||
每个 App Manifest 除 Bundle、签名和 Surface 外,还应声明 Runtime SDK 与消息/工具契约。当前阶段只使用本地 `app_scope`,不在 Manifest 中固化供应商身份或全局命名空间:
|
||||
|
||||
```json
|
||||
{
|
||||
"format": "lineup.app.v1",
|
||||
"app_scope": "whiteboard",
|
||||
"version": "1.2.0",
|
||||
"runtime_sdk": {
|
||||
"api_version": "1",
|
||||
"required_features": ["app.lifecycle.v1", "app.tools.v1", "surface.state.v1"],
|
||||
"optional_features": ["artifact.create.v1"]
|
||||
},
|
||||
"entrypoints": [
|
||||
{"id": "canvas", "kind": "contextual", "default_eligible": false}
|
||||
],
|
||||
"agent_subscriptions": [
|
||||
{"type": "lineup.app.state.patch", "scope": "instance"},
|
||||
{"type": "lineup.tool.invoke", "scope": "conversation"}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"method": "board.create",
|
||||
"contract_version": "1",
|
||||
"invocation": {"kind": "command", "activation": "launch_if_needed"},
|
||||
"input_schema": {"type": "object", "additionalProperties": false},
|
||||
"output_schema": {"type": "object", "additionalProperties": false}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Runtime 在安装、启用和启动时进行 SDK 版本及 feature 协商;不兼容 App 不进入可用 Inventory,也不得启动。
|
||||
|
||||
## 11. Runtime 状态与恢复
|
||||
|
||||
顶层 Runtime State 至少包含:
|
||||
|
||||
```text
|
||||
identity 用户、设备、认证会话
|
||||
connection AppServer 状态、cursor、重试、outbox
|
||||
conversations Agent 映射、消息引用、任务、交互、Artifact 元数据
|
||||
apps App Registry、默认 App、运行实例、App Inbox
|
||||
tools 已声明 Tool、可见性、调用与 operation
|
||||
capabilities 授权、执行状态、最小审计
|
||||
```
|
||||
|
||||
第一版采用“事件驱动状态机 + 必要快照”,而非完整 Event Sourcing:关键入站事件、用户决定、Tool 结果、outbox 和审计记录持久化;渲染细节和短暂 UI 状态不必全部写入事件日志。
|
||||
|
||||
Runtime 生命周期:
|
||||
|
||||
```text
|
||||
created → restoring → authenticating → synchronizing → online
|
||||
↘ reconnecting / offline_degraded
|
||||
online / offline_degraded → stopping → stopped
|
||||
```
|
||||
|
||||
启动时恢复 App Registry、默认入口、会话 cursor、outbox、App Inbox、未完成 Tool 和可恢复实例;追平历史后再开始实时投递。离线时用户/App Action 进入 outbox,重连后按幂等键和顺序处理。
|
||||
|
||||
## 12. 推荐工程边界
|
||||
|
||||
```text
|
||||
lineup-app/
|
||||
├── runtime-core/
|
||||
│ ├── communication/ Transport、sync、outbox
|
||||
│ ├── coordination/ Event、state、router、policy、focus、audit
|
||||
│ ├── apps/ App Manager、Registry、Instance、Focus、Lifecycle Manager
|
||||
│ ├── tools/ Tool Registry、Inventory、Router、call lifecycle
|
||||
│ ├── capabilities/ Capability Gateway
|
||||
│ └── persistence/ Runtime Store、App Inbox、Artifact metadata
|
||||
├── runtime-sdk/
|
||||
│ ├── app.ts Core App SDK 类型
|
||||
│ ├── manifest.ts App/Tool/Subscription Manifest 类型
|
||||
│ ├── protocol.ts Runtime Envelope 与 schema
|
||||
│ ├── errors.ts 稳定错误码
|
||||
│ └── testkit/
|
||||
├── surface-sdk/
|
||||
│ ├── bridge.ts sandbox postMessage bridge
|
||||
│ └── surface.ts Installed App 最小接口
|
||||
└── tauri/
|
||||
├── src/
|
||||
│ ├── bootstrap/ Tauri/Web 的应用启动装配层
|
||||
│ ├── runtime-host/ Runtime Host Provider 适配
|
||||
│ ├── core-apps/ chat、app-registry、settings
|
||||
│ └── shell/ app switcher、default app、recovery
|
||||
└── src-tauri/ 文件、音频、通知、窗口等 Rust Provider
|
||||
```
|
||||
|
||||
当前 `lineup-app/tauri/src/main.ts` 同时承担 Runtime、Chat、Host Adapter 和开发 Fixture 的职责,是拆分的主要对象。现有 `InteractionKernel`、Transport、Conversation Store、Surface Registry、Instance Manager、Bundle Cache、Capability Registry 与 Inventory Publisher 可作为上述 Runtime Core 的迁移基础。
|
||||
|
||||
## 13. 第一阶段落地顺序与验收
|
||||
|
||||
### R0:冻结 Runtime/SDK 合约
|
||||
|
||||
- 固化 `RuntimeEnvelope`、app scope / conversation ID / instance ID 规则;
|
||||
- 固化 Event / Command / Effect 边界;
|
||||
- 定义 `LineUpAppRuntimeSDK`、`Surface Bridge SDK` 与 Manifest TypeScript 类型;
|
||||
- 为 Envelope、App Manifest、Tool Descriptor、SDK 错误码准备 golden fixtures。
|
||||
|
||||
### R1:建立 Runtime Core 和 Interaction Core App(当前 `chat` 实现)
|
||||
|
||||
- 将 Runtime 的创建与装配集中在单一启动入口,不让 `main.ts` 变成通信和业务逻辑的堆放处;
|
||||
- Runtime 独占 Transport、Store、outbox 和入站筛选;
|
||||
- 将当前图文 IM 重构为 Interaction Core App 的 `chat` 实现;
|
||||
- 当前 `chat` 通过 `agentMessages.subscribe()` 获取已验证的 Agent 消息,不再直接依赖 Transport;
|
||||
- 保持当前登录、同步、本地回显、Markdown、Tool Call、Task 与 Artifact 回归行为。
|
||||
|
||||
### R2:App Registry Core App
|
||||
|
||||
- 将现有开发期 `SurfaceRegistry` 升级为 Runtime App Registry;
|
||||
- 实现 Core App 记录、Installed App 记录、enable/disable/remove 和默认 App 选择;
|
||||
- 将 Registry UI 实现为 `app-registry`,仅调用 `RuntimeAppManager`;
|
||||
- App 状态变化后正确更新 Runtime Inventory。
|
||||
|
||||
### R3:Tool Registry 与 App SDK Inbox
|
||||
|
||||
- 实现 Manifest Tool / Subscription 解析与可见性筛选;
|
||||
- 将动态 Inventory 同步给 Agent;
|
||||
- 实现 Agent `tool.invoke` → Runtime → App SDK → `tool.result` 的可靠闭环;
|
||||
- App 未运行时持久化 Inbox,恢复后可幂等补读。
|
||||
|
||||
### R4:第一个 Installed App 闭环
|
||||
|
||||
- 接入一个已签名的画板或任务面板 App;
|
||||
- 验收 install → enable → inventory 更新 → Agent 调用 Tool → App 启动 → Surface 事件 → Agent result → disable/remove;
|
||||
- 后续语音 App 复用同一 SDK、Tool、Capability 与 Artifact 通道,而不是再实现独立通信链路。
|
||||
|
||||
硬性验收:
|
||||
|
||||
```text
|
||||
1. 缺少或非法 app_scope / conversation_id 的可路由消息被 Runtime 拒绝;
|
||||
2. App 不会收到其他 App 或其他 conversation 的 Agent 原始消息;
|
||||
3. App 被禁用后,其 Tool 从 Inventory 移除且新调用被拒绝;
|
||||
4. 断线/重启后 App Inbox、outbox、Tool 调用和实例状态能有界恢复;
|
||||
5. 下载型 App 无法访问 Tauri invoke、Host DOM、token 或未授予 Capability;
|
||||
6. 默认 App 不可用时 Runtime 自动进入 chat Recovery App;
|
||||
7. 所有 Tool 调用、权限拒绝与终态结果均可按 app_scope、conversation_id、call_id 审计和测试。
|
||||
```
|
||||
|
||||
## 14. 与已有正式方案的关系
|
||||
|
||||
- 本文将 [App 层架构方案](lineup-app-layer-architecture.md) 中的 Interaction Runtime 从“聊天 Host 内部模块”收敛为整个 App 的 Runtime,并补充了 App Manager、App Tool Registry、SDK 和消息路由模型。
|
||||
- 本文不废弃 [UI Surface 与 App Capability 协议](lineup-ui-surface-protocol.md) 中已完成的 Surface 隔离、Capability 确认、生产 Bundle 验签和动态 inventory 原则;后续应将其协议字段迁移/扩展为本文定义的 Runtime Envelope、App Manifest 和 Tool Registry,而不能引入绕开 Runtime 的平行通道。
|
||||
- 现有 M2/M4 文档和实现使用的 `app id` 是当前 Surface 协议的实现字段。本方案在 Runtime 重构阶段以 `app_scope` 作为本地路由字段;供应商身份和全局命名空间将在后续生态阶段通过独立、版本化的兼容设计重新引入。
|
||||
- 当前 M0~M4 实现可作为 Runtime 的迁移基础,但其现有文件边界不是最终 Runtime/App/SDK 边界。
|
||||
@@ -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 客户端。
|
||||
@@ -0,0 +1,37 @@
|
||||
# 架构设计主目录
|
||||
|
||||
`02.架构设计/` 现在是 LineUp 项目的统一设计主目录。
|
||||
|
||||
截至 **2026-08-07**:
|
||||
|
||||
- 原 `lineup-app/设计/` 中的当前有效设计文档,已经整体迁入本目录;
|
||||
- 原 `agent_ops/设计/` 中的前期分析、阶段记录和正式方案快照,已经迁入本目录的历史归档区;
|
||||
- 后续关于架构、协议、Runtime、MiniApp、Surface、Capability 和 Agent Tool 的讨论,都应以这里为主入口。
|
||||
|
||||
## 当前结构
|
||||
|
||||
```text
|
||||
02.架构设计/
|
||||
├── README.md
|
||||
├── 00.整合说明/
|
||||
│ ├── 01.设计整合说明与优先级.md
|
||||
│ └── 02.设计冲突与演进顺序.md
|
||||
├── 01.当前有效设计/
|
||||
│ ├── README.md
|
||||
│ ├── APP架构设计.md
|
||||
│ └── 02.正式方案/
|
||||
├── 02.整合结论/
|
||||
│ ├── 01.当前权威架构基线.md
|
||||
│ └── 02.Agent工具与运行时边界.md
|
||||
└── 90.历史设计归档/
|
||||
├── 00.阶段记录/
|
||||
├── 01.前期分析与设计/
|
||||
└── 02.正式方案快照/
|
||||
```
|
||||
|
||||
## 使用方式
|
||||
|
||||
- 想看当前设计正文,进入 `01.当前有效设计/`
|
||||
- 想看整合后的主结论,进入 `02.整合结论/`
|
||||
- 想看冲突判断和演进顺序,进入 `00.整合说明/`
|
||||
- 想追溯早期方案,进入 `90.历史设计归档/`
|
||||
Reference in New Issue
Block a user