feat(runtime): establish miniapp kernel and sdk design

This commit is contained in:
2026-08-05 00:46:16 +08:00
parent d8aa087bc8
commit 9fb8cd1598
86 changed files with 4615 additions and 1364 deletions
+124
View File
@@ -0,0 +1,124 @@
# LineUp App 迭代基线:Runtime 托管 Chat
**迭代编号:** 00.base
**状态:** 已实现、已验证
**日期:** 2026-08-04
**定位:** 后续迭代必须保持的客户端基础能力
## 1. 基线说明
本迭代建立了 LineUp Runtime 承载第一个 Core App 的最小闭环。当前实现中的 `chat`
图文 IM 形态的应用;在后续设计中,它将演进为 `Interaction App` 的 IM 模式,但现阶段
仍保留 `app_scope = "chat"` 作为代码和协议兼容名称。
当前客户端基线是:
```text
Tauri 2 Desktop Host + Web Reference Host
```
两种 Host 运行同一份 TypeScript Runtime 和 Core AppTauri 用于正式桌面交付,Web 用于
浏览器开发、Tailscale 联调和自动化验证。它们不是两套客户端,也不各自实现通信、同步或
消息存储。
## 2. 已实现的 Runtime 边界
`LineUpRuntime` 是以下对象的唯一所有者:
```text
Transport
Conversation Store
sync loop
outbox
App Inbox
作用域筛选
旧 lineup.v1 兼容
去重与恢复
```
Chat 不能直接访问 AppServer、Transport、Store、登录态或 Host 特权能力,只能使用
`ChatRuntimeSDK`
```text
Runtime
→ 校验、持久化和筛选 Agent 消息
→ 投递 app_scope + conversation_id 匹配的消息
→ 通过 SDK 提供订阅、Inbox、ACK 和 Runtime Action
→ 接收 Chat 的用户动作并进入 outbox
```
## 3. 已实现的应用加载关系
当前已经将“默认应用选择”和“应用具体挂载”分开:
```text
CoreAppRegistry
→ 选择默认 app_scope = chat
CoreAppHostRegistry
→ 找到 chat 对应的 Host 装配器
chat-app-host.ts
→ 加载 Chat Shell、样式、Renderer、DOM Context 和 SDK
```
`main.ts` 现在只负责启动 Runtime、创建 Host 绑定和请求挂载默认 App,不再直接导入
Chat 的样式、Renderer 或 Shell。
相关实现:
- `tauri/src/runtime/coordination/lineup-runtime.ts`
- `tauri/src/runtime/app-management/app-registry.ts`
- `tauri/src/runtime/app-management/runtime-app-host.ts`
- `tauri/src/core-apps/chat/chat-app-host.ts`
- `tauri/src/main.ts`
## 4. 已实现的消息和交互能力
当前基线已经覆盖:
- 登录、退出和会话恢复;
- HTTP 增量同步与包含式 cursor
- 用户消息本地回显、发送状态和 outbox;
- Agent Markdown 消息和安全 DOM 渲染;
- Agent status、progress、error 和执行摘要;
- `choice``confirm``input` Tool Call 的状态机和可信交互卡片;
- Tool Result / Tool Cancel 回声;
- Surface 生命周期的基础管理;
- Capability Registry、确认、受限 Host 执行和审计;
- Artifact 元数据校验与 Host 临时内容缓存;
- App Inbox 的持久化、恢复、ACK 和 `message_id` 去重。
所有入站内容必须先经过 Runtime 协议和作用域校验。非法或不匹配的消息不得进入 Chat,
未知内容只能安全降级,不能执行远端脚本或系统能力。
## 5. 基线验证
```text
npm test -- --run
21 个测试文件通过
94 个测试通过
npm run build
TypeScript 检查通过
Vite production build 通过
```
## 6. 本基线的边界
以下能力尚未作为本迭代的完整实现:
```text
动态 App Registry
App 安装、更新、回滚和移除
App Instance Manager
App Focus Manager
Runtime Tool Router
Interaction App 的 IM / Audio / Video 模式统一运行时
扩展 App 的前后台切换和恢复
完整应用市场
```
这些内容属于 [01.kernel.md](01.kernel.md) 定义的下一阶段目标。`00.base` 的意义是保护
已经完成的通信、可靠投递、作用域和安全边界,后续重构不得让这些能力回到 App 或
`main.ts` 中。
+293
View File
@@ -0,0 +1,293 @@
# LineUp App 迭代目标:Runtime Kernel 与应用编排
**迭代编号:** 01.kernel
**状态:** 目标设计,待实现
**日期:** 2026-08-04
**前置基线:** [00.base.md](00.base.md)
## 1. 迭代目标
本迭代要把 LineUp 从“Runtime 加载一个 Chat 页面”推进为“Runtime 管理一个应用工作区”。
Runtime 不仅负责网络、消息和存储,还要负责应用实例、工具路由、前后台焦点和恢复;
`Interaction App` 则负责用户与 Agent 的核心交互体验。
目标结构:
```text
LineUp Runtime
├── Runtime Shell / Desktop
│ ├── 应用启动
│ ├── 应用切换
│ ├── 前后台与焦点
│ ├── 通知和安全恢复
│ └── Host 挂载协调
├── Interaction AppCore App
│ ├── IM Mode:文字、图片、短消息
│ ├── Audio Mode:实时语音
│ ├── Video Mode:实时视频
│ └── 标准交互原语:choice / confirm / input
└── Installed / Extension Apps
├── Whiteboard
├── Draw-and-Guess
├── Task Dashboard
└── 其他可安装应用
```
当前代码中的 `chat` 继续作为兼容实现名称,产品概念上将其定位为:
```text
Interaction App 的 IM 实现
```
后续是否将目录和作用域从 `chat` 正式迁移到 `interaction`,另行作为命名迁移任务处理,
不与本迭代的运行时编排重构混在一起。
## 2. 核心职责分工
### 2.1 LineUp Runtime
Runtime 是整个应用工作区的底层协调中心,拥有最终调度和安全决策权:
- 管理 App Registry、App Instance 和焦点栈;
- 校验 Agent Tool Call、Manifest、Inventory、参数和作用域;
- 决定调用应直接执行、启动 App、切换前台、等待用户交互还是创建异步任务;
- 维护 App 的启动、运行、后台、挂起、恢复、关闭和失败状态;
- 管理 Conversation、Store、App Inbox、outbox、权限和审计;
- 在扩展 App 结束或启动失败时恢复原来的前台实例。
### 2.2 Runtime Shell / Desktop
Runtime Shell 是一个拥有特殊权限的内置工作区。它在产品体验上类似桌面或 Launcher,
但最终的生命周期和安全决策仍由 Runtime Core 管理。
它负责:
- 把 App 实例挂载到 Tauri 或 Web Host
- 显示当前前台 App
- 管理应用切换、恢复和关闭;
- 提供全局连接状态、通知和安全恢复入口;
- 向 App 传递受限的 Host 能力。
普通 App 不能伪造 Runtime Shell,也不能直接修改焦点栈。
### 2.3 Interaction App
Interaction App 是默认的人与 Agent 交互应用,但不是整个应用生态的调度器。
它负责:
- 当前 Conversation 的主要交互体验;
- IM、Audio、Video 等交互模式;
- 选择框、确认框、输入框和进度卡片等标准交互原语;
- 显示普通 Agent 消息、任务、Tool 结果和扩展 App 的结果;
- 通过 SDK 提交用户动作。
它不负责:
- 直接连接 AppServer 或 Agent
- 决定其他 App 是否启动;
- 管理其他 App 的前后台状态;
- 直接执行未经 Runtime 授权的 Tool 或 Capability。
### 2.4 Extension App
扩展 App 与 Interaction App 是 Runtime 上的平级应用。画板、你画我猜和任务面板拥有
自己的页面、状态、Tool、Surface 和实例生命周期,但必须使用 Runtime SDK。
扩展 App 可以请求:
```text
启动自己
创建 Surface
进入前台
进入后台
提交结果
请求关闭
```
最终是否允许、如何持久化、是否需要用户确认,由 Runtime 决定。
## 3. 应用实例与焦点模型
App Registry 管理“有哪些应用”,App Instance Manager 管理“哪些应用正在运行”。两者
不能混为一个状态。
```ts
type AppInstanceRecord = {
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 需要保存当前会话的焦点栈:
```text
focus_stack:
interaction:audio-001
draw-and-guess:game-001
foreground:
draw-and-guess:game-001
background:
interaction:audio-001
```
应用切换不会自动删除原实例。原实例可能进入 `background``suspended`,在新应用关闭、
失败或用户返回时恢复。
## 4. Tool 调度模型
Tool 调度权在 Runtime,不在 Interaction App。
```text
Agent Tool Call
→ Runtime 校验 Envelope / Inventory / Manifest / 参数 / Scope
→ Tool Router 判断处理方式
→ App Orchestrator 创建或切换 App Instance
→ Focus Manager 调整前后台
→ Interaction App / Mode / Extension App 承接
→ App 通过 SDK 返回 progress / result / error
→ Runtime 校验、持久化、审计并回传 Agent
```
Tool 的处理方式至少包括:
```text
direct 直接由 Runtime 或受信 App 处理
interactive 交给 Interaction App 等待用户选择/确认/输入
launch 启动或唤醒一个扩展 App
foreground 要求目标 App 进入前台
operation 创建长时间运行的异步任务
```
Interact 可以请求 Runtime 启动扩展 App,但不能直接加载 Bundle、切换其他 App 或执行
系统能力。
## 5. 典型场景:语音切换到你画我猜
开始时:
```text
Runtime Shell
└── Interaction App
└── Audio Modeforeground
```
用户说:“我们来玩一局你画我猜吧。” Agent 发来启动请求后:
```text
Agent launch(draw-and-guess)
→ Runtime 检查 App 是否已安装、启用和兼容
→ 校验 Tool、Manifest、Inventory、参数和权限
→ 创建 draw-and-guess:game-001
→ 保存 interaction:audio-001 的焦点位置
→ Audio Mode 进入 background / suspended
→ Draw-and-Guess 进入 starting → foreground
```
游戏完成后:
```text
Draw-and-Guess App
→ 通过 SDK 提交 game.result
→ Runtime 持久化并回传 Agent
→ 游戏实例关闭或挂起
→ Runtime 恢复焦点栈中的 Audio Mode
→ Interaction App 显示游戏结果
```
游戏和原来的语音交互使用同一个 `conversation_id`,但拥有不同的 `instance_id`。这样既
能把结果归还给同一个 Agent 会话,也能独立管理每个应用实例。
## 6. 标准交互原语与扩展 App 的边界
以下内容属于 Interaction App 的内建能力,不需要安装独立 App:
```text
choice
confirm
input
progress
error
```
例如 Agent 请求选择框:
```text
Agent tool.call(choice)
→ Runtime 校验并持久化 pending Tool Call
→ Interaction App 在 IM 中显示 Choice Card
→ 用户选择
→ Runtime 校验 call_id 并写入 outbox
→ Choice Card 变为 submitted / completed,不再可操作
→ IM 时间线显示“你选择了……”
```
选择框可以从界面上退出可操作状态,但交互记录不能从 Store 中删除。这样才能支持刷新
恢复、Agent 回声、幂等去重和审计。
画板、游戏和复杂任务面板则属于可安装扩展 App,拥有自己的 Tool 和 Surface。
## 7. 目标模块
```text
runtime/app-management/
├── app-registry.ts
├── app-instance-manager.ts
├── app-focus-manager.ts
├── app-lifecycle-manager.ts
└── runtime-app-host.ts
runtime/coordination/
├── tool-router.ts
├── app-orchestrator.ts
└── interaction-orchestrator.ts
core-apps/interaction/(当前由 core-apps/chat 兼容实现)
├── interaction-runtime.ts
├── interaction-shell.ts
├── interaction-mode-registry.ts
├── modes/im/
├── modes/audio/
├── modes/video/
└── primitives/
installed-apps/
├── whiteboard/
├── draw-and-guess/
└── task-dashboard/
```
`RuntimeAppHost` 负责实际挂载和卸载;App Instance、Focus、Lifecycle 和 Tool Router 负责
状态和决策。这样不会把所有业务规则重新堆回 `main.ts`
## 8. 本迭代验收目标
```text
1. Runtime 能从 App Registry 选择默认 Interaction App。
2. Runtime 能创建、前台化、后台化、挂起、恢复和关闭 App Instance。
3. Agent 启动扩展 App 时,当前前台 App 能安全进入后台。
4. 扩展 App 结束或失败后,Runtime 能恢复原来的焦点和交互模式。
5. Tool 调用必须经过 Runtime 的 Inventory、Manifest、参数、作用域和权限校验。
6. Interaction App 能处理 choice / confirm / input 等标准交互原语。
7. 扩展 App 只能通过 SDK 返回结果,不能直接访问 Agent、Host DOM 或系统特权。
8. 断线或重启后,App Instance、Tool Call、App Inbox、outbox 和焦点栈可以有界恢复。
9. 00.base 中已经通过的 21 个测试文件、94 个测试和生产构建不能退化。
```
+733
View File
@@ -0,0 +1,733 @@
# LineUp App 迭代定义:MiniApp SDK v1 与内置参考 MiniApp
**迭代编号:** 03.sdk_and_coreapp
**状态:** 目标设计,待实现
**日期:** 2026-08-04
**前置基线:** [00.base.md](00.base.md)、[01.kernel.md](01.kernel.md)
**权威架构:** [APP架构设计.md](../APP架构设计.md)
## 1. 迭代目标
本迭代不建设应用市场、服务端 Catalog、远程下载或第三方发布能力。它的目标是定义并实现
**LineUp MiniApp SDK v1**,再用多个随 LineUp 开发版本内置的参考 MiniApp 验证这份 SDK。
```text
LineUp App
→ LineUp Runtime
→ LineUp MiniApp SDK v1
→ Interact MiniApp
→ Task Dashboard MiniApp
→ Whiteboard MiniApp
```
本迭代完成后,应能证明:
```text
Runtime 能以同一套 SDK 语义承载系统级 MiniApp 与受限参考 MiniApp
MiniApp 只能通过 SDK 收消息、接 Tool、报告进度/结果/错误、请求生命周期与 Surface;
Runtime 始终拥有通信、Tool、焦点、存储、权限和审计的最终决定权。
```
这里的“内置”指 `bundled-development`MiniApp 的 Manifest、代码和测试 fixture 随当前
LineUp 开发 Host 提供。它们不是远程下载的 Bundle,也不等于已经开放的应用市场。
## 2. 本迭代的产品与信任模型
### 2.1 正确层级
```text
LineUp App 用户使用的主应用
├── App Shell / Host 挂载、切换、通知、恢复、Host Provider
├── LineUp Runtime 通信、可靠性、MiniApp 调度和安全决策
└── MiniApps 运行在 Runtime 上的功能单元
├── System MiniApp: Interact
├── Bundled Reference: Task Dashboard
└── Bundled Reference: Whiteboard
```
Runtime 不是一个与 MiniApp 平级的产品 App;它是 LineUp App 内部提供给 MiniApp 的运行环境。
### 2.2 Interact 的定位
`Interact` 是第一个系统级 MiniApp,是用户与 Agent 的默认入口:
```text
Interact
├── IM Mode:本迭代必须实现并适配 SDK
├── Audio Mode:只定义 SDK / 生命周期扩展位,不实现真实媒体能力
├── Video Mode:只定义 SDK / 生命周期扩展位,不实现真实媒体能力
└── IM 标准交互的默认 Renderer Providernotice / choice / confirm / input
```
当前实现仍保持兼容名称:
```text
产品概念:Interact MiniApp
当前 app_scopechat
当前目录:tauri/src/core-apps/chat/
```
本迭代不得把 `chat` 重命名为 `interact`,不得迁移目录或旧 `lineup.v1` scope;命名迁移另行
定义,避免破坏已验证的通信与恢复行为。
### 2.3 相同语义,不同权限视图
所有 MiniApp 都遵守 MiniApp SDK v1 的同一语义;系统级与参考/已安装 MiniApp 的区别是来源和
UI 容器,而不是是否可以绕过 Runtime。
| 能力 | Interact System MiniApp | Task Dashboard / Whiteboard |
|---|---:|---:|
| 读取自身 Inbox、ACK | 可以 | 可以 |
| 接收 Runtime Tool Call | 可以 | 可以 |
| 上报 progress / result / error | 可以 | 可以 |
| 请求前台、后台、关闭 | 可以,Runtime 决定 | 可以,Runtime 决定 |
| 请求 Surface / Capability | 可以,经 Policy | 可以,经 Policy |
| 使用可信内建 DOM | 可以 | Task Dashboard 可由受信 Host adapter 挂载;Whiteboard 必须走受限 Surface |
| 直接访问 Transport、Store、Agent | 不可以 | 不可以 |
| 直接访问 Tauri、Host DOM 根节点、任意网络 | 不可以 | 不可以 |
| 修改 Focus、Registry、其他 MiniApp 数据 | 不可以 | 不可以 |
## 3. MiniApp SDK v1 契约
SDK 是 Runtime 根据 Manifest、实例状态、scope 和 Policy 注入的受限对象。SDK 中的所有写操作都
**request**,不是对 Host、Store、Transport 或其他 MiniApp 的直接命令。
```ts
interface LineUpMiniAppSDK {
readonly context: MiniAppContext;
readonly inbox: MiniAppInboxAPI;
readonly tools: MiniAppToolAPI;
readonly ui: StandardInteractionAPI;
readonly lifecycle: MiniAppLifecycleAPI;
readonly surfaces: MiniAppSurfaceAPI;
readonly capabilities: MiniAppCapabilityRequestAPI;
}
type MiniAppContext = {
app_scope: string;
instance_id: string;
conversation_id: string;
app_version: string;
state: "starting" | "foreground" | "background" | "suspended";
};
```
`context` 由 Runtime 在创建实例时注入。MiniApp 不可传入、伪造或修改 `app_scope`
`instance_id``conversation_id`,也不可获得用户身份、Agent 身份、登录 token、AppServer
地址、Transport endpoint 或其他实例上下文。
### 3.1 Inbox API
```ts
interface MiniAppInboxAPI {
list(after_message_id?: string): readonly MiniAppMessage[];
subscribe(listener: (message: MiniAppMessage) => void): Unsubscribe;
acknowledge(message_id: string): boolean;
}
```
行为约束:
```text
Agent / Runtime 消息
→ Runtime 校验 Envelope、scope、目标 app_scope、instance 和去重
→ 先持久化到该 MiniApp Inbox
→ SDK 投递已筛选消息
→ MiniApp 成功接管后 ACK
→ Runtime 移除该可投递记录
```
后台化、刷新、Surface 重载、断线和 Runtime 重启不得丢失未 ACK 的 Inbox 消息。MiniApp 只能
列出和 ACK 自己 `app_scope + conversation_id + instance_id` 范围内的投递。
### 3.2 Tool API
```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>;
}
```
每一个调用都必须有 Runtime 持久化的通用记录:
```ts
type RuntimeToolCallRecord = {
call_id: string;
tool_id: string;
inventory_revision: string;
app_scope: string;
instance_id?: string;
conversation_id: string;
status:
| "received"
| "routing"
| "waiting_for_app"
| "running"
| "submitted"
| "completed"
| "failed"
| "cancelled"
| "expired"
| "rejected";
input: JsonObject;
result?: JsonObject;
error_code?: string;
created_at: string;
updated_at: string;
};
```
`complete``fail``cancel` 绝不能直接发送网络请求。Runtime 必须验证:
```text
call_id 属于当前 MiniApp instance
调用状态允许此迁移
结果符合 ToolDescriptor.output_schema
调用不是重复终态
调用仍属于当前 conversation / scope
MiniApp 和 Tool 仍处于启用状态
```
验证成功后,Runtime 持久化记录和审计,再写入可靠 outbox 并回传 Agent。
### 3.3 Lifecycle API
```ts
interface MiniAppLifecycleAPI {
requestForeground(): Promise<CommandReceipt>;
requestBackground(): Promise<CommandReceipt>;
requestClose(reason?: string): Promise<CommandReceipt>;
}
```
MiniApp 只能请求,不能直接改变焦点或挂载其他 MiniApp:
```text
MiniApp requestClose()
→ Runtime 检查 pending Tool、Surface、operation 和 Policy
→ 允许关闭或返回受控拒绝码
→ 若关闭成功,Runtime 调整 focus stack
→ Runtime 恢复前一有效 foreground instance
```
### 3.4 标准交互 API
SDK v1 通过 `sdk.ui` 提供由 **Runtime 管理的标准交互库**。这是一组低复杂度、可恢复、可审计的
标准输入输出,不是 Interact 专有 API,也不是 MiniApp 取得 Host 弹窗权限的旁路。Runtime 创建、
持久化和校验记录,并依据调用者、容器和 Shell Policy 选择实际呈现方式。
每次请求的 `owner` 必须由 Runtime 根据 SDK 已绑定的 `MiniAppContext` 自动注入;MiniApp 不能传入、
伪造或覆盖 `app_scope``instance_id``conversation_id``surface_id`。调用者可选提供
`parent_call_id`,但 Runtime 必须验证该调用属于当前实例且状态允许关联,不能将其作为可信 owner。
```ts
interface StandardInteractionAPI {
request(
request: StandardInteractionRequest,
options?: {
parent_call_id?: string;
presentation?: "default" | "inline" | "modal";
expires_at?: string;
},
): Promise<InteractionReceipt>;
get(interaction_id: string): StandardInteractionRecord | undefined;
subscribe(listener: (record: StandardInteractionRecord) => void): Unsubscribe;
cancel(interaction_id: string, reason?: string): Promise<CommandReceipt>;
}
type StandardInteractionRequest =
| NoticeRequest
| ChoiceRequest
| ConfirmRequest
| InputRequest;
```
第一版固定支持以下四种原语:
| 原语 | 用途 | 用户结果 |
|---|---|---|
| `notice` | 安全显示只读提示、状态或下一步说明。 | `acknowledged`;也可由 Runtime 记录为只读。 |
| `choice` | 单选、多选或有限按钮选择。 | 有序且去重的 `action_ids`。 |
| `confirm` | 明确同意/取消一个可解释操作。 | `approved: boolean`。 |
| `input` | 受 schema 限制的文本、多行文本或数字输入。 | 按字段 ID 返回的结构化值。 |
最低请求形态:
```ts
type NoticeRequest = {
kind: "notice";
title: string;
message: string;
acknowledge_label?: string;
};
type ChoiceRequest = {
kind: "choice";
title: string;
prompt: string;
mode: "single-choice" | "multi-choice" | "button";
actions: readonly { id: string; label: string; description?: string }[];
};
type ConfirmRequest = {
kind: "confirm";
title: string;
prompt: string;
approve_label?: string;
cancel_label?: string;
};
type InputRequest = {
kind: "input";
title: string;
prompt: string;
fields: readonly {
id: string;
label: string;
type: "text" | "textarea" | "number";
required?: boolean;
placeholder?: string;
min_length?: number;
max_length?: number;
minimum?: number;
maximum?: number;
}[];
submit_label?: string;
cancel_label?: string;
};
```
标准交互的 owner 路由与呈现策略如下:
```text
Task Dashboard / Whiteboard 正在处理自身工作
→ sdk.ui.request(choice / confirm / input / ..., { parent_call_id? })
→ Runtime 从当前 SDK Context 注入不可伪造的 owner,并校验可选 parent_call_id、schema 与 Policy
→ Runtime 创建持久化 StandardInteractionRecord
→ Runtime / Shell 根据 owner 的有效容器选择受批准的标准 Renderer
├── Agent 在 IM 中的提问:由 Interact 在时间线/卡片内呈现
└── 前台 MiniApp 的通用事务提示:在该 MiniApp 自己有效的容器/Surface 中呈现标准 modal、sheet 或 card
→ 用户选择、确认、输入、取消或超时
→ Runtime 持久化结果,仅投递回 owner MiniApp 的 Tool/Inbox
→ 原 MiniApp 决定 complete / fail / 继续 progress
```
`presentation` 只是调用偏好,不是强制指令;Runtime / Shell 可基于焦点、Surface、Policy 和可用
Renderer 降级或拒绝。Agent 发来的 `lineup.v1.tool.call(choice | confirm | input)` 保持兼容:Runtime
创建同一类记录,其 owner 是 Interact 的 IM 上下文(`source = agent_tool`),并由 Interact 作为
默认可信 IM Renderer Provider 呈现。它们不需要 MiniApp 提供父 Tool Call。
这意味着标准交互不会成为 MiniApp 直连用户界面或跨 App 操作的旁路:
```text
MiniApp 不能在 Host DOM 注入任意弹窗、表单或远端 HTML
MiniApp 不能伪造 owner、interaction_id,或替其他 owner 查询、提交、取消结果
Interact 不能直接把结果发送给 Agent
交互结果必须先回到 Runtime,再交给拥有该 owner 的 MiniApp
```
`notice` 是 SDK v1 新增的只读确认原语。Agent IM 与 MiniApp 发起的请求共享一套
`StandardInteractionRecord`、状态机、持久化、过期、去重、owner 路由和标准 Renderer 协议,
但不要求共享一个 Interact 卡片实例或切换到 Interact。
`StandardInteractionRecord` 至少包含以下 Runtime 内部字段;其中 `owner` 只可由 Runtime 写入:
```ts
type StandardInteractionOwner = {
app_scope: string;
instance_id: string;
conversation_id: string;
parent_call_id?: string;
surface_id?: string;
source: "agent_tool" | "miniapp_tool" | "runtime_policy";
};
type StandardInteractionRecord = {
interaction_id: string;
owner: StandardInteractionOwner;
kind: "notice" | "choice" | "confirm" | "input";
request: StandardInteractionRequest;
status: "pending" | "presented" | "submitted" | "completed" | "cancelled" | "expired" | "failed";
result?: StandardInteractionResult;
created_at: string;
updated_at: string;
expires_at?: string;
};
```
状态必须有界:
```text
pending → presented → submitted → completed | cancelled | expired | failed
```
终态不可重新操作;刷新、断线和重启后可恢复尚未终态的可信卡片与未提交的 `input` 草稿。
### 3.5 Surface 与 Capability API
SDK v1 定义请求语义,不直接授予 Host 权限:
```ts
interface MiniAppSurfaceAPI {
requestOpen(request: MiniAppSurfaceRequest): Promise<CommandReceipt>;
update(surface_id: string, patch: JsonObject): Promise<CommandReceipt>;
requestClose(surface_id: string): Promise<CommandReceipt>;
}
interface MiniAppCapabilityRequestAPI {
request(request: {
capability: string;
reason: string;
arguments: JsonObject;
}): Promise<CommandReceipt>;
}
```
Runtime 必须根据 Manifest、实例状态、Capability Policy、用户确认和 Host 支持情况决定是否
执行。SDK v1 不开放:
```text
fetch / 任意网络
任意文件系统
Tauri invoke
直接剪贴板、麦克风、摄像头
Host DOM 根节点
原始 AppServer / Agent 协议
```
本迭代可定义 Capability Request 的类型、拒绝码、审计和测试,但不必为参考 MiniApp 开放真实
麦克风、摄像头或文件选择权限。
## 4. Manifest、Tool Contract 与 Inventory
### 4.1 MiniApp Manifest v1
本迭代冻结最小 Manifest 契约:
```ts
type MiniAppManifest = {
app_scope: string;
version: string;
kind: "system" | "bundled-reference";
tools: readonly ToolDescriptor[];
subscriptions: readonly MiniAppSubscription[];
requested_capabilities: readonly string[];
host: {
min_version: string;
surface_required: boolean;
};
};
```
Manifest 是 Runtime 的输入,不是 MiniApp 可写状态;MiniApp 不可在运行时扩展 Tool、订阅或
权限。动态安装、下载、更新、回滚和移除不属于本迭代。
### 4.2 Tool Descriptor v1
```ts
type ToolDescriptor = {
id: string; // 例如 task-dashboard.open
version: 1;
handling: "direct" | "interactive" | "launch" | "foreground" | "operation";
target: {
app_scope: string;
requires_foreground: boolean;
restore_previous_focus: boolean;
};
input_schema: JsonSchema;
output_schema: JsonSchema;
permissions?: readonly string[];
timeout_ms?: number;
};
```
第一版 JSON Schema 只支持可明确实现和测试的子集:
```text
object / string / number / boolean / array
required
properties
additionalProperties = false
enum
minLength / maxLength
minimum / maximum
maxItems
```
禁止接受远端 schema 中的递归引用、正则执行、脚本、URL 加载、函数名或任意扩展关键字。
### 4.3 Revisioned Inventory
Runtime 在 MiniApp 的启用状态、Manifest Tool 或可见 Capability 发生变化时,生成最小化且带
revision 的 Inventory。Agent Tool Invoke 必须携带该 revision
```text
Agent Tool Invoke
→ Runtime 发现 inventory_revision 不匹配
→ 拒绝,不投递给 MiniApp,不执行任何 Host 行为
→ 以受控状态提示 Agent 使用最新 Inventory
```
本迭代可复用现有 `lineup.v1.client.inventory` 发送路径;AppServer 只需要像普通会话消息一样
透明转发,不需要实现应用目录或下载 API。
### 4.4 通用 Tool 路由
```text
Agent Tool Invoke
→ Runtime 校验 Envelope、Inventory revision、ToolDescriptor、输入 schema、scope、MiniApp 状态和权限
→ 创建 RuntimeToolCallRecord 与审计记录
→ Tool Router 判定 direct / interactive / launch / foreground / operation
→ App Orchestrator 创建或复用 instance,调整 focus
→ SDK Tool Inbox 投递
→ MiniApp 经 SDK 报告 progress / result / error
→ Runtime 校验 output schema,持久化,写 outbox,回传 Agent
```
`notice``choice``confirm``input` 必须映射为 Runtime 统一的 `interactive` Tool Call /
`StandardInteractionRecord`。Interact 负责 Agent/IM 情况下的默认可信渲染;其他 MiniApp 可在其
有效容器中使用 Runtime 批准的同一标准 Renderer,不能因此保留 Chat 专用旁路或获得 Host 特权。
建议稳定拒绝码:
```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
```
## 5. 内置参考 MiniApp 工作定义
本迭代通过三个内置 MiniApp 覆盖 SDK v1 的不同能力面。它们不是应用市场候选,也不要求完整
业务功能;每个 MiniApp 只实现足以验证 SDK 契约的最小真实闭环。
### 5.1 Interact:系统级 MiniApp 样本
**身份:** `kind = system`;当前兼容 `app_scope = chat`
**本迭代交付:** IM Mode 适配到 MiniApp SDK v1。
| 范围 | 工作定义 |
|---|---|
| Inbox | 使用通用 `sdk.inbox` 订阅、恢复和 ACK;不再依赖 Chat 特有投递语义。 |
| 用户消息 | 保留 `conversation.sendText` 这一系统级扩展,但它必须进入 Runtime Action / outbox。 |
| 标准原语 | 作为 Agent/IM 请求的默认可信 Renderer Provider,在时间线/卡片内呈现 Runtime 投递的标准交互记录。 |
| 交互边界 | 不拥有交互记录,也不直接向 Agent 回传结果;结果必须由 Runtime 按 owner 路由。其他 MiniApp 可在自身有效容器使用 Runtime 批准的标准 Renderer。 |
| Tool 结果 | 用户完成、取消或超时后调用 `sdk.tools.complete/fail/cancel`,由 Runtime 回传。 |
| Mode | 明确 IM 的 `mode = im` Context;仅定义 Audio/Video 入口和恢复语义,不启用真实媒体。 |
| 焦点 | 被参考 MiniApp 覆盖时接受 Runtime 的 background/suspended;不能自行切换回前台。 |
**不在范围:** 语音录制、视频通话、摄像头、独立 Audio/Video MiniApp、改名 `chat → interact`
### 5.2 Task DashboardTool 与生命周期样本
**身份:** `kind = bundled-reference``app_scope = task-dashboard`
**目标:** 验证 MiniApp Tool、progress/result/error、instance、focus 和恢复的最小闭环。
最小 Tool
```text
task-dashboard.open
handling = launch
输入:task_id、title、可选初始状态
输出:status = completed | cancelled | failed
task-dashboard.update
handling = operation
输入:task_id、进度或状态
输出:当前任务摘要
```
最小用户体验:
```text
Agent 调用 task-dashboard.open
→ Runtime 创建 task-dashboard instance
→ Interact 进入 background
→ Shell 挂载 Task Dashboard
→ MiniApp 展示任务标题、进度、当前状态和关闭动作
→ MiniApp 用 sdk.tools.reportProgress / complete / fail 上报
→ Runtime 持久化、回传 Agent、关闭实例并恢复 Interact
```
Task Dashboard 的业务状态必须通过 SDK Tool / Inbox 获得;不得自行访问 AppServer、Store 或
Agent。它可以使用随 LineUp 发布的受信 Host adapter,但 adapter 只做渲染装配,不可成为
Transport 或 Tool Router 的第二实现。
Task Dashboard 必须至少使用一次 `sdk.ui.request`:例如在关闭未完成任务前请求 `confirm`,或需要
任务说明时请求 `input`。Runtime 必须从该实例注入 owner,并在 Task Dashboard 当前有效容器中使用
受批准的标准 modal、sheet 或 card;结果经 Runtime 返回该实例,期间不得强制切换到 Interact。
### 5.3 Whiteboard:隔离 Surface 样本
**身份:** `kind = bundled-reference``app_scope = whiteboard`
**目标:** 验证 SDK Surface Bridge、隔离执行、状态 patch、Artifact 元数据与恢复。
最小 Tool
```text
whiteboard.open
handling = launch
输入:board_id、title、可选初始画布状态
输出:status、artifact_id(可选)
whiteboard.submit
handling = operation
输入:board_id、提交请求
输出:artifact_id 或结构化画布摘要
```
最小用户体验:
```text
Agent 调用 whiteboard.open
→ Runtime 校验已注册 bundled-reference Manifest
→ Runtime 创建 whiteboard instance 并请求受限 Surface
→ Host 挂载 opaque-origin iframe / Surface Bridge
→ Whiteboard 仅通过 Bridge SDK 请求状态更新和提交结果
→ Runtime 校验 patch / Artifact 元数据并持久化
→ App 完成或失败,Runtime 关闭 Surface、恢复 Interact
```
Whiteboard 不需要在本迭代实现多人协作、任意网络同步或完整绘图工具;重点是证明隔离 Surface
不能读取 Host DOM、Tauri、认证状态或其他 MiniApp 数据。
Whiteboard 如需要低复杂度的事务提示(例如“确认提交白板?”),可通过 `sdk.ui` 请求标准交互。
但画板内的文字编辑、画笔与颜色选择、拖放、工具栏、上下文菜单等高频或私有业务 UI 必须保留在
Whiteboard 自己的 Surface 中,不能被错误抽象为 Runtime 标准交互。
## 6. Runtime 与 Host 的实现模块
本迭代预计在现有目录中演进,不重建并行 Runtime:
```text
tauri/src/runtime/
├── app-management/
│ ├── miniapp-manifest.ts # Manifest v1、bundled-reference 注册
│ ├── miniapp-sdk.ts # SDK v1 公共类型与受限视图
│ ├── miniapp-tool-call-store.ts # 通用 Tool Call 持久化/恢复
│ └── runtime-app-host.ts # 按 foreground instance 挂载/卸载 Host
├── coordination/
│ ├── tool-router.ts # revision、schema、路由决策
│ ├── miniapp-tool-orchestrator.ts # Tool → instance → SDK Inbox
│ └── standard-interaction-service.ts # owner 注入、记录、路由、恢复与呈现 Policy
├── inventory/
│ └── client-inventory.ts # revisioned Tool Inventory
└── persistence/
└── conversation-store.ts # Tool Call、Inbox、workspace 有界恢复
tauri/src/
├── core-apps/chat/ # Interact IM 的兼容实现
│ └── standard-interaction-im-renderer.ts # Agent/IM 的默认标准交互 Renderer
├── core-apps/task-dashboard/ # bundled-reference Tool/lifecycle 样本
└── core-apps/whiteboard/ # bundled-reference Surface/Bridge 样本
```
若目录名称最终改为 `miniapps/`,应单独进行机械迁移;本迭代优先保证 Runtime 边界和 SDK
兼容,不能让命名迁移扩大风险。
## 7. 不在本迭代范围
以下内容必须明确排除:
```text
服务端 App Catalog 或应用清单 API
远程 Manifest / Bundle 下载
安装、更新、回滚、卸载 UI
第三方开发者发布、账号、审核、评分、支付或搜索
任意网络 API
真实麦克风、摄像头、文件选择或系统通知授权
多人白板、实时游戏、完整语音/视频通话
chat / interaction 命名和协议 scope 迁移
```
这些能力将在 MiniApp SDK 与参考 MiniApp 完成验证后,作为独立的应用分发和市场阶段推进。
## 8. 实施步骤
1. **冻结契约与 golden fixtures**
- 定义 `MiniAppManifest v1``ToolDescriptor v1``MiniAppContext`、通用 Tool Call、
Result/Progress/Error、Standard Interaction、SDK 稳定错误码;
- 为合法、重复、过期 revision、非法 schema、scope 不匹配和终态重复建立 fixture;
- 明确旧 `lineup.v1.tool.call``choice/confirm/input``interactive` Tool 的兼容映射,
并为 `notice`、owner 自动注入、MiniApp 发起的 choice/confirm/input、取消、超时和重启恢复建立 fixture。
2. **实现 Runtime 通用 Tool 闭环**
- 将 Inventory revision、输入/输出 schema、Tool Call Record、审计和 outbox 接入 Runtime
-`ToolRouter``AppOrchestrator` 根据 ToolDescriptor 创建/复用实例并投递 SDK Tool Call
- 完成 Tool、Inbox、workspace 的断线与重启恢复。
3. **适配 Interact IM**
-`sdk.inbox``sdk.tools` 替换 Chat SDK 中的专用交互旁路;
- 实现 Agent/IM 标准交互的默认 Renderer Provider,保持 Markdown、消息、notice、choice、confirm、input、Task 和现有回归行为;
- 固化 IM Mode Context 与被覆盖/恢复的生命周期语义。
4. **实现 Task Dashboard 参考 MiniApp**
- 注册 bundled-reference Manifest 和两个最小 Tool
- 实现启动、进度、结果、错误、关闭、回焦和重启恢复;
- 使用真实 SDK,不测试用 Runtime 内部对象直连。
5. **实现 Whiteboard 参考 MiniApp**
- 注册 bundled-reference Manifest、最小 Tool 和受限 Surface
- 验证 Bridge、patch、Artifact 元数据、关闭、失败和恢复;
- 验证隔离拒绝路径与 Capability Request 拒绝路径。
6. **端到端验收与文档回填**
- 运行完整单元测试和生产构建;
- 通过 Tauri/Web Reference Host 完成代表性浏览器验收;
- 将最终 SDK 形态与参考 MiniApp 结果回填 [APP架构设计.md](../APP架构设计.md)。
## 9. 验收目标
```text
1. Runtime 向每个 MiniApp instance 注入不可伪造、范围受限的 MiniAppContext。
2. Interact、Task Dashboard、Whiteboard 均通过 SDK 获取 Inbox、Tool 和生命周期能力;
不直接访问 Transport、Store、Agent 或 Host 特权。
3. Manifest Tool 具有稳定 ID、输入/输出 schema、handling、目标 scope 和版本。
4. Runtime 只接受当前 Inventory revision 中、输入 schema 合法的 Tool Invoke。
5. Runtime 拒绝未知 Tool、过期 revision、非法输入/输出、scope 不匹配、禁用 MiniApp、
错误 instance 和重复终态;拒绝请求不得到达 MiniApp 或 Host。
6. Interact 的 notice / choice / confirm / input 通过统一 Standard Interaction 在 IM 中完成、恢复和回声,
不退化。
7. Runtime 从 SDK-bound MiniAppContext 自动注入不可伪造的 interaction owner;可选 parent Tool
引用必须归属于当前实例且通过验证。结果只能经 Runtime 返回 owner,不能直接发给 Agent 或其他 MiniApp。
8. Task Dashboard 可验证 launch → foreground → progress → 标准交互 → result/error → close →
Interact 恢复;标准交互在其有效容器呈现且不触发焦点切换。
9. Whiteboard 可验证隔离 Surface open → patch → submit/close,并不能访问 Host DOM/Tauri/token
画板内的文字编辑等私有高频 UI 不通过 `sdk.ui` 路由。
10. MiniApp 的 progress/result/error 经 Runtime schema 校验、持久化、审计和 outbox 后才回传 Agent。
11. 断线或重启后,pending Tool、Standard Interaction、MiniApp Inbox、实例、焦点和 Surface 以有界方式恢复;中断操作
不得伪装为完成。
12. 不新增 AppServer Catalog、下载或市场接口;现有服务端只透明转发会话/Inventory 消息。
13. 00.base 和 01.kernel 中的登录、同步、本地回显、Markdown、安全 fallback、Tool Call、
Task、Surface、Capability、App Inbox、outbox 和焦点恢复测试不退化。
14. `npm test -- --run`、`npm run build`、`git diff --check` 通过;浏览器验收无未处理错误。
```
## 10. 完成定义
本迭代完成不是“已经有应用市场”,也不是“完成全部 MiniApp 业务功能”。完成的判断是:
```text
LineUp Runtime 已提供经过类型、schema、scope、Inventory revision、生命周期、权限、持久化和
审计约束的 MiniApp SDK v1
Interact、Task Dashboard、Whiteboard 已以不同信任和 UI 形式使用同一套 SDK 语义,证明
LineUp 可在不增加旁路通信或 Host 特权泄漏的前提下承载系统级与受限 MiniApp。
```
+287
View File
@@ -0,0 +1,287 @@
# `03.sdk_and_coreapp` 设计评审记录
> 评审日期:2026-08-05
> 评审基线:[APP架构设计.md](../APP架构设计.md)
> 评审对象:[03.sdk_and_coreapp.md](03.sdk_and_coreapp.md)
> 评审方式:独立子 agent 只读评审;本文件记录评审意见,不代表已采纳或已实现。
## 1. 总体结论
`03.sdk_and_coreapp` 的主方向已经与架构基线基本一致,以下核心决策已正确写入:
- 产品层级保持为 **LineUp App → LineUp Runtime → MiniApps**Runtime 没有被写成与 MiniApp 平级的 App。
- SDK 使用 `sdk.ui`,不再使用旧的 `sdk.interactions`
- 标准交互属于 Runtime 提供的标准输入输出库;Runtime 负责记录、owner 注入、校验、恢复、审计、路由和呈现 Policy。
- `owner` 不是 MiniApp 调用参数,而是从 SDK 已绑定的 `MiniAppContext` 自动注入;MiniApp 不能伪造或访问其他 owner 的交互。
- Interact 是 Agent/IM 场景的默认可信 Renderer Provider,不拥有交互记录,也不直接把结果发送给 Agent。
- Task Dashboard 可验证 `sdk.ui` 的通用事务提示,且不应因提示强制切换到 Interact。
- Whiteboard 的文字编辑、画笔、颜色选择、拖放、工具栏等高频或私有 UI 留在自身 Surface,不走 Runtime 标准交互。
- 保留 `lineup.v1.tool.call(choice | confirm | input)` 兼容路径;不把 `chat → interact` 命名迁移放进本迭代。
- 本迭代不建设 AppServer Catalog、下载、安装、更新、市场、远程 Bundle 或真实媒体/文件能力。
但仍存在 **1 个阻塞级信任模型冲突**,以及若干会造成实现分叉或验收不可判定的重要契约缺口。建议在进入实现前完成收敛。
## 2. 阻塞问题
### B1. Task Dashboard 的可信 DOM / Host adapter 权限突破了当前信任模型
**架构基线**在 [APP架构设计.md](../APP架构设计.md) 的 MiniApp 信任模型中明确:
- `Interact / System MiniApp` 可使用可信内建 DOM 组件;
- Installed MiniApp 必须经隔离 iframe / Surface Bridge 运行。
而 [03.sdk_and_coreapp.md](03.sdk_and_coreapp.md) 同时写了:
```text
Task Dashboard 可由受信 Host adapter 挂载;Whiteboard 必须走受限 Surface
```
并将 Task Dashboard 定义为:
```text
kind = bundled-reference
```
且说明它可使用“随 LineUp 发布的受信 Host adapter”。
**问题:** 当前权威架构没有明确授权 `bundled-reference` 获得与 System MiniApp 相同的可信 DOM 容器。若 Host adapter 可执行或挂载 Task Dashboard 的 UI,而未经过受限 Surface,隔离边界、SDK 受限视图和“禁止访问 Host DOM”就无法以同一安全模型证明。
**需要冻结的决策(二选一):**
1. **保守方案,且最符合当前权威基线:** Task Dashboard 和 Whiteboard 都是非 System MiniApp,必须经隔离 Surface / Bridge 运行。Host adapter 仅作为 Runtime/Shell 的不可见装配层,不向 MiniApp 提供可直接操作的可信 Host DOM。
2. **若产品确实需要 Task Dashboard 使用可信原生 DOM** 将其升级为 `kind = system`,并在架构主文档新增或明确“bundled trusted system MiniApp”信任层、发布来源、可用 DOM 权限和审计边界;不能继续称其为普通 `bundled-reference`
**建议验收:**
- 若维持 `bundled-reference`Task Dashboard 无法获得 Host 根 DOM、Tauri invoke 或未授权 Host adapter。
- 若改为 `system`,仍要验证其特权只限 Runtime SDK,不能取得 Transport、Store 或 Agent 访问权。
## 3. 重要问题
### I1. `bundled-development` 与 `bundled-reference` 的术语、Manifest 枚举未建立映射
迭代文档把“内置”解释为 `bundled-development`,但冻结的 `MiniAppManifest.kind` 只有:
```ts
kind: "system" | "bundled-reference";
```
架构主文档也使用了“参考 MiniApp 在此阶段可以是 `bundled-development`”的表述。
这两个词可以共存,但必须明确它们不是互相竞争的 `kind` 枚举值。
**建议补充最小映射表:**
| 概念 | 建议语义 |
|---|---|
| `bundled-development` | 本迭代的交付/加载方式:随开发 Host 预置,不下载、不安装、不经服务端 Catalog。 |
| `kind = bundled-reference` | Manifest 身份:参考 MiniApp,决定 SDK / Surface 信任策略。 |
| `kind = system` | 随产品发布、代码可信的系统 MiniApp,例如 Interact。 |
并明确:`bundled-development` **不是** `MiniAppManifest.kind` 的第三个取值。
### I2. `sdk.ui` 缺少 Renderer 如何安全提交用户结果给 Runtime 的闭环契约
当前公开 API 只有:
```ts
request(...)
get(...)
subscribe(...)
cancel(...)
```
但未定义:
- Runtime 如何把被 Policy 选中、可呈现的 interaction record 交给 renderer
- renderer 如何提交 `submitted``result``cancelled`
- Runtime 如何校验 `interaction_id`、owner、renderer binding、状态迁移、一次性提交和结果 schema;
- 在 opaque-origin iframe / Surface Bridge 内,呈现和回传采用何种受限消息协议;
- renderer 与 owner 是同一 MiniApp 时,是否通过 `sdk.ui` 提交,还是只能通过 Runtime/Shell 私有 Renderer API 提交。
**建议冻结 Runtime 私有 Renderer Contract / 最小 Bridge Contract**
```text
Runtime 分配 interaction_id + renderer_binding + presentation container/surface
→ renderer 仅接收 presentation-safe request projection
→ renderer 向 Runtime-owned endpoint 提交 action/result
→ Runtime 校验:
- 被分配的 renderer / container
- interaction_id
- owner binding
- 当前状态与一次性提交
- result schema
- expiry
→ Runtime 持久化状态迁移,并向 owner-scoped ui 订阅发出更新
```
隔离 Surface 至少还要限定消息白名单、`surface_id` / `instance_id` 绑定和 nonce 或等效关联方式;不得只凭 origin 信任。
### I3. 标准交互与 `RuntimeToolCallRecord` 的关系不清晰
当前文档同时规定:
- MiniApp 可调用 `sdk.ui.request(...)`
- owner 的 source 可为 `agent_tool | miniapp_tool | runtime_policy`
- 所有标准交互“必须映射为 Runtime 统一的 `interactive` Tool Call / `StandardInteractionRecord`”。
因此会产生问题:Task Dashboard 在没有 Agent Tool Call、例如用户点击“关闭未完成任务”时调用 `sdk.ui.request(confirm)`,Runtime 是否必须凭空创建 Agent-facing `RuntimeToolCallRecord`
**建议明确拆成三条路径:**
| 来源 | Runtime 记录 | 与 Agent Tool 的关系 |
|---|---|---|
| Agent / legacy `lineup.v1.tool.call(choice/confirm/input)` | `RuntimeToolCallRecord` + `StandardInteractionRecord` | 交互终态受控驱动该 Tool 的后续处理。 |
| MiniApp `sdk.ui.request(...)` | owner-bound `StandardInteractionRecord` | 不自动创建新的 Agent Tool;若提供合法 `parent_call_id`,只建立关联。父 Tool 的完成由 owner 经 `sdk.tools.*` 决定。 |
| Runtime Policy 自发提示 | `runtime_policy` owner 的 interaction record | 需明确其是否绑定某一 Capability / lifecycle request。 |
没有 parent Tool 的交互只改变 MiniApp 本地工作流,不应制造 Inventory、Agent 回传或 outbox 语义。
### I4. 结果投递位置与公开 API 不一致
流程写“结果仅投递回 owner MiniApp 的 Tool/Inbox”,但 SDK 已公开 `sdk.ui.get/subscribe`
否则实现可能分叉成:
1. 结果通过 `sdk.ui.subscribe` 的 record update 返回;
2. 结果作为 Inbox 消息;
3. 结果通过 parent Tool 的 SDK Tool event 返回。
**建议冻结为:**
```text
sdk.ui.get / sdk.ui.subscribe
= owner 获取标准交互状态与结果的唯一 UI-domain 通道
sdk.inbox
= Agent / Runtime 入站消息;不复用为 interaction result transport
sdk.tools.complete / fail / cancel
= owner 根据 UI result 自行决定是否推进关联 parent Tool
```
还需定义:订阅是否立即回放 owner 当前 pending records、终态保留/清理策略、断线重连后的重放及去重语义。
### I5. Schema、状态机、幂等规则不足以支持安全验收
已有四类 request 及基础状态,但还需要明确:
- `StandardInteractionResult``InteractionReceipt` 的类型;
- `choice.actions[].id` 的唯一性、最大数量、空数组是否允许,以及 single/multi/button 的选择基数;
- `input.fields[].id` 的唯一性、字段数上限、必填、数值解析、空字符串、长度和范围的适用规则;
- `notice` 的“只读”与 `acknowledged` 的精确定义:是否需要用户确认、是否自动完成、是否可超时;
- `request``submit``cancel`、超时、恢复之间的合法状态迁移;
- 并发提交、重放、重复取消、跨 Surface 重放的确定性返回;
- 交互专用稳定拒绝码。
建议至少增加:
```text
interaction_not_found
interaction_owner_mismatch
interaction_state_invalid
interaction_expired
interaction_result_invalid
interaction_renderer_mismatch
parent_call_invalid
```
### I6. 持久化、审计与敏感输入的日志最小化约束尚未衔接
`input` 可以含用户自由文本;标准交互要求持久化、恢复和审计,但架构文档又要求日志不得包含身份、会话、消息正文、Tool 参数、token 或 artifact 内容。
**建议明确:**
- 恢复所需的受保护 operational state、审计最小元数据、日志/Telemetry 三者的分层;
- `input` 草稿和结果的保留期、终态清理策略,以及是否加密;
- 审计仅记录 interaction ID、kind、受保护 owner 标识、状态迁移和错误码;
- title、prompt、字段值和 action label 不进入日志、指标、错误详情或普通审计明文;
- 验收增加日志负向断言。
## 4. 建议完善项
### S1. 扩大 `parent_call_id` 校验条件
除“属于当前实例且状态允许关联”外,Runtime 至少应校验:
```text
同 app_scope
同 instance_id
同 conversation_id
父 Tool 非终态
不存在跨 owner 关联
```
还要定义父 Tool 被取消或超时时关联 interaction 的处理:自动取消、保留为独立操作,或交由 Policy 决定。否则恢复后容易遗留孤儿卡片。
### S2. 将 presentation policy 写成可判定矩阵
至少应覆盖:
| owner 状态 / 容器 | 建议行为 |
|---|---|
| Agent IM / Interact 有效 | Interact IM timeline/card。 |
| owner 前台且存在合法 renderer | owner 容器或已绑定 Surface 的标准 modal/sheet/card。 |
| owner 后台或 suspended | 保持 pending;通知/唤醒仅经 Policy,不得抢占焦点。 |
| Surface 已关闭或失效 | 不向旧 Surface 投递;恢复、降级或安全失败。 |
| renderer 不可用 | 保持 pending 或返回稳定拒绝码;不能把 payload 注入 Host DOM。 |
| 系统能力确认 | 走 Capability Gateway,禁止降级为普通 `confirm`。 |
### S3. 分离 Interact 的 Renderer 行为与 legacy Agent Tool 完结行为
当前“Renderer 呈现标准交互记录”与“Interact 以 `sdk.tools.complete/fail/cancel` 结束 Tool”的表述容易混淆。
建议统一为:
```text
Renderer → Runtime:完成 interaction record
Runtime → ownersdk.ui 状态更新
ownerlegacy Agent Tool 场景中为 Interact)→ sdk.tools.complete / fail / cancel
```
这样不会把一般 Renderer 误写成跨域 Tool 结果出口。
### S4. 统一阶段编号
架构主文档当前既有 `03.sdk_and_coreapp`,又有后续 `03.reference-miniapps`,但前者已经将 Task Dashboard / Whiteboard 的实现和端到端验收列为目标。
需要二选一:
- 删除或合并 `03.reference-miniapps` 到当前 `03.sdk_and_coreapp`;或
- 当前 `03` 只做 SDK/Interact 核心,参考 MiniApp 另起新编号并顺延 Catalog 阶段。
### S5. 在实施步骤中拆出标准交互服务和 renderer/Bridge 契约
建议在通用 Tool 闭环之后、适配 Interact 之前增加独立步骤:
1. 实现 `standard-interaction-service`:owner 注入、记录、状态机、结果校验、过期、恢复、owner-scoped query/subscription
2. 实现 renderer registration 与 presentation policy
3. 定义并测试 System IM renderer 与 isolated Surface renderer 的最小回传协议;
4. 再接入 legacy Tool mapping、Interact 与两个参考 MiniApp。
这样可以避免先把能力实现成 Chat 专用逻辑、后续再进行高风险重构。
## 5. 建议新增的最小验收场景
1. A 实例提交属于 B 实例、不同 conversation 或已终态 Tool 的 `parent_call_id` 时,Runtime 拒绝且不创建 record。
2. A 创建 interaction 后,B 无法 `get`、订阅、取消、提交,或通过伪造 `interaction_id` 获得任何 payload/result。
3. 同一 interaction 的两次 renderer submit 只有一次成功;第二次得到稳定终态错误且不改变已持久化结果。
4.`lineup.v1.tool.call(choice|confirm|input)` 创建关联 Tool Call 和 owner 为 Interact IM context 的 interaction;用户结果先由 Runtime 落库,再由 Interact 以 owner 身份完成 Tool。
5. Task Dashboard 无 parent Tool 的 `sdk.ui.request(confirm)` 不创建 Agent-facing Tool Call、不写 Agent outbox;带合法 parent Tool 时仅关联,不自动完成该 Tool。
6. Task Dashboard 前台时,标准 modal/sheet/card 在其合法容器显示,焦点栈与 Interact background 状态不被改写。
7. Whiteboard 在 Surface reload、关闭、旧 iframe 重放提交时,`surface_id` 与 renderer binding 都被验证,旧 iframe 消息被拒绝。
8. 非法 input、重复/未知 choice action、过期 interaction、跨 owner cancel 都被拒绝且不写错误结果。
9. 标准交互输入值、提示正文、选择标签不出现在日志、Telemetry 或 Audit 明文中;重启恢复仅保留设计允许的最小数据。
10. 若维持 `bundled-reference`Task Dashboard 不能直接挂载可信 Host DOM;若改为 `system`,必须有对应的 manifest/source-trust 回归用例。
## 6. 建议的阅读和决策顺序
明天继续时,建议按以下顺序决策和回填:
1. 先决定 **B1Task Dashboard 的信任级别与 UI 容器**
2. 冻结 **I2Renderer → Runtime 的提交/Bridge 契约**
3. 冻结 **I3/I4StandardInteractionRecord 与 Tool Call 的关系,以及 UI result 的唯一返回通道**
4. 细化 **I5/I6:schema、状态机、幂等、错误码、持久化与日志最小化**
5. 把 S1~S5 与第 5 节的验收场景回填入 [03.sdk_and_coreapp.md](03.sdk_and_coreapp.md) 和必要的 [APP架构设计.md](../APP架构设计.md)。
在上述收敛前,不建议开始 `standard-interaction-service` 或参考 MiniApp 的实现,以免重新形成 Chat / Interact 专用旁路。