869 lines
39 KiB
Markdown
869 lines
39 KiB
Markdown
# LineUp App 最终设计方案
|
||
|
||
**版本:** 1.0(当前开发基线)
|
||
**状态:** 当前 App 设计的唯一汇总入口
|
||
**日期:** 2026-08-06
|
||
**来源:** [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、instance 级业务状态快照、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 与按 instance 隔离的业务状态快照。
|
||
|
||
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 instanceState: MiniAppInstanceStateAPI;
|
||
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 instance 业务状态、Artifact 与 Capability
|
||
|
||
Runtime 为每个 MiniApp instance 提供一份受限的业务状态快照。MiniApp 定义状态的业务语义和
|
||
Manifest schema;Runtime 是持久化、隔离、并发裁决、恢复和清理的唯一所有者。它不是供 App 任意
|
||
查询或读写的数据库,也不等同于 Conversation Store、Tool/operation Store、outbox 或 Artifact Store。
|
||
|
||
```ts
|
||
interface MiniAppInstanceStateAPI<State extends JsonObject = JsonObject> {
|
||
get(): Promise<{
|
||
state: State | null;
|
||
revision: number;
|
||
}>;
|
||
|
||
replace(request: {
|
||
state: State;
|
||
expected_revision: number;
|
||
}): Promise<{
|
||
revision: number;
|
||
}>;
|
||
}
|
||
```
|
||
|
||
每次调用的存储边界由不可伪造的 SDK context 推导:
|
||
|
||
```text
|
||
app_scope + instance_id
|
||
```
|
||
|
||
App 不得指定、读取或写入其他 App、其他 instance、Conversation Store 或 Runtime 内部记录。Runtime
|
||
必须校验 Manifest 声明的 `state_schema`、`state_schema_version`、大小配额与 `expected_revision`;旧
|
||
Surface、重复点击或并发写入不能覆盖较新的快照。刷新、Surface 重载和 Runtime 重启后,Runtime 向
|
||
同一个可恢复 instance 投影最后一个有效快照。实例关闭、App 禁用/移除时,Runtime 按 Manifest 的
|
||
retention policy 冻结、保留或清理快照,MiniApp 本身无权绕过生命周期复活旧状态。
|
||
|
||
第一版的 `retention` 只允许 `delete_on_close`(默认,关闭时删除)和 `retain_readonly`(关闭后
|
||
保留为历史/新 instance 的受控上下文,但旧 instance 不可再写入)。App 禁用或移除时,Runtime 仍按
|
||
用户确认和全局保留策略清理其快照;状态 schema 升级由新 App 版本通过受控读取/替换完成,Runtime 不执行
|
||
任意 App 提供的迁移脚本。
|
||
|
||
业务状态快照不替代 Runtime operation。凡是会影响 Agent Tool 终态、deadline、outbox、焦点、权限或
|
||
实例生命周期的动作,仍必须通过 Runtime Command / operation 原子裁决。例如 Pomodoro 的显示状态
|
||
可以保存在自己的 instance snapshot,但 `ends_at` 到期、用户中断与唯一 Tool result 必须由 Runtime
|
||
的 deadline operation 负责,不能由 MiniApp 自行完成或直接发送结果。
|
||
|
||
```text
|
||
Artifact
|
||
Runtime 管理元数据、受限读取、创建、会话引用、下载和用户保存。
|
||
|
||
Instance State
|
||
Runtime 为每个 app_scope + instance_id 提供隔离的 JSON snapshot;管理 schema、revision、配额、
|
||
恢复与生命周期清理。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
|
||
```
|
||
|
||
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 调用、通过 Bridge 使用 Runtime 提供的自身 instance 状态快照和请求受控 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",
|
||
"app.instance-state.v1"
|
||
],
|
||
"optional_features": ["artifact.create.v1"]
|
||
},
|
||
"entrypoints": [
|
||
{"id": "canvas", "kind": "contextual", "default_eligible": false}
|
||
],
|
||
"instance_state": {
|
||
"state_schema_version": 1,
|
||
"state_schema": {"type": "object", "additionalProperties": false},
|
||
"max_bytes": 65536,
|
||
"retention": "delete_on_close"
|
||
},
|
||
"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、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.instance-state.v1`:按 `app_scope + instance_id` 隔离的 schema 校验状态快照、revision
|
||
并发控制、配额、重启恢复与 lifecycle 清理;它不得替代 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 只能读取和替换自身 instance 的 schema 合法状态快照;旧 revision、越界 instance、超配额
|
||
和非法 schema 均被 Runtime 拒绝,刷新/重启后仅恢复最后一个有效版本。
|
||
[ ] 业务状态快照不能直接完成 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 的平行通道。
|