42 KiB
LineUp App 最终设计方案
版本: 1.0(当前开发基线) 状态: 当前 App 设计的唯一汇总入口 日期: 2026-08-06 来源: App 层架构方案、Runtime 与 App SDK 架构方案、运行时与智能体工具、UI Surface 与 App Capability 协议
专题约束: 远端 Agent 的角色、Agent Tool 与本地 UI Action / Host 生命周期意图的入口边界,以 运行时与智能体工具 为准。
0. 一句话定义
LineUp 是运行在用户设备上的协作 Runtime,也就是整个产品的本地协调中心。
Runtime 负责连接 AppServer 和 Remote Agent,保存会话与消息,管理应用、工具、权限和 本地恢复。图文聊天、应用注册表、语音对话、画板和游戏则是在这个底座上运行的 App。 用户使用 App 完成操作,Agent 发来消息或请求;两边都先交给 Runtime 处理,因此任何 App 都不需要、也不能自己处理连接、登录态或系统权限。
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。
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 关键禁止项
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 分层。
Event:已经发生的事实
Agent 文本、任务进度、用户点击、画板变更、录音完成、连接断开。
Command:希望 Runtime 处理的动作
发送消息、调用 Tool、启动 App、启用 App、请求保存文件。
Effect:Runtime 决定执行的副作用
网络发送、文件选择、录音、通知、创建 Surface、写存储。
处理顺序固定为:
接收 Event / Command
→ 验证与授权
→ 更新 Runtime State
→ 持久化事实 / Outbox
→ 产生 Effect
→ Effect 结果重新进入 Runtime,成为 Event
→ 生成面向 App 的状态投影或消息投递
第一版采用“事件驱动状态机 + 必要快照”,不实施完整 Event Sourcing。关键入站事件、用户决定、Tool 终态、outbox 和审计需持久化;纯渲染细节、焦点和滚动位置不必写入 Runtime 事实日志。
3.2 顶层状态
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 生命周期
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 身份:
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 是安装和状态的唯一事实源:
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,负责应用切换、全局连接状态、通知和安全恢复。其启动目标优先级:
显式目标(深链接 / 通知)
→ 可安全恢复的前台实例
→ 用户设定的 Default App
→ chat Recovery App
chat 是当前图文 IM 主入口,也是不可移除的 Recovery App。未来用户可将 voice 设为默认入口;这只改变主要交互方式,不改变会话、Agent、outbox 或 Artifact 的归属。
4.4 App 实例生命周期
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 的结果投影回会话。
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 状态的一部分:
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 和会话模式。
一次“启动你画我猜”的完整流程是:
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:
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 事件同样保留作用域:
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 重构的规则是:
旧 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 筛选链
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 补读:
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>;
}
投递语义:
- Runtime 先校验、标准化和持久化,后调用订阅者;
- 每条消息有唯一
message_id,App 必须幂等处理; - 展示类消息可自动 ACK;Tool 调用、状态 patch 和需业务处理的消息要求 App 显式 ACK;
- App 未运行、暂停或崩溃时,消息进入该 App 的 Inbox;恢复后
list()与subscribe()补齐; - App 禁用、移除或不兼容时,Runtime 对关键 Agent 调用返回确定拒绝,不能静默丢弃。
实际投递集是:
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。
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、状态和生命周期
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 导航
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。
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 推导:
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 而改变。
Artifact
Runtime 管理元数据、受限读取、创建、会话引用、下载和用户保存。
Session Data
Runtime 为每个 app_scope + app_session_id 提供隔离的通用 JSON 数据;管理 revision、配额、恢复与
生命周期清理,不理解 MiniApp 的业务 schema。UI 动画、滚动位置等短暂渲染状态不必持久化。
Capability
App 以 purpose + input 请求;Runtime 决定是否确认、调用 Host、审计并返回受控结果。
Capability SDK 形状:
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。当前的完整定位是:
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、文件路径或用户私有内容。
Agent 可见 Tool
= 已验证 Manifest Tool
∩ App enabled
∩ Host supported
∩ 用户 / 组织策略允许
∩ Agent + conversation scope 允许
∩ 所需前置条件满足
App 启用、禁用、更新、撤销、Host 能力变化或策略收紧后,Runtime 增加 revision 并重新发布。Agent 的 Tool 调用必须携带 inventory_revision;旧 revision 的调用必须安全拒绝并提示 Agent 刷新清单。
7.3 Tool 调用闭环
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。
至少支持以下确定错误码:
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 运行在隔离容器中:
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 不能:
读取父页面 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 映射。
{
"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 参考实现的最终模块边界
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-registryCore 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. 完成准入条件
消息与隔离
[ ] 缺少/非法 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 层架构方案 | M0~M4 已实现边界、测试和验收事实;Markdown、Surface、Capability 原则 | 迁移基础与历史实施记录。 |
| Runtime 与 App SDK 架构方案 | Runtime、App Manager、Tool Registry、SDK、消息筛选的完整初稿 | 被本文件收敛后的详细来源;以本文件的 app_scope 和实施顺序为准。 |
| UI Surface 与 App Capability 协议 | Surface sandbox、bridge、Capability 风险分级、inventory 和 Bundle 安全规则 | 协议细节来源;R0 需完成其旧字段到 RuntimeEnvelope 的版本化映射。 |
以后新增 App 设计、SDK 方法、Agent Tool、Surface 或 Capability 时,先修改本文件的边界/契约,再实施代码和细节协议,避免重新把功能堆回聊天页面或创建绕开 Runtime 的平行通道。