Files

786 lines
38 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 AppIM / 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 activationRuntime 在写入 `starting → ready | failed | cancelled` 后,通过同一 call_id 的受控
protocol receipt / progress 通知 Agent。`activation_ready` 表示 App 已能接收后续 `app_ready` ToolAgent 可据此顺序
编排调用,但 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/CapabilityInventory、方法、参数、策略、前台条件
→ Scopeconversation、instance、operation 所属关系
→ SubscriptionApp Manifest 声明与 SDK 订阅交集
→ PersistRuntime Event / App Inbox / cursor
→ Deliver:投递给目标 App 的 SDK
```
Runtime 不对所有 App 广播原始消息。一个白板 App 即使与 Chat 位于同一 conversation,也只能收到其 Manifest 声明且 Runtime 授权的实例 patch、Tool 调用或事件。
## 8. Runtime SDK v1
### 8.1 双通道模型
```text
InboundRuntime → App
- 已验证 Agent 消息
- App 专属状态投影
- Tool 调用
- Runtime / App 生命周期
OutboundApp → 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 +
paramsSDK 再在 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 回归行为。
### R2App Registry Core App
- 将现有开发期 `SurfaceRegistry` 升级为 Runtime App Registry
- 实现 Core App 记录、Installed App 记录、enable/disable/remove 和默认 App 选择;
- 将 Registry UI 实现为 `app-registry`,仅调用 `RuntimeAppManager`
- App 状态变化后正确更新 Runtime Inventory。
### R3Tool 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` 作为本地路由字段;供应商身份和全局命名空间将在后续生态阶段通过独立、版本化的兼容设计重新引入。
- 当前 M0M4 实现可作为 Runtime 的迁移基础,但其现有文件边界不是最终 Runtime/App/SDK 边界。