初始化 agent_ops 文档治理体系

This commit is contained in:
2026-08-07 16:56:51 +08:00
commit 10840909ab
75 changed files with 15750 additions and 0 deletions
@@ -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 汇总为动态 InventoryAgent 只能调用当前 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、请求保存文件。
EffectRuntime 决定执行的副作用
网络发送、文件选择、录音、通知、创建 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 AppCore 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 实现与 M0M4 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 SDKInstalled 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 直接联系 AgentRuntime 持久化
`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 行为。
### F1Runtime 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 不读取旧协议字段,也不需要同步升级远端。
### F2App 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 和恢复策略。
### F3Tool Registry、Inventory 与 SDK Inbox
- 解析 Manifest Tool/Subscription 并计算可见性;
- 在 Runtime 连接与状态变化时同步 revisioned Inventory 给 AgentTool 可声明是否需要启动 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.0Runtime 基线)
**状态:** 当前实现边界与历史迁移记录
**日期:** 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-R1Runtime 托管 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. 历史迁移的保留价值
早期 M0M4 工作建立了当前 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 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 边界。
@@ -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 使用解析器后仍须 sanitizerHTML Surface 只能放进 sandbox `srcdoc`
7. 生产环境应只接受已签名或在 Agent allowlist 中的 bundle hashReference 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-03Tauri Reference Host 已完成 M0 和 M1-01M1-03:严格 Envelope / Kernel / Store / Renderer 边界,Markdown、status/progress/error、choice、confirm、input 的可信原生渲染与本地状态恢复。它**尚未**实现 `client.inventory` 发送、应用中心、`ui.open / patch / close` 的 Surface Host、远端或本地 app bundle 加载、`app.call` 执行、Capability Registry 或 `app.result` 网络回传。
因此,这份协议中的 Surface、inventory 与 Capability 段落是后续 M2M4 的正式契约,不是当前 Web Chat 已开放的功能。M2 建立应用中心本地启用注册表与隔离 Surface;M3 才生成/更新并通知 `client.inventory`,再实施 Capability policy;M4 负责下载、完整性校验、缓存、回滚与 Host 一致性。当前只以 Tauri Desktop Host 与同代码 Web Reference Host 实现这些边界;不规划独立 Android/iOS/Wails 客户端。
@@ -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,而不是第二份手写 ManifestMiniApp 用 `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 上下文)
│ ├── activationstarting / 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 operationPomodoro 进入 focusing 并被动显示
用户在 IM 中说:停止或结束这次专注
→ 远端 Agent 调用 pomodoro.interrupt
→ 若仍在 startingRuntime 取消启动;若已 focusingRuntime 原子收口当前 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 ActionRuntime 可以让它复用创建 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 Providernotice / choice / confirm / input
```
当前实现仍保留兼容名称:
```text
产品概念:Interact MiniApp
当前 app_scopechat
当前目录: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 中声明 ToolAgent 只能调用当前 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 设计的主入口。