docs(iteration): freeze sdk v1 contract fixtures
This commit is contained in:
+115
-56
@@ -225,7 +225,6 @@ interface LineUpMiniAppSDK {
|
||||
|
||||
inbox: MiniAppInboxAPI;
|
||||
tools: MiniAppToolAPI;
|
||||
ui: StandardInteractionAPI;
|
||||
lifecycle: MiniAppLifecycleAPI;
|
||||
surfaces: MiniAppSurfaceAPI;
|
||||
capabilities: MiniAppCapabilityRequestAPI;
|
||||
@@ -270,38 +269,79 @@ interface MiniAppToolAPI {
|
||||
`call_id` 的实例归属、合法状态迁移、输入/输出 schema、幂等性和权限,然后持久化、审计并
|
||||
写入 outbox。
|
||||
|
||||
### 7.3 标准交互、Lifecycle、Surface 与 Capability
|
||||
一旦 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 交互。
|
||||
|
||||
Runtime 通过 `sdk.ui` 提供 `notice`、`choice`、`confirm`、`input` 四种标准交互原语。这是由
|
||||
Runtime 统一拥有的标准输入输出库:Runtime 创建和持久化交互记录,注入 owner、校验请求/结果、
|
||||
负责恢复、审计与路由,并依照 Shell Policy 决定呈现位置。
|
||||
### 7.3 人与 Agent 标准交互、Lifecycle、Surface 与 Capability
|
||||
|
||||
`owner` 不属于 SDK 调用参数。Runtime 仅从 SDK 已绑定的 `MiniAppContext` 注入
|
||||
`app_scope`、`instance_id`、`conversation_id`,并可带有已验证的 `parent_call_id`、`surface_id` 与
|
||||
来源;MiniApp 不可伪造、覆盖或访问其他 owner 的交互。`presentation` 只能作为 `inline`、`modal`
|
||||
或默认形式的偏好,最终仍由 Runtime/Shell 决定。
|
||||
`notice`、`choice`、`confirm`、`input` 是 Interact 的人与 Agent 交互组件,与 IM 消息同属于
|
||||
当前会话;它们不是 MiniApp SDK,也不用于 MiniApp 自己的业务表单、确认或输入。Agent 通过
|
||||
标准 Tool 交互请求发起它们,Runtime 校验、持久化、恢复、审计并可靠回传结果,Interact 负责呈现。
|
||||
|
||||
Runtime 内部至少持久化以下归属信息:
|
||||
这四种是普通的人与 Agent 会话交互,不在 SDK v1 中统一定义为密码或秘密输入。已提交内容遵循普通 IM
|
||||
的会话历史、保留和日志基线;尤其不能因为它们是普通输入,就突破全局“日志不记录会话正文、Tool 参数、
|
||||
token 等内容”的约束。未提交 `input` 草稿仅保留在当前运行期,重启清空。密码、卡密、私钥、临时 token
|
||||
等真正秘密输入留待未来作为独立的 `password-input` 原语设计;届时 Runtime 必须按类型强制其展示、记录、
|
||||
传递和清理规则,Agent 不能用普通 `input` 绕过这些保护。
|
||||
|
||||
```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";
|
||||
};
|
||||
```
|
||||
每条人与 Agent 的标准交互都必须在 Interact / IM 中留下相应的卡片、气泡或结果记录。`notice` 是只读
|
||||
IM 卡片;当前界面可同时短暂 Toast 提醒,但 Toast 只是同一条记录的辅助呈现,不能作为唯一消息或用户
|
||||
已阅读的证明。Runtime 成功持久化 notice 卡片并交给 Interact 呈现后,即可向 Agent 返回 `accepted`。
|
||||
|
||||
Agent 在 IM 中的提问由 Interact 作为默认可信 Renderer Provider 呈现在 IM 时间线/卡片内。前台
|
||||
MiniApp 自己请求的低复杂度事务提示,可在该 MiniApp 的有效容器或 Surface 中以 Runtime 批准的
|
||||
标准 modal、sheet 或 card 呈现,不能因此强制切换到 Interact。结果始终先返回 Runtime,再仅投递
|
||||
给 owner MiniApp;MiniApp 不可自行创建 Host 弹窗或直接将结果发送给 Agent。
|
||||
MVP 中,一个 LineUp Runtime 只连接一个 Agent,当前登录会话的 `agent_uid` 作为每条交互和
|
||||
App 子会话的 `agent_id`。此处保存 Agent 身份是为了让答案按创建时的上下文回到正确对象;本阶段
|
||||
不实现多 Agent 连接、切换、会话列表、outbox 或跨 Agent 路由。
|
||||
|
||||
复杂、高频或私有的业务 UI(例如 Whiteboard 画布文字编辑、画笔、颜色选择、拖放和工具栏)留在
|
||||
MiniApp 自己的 Surface 内,不使用标准交互库。系统权限确认(文件、麦克风、通知等)则属于
|
||||
Runtime/Host Capability Gateway,而不是普通 `confirm`。
|
||||
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 {
|
||||
@@ -311,8 +351,8 @@ interface MiniAppLifecycleAPI {
|
||||
}
|
||||
```
|
||||
|
||||
MiniApp 只能请求生命周期变化;Runtime 决定是否允许,处理 pending Tool/Surface,并负责
|
||||
焦点恢复。
|
||||
MiniApp 只能请求生命周期变化;Runtime 决定是否允许。若接受关闭,则按上面的 `app_closed` 收敛普通 Tool、
|
||||
处理 Surface 并恢复焦点;若用户只想暂时离开,应请求后台化。
|
||||
|
||||
Surface 与 Capability 也只能请求:
|
||||
|
||||
@@ -331,20 +371,26 @@ MiniApp requestOpenSurface / requestCapability
|
||||
MiniApp 只能在 Manifest 中声明 Tool;Agent 只能调用当前 Runtime 已公布 Inventory 中的 Tool。
|
||||
|
||||
```ts
|
||||
type ToolDescriptor = {
|
||||
type ToolDescriptorBase = {
|
||||
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;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
统一调度路径:
|
||||
@@ -354,15 +400,19 @@ Agent Tool Invoke
|
||||
→ Runtime 校验 Envelope、Inventory revision、Tool Descriptor、参数、scope、App 状态和权限
|
||||
→ 创建可持久化 Tool Call / 审计记录
|
||||
→ Tool Router 判定 direct / interactive / launch / foreground / operation
|
||||
→ 投递到目标 MiniApp instance
|
||||
├── interactive:不使用 target、不创建业务 instance,只由 Runtime 建立 Interact 会话交互
|
||||
└── 其他 handling:投递到目标 MiniApp instance
|
||||
→ MiniApp SDK 报告 progress / result / error
|
||||
→ Runtime 校验输出 schema、持久化、更新焦点、写 outbox
|
||||
→ Agent 收到可靠结果
|
||||
```
|
||||
|
||||
`notice`、`choice`、`confirm`、`input` 是 Runtime 的统一 `interactive` Tool/交互记录能力。
|
||||
Interact 提供 Agent/IM 场景的默认 Renderer,而不拥有记录或绕过统一 Tool Call、持久化和结果路径;
|
||||
其他 MiniApp 只能通过 `sdk.ui` 在自身有效容器使用 Runtime 批准的标准 Renderer。
|
||||
`notice`、`choice`、`confirm`、`input` 是 Agent 调起的 Runtime 统一 `interactive` Tool/交互记录
|
||||
能力。Interact 提供会话语义和呈现,Runtime 持有记录与可靠结果路径;其他 MiniApp 只能作为当前
|
||||
前台界面被 Interact 交互层覆盖,不能调用、实现或取得该交互的内容与结果。
|
||||
|
||||
可回答交互的请求以相对 `expires_in_ms` 指定期限;Runtime 计算和持久化 `expires_at`,缺省 15 分钟,
|
||||
仅接受 1 分钟至 24 小时。notice 不等待回答,也不接受期限。
|
||||
|
||||
建议稳定拒绝码至少包括:
|
||||
|
||||
@@ -466,7 +516,7 @@ tauri/src/core-apps/chat/
|
||||
### 10.3 下一阶段:03.sdk_and_coreapp
|
||||
|
||||
下一阶段不是应用市场,也不是立即建设服务端下载目录。目标见
|
||||
[03.sdk_and_coreapp.md](迭代/03.sdk_and_coreapp.md):定义并验证 MiniApp SDK v1:
|
||||
[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 和错误码
|
||||
@@ -476,17 +526,15 @@ tauri/src/core-apps/chat/
|
||||
→ Whiteboard 验证受限 Surface、状态 patch、Artifact 与 Capability 请求
|
||||
```
|
||||
|
||||
参考 MiniApp 在此阶段可以是 `bundled-development`:随开发 Host 内置、有 Manifest 和 Runtime
|
||||
注册记录,但不依赖服务端 Catalog、远程下载或第三方发布。
|
||||
参考 MiniApp 在此阶段使用 `kind = bundled`:随开发 Host 内置、有 Manifest 和 Runtime 注册记录,
|
||||
但不依赖服务端 Catalog、远程下载或第三方发布。`bundled` 不是系统级信任;参考只描述这些
|
||||
MiniApp 在当前阶段用于验证 SDK 的工作目的,不是 Manifest 类型名称。
|
||||
|
||||
### 10.4 后续阶段
|
||||
|
||||
MiniApp SDK 与参考实现稳定后,再按顺序推进:
|
||||
|
||||
```text
|
||||
03.reference-miniapps
|
||||
→ 完成 Task Dashboard / Whiteboard 的端到端参考实现
|
||||
|
||||
04.app-delivery-registry
|
||||
→ Catalog Entry、Manifest/Bundle 下载、验签、安装、启用、禁用、更新、回滚、移除
|
||||
|
||||
@@ -494,6 +542,9 @@ MiniApp SDK 与参考实现稳定后,再按顺序推进:
|
||||
→ 搜索、分类、发布者、安装 UX、组织分发与可能的商业能力
|
||||
```
|
||||
|
||||
`03.sdk_and_coreapp` 已经包含 Task Dashboard 和 Whiteboard 的端到端参考实现;不再另设
|
||||
`03.reference-miniapps`,以免把同一批工作拆成两个编号。
|
||||
|
||||
“市场”属于最后的产品分发层,不能反向决定 Runtime、SDK、Tool 或安全模型。
|
||||
|
||||
## 11. 验收与发布门槛
|
||||
@@ -508,19 +559,27 @@ npm run build
|
||||
对于 SDK/参考 MiniApp,还必须有以下证据:
|
||||
|
||||
1. 合法与非法 Manifest、Inventory revision、Tool 输入/输出 schema 的自动化测试;
|
||||
2. Tool 启动 MiniApp、前后台切换、结果回传、失败回焦和重启恢复的场景测试;
|
||||
3. Interact 的 `notice / choice / confirm / input` IM 呈现不退化,且覆盖 owner 自动注入、跨 owner
|
||||
访问/提交拒绝、owner 结果回路与 MiniApp 容器内呈现的测试;
|
||||
4. Installed MiniApp 无法取得 Host DOM、Tauri invoke、token、任意网络或其他 MiniApp 数据;
|
||||
5. 有头浏览器验证登录、同步、本地回显和代表性 MiniApp 路径;
|
||||
6. 对生产 Bundle,验证签名 `open → patch → close → rollback`、隔离 Bridge 和 Capability 拒绝路径;
|
||||
7. `git diff --check` 无格式错误,日志不得包含身份、会话、消息正文、Tool 参数、token 或
|
||||
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.md](迭代/00.base.md):Runtime 托管 Interact IM 的稳定基线。
|
||||
- [迭代/01.kernel.md](迭代/01.kernel.md):Runtime Kernel 与 MiniApp 编排目标和验收。
|
||||
- [迭代/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):实现文件和功能说明。
|
||||
|
||||
@@ -96,7 +96,7 @@ npm run desktop:dev
|
||||
- [程序文件清单与功能说明.md](程序文件清单与功能说明.md):客户端 Runtime、Chat Core App、Tauri/Web Host 与测试的文件清单;
|
||||
- [tauri/src/README.md](tauri/src/README.md):源码目录、依赖方向与模块放置规则;
|
||||
- [tauri/README.md](tauri/README.md):Tauri/Web Host 的运行、构建、行为回归和历史里程碑;
|
||||
- [迭代/](迭代/00.base.md):按迭代记录当前基线和后续 Runtime Kernel / 应用编排目标;
|
||||
- [迭代/](迭代/00.base/00.base.md):按迭代目录记录当前基线、设计评审和后续 Runtime Kernel / 应用编排目标;
|
||||
- [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 / SDK 的详细契约来源;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"contract": "lineup-miniapp-sdk-v1-golden-1",
|
||||
"description": "03.sdk_and_coreapp frozen runtime contract cases. These cases express the required observable result; they do not grant MiniApps access to Runtime internals.",
|
||||
"cases": [
|
||||
{
|
||||
"id": "bundled-manifest-requires-surface",
|
||||
"input": { "manifest": { "kind": "bundled", "host": { "surface_required": true } } },
|
||||
"expected": { "accepted": true }
|
||||
},
|
||||
{
|
||||
"id": "bundled-manifest-without-surface-rejected",
|
||||
"input": { "manifest": { "kind": "bundled", "host": { "surface_required": false } } },
|
||||
"expected": { "accepted": false, "code": "manifest_denied" }
|
||||
},
|
||||
{
|
||||
"id": "interactive-without-app-context-goes-to-main-im",
|
||||
"input": { "tool": { "handling": "interactive" } },
|
||||
"expected": { "app_session_id": null, "creates_business_instance": false, "enters_miniapp_inbox": false }
|
||||
},
|
||||
{
|
||||
"id": "interactive-with-verified-closed-app-session-keeps-history-context",
|
||||
"input": { "tool": { "handling": "interactive" }, "app_session_context": { "app_session_id": "session-whiteboard-1", "state": "ended", "same_agent": true, "same_conversation": true } },
|
||||
"expected": { "app_session_id": "session-whiteboard-1", "creates_business_instance": false, "revives_instance": false }
|
||||
},
|
||||
{
|
||||
"id": "interactive-with-cross-conversation-app-session-rejected",
|
||||
"input": { "tool": { "handling": "interactive" }, "app_session_context": { "app_session_id": "session-other", "state": "active", "same_agent": true, "same_conversation": false } },
|
||||
"expected": { "accepted": false, "code": "app_session_context_invalid", "creates_interaction": false }
|
||||
},
|
||||
{
|
||||
"id": "answerable-interaction-uses-default-expiry",
|
||||
"input": { "request": { "kind": "confirm" } },
|
||||
"expected": { "expires_in_ms": 900000 }
|
||||
},
|
||||
{
|
||||
"id": "answerable-interaction-expiry-boundary",
|
||||
"input": { "request": { "kind": "input", "expires_in_ms": 60000 } },
|
||||
"expected": { "accepted": true, "expires_in_ms": 60000 }
|
||||
},
|
||||
{
|
||||
"id": "notice-does-not-accept-expiry",
|
||||
"input": { "request": { "kind": "notice", "expires_in_ms": 60000 } },
|
||||
"expected": { "accepted": false, "code": "interaction_request_invalid" }
|
||||
},
|
||||
{
|
||||
"id": "dismiss-is-idempotent-and-addressed-by-call-id",
|
||||
"input": { "dismiss": { "control_id": "dismiss-1", "call_id": "interactive-call-1" }, "interaction": { "status": "pending", "same_agent": true, "same_conversation": true } },
|
||||
"expected": { "status": "cancelled", "tool_outbox_count": 1, "replay_outbox_count": 0 }
|
||||
},
|
||||
{
|
||||
"id": "accepted-app-close-cancels-running-tool",
|
||||
"input": { "close": { "accepted": true }, "tool": { "status": "running", "instance_id": "task-dashboard-1" } },
|
||||
"expected": { "tool_status": "cancelled", "cancel_reason": "app_closed", "tool_outbox_count": 1, "close_surface": true, "stop_instance": true }
|
||||
},
|
||||
{
|
||||
"id": "accepted-app-close-preserves-submitted-result",
|
||||
"input": { "close": { "accepted": true }, "tool": { "status": "submitted", "instance_id": "whiteboard-1" } },
|
||||
"expected": { "tool_status": "submitted", "continue_existing_outbox": true, "close_surface": true, "stop_instance": true }
|
||||
},
|
||||
{
|
||||
"id": "tool-complete-does-not-close-bundled-app",
|
||||
"input": { "tool": { "status": "completed", "app_kind": "bundled" } },
|
||||
"expected": { "close_surface": false, "stop_instance": false, "end_app_session": false }
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import fixture from "@/runtime/app-management/golden/miniapp-sdk-v1.json";
|
||||
|
||||
type GoldenCase = Readonly<{
|
||||
id: string;
|
||||
input: Readonly<Record<string, unknown>>;
|
||||
expected: Readonly<Record<string, unknown>>;
|
||||
}>;
|
||||
|
||||
const cases = fixture.cases as readonly GoldenCase[];
|
||||
|
||||
describe("MiniApp SDK v1 frozen golden contract", () => {
|
||||
it("uses the versioned 03.sdk_and_coreapp fixture and stable case ordering", () => {
|
||||
expect(fixture.contract).toBe("lineup-miniapp-sdk-v1-golden-1");
|
||||
expect(cases.map(entry => entry.id)).toEqual([
|
||||
"bundled-manifest-requires-surface",
|
||||
"bundled-manifest-without-surface-rejected",
|
||||
"interactive-without-app-context-goes-to-main-im",
|
||||
"interactive-with-verified-closed-app-session-keeps-history-context",
|
||||
"interactive-with-cross-conversation-app-session-rejected",
|
||||
"answerable-interaction-uses-default-expiry",
|
||||
"answerable-interaction-expiry-boundary",
|
||||
"notice-does-not-accept-expiry",
|
||||
"dismiss-is-idempotent-and-addressed-by-call-id",
|
||||
"accepted-app-close-cancels-running-tool",
|
||||
"accepted-app-close-preserves-submitted-result",
|
||||
"tool-complete-does-not-close-bundled-app",
|
||||
]);
|
||||
});
|
||||
|
||||
it("contains only cases with an input and observable expected result", () => {
|
||||
for (const entry of cases) {
|
||||
expect(entry.id).toMatch(/^[a-z][a-z0-9-]+$/);
|
||||
expect(Object.keys(entry.input).length).toBeGreaterThan(0);
|
||||
expect(Object.keys(entry.expected).length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -151,7 +151,7 @@ npm run desktop:dev
|
||||
## 7. 相关文档
|
||||
|
||||
- [工作区 README](README.md):产品基线、MVP-R1、开发入口与文档导航。
|
||||
- [迭代基线与目标](迭代/00.base.md):`00.base` 已实现内容和 `01.kernel` Runtime Kernel 目标。
|
||||
- [迭代基线与目标](迭代/00.base/00.base.md):`00.base` 已实现内容和 `01.kernel` Runtime Kernel 目标。
|
||||
- [APP 架构设计](APP架构设计.md):LineUp App、Runtime、MiniApp SDK、发布安全与后续阶段。
|
||||
- [Tauri/Web 源码导航](tauri/src/README.md):目录边界与依赖方向。
|
||||
- [Tauri/Web Host README](tauri/README.md):Host 运行、构建、行为回归与历史记录。
|
||||
|
||||
@@ -119,6 +119,6 @@ Interaction App 的 IM / Audio / Video 模式统一运行时
|
||||
完整应用市场
|
||||
```
|
||||
|
||||
这些内容属于 [01.kernel.md](01.kernel.md) 定义的下一阶段目标。`00.base` 的意义是保护
|
||||
这些内容属于 [01.kernel.md](../01.kernel/01.kernel.md) 定义的下一阶段目标。`00.base` 的意义是保护
|
||||
已经完成的通信、可靠投递、作用域和安全边界,后续重构不得让这些能力回到 App 或
|
||||
`main.ts` 中。
|
||||
@@ -3,7 +3,7 @@
|
||||
**迭代编号:** 01.kernel
|
||||
**状态:** 目标设计,待实现
|
||||
**日期:** 2026-08-04
|
||||
**前置基线:** [00.base.md](00.base.md)
|
||||
**前置基线:** [00.base.md](../00.base/00.base.md)
|
||||
|
||||
## 1. 迭代目标
|
||||
|
||||
@@ -1,733 +0,0 @@
|
||||
# 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 Provider:notice / choice / confirm / input
|
||||
```
|
||||
|
||||
当前实现仍保持兼容名称:
|
||||
|
||||
```text
|
||||
产品概念:Interact MiniApp
|
||||
当前 app_scope:chat
|
||||
当前目录: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 Dashboard:Tool 与生命周期样本
|
||||
|
||||
**身份:** `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。
|
||||
```
|
||||
@@ -1,31 +1,91 @@
|
||||
# `03.sdk_and_coreapp` 设计评审记录
|
||||
# `03.sdk_and_coreapp` 第 1 次设计评审记录
|
||||
|
||||
> 评审日期:2026-08-05
|
||||
> 评审基线:[APP架构设计.md](../APP架构设计.md)
|
||||
> 评审编号:01
|
||||
> 评审基线:[APP架构设计.md](../../APP架构设计.md)
|
||||
> 评审对象:[03.sdk_and_coreapp.md](03.sdk_and_coreapp.md)
|
||||
> 评审方式:独立子 agent 只读评审;本文件记录评审意见,不代表已采纳或已实现。
|
||||
|
||||
> **后续决议(2026-08-05):** `MiniAppManifest.kind` 收敛为 `system | bundled`。Task Dashboard
|
||||
> 与 Whiteboard 均为非系统级 `bundled` MiniApp,必须采用与一般 MiniApp 相同的受限 Surface /
|
||||
> Bridge 模型;“参考”仅描述它们在本迭代验证 SDK 的目的。下文的 `bundled-reference` 为评审时的
|
||||
> 历史术语,已由此决议取代。
|
||||
|
||||
> **后续决议(2026-08-05):** `notice / choice / confirm / input` 始终是 Interact 的人与 Agent
|
||||
> 会话交互,不属于 MiniApp SDK,也不用于 MiniApp 内部业务逻辑。当前 bundled MiniApp 位于前台时,
|
||||
> Interact / Shell 可在其上方显示同一会话的交互层;请求与答案仍只属于 Interact、当前会话和 Agent。
|
||||
|
||||
> **后续决议(2026-08-05):** 每个启动的 App instance 在主 IM 中创建或恢复一个 App 子会话;其中
|
||||
> 只保留人与 Agent 围绕该 App 的提问、回答和简洁结果。App 的内部按钮、表单、画布编辑等业务操作
|
||||
> 不进入子会话。后台化不结束子会话;真正结束时保留折叠历史;新的 instance 创建新的子会话。
|
||||
|
||||
> **后续决议(2026-08-05):** 已结束的 App 子会话可以在主 IM 中只读展开。用户选择“继续处理”时,
|
||||
> Runtime 创建新的 App instance 和新的子会话;新会话可引用旧会话或 App 数据,但不能续写旧记录或
|
||||
> 复活旧问题。
|
||||
|
||||
> **后续决议(2026-08-05,已更新):** App 子会话只在 `AppLifecycleManager.close` 使对应 instance
|
||||
> 停止或失败时结束;前后台切换、暂停和 Agent Tool 完成都不结束子会话。关闭时,Runtime 向 Agent
|
||||
> 可靠发送 App 已关闭事件;尚未回答的 Interact 交互不自动取消,Agent 可 remote dismiss,或让其继续
|
||||
> 在主 IM 中等待用户回答/超时。子会话保留为主 IM 历史。
|
||||
|
||||
> **后续决议(2026-08-05):** MVP 中一个 LineUp Runtime 只连接一个 Agent。每条标准交互和
|
||||
> App 子会话仍保存当前 `agent_uid` 作为 `agent_id`,但本迭代不实现多个 Agent 的连接、切换、
|
||||
> 会话列表、outbox 或路由。
|
||||
|
||||
## 问题清单(Outline)
|
||||
|
||||
> **状态标记:** ✅ 已解决并回填设计; 🟡 待产品/架构决策; 🔵 待在契约中细化; ⚪ 待实施编排或文档整理; — 不再适用。
|
||||
>
|
||||
> 本清单是当前有效视图;后文保留评审时的原始问题、分析和建议作为依据。每次确认一个决策或完成
|
||||
> 回填时,应先更新此处状态,再更新对应正文和验收项。
|
||||
|
||||
| 状态 | 编号 | 问题 | 当前结论 / 下一步 |
|
||||
|---|---|---|---|
|
||||
| ✅ | B1 | Task Dashboard 的信任级别与 UI 容器 | 已确认:Task Dashboard、Whiteboard 均为非系统级 `kind = bundled` MiniApp,必须运行于受限 Surface / Bridge,不能使用可信 Host DOM。 |
|
||||
| ✅ | I1 | `bundled-reference` / `bundled-development` 命名歧义 | 已确认:Manifest 枚举为 `system \| bundled`;“参考”仅描述当前迭代的工作目的。 |
|
||||
| ✅ | I2 | 标准交互由谁显示、答案如何回传 | 已确认:Interact / Shell 显示人与 Agent 的交互;bundled MiniApp 前台时可被该交互层覆盖。结果由 Runtime 可靠回传 Agent,不交给 MiniApp。 |
|
||||
| ✅ | I3 | 标准交互与 Agent Tool Call 的关系 | 已确认:标准交互仅由 Agent Tool 调起并创建交互记录;MiniApp 不存在 `sdk.ui.request`,不会自行创建 Agent Tool Call。 |
|
||||
| ✅ | I4 | 标准交互结果的返回通道 | 已确认:用户答案先回 Runtime,再可靠回传 Agent;不经 MiniApp Inbox、Tool 订阅或 MiniApp SDK 返回。 |
|
||||
| ✅ | I5 | Request/Result schema、状态机、幂等与错误码 | 已确认第一版:`notice` 非阻塞;`confirm` 二选一且默认取消;`choice` 只支持 2~6 项单选;`input` 只支持一个文本输入;首次有效回答终结交互;默认 15 分钟超时,可在 1 分钟~24 小时内调整。 |
|
||||
| ✅ | I6 | 敏感输入的持久化、恢复、审计和日志边界 | 已确认:未提交草稿仅存在当前运行期间,重启即清空;已提交的人与 Agent 答案保留在所属主 IM 或 App 子会话中,随父 IM 会话处理;二者均不写日志、Telemetry 或普通审计明文。 |
|
||||
| ✅ | I7 | App 子会话与主 IM 的关系 | 已确认:每个 App instance 创建/恢复一个子会话;后台保留,真正结束后在主 IM 中折叠存档,新 instance 独立。仅记录人与 Agent 围绕 App 的交互,不记录 App 内部业务操作。 |
|
||||
| ✅ | I8 | 已结束 App 子会话的查看与继续处理 | 已确认:旧子会话可只读展开;继续旧工作创建新 instance / 新子会话,可引用旧上下文,但不追加旧记录或复活旧问题。 |
|
||||
| ✅ | I9 | App 子会话何时结束 | 已确认:仅 `AppLifecycleManager.close` 导致 instance stopped/failed 时结束;前后台切换、暂停、Agent Tool 完成不结束。关闭时 Runtime 向 Agent 发送 App 已关闭事件,但不自动取消未回答的 Interact 交互。 |
|
||||
| ✅ | I10 | MVP 的 Agent 身份范围 | 已确认:Runtime 仅连接一个 Agent;交互和 App 子会话保存该 `agent_id`,但不实现多 Agent 连接、切换或路由。 |
|
||||
| — | S1 | `parent_call_id` 的关联校验与父 Tool 终止后的处置 | 不再适用:MiniApp 不再发起标准交互;标准交互直接绑定 Agent call 与 conversation。 |
|
||||
| ✅ | S2 | 交互层显示与恢复规则 | 已确认:原 App 前台时显示交互层;切换到其他 App/IM 时收起并在子会话标记“等待你的回答”;用户可在 IM 或回到原 App 后回答;关闭 App 只移除覆盖层并通知 Agent,不自动取消交互。 |
|
||||
| ✅ | S3 | Interact 呈现与 Agent Tool 回传的分层 | 已确认:Interact / Shell 只显示并把用户动作/答案交给 Runtime;Runtime 根据已保存的交互上下文校验、持久化和可靠回传 Agent;不经过 MiniApp。 |
|
||||
| ✅ | S4 | 阶段编号重复 | 已确认:Task Dashboard / Whiteboard 的端到端参考实现属于 `03.sdk_and_coreapp`;移除重复的 `03.reference-miniapps`,后续应用分发阶段保持为 `04.app-delivery-registry`。 |
|
||||
| ✅ | S5 | 实施步骤顺序 | 已确认:先完成通用 Tool 闭环,再完成 Runtime 独占的 Agent interaction service 和 Interact 回传契约,之后才适配 Interact、实现两个 bundled 参考 MiniApp,最后端到端验收。 |
|
||||
|
||||
## 1. 总体结论
|
||||
|
||||
`03.sdk_and_coreapp` 的主方向已经与架构基线基本一致,以下核心决策已正确写入:
|
||||
这份文件保留了首次评审时提出的问题和建议,方便追溯讨论过程;其中涉及 `sdk.ui`、MiniApp 发起
|
||||
标准交互、owner 注入等早期设想,均已被本文件顶部的“后续决议”和问题清单中的最终结论取代,不能作为
|
||||
实施依据。
|
||||
|
||||
- 产品层级保持为 **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 个阻塞级信任模型冲突**,以及若干会造成实现分叉或验收不可判定的重要契约缺口。建议在进入实现前完成收敛。
|
||||
- 产品层级是 **LineUp App → LineUp Runtime → MiniApps**,Runtime 不是与 MiniApp 平级的产品 App。
|
||||
- `notice / choice / confirm / input` 是 Interact 中人与 Agent 对话的一部分,不是 MiniApp SDK,也不能由
|
||||
bundled MiniApp 发起、读取、提交或取消。
|
||||
- Interact / Shell 只呈现和收集用户答案;Runtime 保存交互归属、校验首次有效回答、写入可靠 outbox,并
|
||||
向当前唯一 Agent 回传结果。
|
||||
- Task Dashboard 与 Whiteboard 都是 `kind = bundled`;它们使用一般 MiniApp 的受限 Surface、Bridge、
|
||||
生命周期和 SDK,只保留自身的业务 UI。
|
||||
- 保留 `lineup.v1.tool.call(choice | confirm | input)` 的 Agent 交互兼容入口;不在本迭代迁移
|
||||
`chat → interact` 命名。
|
||||
- 本迭代不建设 AppServer Catalog、下载、安装、更新、市场、远程 Bundle、真实媒体/文件能力或多 Agent
|
||||
Runtime 连接。
|
||||
|
||||
本轮评审问题均已得到设计结论,后续进入实现时应以 [03.sdk_and_coreapp.md](03.sdk_and_coreapp.md)
|
||||
和 [APP架构设计.md](../../APP架构设计.md) 为准。
|
||||
|
||||
## 2. 阻塞问题
|
||||
|
||||
### B1. Task Dashboard 的可信 DOM / Host adapter 权限突破了当前信任模型
|
||||
|
||||
**架构基线**在 [APP架构设计.md](../APP架构设计.md) 的 MiniApp 信任模型中明确:
|
||||
**架构基线**在 [APP架构设计.md](../../APP架构设计.md) 的 MiniApp 信任模型中明确:
|
||||
|
||||
- `Interact / System MiniApp` 可使用可信内建 DOM 组件;
|
||||
- Installed MiniApp 必须经隔离 iframe / Surface Bridge 运行。
|
||||
@@ -227,39 +287,45 @@ parent_call_invalid
|
||||
| renderer 不可用 | 保持 pending 或返回稳定拒绝码;不能把 payload 注入 Host DOM。 |
|
||||
| 系统能力确认 | 走 Capability Gateway,禁止降级为普通 `confirm`。 |
|
||||
|
||||
### S3. 分离 Interact 的 Renderer 行为与 legacy Agent Tool 完结行为
|
||||
### S3. Interact 呈现与 Agent Tool 回传的分层
|
||||
|
||||
当前“Renderer 呈现标准交互记录”与“Interact 以 `sdk.tools.complete/fail/cancel` 结束 Tool”的表述容易混淆。
|
||||
|
||||
建议统一为:
|
||||
**已确认(2026-08-05):** 用户在 Interact 中回答时,Interact 只负责显示问题、收集用户动作并把
|
||||
答案交给 Runtime。它不是 Agent 的消息发送端,也不负责判断答案属于哪个 Agent、哪个会话或哪个 App
|
||||
子会话。
|
||||
|
||||
```text
|
||||
Renderer → Runtime:完成 interaction record
|
||||
Runtime → owner:sdk.ui 状态更新
|
||||
owner(legacy Agent Tool 场景中为 Interact)→ sdk.tools.complete / fail / cancel
|
||||
Interact / Shell 显示问题
|
||||
→ 用户作答
|
||||
→ Interact 向 Runtime 提交「interaction_id + 用户动作/答案」
|
||||
→ Runtime 从已保存的交互记录取得 agent_id、conversation、App 子会话和 Agent Tool 归属
|
||||
→ Runtime 核对 Interact 实例、展示位置、状态、期限、答案格式和首次提交
|
||||
→ Runtime 持久化会话记录与终态,并写入可靠 outbox
|
||||
→ Runtime 向当前唯一 Agent 回传一次结果
|
||||
```
|
||||
|
||||
这样不会把一般 Renderer 误写成跨域 Tool 结果出口。
|
||||
提交端不能传递或覆盖 `agent_id`、`conversation_id`、`call_id`、`app_session_id` 等归属信息;这些
|
||||
只能由 Runtime 创建交互时保存。重复点击、刷新后的旧页面、无效展示位置、过期或已结束问题的提交都
|
||||
必须被拒绝,且不能改变已经保存的结果或再次通知 Agent。Task Dashboard、Whiteboard 等 bundled
|
||||
MiniApp 始终不参与这条链路,也读不到问题或答案。
|
||||
|
||||
### S4. 统一阶段编号
|
||||
|
||||
架构主文档当前既有 `03.sdk_and_coreapp`,又有后续 `03.reference-miniapps`,但前者已经将 Task Dashboard / Whiteboard 的实现和端到端验收列为目标。
|
||||
|
||||
需要二选一:
|
||||
|
||||
- 删除或合并 `03.reference-miniapps` 到当前 `03.sdk_and_coreapp`;或
|
||||
- 当前 `03` 只做 SDK/Interact 核心,参考 MiniApp 另起新编号并顺延 Catalog 阶段。
|
||||
**已确认(2026-08-05):** Task Dashboard 与 Whiteboard 是 `03.sdk_and_coreapp` 中用来验证 SDK 的
|
||||
bundled 参考 MiniApp,本阶段就完成其端到端路径。因此删除重复的 `03.reference-miniapps`;后续阶段
|
||||
保持为 `04.app-delivery-registry`,不需要整体重编号。
|
||||
|
||||
### S5. 在实施步骤中拆出标准交互服务和 renderer/Bridge 契约
|
||||
|
||||
建议在通用 Tool 闭环之后、适配 Interact 之前增加独立步骤:
|
||||
**已确认(2026-08-05):** 实施顺序已调整为:
|
||||
|
||||
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。
|
||||
1. 冻结契约与测试样本;
|
||||
2. 实现 Runtime 通用 Tool 闭环;
|
||||
3. 实现 Runtime 独占的 Agent interaction service,以及 Runtime ↔ Interact 的最小呈现/提交契约;
|
||||
4. 再适配 Interact IM 和 App 子会话;
|
||||
5. 最后通过 Task Dashboard、Whiteboard 验证一般 bundled MiniApp 路径,并进行端到端验收。
|
||||
|
||||
这样可以避免先把能力实现成 Chat 专用逻辑、后续再进行高风险重构。
|
||||
这样标准交互不会先被做成 Chat 专用或 MiniApp SDK 能力;Interact 只是第一个使用 Runtime 私有交互
|
||||
契约的系统级呈现者,两个 bundled MiniApp 则只验证各自的 Tool、Surface 和业务 UI 边界。
|
||||
|
||||
## 5. 建议新增的最小验收场景
|
||||
|
||||
@@ -282,6 +348,6 @@ owner(legacy Agent Tool 场景中为 Interact)→ sdk.tools.complete / fail
|
||||
2. 冻结 **I2:Renderer → Runtime 的提交/Bridge 契约**;
|
||||
3. 冻结 **I3/I4:StandardInteractionRecord 与 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)。
|
||||
5. 把 S1~S5 与第 5 节的验收场景回填入 [03.sdk_and_coreapp.md](03.sdk_and_coreapp.md) 和必要的 [APP架构设计.md](../../APP架构设计.md)。
|
||||
|
||||
在上述收敛前,不建议开始 `standard-interaction-service` 或参考 MiniApp 的实现,以免重新形成 Chat / Interact 专用旁路。
|
||||
@@ -0,0 +1,164 @@
|
||||
# `03.sdk_and_coreapp` 第 2 次设计评审记录
|
||||
|
||||
> 评审编号:02
|
||||
> 评审日期:2026-08-05
|
||||
> 评审对象:[03.sdk_and_coreapp.md](03.sdk_and_coreapp.md)、[01.design_review.md](01.design_review.md)
|
||||
> 评审方式:独立子 agent 只读复核
|
||||
> 结论:核心方向已收敛;没有 P0 阻塞问题。以下 P1 项需在开始编码前逐项确认并回填主定义文档。
|
||||
|
||||
## 问题清单(Outline)
|
||||
|
||||
> **状态标记:** ✅ 已确认并回填设计; 🟡 待产品/架构决策; 🔵 待在契约中细化; ⚪ 文档或实施编排建议; — 不适用或无此问题。
|
||||
>
|
||||
> 本清单是本次评审的当前有效视图。后文保留每个问题的背景和建议;在问题被确认前,建议不是实施依据。
|
||||
> 确认后应先更新本清单,再回填 [03.sdk_and_coreapp.md](03.sdk_and_coreapp.md) 的契约、实施步骤和验收项。
|
||||
|
||||
| 状态 | 编号 | 问题 | 当前结论 / 下一步 |
|
||||
|---|---|---|---|
|
||||
| — | P0 | 阻塞级架构冲突 | 本次未发现需要推倒既有模型的 P0 问题。 |
|
||||
| ✅ | P1-1 | Task Dashboard 示例中 Tool 完成后关闭 App | 已回填:Tool 完成只回传 Agent;App 保留、后台或关闭由用户动作、`requestClose` 或 Lifecycle Policy 决定,只有真正 `AppLifecycleManager.close` 才结束 App 与子会话。 |
|
||||
| ✅ | P1-2 | `interactive` Tool 的专用路由 | 已回填:`interactive` 是 Runtime 自己处理的 Agent 交互分支,不创建/复用业务 MiniApp instance,不调整业务 App 焦点,也不进入任何 MiniApp SDK Tool Inbox;Runtime 私有契约交给 Interact / Shell 呈现。 |
|
||||
| — | P1-3 | 展示位置切换后的提交资格 | 不再适用:展示位置不是交互 owner 或提交权限。切换 App 前后台、关闭 App 或改在 IM 显示不改变同一 `interaction_id` 的归属;Runtime 只接受首次有效回答,之后才拒绝重复/重放。 |
|
||||
| ✅ | P1-4 | Tool 与交互的取消、超时和并发提交 | 已回填:用户回答、Agent dismiss、到期和 Runtime 失败竞争同一唯一终态;Runtime 第一个原子状态写入获胜,Tool 跟随同一结果,且只写一条 outbox。interactive Tool 仅使用交互 `expires_at`,不维护独立超时。 |
|
||||
| ✅ | P1-5 | `notice` 的 Tool 完成时点与结果 | 已回填:notice 必须保留为 Interact / IM 的只读卡片,可选同时 Toast 数秒;Runtime 持久化卡片并安排呈现后,立即以 `{ outcome: "accepted" }` 完成并回传,不表示用户已阅读。 |
|
||||
| — | P2-1 | 将普通标准交互统一当作敏感秘密输入 | 不再适用:`notice / choice / confirm / input` 按普通 IM 会话内容与日志基线处理;本迭代不为它们另设秘密输入契约或专门负向扫描。未提交草稿仍只在运行期存在,重启清空。 |
|
||||
| ⚪ | P4-1 | 首次评审中的历史提案可读性 | **遗留问题:** 不阻塞本迭代;建议在下一次文档整理或新评审时,进一步突出其中 `sdk.ui`、owner、`parent_call_id` 等旧提案仅供追溯、不可实施。 |
|
||||
| ⚪ | P4-2 | `password-input` 秘密输入原语 | **遗留问题:** 当前不阻塞 `03`;真正出现密码、卡密、私钥或临时 token 的 Agent 交互需求时,单独定义该原语及其不显示明文、不进入普通 IM 正文/日志/Telemetry/审计、可靠传递与清理等安全契约。 |
|
||||
|
||||
## 通过项
|
||||
|
||||
- 标准交互已经清楚限定为 Interact 中的人与 Agent 会话交互,而非 MiniApp SDK;公开 SDK 不包含 `sdk.ui`。
|
||||
- Task Dashboard、Whiteboard 已收敛为 `kind = bundled`,必须走受限 Surface / Bridge,不能取得可信 Host DOM、Tauri、Transport、Store、Agent 或其他 MiniApp 数据。
|
||||
- 用户答案经 Interact 交给 Runtime;Runtime 保存交互归属、校验、持久化并可靠回传当前唯一 Agent,bundled MiniApp 不参与。
|
||||
- App 子会话正确区分了人与 Agent 的记录和 MiniApp 内部业务操作;前后台、关闭、历史只读和“继续处理”的总体规则一致。
|
||||
- MVP 单 Agent 边界明确,没有提前引入多 Agent 连接、切换或路由。
|
||||
|
||||
## P0:阻塞问题
|
||||
|
||||
无。
|
||||
|
||||
## P1:开始编码前需要确认的事项
|
||||
|
||||
### P1-1:Tool 完成不应自动关闭 Task Dashboard
|
||||
|
||||
**已解决(2026-08-05):** 主定义文档已经确认“Agent Tool 完成不自动关闭 App,也不结束 App
|
||||
子会话”。Task Dashboard 示例与验收现已统一为该规则。
|
||||
|
||||
当前规则:
|
||||
|
||||
```text
|
||||
Tool 完成
|
||||
→ Runtime 持久化并回传 Agent
|
||||
→ App 是否保持前台、进入后台或关闭,取决于用户动作、MiniApp requestClose 或 Lifecycle Policy
|
||||
→ 只有真正执行 AppLifecycleManager.close,才结束 App 与 App 子会话
|
||||
```
|
||||
|
||||
验收已要求:Tool 成功回传后 Dashboard 可继续使用;只有真正关闭才结束子会话。
|
||||
|
||||
### P1-2:interactive Tool 必须不进入 MiniApp SDK Tool Inbox
|
||||
|
||||
**已解决(2026-08-05):** 通用 Tool 路由、实施步骤和验收已明确拆开 `interactive` 与一般 Tool。
|
||||
`notice / choice / confirm / input` 不会投递到 App Orchestrator、业务 MiniApp instance、SDK Tool Inbox
|
||||
或 `sdk.tools.subscribe`。
|
||||
|
||||
当前规则:
|
||||
|
||||
```text
|
||||
handling = interactive
|
||||
→ 不投递任何 MiniApp SDK Tool Inbox
|
||||
→ Runtime Agent interaction service 创建 StandardInteractionRecord
|
||||
→ Runtime 私有契约交给 Interact / Shell 呈现
|
||||
→ Interact 提交 interaction_id + 用户动作/答案
|
||||
→ Runtime 校验、持久化、outbox 回传 Agent
|
||||
|
||||
handling = direct / launch / foreground / operation
|
||||
→ 才进入 App Orchestrator、SDK Tool Inbox、MiniApp tools.* 路径
|
||||
```
|
||||
|
||||
验收已要求:Agent 发起的四类标准交互不得出现在任何 MiniApp Inbox 或 `sdk.tools.subscribe`。
|
||||
|
||||
### P1-3:切换展示位置后,旧页面必须失去提交资格
|
||||
|
||||
**不再适用(2026-08-05):** 该问题错误地把 App 上方覆盖层或 IM 卡片当成了交互的 owner。标准交互
|
||||
始终属于 Interact / 主 IM;`app_session_id` 只保留与哪段 App 工作相关的上下文,不形成 UI 父子关系。
|
||||
|
||||
切换 App 前后台、关闭 App 或改变展示位置不改变同一个 `interaction_id` 的归属,也不需要用
|
||||
`presentation_id` / `presentation_revision` 废止旧页面的回答资格。只要用户仍处于有效 LineUp /
|
||||
Interact 会话且交互尚未终态,Runtime 可接受该交互的首次有效回答;以后到达的重复、重放或迟到提交
|
||||
因交互已经终态而被拒绝。Shell 可以避免用户看到重复视觉卡片,但这是体验策略,不是结果正确性的依据。
|
||||
|
||||
验收应覆盖:交互从 Whiteboard 覆盖层改在 IM 中显示、Whiteboard 关闭后交互仍 pending 时,任一有效
|
||||
Interact 页面提交的第一份答案只产生一次持久化结果和一次 Agent 回传;后续提交不改变结果。
|
||||
|
||||
### P1-4:Tool 与交互的取消、超时和并发提交需要收敛规则
|
||||
|
||||
**已解决(2026-08-05):** Runtime 是标准交互的唯一终态裁决者。用户回答、Agent dismiss、到期和
|
||||
Runtime 失败都竞争同一条交互的唯一结果。
|
||||
|
||||
当前规则:
|
||||
|
||||
```text
|
||||
Runtime 对仍为 pending / presented 的 interaction 执行原子状态写入
|
||||
→ 第一个成功写入的动作获胜
|
||||
→ 用户回答:持久化答案,Tool 得到 answered
|
||||
→ Agent dismiss:交互与 Tool 得到 cancelled
|
||||
→ expires_at 到期:交互与 Tool 得到 expired
|
||||
→ Runtime 失败:交互与 Tool 得到 failed
|
||||
→ 同一事务或等价原子动作中只创建一条 Tool 结果 outbox
|
||||
|
||||
任何后到的回答、dismiss、取消或超时处理
|
||||
→ interaction_already_final
|
||||
→ 不覆盖结果,不创建第二条 outbox
|
||||
```
|
||||
|
||||
MVP 中 interactive Tool 没有另一套独立 timeout;其期限就是交互 `expires_at`。Agent 主动停止等待必须走
|
||||
`dismiss`,不走独立的通用 Tool 取消路径。
|
||||
|
||||
### P1-5:`notice` 的完成时点和 Tool 结果需要固定
|
||||
|
||||
**已解决(2026-08-05):** `notice` 是 Interact / IM 中持久保留的只读卡片;Toast 只是同一条 notice
|
||||
记录的可选短暂提醒,不是没有历史的独立消息。
|
||||
|
||||
```text
|
||||
Runtime 校验并持久化 notice 卡片
|
||||
→ 交给 Interact 呈现
|
||||
→ 当前前台界面可同时 Toast 数秒
|
||||
→ Runtime 立即以 { outcome: "accepted" } 完成并回传一次 Tool 结果
|
||||
```
|
||||
|
||||
`accepted` 只表示 Runtime 已接受、保存并安排呈现,不表示用户已看见或阅读。Toast 自动消失、用户忽略
|
||||
Toast 或收起 notice 卡片,均不产生新的 Agent 结果;持久化或安排呈现前 Runtime 失败时,结果为 `failed`。
|
||||
|
||||
## P2:本迭代验收前必须处理(本次无未决项)
|
||||
|
||||
### P2-1:将普通标准交互统一当作敏感秘密输入
|
||||
|
||||
**不再适用(2026-08-05):** 此问题把所有标准交互一概当成秘密输入,边界过重且不符合产品语义。
|
||||
`notice`、`choice`、`confirm` 与普通 `input` 是人与 Agent 的正常 IM 会话内容:已提交的内容按普通 IM
|
||||
会话历史、保留与日志基线处理,不在 SDK v1 中额外定义为密码或秘密。全局日志基线仍然有效,不能因此
|
||||
随意打印会话正文、Tool 参数或 token。
|
||||
|
||||
未提交的 `input` 草稿仍只能存在于当前运行期;重启后清空,不自动提交、发送或进入 outbox。这是恢复
|
||||
和正确性要求,不代表普通 `input` 已升级为秘密输入能力。
|
||||
|
||||
## P4:遗留问题(不阻塞当前迭代)
|
||||
|
||||
### P4-1:首次评审中的历史提案可读性
|
||||
|
||||
**延期原因:** 顶部 Outline 和总体结论已经说明早期 `sdk.ui`、owner、`parent_call_id` 等提案不再是
|
||||
实施依据;这不影响当前 SDK、Runtime 或验收实现。
|
||||
|
||||
建议在下一次文档整理或新的设计评审时,将原始评审正文加上“仅供追溯,禁止作为实现、测试或验收依据”
|
||||
的更醒目标识,或移至附录。当前有效结论始终以主定义文档和评审记录顶部的 Outline 为准。
|
||||
|
||||
### P4-2:`password-input` 秘密输入原语
|
||||
|
||||
**延期原因:** 当前 `03` 要完成的是 `notice / choice / confirm / input` 的 SDK v1 与会话交互闭环;
|
||||
目前没有真实的密码、卡密、私钥或临时 token 输入场景。把秘密输入仅做成普通 `input` 的掩码样式,不能
|
||||
解决内容怎样保留、传递、重试和清理的问题,因此不在本迭代仓促实现。
|
||||
|
||||
**重新评估条件:** Agent 确实需要向用户收集密码、卡密、私钥、临时 token 或同类秘密时,启动单独设计。
|
||||
届时 `password-input` 应作为与 `input` 并列的 Agent 交互原语,由 Runtime 按 `kind` 强制执行安全契约,
|
||||
而不是允许 Agent 用普通 `input` 自行约定保护方式。至少要明确:界面不显示明文;主 IM 只保留“已提交
|
||||
敏感信息”等替代记录;内容不进入普通日志、Telemetry 或审计;可靠 outbox 的暂存、加密(如需要)、
|
||||
Agent 接收后的清理、重启、失败、重试与一次性传递规则。
|
||||
@@ -0,0 +1,174 @@
|
||||
# `03.sdk_and_coreapp` 第 3 次设计评审记录
|
||||
|
||||
> 评审编号:03
|
||||
> 评审日期:2026-08-05
|
||||
> 评审对象:[03.sdk_and_coreapp.md](03.sdk_and_coreapp.md)
|
||||
> 参考资料:[APP架构设计.md](../../APP架构设计.md)、[01.design_review.md](01.design_review.md)、[02.design_review.md](02.design_review.md)
|
||||
> 评审方式:基于当前主定义的独立只读复核;重点检查已确认的边界在契约、实施步骤与验收目标之间是否能够由同一套实现兑现。
|
||||
> 结论:产品层级、标准交互归属和受限 MiniApp 信任模型已稳定;未发现 P0 架构冲突。经本轮统一收敛,3 项 P1 与 2 项 P3 均已确认并回填主定义文档;后续不再进行纯设计评审,直接进入实现,并在实现完成后做一次关闭式复核。
|
||||
|
||||
## 问题清单(Outline)
|
||||
|
||||
> **状态标记:** ✅ 已确认并回填设计; 🟡 待产品/架构决策; 🔵 待在契约中细化; ⚪ 文档或实施编排建议; — 不适用或无此问题。
|
||||
>
|
||||
> 本清单是本次评审的当前有效视图。后文说明问题为何会造成实现分歧,并给出需要冻结的最小决策;在问题确认前,建议不是实施依据。确认后应先更新本清单,再回填 [03.sdk_and_coreapp.md](03.sdk_and_coreapp.md) 的契约、实施步骤和验收项。
|
||||
|
||||
| 状态 | 编号 | 问题 | 当前结论 / 下一步 |
|
||||
|---|---|---|---|
|
||||
| — | P0 | 阻塞级架构冲突 | 未发现。Runtime 为唯一通信与裁决方、标准交互属于 Interact、bundled MiniApp 受限运行的主线一致。 |
|
||||
| ✅ | P1-1 | 用户关闭 App 时,尚未结束的普通 MiniApp Tool 怎样收敛 | 已确认并回填:一旦 Runtime 接受关闭,未终态的普通 Tool 原子转为 `cancelled(app_closed)`;已提交结果只继续 outbox;随后关闭 Surface、停止 instance、结束子会话并恢复焦点。标准交互不自动取消。 |
|
||||
| ✅ | P1-2 | Agent `dismiss` 等待中的标准交互,缺少可执行的控制契约 | 已确认并回填:Runtime 私有 `interaction.dismiss` 以 `control_id + call_id` 请求;Runtime 从受认证 Envelope 推导 Agent/会话,幂等处理并只写唯一 cancelled outbox。App 已关闭事件带出仍 pending 的交互 call_id。 |
|
||||
| ✅ | P1-3 | `interactive` Tool 如何确定 App 子会话关联,和通用 `ToolDescriptor.target` 怎样一致 | 已确认并回填:无 `app_session_context` 一律归主 IM;有上下文时 Runtime 验证同 Agent、同父会话的 App 子会话,已关闭会话可仅作历史关联。interactive 不使用普通 Tool target。 |
|
||||
| ✅ | P3-1 | 标准交互请求类型不能直接作为 fixture 或代码依据 | 已确认并回填:去除重复字段,定义请求联合类型;可回答交互使用 Runtime 计算的 `expires_in_ms`,默认 15 分钟、范围 1 分钟至 24 小时;notice 不接受期限。 |
|
||||
| ✅ | P3-2 | Whiteboard 的完成后关闭表述与统一生命周期规则冲突 | 已确认并回填:所有 bundled App 的普通 Tool 完成只结束 Tool;关闭 Surface/instance/子会话只能由显式 Lifecycle 关闭处理。重复目录条目已删除。 |
|
||||
|
||||
## 通过项
|
||||
|
||||
- `Interact` 是唯一 `kind = system` MiniApp;Task Dashboard 与 Whiteboard 均是 `kind = bundled`,没有可信 Host DOM、Tauri、Transport、Store、Agent 或任意网络特权。
|
||||
- `notice / choice / confirm / input` 始终是人与 Agent 的会话交互,不进入任何 MiniApp SDK Inbox 或 `sdk.tools.subscribe`;前台 bundled App 上的视觉覆盖不成为 owner 或提交权限。
|
||||
- Runtime 保存交互归属并以原子状态写入决定唯一结果;用户首次有效回答、Agent dismiss、到期和 Runtime 失败不会产生多条 Agent outbox。
|
||||
- App 子会话只保存人与 Agent 围绕 App 的交互,不记录 Dashboard 表单、画板编辑等 App 内部业务动作;关闭 App 不自动取消仍 pending 的 Interact 交互。
|
||||
- 普通 `input` 按现有 IM 内容与日志基线处理;秘密输入已正确作为未来独立的 `password-input` P4 遗留事项保留在 [02.design_review.md](02.design_review.md)。
|
||||
|
||||
## P0:阻塞问题
|
||||
|
||||
无。
|
||||
|
||||
## P1:开始相关编码前必须确认(均已解决)
|
||||
|
||||
### P1-1:用户关闭 App 时,尚未结束的普通 MiniApp Tool 怎样收敛
|
||||
|
||||
**用人话说:** 用户把 Task Dashboard 或 Whiteboard 关掉时,Runtime 不能让刚才交给那个 App 的工作
|
||||
悬在半空。Agent 要么收到“这项工作已取消/失败”的唯一结果,要么 Runtime 明确保留一个可恢复、仍有执行者
|
||||
的工作;不能只关闭画面而不说明 Tool 的命运。
|
||||
|
||||
当前文档同时出现了三种没有被统一的说法:
|
||||
|
||||
```text
|
||||
Task Dashboard Tool 完成
|
||||
→ 不自动关闭 Dashboard 或 App 子会话
|
||||
|
||||
Whiteboard App 完成或失败
|
||||
→ Runtime 关闭 Surface、恢复 Interact
|
||||
|
||||
MiniApp requestClose()
|
||||
→ Runtime 检查 pending Tool、Surface、operation 和 Policy
|
||||
→ 允许关闭或返回拒绝码
|
||||
```
|
||||
|
||||
最后一条没有说明“检查之后”的规则。若用户关闭正在执行 Tool 的 App,Tool 是被拒绝关闭、由 Runtime 先取消
|
||||
Tool、由 App 收到取消后再关闭,还是允许 Surface 消失但实例在后台恢复执行?这些选择对 outbox、恢复和用户
|
||||
看到的状态都不同。Whiteboard 的“完成后关闭”也与 Task Dashboard 的“完成不关闭”相互冲突。
|
||||
|
||||
**已确认(2026-08-05):** 一旦 Runtime 接受用户、MiniApp 或 Policy 的关闭请求,关闭的意思就是释放该
|
||||
App instance,不是把它偷偷留在后台。Runtime 不再向该 instance 投递新 Tool;对绑定该 instance 且仍为
|
||||
`received / routing / waiting_for_app / running` 的普通 Tool,以 `app_closed` 原因原子转为 `cancelled`,并且
|
||||
每条 Tool 只写一条 cancelled outbox。已处于 `submitted` 的 Tool 结果不可改写,Runtime 继续将已固定的结果
|
||||
可靠发出。之后 Runtime 关闭 Surface、停止 instance、结束 App 子会话并恢复前一有效前台 App。
|
||||
|
||||
同一时刻 Tool 完成与关闭竞争时,第一个原子终态写入获胜;重复关闭、重启恢复和迟到上报不得产生第二条
|
||||
outbox。用户若只想暂时离开 App,应进入后台而不是关闭。此规则只处理普通 MiniApp Tool;仍 pending 的
|
||||
人与 Agent 标准交互不自动取消,Runtime 仅通知 Agent,由 Agent 选择是否 dismiss。
|
||||
|
||||
以下原“需要冻结”的项目均由上述决议覆盖:
|
||||
|
||||
1. 对每种 Tool 状态(`waiting_for_app`、`running`、`submitted` 等),规定用户/Policy 请求关闭时的行为;
|
||||
2. 规定普通 Tool 的最终结果由谁写入、是否必须先让 Tool 进入 `completed / failed / cancelled / expired` 才能完成关闭;
|
||||
3. 明确“关闭 App instance”“关闭该 App 的 Surface”“Tool 成功/失败”三者不是同一个事件,并定义允许的先后顺序;
|
||||
4. 将 Task Dashboard 与 Whiteboard 的完成、关闭和焦点恢复规则统一到同一条生命周期原则;
|
||||
5. 增加关闭进行中 Tool、重启恢复和重复关闭只产生一个最终 Tool outbox 的验收 fixture。
|
||||
|
||||
### P1-2:Agent `dismiss` 等待中的标准交互,缺少可执行的控制契约
|
||||
|
||||
**用人话说:** 文档已经允许 Agent 看到“画板已关闭”后,决定把之前的问题收起来。这很好;但还没有写清
|
||||
Agent 发来的“收起这题”消息长什么样,Runtime 怎么确认它收的是正确那一道题,以及网络重发时如何不重复
|
||||
通知 Agent。实现者因此可能各自发明一个临时控制消息。
|
||||
|
||||
当前仅有行为描述:
|
||||
|
||||
```text
|
||||
Agent remote dismiss
|
||||
→ Runtime 将 pending / presented interaction 终结为 cancelled
|
||||
→ 写入唯一 cancelled outbox
|
||||
```
|
||||
|
||||
但缺少下面的契约:
|
||||
|
||||
- Agent 发起 `dismiss` 使用 `interaction_id`、原始 `call_id`,还是二者都使用;
|
||||
- Runtime 如何从已保存记录校验 `agent_id`、`conversation_id` 与当前状态,而不是信任客户端字段;
|
||||
- 目标已回答、已到期或同一条 dismiss 重放时,应得到何种稳定回执,是否绝不新增 outbox;
|
||||
- Runtime 发出的 App 已关闭事件如何关联原 Tool/interaction,使 Agent 能准确选择要 dismiss 的问题;
|
||||
- dismiss 控制请求本身如何去重、审计并在 Runtime 重启后恢复处理。
|
||||
|
||||
**已确认(2026-08-05):** 定义 Runtime 私有的 Agent → Runtime `interaction.dismiss` 控制契约。Agent 使用
|
||||
自己发起原 interactive Tool 时持有的 `call_id` 定位目标,并为每次控制请求提供唯一 `control_id`。Runtime 从
|
||||
受认证 Envelope 取得 `agent_id` 与 `conversation_id`,不信任请求额外携带的归属字段;它以这两个值和
|
||||
`call_id` 查找交互,原子地将仍 pending / presented 的记录转为 cancelled 并写入唯一 outbox。已终态或重放
|
||||
请求只返回稳定幂等回执,不覆盖结果、不再写 outbox。
|
||||
|
||||
App 已关闭事件会携带仍 pending 的 `pending_interaction_call_ids`,让 Agent 能按业务需要精确 dismiss;该控制
|
||||
契约不属于 MiniApp SDK,也不能投递给 Interact 或 bundled MiniApp。
|
||||
|
||||
### P1-3:`interactive` Tool 如何确定 App 子会话关联,和通用 `ToolDescriptor.target` 怎样一致
|
||||
|
||||
**用人话说:** 视觉上哪个 App 在前台,不等于 Agent 的问题就属于哪个 App。例如用户正看 Whiteboard,
|
||||
Agent 仍可能问一条普通 IM 问题;反过来,画板已经关了,Agent 仍可能针对刚才的画板提问。因此 Runtime
|
||||
不能用“当前前台 App”猜测问题应该放进哪个子会话。
|
||||
|
||||
当前记录有可选 `app_session_id`,且要求与 App 有关的问题写进子会话;但没有说明 Agent Tool 请求如何
|
||||
表达这层上下文、Runtime 如何验证它。与此同时,所有 `ToolDescriptor` 都要求 `target.app_scope`,而
|
||||
`interactive` 明确不创建/复用任何业务 MiniApp,也不投递给 Interact 的 SDK Tool Inbox。这会造成至少两种
|
||||
不兼容实现:有人将交互强行 target 到 `chat`,有人直接跳过 `target`,也有人按当前前台 App 自动归档。
|
||||
|
||||
**已确认(2026-08-05):** 不带 `app_session_context` 的 interactive Tool 一律记录在主 IM,不创建或猜测
|
||||
App 子会话。Agent 若要关联某段 App 工作,可在 interactive Tool 的运行时上下文中提供
|
||||
`app_session_context.app_session_id`;Runtime 必须验证其属于同一 `agent_id`、父 conversation 和真实 App
|
||||
子会话。已经关闭的子会话仍可作为历史上下文关联,但不会复活 instance 或旧问题。验证失败则拒绝该 Tool,
|
||||
不创建交互。
|
||||
|
||||
`handling = interactive` 不使用普通 MiniApp Tool 的 `target`,也不因此变为 Interact MiniApp Tool。Runtime
|
||||
在创建 App 子会话时向当前 Agent 可靠发送其稳定 ID,供后续 Agent 交互引用。
|
||||
|
||||
以下原“需要冻结”的项目均由上述决议覆盖:
|
||||
|
||||
1. 不带经验证 App 上下文的 `interactive` Tool 一律写入主 IM,不创建 App 子会话;
|
||||
2. 若 Agent 需要关联某段 App 工作,定义它可引用的稳定 App 子会话标识,以及 Runtime 必须验证的
|
||||
`agent_id`、父 conversation、App session 状态和权限边界;已关闭 App 关联的问题是否仍允许保留,需要明确;
|
||||
3. 为 `handling = interactive` 定义与普通 `ToolDescriptor.target` 不同的明确规则:例如 target 对它不适用,
|
||||
或只允许一个受限的“会话上下文”字段;不能让它暗中变成 Interact MiniApp Tool;
|
||||
4. 在 fixture 中覆盖“Whiteboard 前台但问题属于主 IM”“App 已关闭但问题仍关联旧子会话”“非法/跨会话
|
||||
`app_session_id` 被拒绝并不产生交互”三种情况。
|
||||
|
||||
## P2:本迭代验收前必须处理(本次无未决项)
|
||||
|
||||
本次未发现独立 P2 问题。P1 项处理后,应把其中的关闭、dismiss、上下文绑定 fixture 纳入本迭代验收。
|
||||
|
||||
## P3:本迭代结束前必须处理(均已解决)
|
||||
|
||||
### P3-1:标准交互请求类型不能直接作为 fixture 或代码依据
|
||||
|
||||
`ChoiceRequest` 代码块中 `prompt` 出现两次。它不是不同字段,直接照抄会造成 TypeScript 重复属性定义。
|
||||
此外,记录使用了尚未声明的 `StandardInteractionRequest`,文字规定 Agent 可给出 1 分钟至 24 小时的期限,
|
||||
但四种最低请求类型都没有统一的期限字段。
|
||||
|
||||
**已确认(2026-08-05):** 删除重复 `prompt`;`StandardInteractionRequest` 由四种请求组成。可回答交互
|
||||
在请求中使用 `expires_in_ms`,Runtime 根据自身当前时间计算并持久化 `expires_at`;未提供时默认 15 分钟,
|
||||
仅接受 1 分钟至 24 小时。notice 不等待回答,也不接受期限。fixture 覆盖默认、越界和 notice 错带期限。
|
||||
|
||||
### P3-2:Whiteboard 的完成后关闭表述与统一生命周期规则冲突
|
||||
|
||||
Task Dashboard 已清楚规定 Tool 完成不会关闭 App;Whiteboard 的最小体验却写成“App 完成或失败,Runtime
|
||||
关闭 Surface、恢复 Interact”。这会让两个同为 `kind = bundled` 的参考 App 获得不同且没有声明依据的
|
||||
生命周期语义,也和“只有 `AppLifecycleManager.close(instance_id)` 才停止实例/结束子会话”不一致。
|
||||
|
||||
**已确认(2026-08-05):** Whiteboard 与 Task Dashboard 使用相同规则:Tool `complete / fail` 只结束 Tool;
|
||||
Surface、instance、焦点和 App 子会话仅由前后台或显式 `AppLifecycleManager.close` 处理。SDK v1 不为
|
||||
Whiteboard 预设“提交后自动关闭”的例外;将来若需要,必须作为显式 Lifecycle Policy 并遵循 P1-1 的关闭收敛。
|
||||
|
||||
实现模块目录树中的 `conversation-store.ts` 也重复出现一次,应一并删除重复行,避免错误引导目录改动。
|
||||
|
||||
## P4~P5:遗留问题
|
||||
|
||||
本次没有新增 P4/P5。第 2 次评审已登记的 `P4-1`(历史提案可读性)和 `P4-2`(未来 `password-input`
|
||||
秘密输入原语)继续作为不阻塞当前迭代的遗留问题,其延期原因与重新评估条件以
|
||||
[02.design_review.md](02.design_review.md) 为准。
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
||||
# LineUp App 迭代文档
|
||||
|
||||
每个迭代使用一个独立目录,目录名固定为“迭代编号.简短名称”。目录内至少保留该迭代的主定义文档;
|
||||
每进行一次设计、架构或实现评审,都必须新增一份独立评审记录,不覆盖之前的记录。
|
||||
|
||||
```text
|
||||
迭代/
|
||||
├── 00.base/
|
||||
│ └── 00.base.md
|
||||
├── 01.kernel/
|
||||
│ └── 01.kernel.md
|
||||
└── 03.sdk_and_coreapp/
|
||||
├── 03.sdk_and_coreapp.md # 本迭代的权威目标、范围、契约、步骤和验收
|
||||
├── 01.design_review.md # 第 1 次评审记录
|
||||
├── 02.design_review.md # 第 2 次评审记录
|
||||
└── 03.design_review.md # 第 3 次评审记录
|
||||
```
|
||||
|
||||
约定如下:
|
||||
|
||||
1. 主定义文档命名为 `<迭代目录名>.md`,是当前实施依据;
|
||||
2. 评审记录按发生顺序编号,命名为 `<两位序号>.design_review.md`;
|
||||
3. 每份评审记录必须在元信息之后、评审正文之前维护“问题清单(Outline)”,格式参考
|
||||
[`01.design_review.md`](03.sdk_and_coreapp/01.design_review.md):列出状态、编号、问题和当前结论/下一步;
|
||||
4. 评审记录必须写明评审日期、编号、对象、方式和总体结论。新问题先在 Outline 中登记;问题确认后先
|
||||
更新 Outline 状态,再回填主定义文档、验收项和评审正文;
|
||||
5. 已确认的结论需要回填主定义文档和验收项;评审记录保留原始意见,用于追溯,不能替代主定义文档;
|
||||
6. 迭代完成后的实现验收、发布复盘等文档也保留在对应目录内,并使用清晰的递增编号。
|
||||
|
||||
## 评审优先级与遗留问题
|
||||
|
||||
评审问题使用 `P0`~`P5` 标示处理优先级。优先级表达的是“最晚何时必须解决”,而不是问题描述的
|
||||
修辞强弱。
|
||||
|
||||
| 级别 | 含义 | 当前迭代的处理规则 |
|
||||
|---|---|---|
|
||||
| P0 | 根本性阻塞:安全边界、数据正确性或总体架构不能成立。 | 立即处理;在解决前不得进入相关实现。 |
|
||||
| P1 | 关键规则未定:不解决会让不同实现互相冲突或明显返工。 | 在开始相关模块实现前处理。 |
|
||||
| P2 | 交付或验收缺口:核心方向正确,但缺失会使恢复、安全验证或验收不完整。 | 必须在本迭代验收前处理。 |
|
||||
| P3 | 局部行为、一致性或质量问题:存在安全默认行为,但相关模块完成前仍需收敛。 | 必须在本迭代结束前处理。 |
|
||||
| P4 | 优化、可读性或维护性建议。 | 可以登记为遗留问题,不阻塞当前迭代。 |
|
||||
| P5 | 观察记录或未来机会,当前没有足够的产品需求或证据进入排期。 | 可以登记为遗留问题,不纳入当前迭代。 |
|
||||
|
||||
因此,**P0~P3 必须在当前迭代处理完毕,P4~P5 可以作为遗留问题延期处理。**
|
||||
|
||||
延期不是删除:评审中出现 P4/P5 时,必须在该评审文件的 Outline 或“遗留问题”段落中保留编号、问题、
|
||||
延期原因和建议重新评估的阶段/条件。只有在对应迭代主定义文档或新的评审记录中明确重新纳入后,才将
|
||||
其提升为当前迭代的 P0~P3 问题。
|
||||
Reference in New Issue
Block a user