diff --git a/设计/02.正式方案/app_final_design.md b/设计/02.正式方案/app_final_design.md index aa1a22b..251d847 100644 --- a/设计/02.正式方案/app_final_design.md +++ b/设计/02.正式方案/app_final_design.md @@ -2,7 +2,7 @@ **版本:** 1.0(当前开发基线) **状态:** 当前 App 设计的唯一汇总入口 -**日期:** 2026-08-04 +**日期:** 2026-08-06 **来源:** [App 层架构方案](lineup-app-layer-architecture.md)、[Runtime 与 App SDK 架构方案](lineup-runtime-sdk-architecture.md)、[UI Surface 与 App Capability 协议](lineup-ui-surface-protocol.md) --- @@ -50,7 +50,7 @@ AppServer / IM | App 生态阶段 | 当前只使用本地短作用域,如 `chat`、`whiteboard`、`draw-and-guess`;暂不引入供应商身份、反向域名 App ID 或全局命名空间。 | | 丰富交互 | 标准内容先用可信内建组件;复杂互动用受限 Surface;设备/系统动作只能经 Capability Gateway。 | | Agent 工具 | App 在 Manifest 声明方法,Runtime 汇总为动态 Inventory;Agent 只能调用当前 Inventory 中的方法。 | -| Runtime SDK | SDK 提供状态订阅、Agent 消息订阅、Action、Tool、应用导航、Artifact、存储、Capability 和生命周期接口。 | +| Runtime SDK | SDK 提供状态订阅、Agent 消息订阅、Action、Tool、应用导航、Artifact、instance 级业务状态快照、Capability 和生命周期接口。业务状态由 Runtime 持久化和隔离,App 不直接拥有存储。 | | Host | Tauri Desktop Host 与同代码的 Web Reference Host 是当前唯一实现/验收基线:前者是正式桌面外壳,后者是浏览器开发和验证入口;它们不是两个客户端。不规划独立 Android/Kotlin 客户端或 Wails Host。 | 本文件优先于来源文档中关于**后续 Runtime 重构**的结构描述。来源文档中的 M0~M4 完成记录、当前已实现字段和历史验收事实仍然有效;它们不自动成为新 Runtime 的长期模块边界。 @@ -137,7 +137,7 @@ conversations conversation、关联 Agent、消息引用、任务、交互、Artifact 元数据。 apps - App Registry、默认 App、运行实例、每个 App 的可靠 Inbox。 + App Registry、默认 App、运行实例、每个 App 的可靠 Inbox 与按 instance 隔离的业务状态快照。 tools Tool 声明、Inventory revision、调用记录、operation 与进度。 @@ -449,7 +449,7 @@ interface LineUpAppRuntimeSDK { readonly tools: AppToolAPI; readonly apps: AppNavigationAPI; readonly artifacts: ArtifactAPI; - readonly storage: ScopedStorageAPI; + readonly instanceState: MiniAppInstanceStateAPI; readonly capabilities: CapabilityRequestAPI; readonly diagnostics: DiagnosticsAPI; } @@ -505,14 +505,57 @@ interface AppNavigationAPI { App 只能提交声明性 Action,不能构造原始 Agent Envelope。跨 App 打开、聚焦和关闭也必须经 Runtime 校验目标 App 的启用状态、入口、展示模式和会话作用域。 -### 6.3 Artifact、存储与 Capability +### 6.3 MiniApp instance 业务状态、Artifact 与 Capability + +Runtime 为每个 MiniApp instance 提供一份受限的业务状态快照。MiniApp 定义状态的业务语义和 +Manifest schema;Runtime 是持久化、隔离、并发裁决、恢复和清理的唯一所有者。它不是供 App 任意 +查询或读写的数据库,也不等同于 Conversation Store、Tool/operation Store、outbox 或 Artifact Store。 + +```ts +interface MiniAppInstanceStateAPI { + get(): Promise<{ + state: State | null; + revision: number; + }>; + + replace(request: { + state: State; + expected_revision: number; + }): Promise<{ + revision: number; + }>; +} +``` + +每次调用的存储边界由不可伪造的 SDK context 推导: + +```text +app_scope + instance_id +``` + +App 不得指定、读取或写入其他 App、其他 instance、Conversation Store 或 Runtime 内部记录。Runtime +必须校验 Manifest 声明的 `state_schema`、`state_schema_version`、大小配额与 `expected_revision`;旧 +Surface、重复点击或并发写入不能覆盖较新的快照。刷新、Surface 重载和 Runtime 重启后,Runtime 向 +同一个可恢复 instance 投影最后一个有效快照。实例关闭、App 禁用/移除时,Runtime 按 Manifest 的 +retention policy 冻结、保留或清理快照,MiniApp 本身无权绕过生命周期复活旧状态。 + +第一版的 `retention` 只允许 `delete_on_close`(默认,关闭时删除)和 `retain_readonly`(关闭后 +保留为历史/新 instance 的受控上下文,但旧 instance 不可再写入)。App 禁用或移除时,Runtime 仍按 +用户确认和全局保留策略清理其快照;状态 schema 升级由新 App 版本通过受控读取/替换完成,Runtime 不执行 +任意 App 提供的迁移脚本。 + +业务状态快照不替代 Runtime operation。凡是会影响 Agent Tool 终态、deadline、outbox、焦点、权限或 +实例生命周期的动作,仍必须通过 Runtime Command / operation 原子裁决。例如 Pomodoro 的显示状态 +可以保存在自己的 instance snapshot,但 `ends_at` 到期、用户中断与唯一 Tool result 必须由 Runtime +的 deadline operation 负责,不能由 MiniApp 自行完成或直接发送结果。 ```text Artifact Runtime 管理元数据、受限读取、创建、会话引用、下载和用户保存。 -Storage - Runtime 为每个 app_scope 提供隔离 JSON 数据与 snapshot;管理配额、迁移和清理。 +Instance State + Runtime 为每个 app_scope + instance_id 提供隔离的 JSON snapshot;管理 schema、revision、配额、 + 恢复与生命周期清理。UI 动画、滚动位置等短暂渲染状态不必持久化。 Capability App 以 purpose + input 请求;Runtime 决定是否确认、调用 Host、审计并返回受控结果。 @@ -617,7 +660,7 @@ iframe sandbox="allow-scripts" - 只经严格 postMessage / Surface Bridge SDK 通信 ``` -Runtime/Host 必须验证 `event.source`、当前 `instance_id`、消息类型、事件名、JSON schema、消息大小和声明的投递策略。Surface 只能接收 state patch、触发已声明事件、处理已声明 Tool 调用、使用隔离存储和请求受控 Capability。 +Runtime/Host 必须验证 `event.source`、当前 `instance_id`、消息类型、事件名、JSON schema、消息大小和声明的投递策略。Surface 只能接收 state patch、触发已声明事件、处理已声明 Tool 调用、通过 Bridge 使用 Runtime 提供的自身 instance 状态快照和请求受控 Capability。 Surface 不能: @@ -650,13 +693,20 @@ Surface 不能: "required_features": [ "app.lifecycle.v1", "app.tools.v1", - "surface.state.v1" + "surface.state.v1", + "app.instance-state.v1" ], "optional_features": ["artifact.create.v1"] }, "entrypoints": [ {"id": "canvas", "kind": "contextual", "default_eligible": false} ], + "instance_state": { + "state_schema_version": 1, + "state_schema": {"type": "object", "additionalProperties": false}, + "max_bytes": 65536, + "retention": "delete_on_close" + }, "agent_subscriptions": [ {"type": "lineup.app.state.patch", "scope": "instance"}, {"type": "lineup.tool.invoke", "scope": "conversation"} @@ -685,7 +735,7 @@ lineup-app/ │ ├── apps/ app registry、instance manager、default app │ ├── tools/ tool registry、inventory、call lifecycle │ ├── capabilities/ capability gateway -│ └── persistence/ runtime store、app inbox、artifact metadata +│ └── persistence/ runtime store、app inbox、instance state snapshot、artifact metadata ├── runtime-sdk/ │ ├── app.ts Core App SDK │ ├── manifest.ts App/Tool/Subscription 类型 @@ -771,6 +821,8 @@ F0 与 F1 中支撑图文聊天迁移的最小闭环已落地并经自动化回 - 在 Runtime 连接与状态变化时同步 revisioned Inventory 给 Agent;Tool 可声明是否需要启动 App、是否要求前台以及结束后是否恢复原焦点; - 实现 Agent Tool → Runtime → App SDK → result/progress 的可靠闭环; - 将 MVP-R1 已实现的 Chat App Inbox、ACK、断线/崩溃恢复和拒绝码推广为所有受管理 App 的通用可靠投递机制。 +- 实现 `app.instance-state.v1`:按 `app_scope + instance_id` 隔离的 schema 校验状态快照、revision + 并发控制、配额、重启恢复与 lifecycle 清理;它不得替代 Tool/operation/outbox 的终态裁决。 ### F4:第一个 Installed App @@ -795,6 +847,9 @@ F0 与 F1 中支撑图文聊天迁移的最小闭环已落地并经自动化回 工具与能力 [ ] Agent 只能调用当前 Inventory 中、参数 schema 合法的 Tool。 [ ] Tool 的 result/progress/error 经 Runtime 校验、持久化和审计。 + [ ] MiniApp 只能读取和替换自身 instance 的 schema 合法状态快照;旧 revision、越界 instance、超配额 + 和非法 schema 均被 Runtime 拒绝,刷新/重启后仅恢复最后一个有效版本。 + [ ] 业务状态快照不能直接完成 Tool、写 outbox、改变焦点或绕过实例生命周期。 [ ] App / Surface 无法绕过 Capability Gateway 获得系统权限。 安全 diff --git a/设计/02.正式方案/lineup-app-layer-architecture.md b/设计/02.正式方案/lineup-app-layer-architecture.md index de9f7b3..c92ba6e 100644 --- a/设计/02.正式方案/lineup-app-layer-architecture.md +++ b/设计/02.正式方案/lineup-app-layer-architecture.md @@ -33,7 +33,7 @@ Remote Agent / AppServer ┌────────────────────────────────────────────────────────┐ │ LineUpRuntime │ │ Transport · sync loop · Store · Outbox · App Inbox │ -│ scope 路由 · Compatibility Adapter · Tool · Capability │ +│ instance state · scope 路由 · Tool · Capability │ └─────────────────────┬──────────────────────────────────┘ │ Runtime SDK ▼ @@ -45,9 +45,9 @@ Remote Agent / AppServer | 层 | 必须负责 | 不得负责 | |---|---|---| -| `LineUpRuntime` | 处理 AppServer/Agent 通信、会话、原始协议兼容、scope 筛选、Store、sync、outbox、App Inbox、Tool 路由、App 实例和前台焦点、权限与恢复。 | 具体业务页面 DOM。 | +| `LineUpRuntime` | 处理 AppServer/Agent 通信、会话、原始协议兼容、scope 筛选、Store、sync、outbox、App Inbox、Tool 路由、App 实例和前台焦点、权限、按 instance 隔离的 MiniApp 业务状态快照与恢复。 | 具体业务页面 DOM 或任意 MiniApp 的业务语义。 | | Interaction Core App | 用户实际使用的主交互界面:当前是 Chat/IM,未来包含 Audio/Video 模式、标准交互原语和扩展结果投影。 | 直连 AppServer、维护 cursor、解析 Agent 原始包、调度其他 App 或直接写 Runtime Store。 | -| Runtime SDK | App 与 Runtime 之间唯一的受限接口:提供 snapshot、订阅、Agent 消息、Inbox ACK、Runtime Action、App 导航和生命周期请求。 | 把 Transport、token、Host 特权 API 暴露给 App。 | +| Runtime SDK | App 与 Runtime 之间唯一的受限接口:提供 snapshot、订阅、Agent 消息、Inbox ACK、Runtime Action、App 导航、生命周期请求及自身 instance 的 schema 校验业务状态快照。 | 把 Transport、token、Host 特权 API、Conversation Store 或其他 App/instance 状态暴露给 App。 | | Tauri/Web Host | 把 Runtime 放进桌面窗口或浏览器,并提供对应的 DOM/系统能力。 | 解释协议、决定 App 调度、绕过 Runtime 路由和策略。 | | Surface | 在隔离执行域呈现已验证 Bundle,并经受限 bridge 上报事件。 | 访问 Host DOM、登录态、Tauri API、任意网络。 | @@ -78,7 +78,7 @@ lineup-app/tauri/src/ ├── app-management/ # Registry、App Instance、焦点、生命周期、Host ├── communication/ # Transport Adapter ├── coordination/ # Runtime、Kernel、Envelope、Tool/Task/交互编排 - ├── persistence/ # Conversation Store、Outbox、App Inbox + ├── persistence/ # Conversation Store、Outbox、App Inbox、instance state snapshot ├── protocol/ # lineup.v1 解码与 golden fixture ├── surfaces/ # Surface 生命周期、Bundle、隔离 Host ├── capabilities/ # Registry、执行、审计 diff --git a/设计/02.正式方案/lineup-runtime-sdk-architecture.md b/设计/02.正式方案/lineup-runtime-sdk-architecture.md index fa433b8..503780a 100644 --- a/设计/02.正式方案/lineup-runtime-sdk-architecture.md +++ b/设计/02.正式方案/lineup-runtime-sdk-architecture.md @@ -2,7 +2,7 @@ **版本:** 0.1(架构基线) **状态:** 已确认的 Runtime / SDK 方向;尚未完全落地到当前 Tauri 实现 -**日期:** 2026-08-04 +**日期:** 2026-08-06 **关联方案:** [LineUp App 层架构设计方案](lineup-app-layer-architecture.md)、[LineUp UI Surface 与 App Capability 协议](lineup-ui-surface-protocol.md) > **汇总入口:** 本文是 Runtime / SDK 初稿的详细来源。后续设计以 [LineUp App 最终设计方案](app_final_design.md) 为准;后者收敛了本地 `app_scope`、兼容迁移和实施顺序。 @@ -71,6 +71,8 @@ AppServer / IM Transport 6. **系统能力由 Runtime 独占。** Agent 和 App 只能请求 Capability;文件、麦克风、通知、剪贴板、窗口、密钥等均不得直接访问。 7. **应用状态、消息和副作用分离。** Event 是已发生事实,Command 是请求动作,Effect 是 Runtime 执行的受控副作用。 8. **UI 是 Runtime 状态的投影。** App 重启、断网或 Surface 回收后,Runtime 能依据持久化状态恢复到可解释的协作状态。 +9. **MiniApp 业务状态由 Runtime 托管。** App 只能通过 SDK 保存其当前 instance 的、Manifest schema + 声明的状态快照;不能直接访问浏览器/Host 存储、Conversation Store、Tool/operation Store 或其他 App 数据。 ## 3. 运行时分层 @@ -431,7 +433,7 @@ interface LineUpAppRuntimeSDK { readonly tools: AppToolAPI; readonly apps: AppNavigationAPI; readonly artifacts: ArtifactAPI; - readonly storage: ScopedStorageAPI; + readonly instanceState: MiniAppInstanceStateAPI; readonly capabilities: CapabilityRequestAPI; readonly diagnostics: DiagnosticsAPI; } @@ -542,18 +544,48 @@ App,但不能直接决定其他 App 的生命周期。 Artifact、媒体与文件通过 Runtime Artifact Store 暴露元数据、受限读取、创建、会话引用和用户保存;App 不取得任意路径或跨 App 文件访问权。 -### 8.5 存储与 Capability +### 8.5 MiniApp instance 状态与 Capability -每个 App 使用 Runtime 管理的作用域隔离存储: +每个 MiniApp instance 使用 Runtime 管理的、按实例隔离的业务状态快照: ```text -app-data/chat/ -app-data/app-registry/ -app-data/voice/ -app-data/whiteboard/ +app_scope + instance_id + → 一份 schema 校验的 JSON state snapshot + → revisioned replace + → 恢复同一个未结束 instance 时投影给该 App ``` -SDK 中的 `storage` 提供 JSON 数据和可恢复 snapshot,Runtime 负责配额、升级迁移、禁用和移除时的清理。Capability 必须通过统一请求接口: +这不是通用文件系统、键值数据库或跨 instance 查询接口。App 负责定义状态的业务含义;Runtime 负责 +根据 Manifest 校验 schema、schema version、大小配额和保留策略,并保证调用方只能操作当前 SDK context +中的 instance。Runtime 也负责 revision 比较、原子写入、重启恢复以及禁用、移除和关闭时的冻结/保留/清理。 +第一版 `retention` 仅支持 `delete_on_close`(默认)与 `retain_readonly`;后者只保留历史或向新 instance +提供受控上下文,不能让已关闭 instance 再次写入。App 禁用或移除后的最终清理由 Runtime 按用户确认和全局 +保留策略执行;状态 schema 升级只能经新 App 版本的受控读取/替换完成,Runtime 不执行 App 提供的迁移脚本。 + +```ts +interface MiniAppInstanceStateAPI { + get(): Promise<{ + state: State | null; + revision: number; + }>; + + replace(request: { + state: State; + expected_revision: number; + }): Promise<{ + revision: number; + }>; +} +``` + +`expected_revision` 不匹配时 Runtime 返回稳定的 `state_revision_conflict`;非法 schema、越过实例边界或 +超出配额时分别返回 `state_invalid`、`state_scope_mismatch`、`state_quota_exceeded`。App 不得用状态 +快照直接完成 Agent Tool、写 outbox、改变焦点或执行 Capability。涉及 deadline、Tool 终态、outbox、焦点 +或生命周期的业务命令仍要走 Runtime 的 Command / operation 路径;Runtime 可在同一原子事务中更新 +operation 与 instance state,但两者保持不同的事实来源。 + +MiniApp 的短暂渲染状态(动画、滚动位置、临时 loading)不要求保存;Conversation / App 子会话保存人与 +Agent 可见的静态交互和结果,也不是 MiniApp 业务状态的事实来源。Capability 必须通过统一请求接口: ```ts interface CapabilityRequestAPI { @@ -573,7 +605,7 @@ Runtime 检查 Manifest 声明、App 启用状态、Host 支持、前台要求 | 层级 | 例子 | 可用接口 | 明确禁止 | |---|---|---|---| | Core App | Chat、App Registry、Settings | 完整 App Runtime SDK(仍受 Scope 和 Capability 约束) | 直接绕过 Transport/Capability Gateway。 | -| Installed App | 白板、语音、游戏 | 受限 Surface Bridge SDK:状态、已声明 Tool、事件、隔离存储、Capability request | Tauri invoke、父 DOM、token、任意网络、其他 App 数据。 | +| Installed App | 白板、语音、游戏 | 受限 Surface Bridge SDK:状态、已声明 Tool、事件、当前 instance 的 Runtime 状态快照、Capability request | Tauri invoke、父 DOM、token、任意网络、其他 App 数据。 | | Host Provider | Tauri Desktop / Web Reference Host 实现 | Runtime 内部 Host Provider API | 向 Runtime 提供系统能力;不直接暴露给 Agent/Surface。 | 下载型 App 使用 `iframe sandbox="allow-scripts"` 或等价隔离容器;不包含 `allow-same-origin`。它经严格 CSP、来源验证和消息 schema 校验的桥接访问 Surface SDK。第一版不支持市场下载包执行本地 Rust、Node、Shell 或任意浏览器特权代码。 @@ -589,12 +621,18 @@ Runtime 检查 Manifest 声明、App 启用状态、Host 支持、前台要求 "version": "1.2.0", "runtime_sdk": { "api_version": "1", - "required_features": ["app.lifecycle.v1", "app.tools.v1", "surface.state.v1"], + "required_features": ["app.lifecycle.v1", "app.tools.v1", "surface.state.v1", "app.instance-state.v1"], "optional_features": ["artifact.create.v1"] }, "entrypoints": [ {"id": "canvas", "kind": "contextual", "default_eligible": false} ], + "instance_state": { + "state_schema_version": 1, + "state_schema": {"type": "object", "additionalProperties": false}, + "max_bytes": 65536, + "retention": "delete_on_close" + }, "agent_subscriptions": [ {"type": "lineup.app.state.patch", "scope": "instance"}, {"type": "lineup.tool.invoke", "scope": "conversation"} @@ -622,6 +660,7 @@ identity 用户、设备、认证会话 connection AppServer 状态、cursor、重试、outbox conversations Agent 映射、消息引用、任务、交互、Artifact 元数据 apps App Registry、默认 App、运行实例、App Inbox +app_state 按 app_scope + instance_id 隔离的业务状态快照与 revision tools 已声明 Tool、可见性、调用与 operation capabilities 授权、执行状态、最小审计 ``` @@ -648,7 +687,7 @@ lineup-app/ │ ├── apps/ App Manager、Registry、Instance、Focus、Lifecycle Manager │ ├── tools/ Tool Registry、Inventory、Router、call lifecycle │ ├── capabilities/ Capability Gateway -│ └── persistence/ Runtime Store、App Inbox、Artifact metadata +│ └── persistence/ Runtime Store、App Inbox、instance state snapshot、Artifact metadata ├── runtime-sdk/ │ ├── app.ts Core App SDK 类型 │ ├── manifest.ts App/Tool/Subscription Manifest 类型 @@ -699,6 +738,8 @@ lineup-app/ - 将动态 Inventory 同步给 Agent; - 实现 Agent `tool.invoke` → Runtime → App SDK → `tool.result` 的可靠闭环; - App 未运行时持久化 Inbox,恢复后可幂等补读。 +- 实现 `app.instance-state.v1`:Manifest schema / schema version / quota / retention 校验、instance 隔离、 + revisioned replace、刷新/重启恢复与关闭/禁用/移除清理;Tool/operation/outbox 不得由该 API 直接改写。 ### R4:第一个 Installed App 闭环 @@ -716,6 +757,8 @@ lineup-app/ 5. 下载型 App 无法访问 Tauri invoke、Host DOM、token 或未授予 Capability; 6. 默认 App 不可用时 Runtime 自动进入 chat Recovery App; 7. 所有 Tool 调用、权限拒绝与终态结果均可按 app_scope、conversation_id、call_id 审计和测试。 +8. MiniApp 只能读写自身当前 instance 的状态快照;非法 schema、旧 revision、跨 instance、超配额以及 + 关闭后的写入均被稳定拒绝,重启只恢复最后一个有效快照。 ``` ## 14. 与已有正式方案的关系 diff --git a/迭代/04.runtime_workspace/01.design_review.md b/迭代/04.runtime_workspace/01.design_review.md new file mode 100644 index 0000000..7ed7d8d --- /dev/null +++ b/迭代/04.runtime_workspace/01.design_review.md @@ -0,0 +1,120 @@ +# 04.runtime_workspace 设计评审(一) + +**评审编号:** 01 +**日期:** 2026-08-06 +**评审对象:** [04.runtime_workspace.md](04.runtime_workspace.md)、[03.sdk_and_coreapp.md](../03.sdk_and_coreapp/03.sdk_and_coreapp.md)、[01.kernel.md](../01.kernel/01.kernel.md)、[00.base.md](../00.base/00.base.md)、[plan.md](../plan.md)、[APP架构设计.md](../../设计/APP架构设计.md) +**评审方法:** 对照既有 Runtime 所有权、SDK v1、Tool、生命周期、App 子会话和 Web/Tauri Host 契约,检查第 04 次迭代是否引入相互矛盾或无法按既有边界实现的设定。 +**总体结论:** 第 04 次迭代对 MiniApp 隔离、Interact 标准交互、关闭收口和 Host 基线的方向与第 03 次迭代及权威架构一致。Pomodoro 已收敛为 Agent 直接启动的单次专注 operation:Runtime 托管 instance state、deadline 和唯一结果;Pomodoro 只渲染与请求退出。D04-01~D04-05 已回填主定义或总体计划,所有 P0~P3 设计问题已清零;D04-06 是有明确触发条件的 P5 遗留项,不阻塞实施。 + +## 问题清单(Outline) + +状态标记:🔴 未解决,必须处理;🟡 已提出修复方向,尚未确认或落入主定义;✅ 已解决;⚪ 可延期但必须保留记录。P0~P3 必须在当前迭代处理;P4~P5 可以延期,但必须记录延期原因和重新评估条件。 + +| 状态 | 优先级 | 编号 | 问题 | 当前结论 / 下一步 | +|---|---:|---|---|---| +| ✅ | P1 | D04-01 | Pomodoro 状态由 Runtime 保存,但 Runtime 又“不解释番茄钟业务”;SDK v1 没有对应的私有状态持久化/恢复契约 | Runtime 已在正式方案中定义按 `app_scope + instance_id` 隔离、Manifest schema 校验、revision 控制的业务状态快照;04 已明确 operation 与 instance state 的不同事实来源和最小数据。 | +| ✅ | P1 | D04-02 | 用户暂停/继续/取消与 Agent Tool 的关系未定义;现有 `sdk.tools.*` 只能终结 Runtime 已投递的 Agent Tool | 已收敛为 Agent Tool `pomodoro.start`(长操作)和 `pomodoro.interrupt`(短操作);不支持暂停/继续。用户以语言请求停止时由 Agent 调用 interrupt;切换/关闭由 Runtime 生命周期以相同规则中断,均不伪造 Agent Tool。 | +| ✅ | P1 | D04-03 | “后台继续计时、到点回传、重启按 `ends_at` 恢复”没有 Runtime deadline 裁决和 Tool 终态规则 | 已确定 Runtime deadline operation 是唯一裁决者:持久化 `ends_at`,到点/中断竞争一个原子终态,唯一 result/outbox;重启已到点结算一次,进程未运行时不承诺即时提醒。 | +| ✅ | P2 | D04-04 | “创建或恢复 App 子会话”与“新 instance 必须新子会话”、以及关闭后 pending Interact 问题仍可回答的既有规则未完整写出 | 已明确仅恢复同一个未结束 instance 才复用子会话;关闭后子会话只读,pending Interact 问题留在主 IM 等待且不得追加到旧子会话;验收已加入该场景。 | +| ✅ | P3 | D04-05 | 总体计划把 Pomodoro 写成第 03 次迭代已完成的参考 MiniApp,并要求第 04 次工作区可切换 Whiteboard;第 04 次定义却将 Pomodoro 作为新验证应用且只承诺 Interact/Pomodoro 切换 | 已更新 `plan.md`:第 03 次参考实现只含 Task Dashboard / Whiteboard;Pomodoro 在第 04 次引入。Whiteboard 本轮仅保持第 03 次回归,不接入工作区切换或完成范围。 | +| ⚪ | P5 | D04-06 | 未来是否将 SDK 的实现迁入 Rust 层以提升运行效率 | **延期讨论:** 当前没有性能瓶颈证据,且 Web Reference Host / sandbox iframe 中的 SDK 与 UI Bridge 必须保留 TypeScript/浏览器侧实现。未来可评估把状态存储、schema 校验、deadline 调度、Tool/outbox 原子收口等无 UI Runtime Core 下沉到 Rust。重新评估触发:profiling 证明这些路径是热点,或 Rust Runtime Core 成为跨 Host 的正式实现边界。 | + +## 通过项 + +- **信任与隔离没有倒退。** 第 04 次迭代将 Pomodoro 定为 `bundled`,要求受限 Surface/SDK,禁止访问 Transport、Store、Agent、Host DOM、Tauri 和系统能力。这与权威架构对 bundled MiniApp 的限制一致,也避免了第 03 次验收已修复的“业务逻辑回到可信 Host JS”问题。 +- **Interact 的归属正确。** `notice / choice / confirm / input` 仍由 Interact 呈现;Pomodoro 前台时仅允许 Interact 覆盖层,视觉位置不改变 owner。这符合既有“标准交互不属于 MiniApp SDK”的规则。 +- **关闭收口正确。** 文档保留“停止新普通 Tool 投递 → 未终态 Tool 收敛为 `cancelled(app_closed)` → 已 submitted 结果继续 outbox → 恢复有效前台 App”的顺序;也没有把 Tool 完成错误地等同于关闭 App。 +- **Host 范围正确。** Web Reference Host 与 Tauri Desktop Host 作为同一 Runtime/MiniApp 代码的两种 Host 验收,与权威架构一致。 + +## D04-01:状态所有权与持久化边界未定义 + +**已解决(2026-08-06)。** 正式方案已增加 `app.instance-state.v1`:Runtime 为当前 +`app_scope + instance_id` 托管 Manifest schema 校验、revision 控制和 lifecycle 清理的业务状态快照。 +本迭代已将 Pomodoro 的 instance snapshot 与 deadline operation 分开:前者服务 UI 恢复,后者是 +`ends_at`、Tool 终态和 outbox 的唯一事实来源。以下为发现时的风险分析。 + +第 04 次定义要求 Runtime“保存计时状态和工作区快照”,并要求 Pomodoro 根据 Runtime 状态刷新;同时又规定 Runtime 不解释番茄钟业务,Pomodoro 负责暂停、继续和取消。它给出的 `timer_id`、`duration_seconds`、`started_at`、`ends_at`、`remaining_seconds`、`state` 正是需要跨刷新和重启持久化的业务状态。 + +但既有 SDK v1 只开放 Inbox、Agent Tool 的 `progress / complete / fail / cancel`、生命周期、Surface 与 Capability。它不提供 MiniApp 私有状态的读取、schema 校验写入、版本迁移或恢复快照 API;并且 Runtime 是 Conversation Store 的唯一所有者,bundled iframe 不能直接写它。若不补契约,实现只能在两条均违反边界的路径中选择:由 Runtime 写死 Pomodoro 的字段/状态机,或由 MiniApp 自己绕过 SDK 访问持久化。 + +建议把 Runtime 的职责限定为通用的、按 instance 隔离的持久化容器和 deadline-operation 调度,而非理解“番茄钟”。Pomodoro Manifest 应声明其状态 schema 和可恢复 operation schema;MiniApp 通过一个最小、受 Runtime 校验的 SDK 请求提交状态变更;Runtime 负责原子持久化、恢复投影和审计元数据。主定义还应说明:`remaining_seconds` 是派生展示值,还是暂停时的持久化事实值,避免与 `ends_at` 双事实源冲突。 + +## D04-02:用户控制动作没有合法的 SDK 入口 + +**已解决(2026-08-06,后续澄清)。** Pomodoro 接受 Agent 的长期 Tool `pomodoro.start` 和短 Tool +`pomodoro.interrupt`;用户不再有暂停或继续入口。用户以 IM / Voice 表达“停止/结束这次专注”时,由 Agent +调用 `pomodoro.interrupt({})`;Runtime 只从该调用所在会话绑定当前 `focusing` instance,负责路由与唯一终态 +裁决。切换工作区和关闭则由 Runtime 生命周期以同一中断规则处理,但不伪造 Agent Tool。Runtime 只接受第一 +次有效中断;重复/迟到调用只返回稳定回执,不能改写终态或直接写第二条 outbox。以下为发现时的备选分析。 + +第 04 次迭代要求用户在 Pomodoro 内暂停、继续、取消;又列出 `pomodoro.start / pause / resume / cancel / status`。前置契约中,`sdk.tools.*` 表示 MiniApp 对 Runtime 已投递的 Agent Tool Call 报告进度或唯一终态,不能被用于任意用户动作,更不能由 bundled MiniApp 自行构造 Agent call 或写 outbox。 + +因此必须先选择并写清一个模型: + +1. `pomodoro.pause / resume / cancel / status` 都是 Agent 可调用 Tool,用户点击只请求 Runtime 执行相同的已声明 MiniApp command;或 +2. 只有 `pomodoro.start` 是长操作 Tool,用户动作是独立的实例内 command,不会伪造额外 Agent Tool,Runtime 可按产品协议选择是否产生状态事件;或 +3. 另一种明确的、同样受 Manifest、schema、instance 和幂等校验约束的模型。 + +无论选择哪种,需给出 command ID、输入/输出 schema、目标 `instance_id`、重复点击行为、状态非法时的稳定拒绝码,以及关闭竞态下命令被拒绝还是已持久化。否则 Web/Tauri 两个 Host 会分别把按钮实现为局部状态或直接 Tool 操作,无法证明 Runtime 的唯一所有权。 + +## D04-03:后台/重启到点完成缺少唯一裁决者 + +**已解决(2026-08-06)。** Runtime 的通用 deadline operation 持久化绝对 `ends_at`;到点、用户中断和 +关闭竞争同一原子终态,先成功者产生唯一 Tool result/outbox。自动暗屏、锁屏和 Surface 重载不终止专注; +重启发现已到点时只结算一次。Runtime / Host 完全未运行时不承诺即时提醒。以下为发现时的风险分析。 + +“后台继续运行”在受限 iframe 不等于存在可靠的定时器:Surface 可能卸载、浏览器页面可能被节流,进程也可能在 deadline 前后重启。仅在 UI 中 `setTimeout` 会导致漏完成、重复完成或把全时长重新开始。第 04 次虽然正确要求重启按 `ends_at` 计算剩余时间,却没有定义 Runtime 在何时把 operation 原子转为 `completed`、谁完成关联 Tool、以及同时发生暂停、取消、App close、恢复和 deadline 时谁胜出。 + +建议新增一个通用 Runtime deadline-operation 状态机。运行中的 operation 持久化绝对 `ends_at`;恢复时 Runtime 用当前时间重新计算,若已到期则以一次原子转换完成并写唯一 result/outbox,若未到期则投影更新。暂停把剩余时长固化并清除/失效 deadline;恢复从新的 deadline 开始。接受 `AppLifecycleManager.close` 时,先让关闭取得与 operation 完成相同的终态竞争权:先完成者生效,另一个仅收到稳定幂等回执。这样 Runtime 仍不需要知道“番茄钟”,只实现可复用的 deadline 可靠性。 + +还须明确第一版“App 内完成提示”的含义:它应由 MiniApp 在收到 Runtime 已完成状态时显示;系统通知仍不在范围内。若运行进程完全不存在,到点时不承诺即时可见提示,但下次 Runtime/Host 可运行时必须按已到期状态恢复,不能重置或重复回传。 + +## D04-04:子会话复用、只读与关闭后问题的措辞不完整 + +**已解决(2026-08-06)。** 主定义已限定:只有恢复同一个未结束 instance 才能复用子会话;关闭后子会话 +只读。仍 pending 的 Interact 标准交互保留在主 IM 中等待回答、dismiss、超时或 Runtime 失败,且不得向 +已结束子会话追加记录。以下为发现时的风险分析。 + +前置契约只允许在“恢复同一个未结束 instance”时复用同一个 App 子会话;以后启动的每个新 instance 都必须新建子会话。第 04 次的“创建或恢复 App 子会话”没有限定这一前提,容易被实现为按 `app_scope` 或旧子会话复用,和“继续处理新建 instance / 新子会话”冲突。 + +另外,既有规则规定 App 真正关闭后:子会话结束并成为只读历史,仍 pending 的标准交互不被取消,留在 Interact/主 IM 中等待回答、dismiss、超时或 Runtime 失败。第 04 次仅说“已结束子会话只读”,没有写出这个例外;若把回答追加进已关闭子会话,就破坏只读,若关闭时取消问题,又违反 Interact 归属规则。 + +建议在第 04 次的子会话章节和验收中逐字继承这一关闭后路径,并加一条自动化场景:Pomodoro 关闭时存在带 `app_session_context` 的 pending `choice`,App 覆盖层消失、子会话只读、用户仍可在主 IM 回答,且产生一次结果/outbox。 + +## D04-05:计划与第 04 次最小范围相互矛盾 + +**已解决(2026-08-06)。** 总体计划已将 Pomodoro 从第 03 次已完成参考实现中移除,明确它在第 04 次 +作为 Agent 直接启动的专注验证应用引入;Whiteboard 在本轮仅保持第 03 次已有回归,不参与工作区切换、 +完整闭环或完成验收。以下为发现时的风险分析。 + +`plan.md` 说第 03 次已完成的参考 MiniApp 包含 Pomodoro,但第 03 次主定义及其验收对象是 Interact、Task Dashboard 和 Whiteboard;第 04 次又明确把 Pomodoro 选为本轮验证应用。与此同时,计划要求第 04 次工作区支持 Interact、Pomodoro、Whiteboard 切换,而第 04 次主定义只承诺 Interact/Pomodoro。 + +这不会改变底层架构,却会直接导致实现范围、浏览器验收用例和“第一条完整用户流程”的判断不一致。建议以第 04 次主定义选择的“单一 Pomodoro 闭环”为基准:将计划中的“Pomodoro 已完成参考实现”改成“Pomodoro 在第 04 次引入”;对白板明确标注为本轮仅保持既有回归,或若确实要求可切换则把它加入第 04 次工作内容、验收和时间预算。 + +## D04-06:SDK 是否迁入 Rust 层(P5,后续讨论) + +这是值得保留的性能和实现演进方向,但当前没有证据表明它阻塞第 04 次迭代。需要先分清 SDK 的 +协议/能力语义和 SDK 的运行位置:协议必须跨 Host 保持一致,具体实现不必全部使用同一种语言。 + +```text +必须留在 TypeScript / 浏览器侧的部分 +→ Web Reference Host、bundled MiniApp 的 sandbox iframe、postMessage Surface Bridge、DOM 与 UI 渲染。 + +适合未来评估迁入 Rust 的 Runtime Core 部分 +→ 状态快照持久化、schema 校验、revision 比较、deadline operation 调度、Tool/outbox 原子终态、 + 生命周期收口和审计。 +``` + +如果把全部 SDK 都迁入 Rust,bundled MiniApp 仍必须经浏览器 Bridge 调用 Runtime,反而可能增加 +Tauri IPC、序列化和跨语言错误处理开销;它不能替代 Web Reference Host 中必须运行的 TypeScript Bridge。 +因此候选方向是“保持版本化 SDK 契约与 TypeScript Surface API,按需把 Runtime 的无 UI 核心实现下沉到 Rust”, +而不是把 MiniApp SDK 整体替换为 Rust。 + +重新评估前必须先有可重复的 profiling 证据,包括:状态快照读写频率与耗时、deadline/Tool 路由吞吐、主线程 +阻塞、Tauri IPC 往返、JSON 序列化成本以及 Web/Tauri 两个 Host 的差异。只有收益大于跨 Host 实现、测试、 +调试和错误边界的复杂度时,才为此创建独立设计和迁移迭代。 + +## 设计就绪条件 + +D04-01~D04-05 已形成决议并回填到 [04.runtime_workspace.md](04.runtime_workspace.md) 与 +[plan.md](../plan.md)。所有 P0~P3 设计问题已清零;D04-06 作为 P5 carryover 保留,不阻塞本迭代。 diff --git a/迭代/04.runtime_workspace/02.design_review.md b/迭代/04.runtime_workspace/02.design_review.md new file mode 100644 index 0000000..f08dad0 --- /dev/null +++ b/迭代/04.runtime_workspace/02.design_review.md @@ -0,0 +1,178 @@ +# 04.runtime_workspace 设计评审(二) + +**评审编号:** 02 +**日期:** 2026-08-06 +**评审对象:** [04.runtime_workspace.md](04.runtime_workspace.md)(实施权威)、[01.design_review.md](01.design_review.md)、[00.base.md](../00.base/00.base.md)、[01.kernel.md](../01.kernel/01.kernel.md)、[03.sdk_and_coreapp.md](../03.sdk_and_coreapp/03.sdk_and_coreapp.md)、[plan.md](../plan.md)、[app_final_design.md](../../设计/02.正式方案/app_final_design.md)、[lineup-runtime-sdk-architecture.md](../../设计/02.正式方案/lineup-runtime-sdk-architecture.md)、[lineup-app-layer-architecture.md](../../设计/02.正式方案/lineup-app-layer-architecture.md)。 +**评审方法:** 独立地以第 04 次迭代定义为实现依据,逐项回溯前三次迭代和正式 Runtime 契约;重点检查 instance state / deadline operation 的所有权、`pomodoro.start` 与用户中断的终态竞争、离开/暗屏/重启、App 子会话与 pending Interact、Whiteboard 范围和 Rust P5 延期。 +**总体结论:** D04-01~D04-09 已确认的方向均已被第 04 次主定义或计划正确吸收:业务快照与 deadline operation 分离、Agent Tool `pomodoro.start` / `pomodoro.interrupt`、到点/中断原子终态、暗屏/锁屏不等于离开、子会话只读与 pending Interact 留在主 IM、Whiteboard 不进入本轮闭环、Rust 仅为 P5 后续评估。D04-07 已澄清为 Agent Tool 路由契约;D04-08 已选择“立即收口、由 Interact 呈现完成结果”;D04-09 已选择 `retain_readonly` 快照保留策略。所有 P0~P3 设计问题已清零,本迭代设计已就绪,待实施。 + +## 问题清单(Outline) + +状态标记:🔴 未解决,必须处理;🟡 已提出修复方向,尚未确认或未回填实施权威;✅ 已解决;⚪ 可延期但必须保留记录。P0~P3 必须在当前迭代处理;P4~P5 可以延期,但必须记录延期原因和重新评估条件。 + +| 状态 | 优先级 | 编号 | 问题 | 当前结论 / 下一步 | +|---|---:|---|---|---| +| ✅ | P1 | D04-07 | `pomodoro.interrupt` 的调用方向与目标绑定未明确。 | 已确定为 Pomodoro 在 Manifest 中声明、由 Agent 调用的短 Tool;Runtime 从调用会话绑定当前 focusing operation,负责路由、原子裁决和稳定回执。 | +| ✅ | P1 | D04-08 | `completed` 后既要求 Pomodoro 显示自身完成提示,又在主流程中要求 Runtime 收口关闭 App、恢复 Interact;没有定义两者的顺序和触发者。 | 已选择立即收口:Runtime 原子完成并投递唯一结果/outbox 后立即关闭 Pomodoro、结束子会话、恢复 Interact;完成提示由 Interact / Agent 呈现。 | +| ✅ | P2 | D04-09 | Pomodoro 要保存 instance snapshot,但没有声明其 `app.instance-state.v1` Manifest 配置、schema 版本/字段、配额和 `retention` 选择。 | 已确定 `app.instance-state.v1` 最小 Manifest:严格 schema v1、4 KiB 配额、`retain_readonly`。关闭后旧快照只作历史展示,不可写入、不可复活为 focusing。 | +| ✅ | — | D04-C1 | instance state 与 deadline operation 的事实来源和持久化边界 | 已正确同步;不构成新问题。 | +| ✅ | — | D04-C2 | `pomodoro.start`、到点/中断竞态和 outbox 唯一性 | 已正确同步;D04-07 补足 Agent Tool 的路由与目标绑定契约。 | +| ✅ | — | D04-C3 | 用户离开、自动暗屏/锁屏、Surface 重载和重启 | 已正确同步;不构成新问题。 | +| ✅ | — | D04-C4 | App 子会话、只读历史和 pending Interact | 已正确同步;不构成新问题。 | +| ✅ | — | D04-C5 | Whiteboard 第 04 次范围 | 已正确同步到主定义和 `plan.md`;不构成新问题。 | +| ⚪ | P5 | D04-C6 | Runtime Core / SDK 是否迁入 Rust | 已保留为延期项;当前不应扩大到本轮实现。重新评估条件仍为 profiling 证明 Runtime Core 是性能热点。 | + +## 通过项与一致性证据 + +### D04-C1:instance state 与 deadline operation 的边界已一致 + +主定义第 2.2、2.3 和第 3 节将 Runtime 定为 schema/revision 受控的 instance snapshot 保存者,同时将 +`ends_at`、Tool 终态和 outbox 交给 deadline operation;`remaining_seconds` 仅由 UI 按绝对截止时间派生。 +这与正式方案的明确边界一致:`app_scope + instance_id` 隔离状态快照,快照不能完成 Tool、写 outbox、改变 +焦点或绕过 lifecycle;Pomodoro 的到期/中断和唯一 Tool result 必须由 Runtime deadline operation 裁决。 + +这也没有把静态 IM/App 子会话误当作 MiniApp 业务存储:第 04 次主定义仍只让子会话记录人与 Agent 的交互和 +结果,符合第 03 次“App 子会话不是 App 操作日志”的规则。 + +### D04-C2:Agent Tool 的终态竞争与 outbox 所有权已一致 + +第 04 次主定义将 `pomodoro.start({ duration_seconds, activity? })` 定义为长期 Agent operation,并由 Agent +调用短 Tool `pomodoro.interrupt({})` 请求停止当前专注;到点、Agent 中断和关闭竞争一个原子终态,第一成功者 +产生一次结果/outbox;重复或迟到的中断只获得稳定幂等回执。 +这符合第 03 次对 Tool/交互的“Runtime 原子终态 + 唯一 outbox”模式,也符合正式方案中 App 不能自行完成 +Tool 或写 outbox 的限制。D04-07 已补足:用户以语言提出停止意图后,Agent 调用 Pomodoro 声明的 Tool, +Runtime 而非 MiniApp 界面负责最终裁决。 + +### D04-C3:离开、自动暗屏、重启的产品语义已一致 + +主定义已清楚区分:用户切回 Interact、切到其他 MiniApp 或关闭 Pomodoro 会中断;自动暗屏、锁屏、Surface +重载不会中断;重启按持久化 `ends_at` 恢复或一次性结算;Runtime/Host 完全未运行时不承诺即时系统提醒。 +这不再依赖 iframe/UI 定时器,且没有把来电检测、系统免打扰、静音或系统通知偷偷纳入第 04 次范围。 + +### D04-C4:App 子会话和 pending Interact 已一致 + +主定义限制“仅恢复同一个未结束 instance 时复用同一个子会话”;结束后子会话只读;关闭时仍 pending 的 +Interact 标准交互留在主 IM/Interact 中,回答不得追加到旧子会话。该规则与第 03 次的 instance 生命周期、 +`pending_interaction_call_ids` 和“关闭不自动取消 Interact 交互”规则一致。前台 Pomodoro 上的 Interact +覆盖层也没有改变交互 owner。 + +### D04-C5:Whiteboard 范围已消除冲突 + +`04.runtime_workspace.md` 第 5 节与 `plan.md` 第 3 节均明确:Whiteboard 只保持第 03 次已完成的 SDK/隔离 +回归,不接入第 04 次工作区切换或完整用户流程;Pomodoro 才是本轮唯一新增的工作区闭环验证应用。 + +### D04-C6:Rust 为正确的 P5 carryover + +主定义明确本轮保持 TypeScript Runtime / Web Bridge 基线,且仅在 profiling 已证明 Runtime Core 是热点后另行 +评估。该范围不会破坏 Web Reference Host、sandbox iframe、postMessage Bridge 和 UI 必须保留在浏览器侧的 +既有边界。 + +## D04-07:用户中断的 Agent Tool 路由契约 + +**已解决(2026-08-06,用户决议)。** + +`pomodoro.interrupt` 不是 MiniApp 通过 SDK 主动发送的 Runtime command,而是 Pomodoro 在 Manifest 中声明、供 +Agent 调用的短 Tool。用户主要经 IM / Voice 向 Agent 表达业务意图;MiniApp 是 Agent 的业务工具,而不是 +用户直接操作业务状态的独立应用。Tool Router 从该 Tool 调用的 `conversation_id` 自动绑定当前唯一 +`focusing` Pomodoro operation,调用者不能提供 `instance_id`、`app_scope` 或 `operation_id`。Runtime 路由调用、 +在与 deadline/close 相同的原子裁决中将长 Tool `pomodoro.start` 收口为 `interrupted`,并给短 Tool 返回 +`interrupted`、`no_active_focusing_operation` 或 `operation_already_final` 的稳定回执。若 Surface 不在,Runtime +仍可完成裁决;若存在,只投影最终状态。用户直接离开工作区仍由 Runtime 生命周期中断处理,不伪造 Agent Tool。 + +以下为发现时、尚未澄清调用方向的风险分析。 + +第 04 次主定义第 3 节规定用户显式退出通过受限 SDK Runtime command +`pomodoro.interrupt(reason = "user_exit")` 请求;又规定 Runtime 仅接受当前 `focusing` instance 的第一次有效 +中断,迟到请求返回幂等回执。这个产品语义正确,但尚不足以决定 SDK/Bridge、Web Host 和 Tauri Host 应如何 +实现同一动作。 + +现有正式 SDK 的唯一通用出站入口是 `actions.dispatch(action: AppAction)`,但没有定义 `AppAction` 的 +schema、Command type、由 context 派生的 instance 绑定、receipt 格式或稳定拒绝码。第 03 次冻结的 Manifest +也只定义 Agent Tool(`direct / launch / foreground / operation`)和 `AppNavigationAPI.close`;它没有把 +业务 MiniApp 任意命令自动变成合法 Agent Tool。若不冻结该层,至少会出现两种相互冲突的实现:MiniApp 用 +`sdk.tools.complete/fail` 伪造 `pomodoro.start` 终态,或 Web/Tauri Host 在 UI 侧直接关闭实例;两者都绕开 +了 Runtime 的 deadline/outbox 原子裁决。 + +建议在主定义中明确一个最小、仅 Runtime 可执行的 command,例如概念上: + +```ts +type PomodoroInterruptCommand = { + type: "pomodoro.interrupt"; + reason: "user_exit"; + // instance_id、app_scope、conversation_id 从不可伪造 SDK context 推导,调用者不得传入。 + // Runtime 从当前 instance 关联的 operation / source_tool_call_id 查找目标。 +}; + +type PomodoroInterruptReceipt = + | { accepted: true; operation_id: string; state: "interrupted" } + | { accepted: false; code: "operation_already_final" | "operation_not_focusing" | "instance_closing" }; +``` + +实际字段命名可以不同,但必须同时冻结以下规则:调用者只能操作自己的当前 instance;谁提供或由 Runtime +生成幂等键;`focusing`、deadline、`closing` 与已终态各自的稳定回执;以及 command、deadline 与 +`AppLifecycleManager.close` 如何在同一持久化事务或等价 compare-and-set 中竞争唯一终态和唯一 outbox。 +Host 的“返回/切换/关闭”应调用同一个 Runtime 内部操作,而不是从 Surface 绕过该 Command。 + +## D04-08:完成提示与关闭的顺序未定义 + +**已解决(2026-08-06,用户决议:方案 B)。** Runtime 到达 `ends_at` 后,先原子写入 `completed`、唯一 +`pomodoro.start` 结果与 outbox;随后立即关闭 Pomodoro、结束 App 子会话并恢复 Interact。Pomodoro 不显示 +独立的完成提示;完成结果由 Interact / Agent 呈现。关闭后的 outbox 重试不依赖 Pomodoro Surface,且不得再次 +完成 Tool。完成与关闭并发时,已提交的 `completed` 终态不得被 `cancelled(app_closed)` 改写。 + +以下为决议前的风险分析。 + +主定义同时作出了两项要求:Pomodoro 到时间“显示自己的完成提醒”(第 2.3、3、4.4 节),迭代目标的主流程又 +写为“到时间显示完成提醒并回传结果 → Runtime 收口并关闭 App,恢复 Interact”。但它没有规定完成态是否先 +投影给仍存活的 Surface、提示展示多久或由谁确认、何时调用 `AppLifecycleManager.close`、以及 App 子会话在 +哪个时点结束。 + +这是可观测行为的分歧,不是纯 UI 细节。若 Runtime 原子完成后立即按一般收口关闭 instance,Surface 会先被 +卸载,Pomodoro 无法兑现“App 内完成提示”;若 UI 本地自行显示后再关闭,则可能在重启、旧 Surface 或重复 +回调下与已提交 outbox 脱节。第 03 次的既有规则还区分“Tool 完成不自动关闭 App/子会话”和 +`AppLifecycleManager.close` 才结束子会话;第 04 次为 Pomodoro 采用不同的自动关闭策略是允许的,但必须 +明确这是 Pomodoro 的特例及其顺序。 + +建议二选一并写入工作内容、恢复规则和验收: + +1. **保留完成展示窗口。** deadline operation 原子写 `completed` 和唯一 result/outbox,Runtime 将终态投影给 + Pomodoro;Surface 在一个明确的受控窗口内显示完成提示;窗口结束、用户确认或用户离开后由 Runtime 发起 + `AppLifecycleManager.close`,再结束子会话并恢复 Interact。重启落在窗口内时须能按持久化终态恢复到同一 + 收口路径,而不得再写结果。 +2. **立即收口。** Runtime 完成后立刻关闭 App、结束子会话、恢复 Interact;删除“Pomodoro 显示自己的完成 + 提示”,或将完成提示明确改为 Interact 的可信呈现而非已关闭 MiniApp 的 UI。 + +无论选择哪种,验收都应覆盖 `completed` 与 close 并发、已提交 outbox 的重试、终态 UI 不产生第二次 Tool +完成,以及完成前/后 pending Interact 的归属。 + +## D04-09:Pomodoro 的 instance-state Manifest 与关闭后保留策略未确定 + +**已解决(2026-08-06,用户决议:方案 B)。** Pomodoro 声明 `app.instance-state.v1`,使用严格的 schema +version 1、4 KiB 配额和 `retain_readonly`。快照只保存 UI/历史展示所需的活动、关联 operation、时间和展示 +状态;operation 仍是终态、deadline 与 outbox 的唯一事实来源。App 关闭后,旧快照仅供只读历史展示,不能 +再写入、不能恢复为 `focusing`,也不能作为新一轮专注的运行状态。 + +以下为决议前的风险分析。 + +主定义要求 Pomodoro 通过 SDK 保存展示所需的 instance snapshot,并列出可能字段;正式 Runtime 契约则规定 +只有声明 `app.instance-state.v1` 的 Manifest 才能使用 schema/revision 受控状态,并要求声明 +`state_schema_version`、`state_schema`、`max_bytes` 和 `retention`。第 04 次定义尚未选择 Pomodoro 的实际 +Manifest 配置,也没有说明关闭后应使用默认 `delete_on_close` 还是 `retain_readonly`。 + +这会直接改变重启/关闭后的可见行为和验收:`delete_on_close` 允许完成或中断时清理 UI snapshot,而历史由 +App 子会话和 Tool/operation 记录承载;`retain_readonly` 会保留一个不可写旧 snapshot,只能作为历史或新 +instance 的受控上下文。两者都可以符合产品目标,但实现、fixture 和隐私/清理预期不同;不能靠默认值隐式 +决定。 + +建议主定义补一份 Pomodoro 的最小 Manifest 片段或等价表格:required feature、严格 schema(至少允许的展示 +字段)、schema version、最大大小和明确 retention。还应说明完成/中断/关闭时 instance snapshot 与 deadline +operation、App 子会话、Tool/outbox 的独立清理顺序,确保“保留 UI 状态”不会被误解为旧 instance 可恢复为 +`focusing` 或可再次写入。 + +## 评审关闭条件 + +- D04-07~D04-09 已回填本轮实施权威 [04.runtime_workspace.md](04.runtime_workspace.md) 和 + [plan.md](../plan.md);实施时须依照已冻结的 Tool、完成收口和 Manifest 契约提供 fixture 与自动化验收; +- D04-C6 继续作为 P5 carryover 留在本记录与第一轮记录中;没有 profiling 证据前,不把 Rust 迁移纳入当前 + 实现范围; +- 本评审只记录发现和建议,未修改第 04 次主定义、正式方案、计划或实现。 diff --git a/迭代/04.runtime_workspace/03.design_review.md b/迭代/04.runtime_workspace/03.design_review.md new file mode 100644 index 0000000..524ae8b --- /dev/null +++ b/迭代/04.runtime_workspace/03.design_review.md @@ -0,0 +1,153 @@ +# 04.runtime_workspace 设计评审(三) + +**评审编号:** 03 +**日期:** 2026-08-06 +**评审对象:** [04.runtime_workspace.md](04.runtime_workspace.md)(实施权威)、[01.design_review.md](01.design_review.md)、[02.design_review.md](02.design_review.md)、[00.base.md](../00.base/00.base.md)、[01.kernel.md](../01.kernel/01.kernel.md)、[03.sdk_and_coreapp.md](../03.sdk_and_coreapp/03.sdk_and_coreapp.md)、[plan.md](../plan.md)、[app_final_design.md](../../设计/02.正式方案/app_final_design.md)、[lineup-runtime-sdk-architecture.md](../../设计/02.正式方案/lineup-runtime-sdk-architecture.md)、[lineup-app-layer-architecture.md](../../设计/02.正式方案/lineup-app-layer-architecture.md)。 +**评审方法:** 独立以第 04 次主定义作为唯一实施依据,对照前置迭代冻结的 Manifest / Tool Descriptor / 生命周期契约和正式 Runtime 方案。只记录会使本轮实现或验收无法得到唯一结论的问题;不把 Rust 演进、系统通知、真实语音、Whiteboard 工作区或其他范围外设想重新升级为当前问题。 +**总体结论:** 已确认的核心方向保持一致:MiniApp 是 Agent 的业务工具;`pomodoro.start` / `pomodoro.interrupt` 由 Agent 调用;deadline、Agent 中断和生命周期中断由 Runtime 原子裁决;完成后立即关闭并由 Interact 呈现;快照采用 `retain_readonly`;子会话和 pending Interact 的归属规则正确;Whiteboard 和 Rust Core 均未误入本轮范围。D04-10~D04-12 已按收敛决议回填:两个 Tool 已有最小可发布 Descriptor,同一会话只允许一轮 `focusing` 专注,当前验收入口限定为 IM、未来 Voice 只复用语义。所有 P0~P3 设计问题已清零,本迭代设计就绪,待实施。 + +## 问题清单(Outline) + +状态标记:🔴 未解决,必须处理;🟡 已提出修复方向,尚未确认或未回填实施权威;✅ 已解决;⚪ 可延期但必须保留记录。P0~P3 必须在当前迭代处理;P4~P5 可以延期,但必须记录延期原因和重新评估条件。 + +| 状态 | 优先级 | 编号 | 问题 | 当前结论 / 下一步 | +|---|---:|---|---|---| +| ✅ | P1 | D04-10 | 已命名 `pomodoro.start` / `pomodoro.interrupt`,但尚未给出能进入 Agent Inventory 的完整 Tool Descriptor,也未明确短 Tool 的 Runtime 与 Pomodoro 各自承担的处理步骤。 | 已冻结两个 Tool 的 v1 Descriptor:输入 / 输出 schema、调用模型、启动/前台策略、超时/幂等和 Runtime 优先的原子裁决顺序。 | +| ✅ | P1 | D04-11 | `interrupt` 假定每个会话只有一个 `focusing` operation,但再次收到同一会话的 `pomodoro.start` 时的规则没有定义。 | 已确定同一 `conversation_id` 只允许一轮 `focusing` operation;不同 call 的第二次 start 稳定拒绝为 `active_focusing_operation`,不创建任何新资源。 | +| ✅ | P2 | D04-12 | 主定义和验收把“IM 或 Voice”写为当前可验收入口,但本迭代明确不实现真实 Audio Mode。 | 已限定第 04 次仅以 IM 验收完整闭环;未来 Voice 复用同一意图和 Tool 语义,不要求本轮实现或验收。 | +| ✅ | — | D04-C7 | Agent 驱动、唯一终态与 outbox | `pomodoro.interrupt` 已不再被误作用户直接 SDK command;Agent Tool、deadline 和 lifecycle 共同进入 Runtime 的原子终态裁决,第一结果唯一。D04-10 仅补 Tool Descriptor 层,未推翻此决议。 | +| ✅ | — | D04-C8 | 完成后的工作区和会话收口 | 已确定 `completed` 后 Runtime 写唯一结果 / outbox,立即关闭 Pomodoro、结束子会话、恢复 Interact;不保留 Pomodoro 完成页。 | +| ✅ | — | D04-C9 | 业务快照、子会话与 Interact | snapshot 只作 UI / 历史展示,`retain_readonly` 旧实例不可写、不可复活;已结束子会话只读,pending 标准交互留在主 IM。 | +| ✅ | — | D04-C10 | 当前范围 | Whiteboard 仅保持第 03 次 SDK / 隔离回归;系统通知、来电检测、免打扰和真实 Audio Mode 均不在第 04 次范围。 | +| ⚪ | P5 | D04-C11 | Runtime Core 是否下沉 Rust | 已在 `plan.md` 留作后续方向;没有 profiling 证明状态、deadline、Tool/outbox 或 lifecycle 是热点之前,不进入第 04 次实施。重新评估触发条件不变。 | + +## 已确认的一致性 + +### Agent 是业务入口,Pomodoro 不是用户直接操作的独立 App + +主定义第 2、3 节已清楚表达:用户用当前 IM(未来可用 Voice)向 Agent 说明开始或停止的意图;Agent 调用 +Pomodoro Manifest 声明的 Tool;Pomodoro 本身只显示和接收 Runtime 投影,不设置暂停、继续或结束专注的业务按钮。 +这与前置迭代“Agent Tool 经 Runtime Tool Router 和动态 Inventory 调用、bundled App 不直连 Agent”的边界一致。 + +### 三种结束来源已有共同的可靠收口点 + +到达 `ends_at`、Agent 调用 `pomodoro.interrupt({})`、用户返回 Interact / 切换 App / 关闭工作区,已明确竞争同一 +Runtime 原子终态。先成功者把长期 `pomodoro.start` 收口为 `completed` 或 `interrupted`,并只产生一次结果和 +outbox;迟到事件获得稳定回执。自动暗屏、锁屏与 Surface 重载不是退出,刷新或重启按绝对 `ends_at` 恢复或结算。 +这符合 Runtime 对 operation、outbox、焦点与生命周期拥有最终权力的正式架构。 + +### 完成、历史和 Interact 归属已经无冲突 + +完成路径已选择“立即收口”:Runtime 原子完成后立即关闭 Pomodoro、结束子会话并恢复 Interact,由 Interact / +Agent 呈现结果。`app.instance-state.v1` 的 snapshot 与 deadline operation 分属不同事实来源;Pomodoro 使用严格 +schema v1、4 KiB、`retain_readonly`,关闭后只能作为只读历史。已结束子会话同样只读;仍 pending 的标准交互 +由 Interact / 主 IM 继续承接,不能追加到旧子会话。 + +## D04-10:两个 Agent Tool 缺少可执行的 Manifest / 路由契约 + +**已解决(2026-08-06,收敛决议)。** 主定义已冻结 `pomodoro.start` / `pomodoro.interrupt` 的 v1 Descriptor: +Runtime 先校验、定位和原子裁决;Pomodoro Surface 只接收已裁决投影,既不决定终态也不影响短 Tool 的回执。 +以下为决议前的风险分析。 + +### 为什么这是 P1 + +第 04 次主定义已经确定两个名称和高层语义: + +```text +pomodoro.start({ duration_seconds, activity? }) 长期 operation +pomodoro.interrupt({}) 短 Tool +``` + +但第 03 次迭代冻结的 Tool Descriptor 要求每项 Agent Tool 至少具备稳定 ID、版本、输入 / 输出 JSON Schema、 +`handling`、目标 App、前台要求和超时等信息;正式 Runtime 方案还要求声明面向 Agent 的说明、幂等规则、风险和 +启动策略。这些字段决定 Runtime 是否创建 instance、何时前台化、如何验证输入、以及何时可向 Agent 发送结果。 +当前 Pomodoro Manifest 片段只声明了 `app.instance-state.v1`,未给出这两项 Tool 的等价契约。 + +这在 `pomodoro.interrupt` 上尤其不能由实现自行猜测:当前主定义同时说“Runtime 将调用路由给该 Pomodoro +instance”与“Runtime 在同一原子裁决中将 `pomodoro.start` 收口”。若没有清晰的 Descriptor 和处理顺序,Web / +Tauri 实现可能分别选择“Surface 收到短 Tool 后自己 complete”或“Runtime 直接给 Agent 回执”。前一种会使 +Surface 的存活状况影响中断可靠性,违反已确认的终态所有权;后一种是合理选择,但必须作为契约写明。 + +### 最小收敛内容 + +不需要新增用户能力或扩大 SDK。建议在实施权威中为两个 Tool 加一个最小 Manifest 表 / JSON 片段,至少固定: + +1. `pomodoro.start` 的 `operation` 调用模型、输入 schema(`duration_seconds` 为正整数,`activity` 为可选受限字符串)、最终输出 schema(`completed` / `interrupted` 及约定的结果字段)、启动 / 前台策略、超时或由 `ends_at` 约束的规则、幂等键与重复调用结果; +2. `pomodoro.interrupt` 的短调用模型、空对象输入 schema、三种既定稳定回执的输出 schema,以及它不接受目标 ID 的规则; +3. Runtime 在验证、按 `conversation_id` 定位并原子收口后写入短 Tool 自身回执和长期 `start` 的唯一结果 / outbox;Pomodoro Surface 只接收已裁决状态投影,不能以 `sdk.tools.complete` 决定或补写任何终态; +4. 两项 Tool 在当前 app 未运行、Surface 已卸载、instance 正在 closing、Inventory revision 过期和 schema 非法时的受控拒绝 / 恢复路径。 + +字段名称不必照搬本记录;关键是由 Runtime 发布到 Agent 的契约能让 Web 与 Tauri 得到同一行为。完成回填后,本项可关闭。 + +## D04-11:同一会话的第二次 `pomodoro.start` 没有唯一规则 + +**已解决(2026-08-06,收敛决议)。** 同一 `conversation_id` 只允许一个 `focusing` Pomodoro operation。 +不同 `source_tool_call_id` 的第二次 `pomodoro.start` 稳定返回 `active_focusing_operation`,不创建 instance、 +子会话、deadline operation、焦点变更或 outbox;Agent 必须先停止旧轮,或等待其已经终态。 +以下为决议前的风险分析。 + +### 为什么这是 P1 + +主定义让 `pomodoro.interrupt({})` 从调用的 `conversation_id` 绑定“当前唯一的 `focusing` Pomodoro operation”。 +但 `pomodoro.start` 在已有 `focusing` operation 时是否可以再创建一个 instance / 子会话 / deadline operation 尚未 +定义。若两个 Host 自行选择不同处理,至少会产生以下不兼容情况: + +```text +实现 A:允许第二个 start + → 同一 conversation 同时存在两个 focusing operation + → interrupt({}) 无法再唯一定位目标。 + +实现 B:静默覆盖第一个 start + → 第一个长期 Tool 没有可靠的 interrupted 结果 / outbox。 + +实现 C:拒绝第二个 start + → 需要向 Agent 返回什么稳定结果尚未定义。 +``` + +这不是未来“多个计时器”功能的讨论,而是当前单次专注约束必须明确拒绝或替换的边界。否则无法完成 +`pomodoro.interrupt` 的唯一目标验收,也无法实现本轮的唯一 outbox 要求。 + +### 最小收敛内容 + +推荐第一版采用最保守规则:同一 `conversation_id` 已有 `focusing` Pomodoro operation 时,新的 +`pomodoro.start` 被 Runtime 稳定拒绝(例如 `active_focusing_operation`),不创建任何 instance、子会话、deadline +或 outbox;Agent 必须先调用 `pomodoro.interrupt({})`,或在旧 operation 已 `completed` / `interrupted` 后再开始。 +若产品希望“新的开始替换旧的开始”,也可采用先原子中断旧 operation、再创建新 operation 的规则,但必须定义两个 +Tool 结果 / outbox 的先后和任一事务失败时的恢复,复杂度更高。 + +无论选择哪种,都应在验收加入:重复 start、并发 start、start 与 interrupt 并发、start 与 deadline 并发,且确认 +每个 operation 只有一次终态和一次长期 Tool outbox。 + +## D04-12:当前 IM 验收与未来 Voice 语义混在一起 + +**已解决(2026-08-06,收敛决议)。** 第 04 次仅通过 IM 验证完整闭环;未来 Voice 只能复用相同的自然语言 +意图、Agent Tool 和 Runtime 语义,本轮不实现或验收 Audio Mode、语音采集、识别或媒体能力。 +以下为决议前的风险分析。 + +### 为什么这是 P2 + +用户已经确认:当前用户主要以 IM 消息与 Agent 交互,未来 Voice 必须复用同一“自然语言意图 → Agent Tool → +Runtime”模型。这个语义是正确的。可是主定义的目标和验收第 1、4 条目前写成“IM 或 Voice”,同时本迭代范围又 +明确排除 Audio Mode 的真实媒体能力。按字面,验收人员无法判断是否必须交付语音采集、识别、语音对话入口或 +Voice 端到端测试。 + +这不要求本轮实现 Voice,也不要求改变 Agent Tool。需要的只是把时间边界写清:第 04 次实际验证当前 IM;未来 +Voice 接入后必须把识别出的同类意图映射到相同的两个 Tool,并遵守相同的会话绑定、终态和 outbox 语义。 + +### 最小收敛内容 + +将主定义、总体计划和验收中的“IM 或 Voice”改为类似表述: + +```text +第 04 次通过 IM 验证用户向 Agent 表达开始 / 停止意图的完整闭环。 +未来 Voice 复用相同的 Agent Tool 和 Runtime 语义;本轮不实现或验收真实 Audio Mode、语音采集、识别或媒体能力。 +``` + +然后保留 Web Reference Host 和 Tauri Desktop Host 的 IM 代表性流程验收。回填后,本项可关闭。 + +## 评审关闭条件 + +1. D04-10~D04-12 已回填 [04.runtime_workspace.md](04.runtime_workspace.md)、[plan.md](../plan.md) 和验收条目; +2. 实施时依据冻结后的 Tool Descriptor 增加合法、非法输入、Inventory 过期、重复 / 并发和 Surface 缺席的 fixture 与自动化验收; +3. D04-C11 继续作为 P5 carryover 留在计划中;没有 profiling 证据前,不启动 Rust 迁移; +4. 本评审只记录发现,未修改主定义、计划、正式方案或实现。 diff --git a/迭代/04.runtime_workspace/04.runtime_workspace.md b/迭代/04.runtime_workspace/04.runtime_workspace.md index 73221eb..563d1e4 100644 --- a/迭代/04.runtime_workspace/04.runtime_workspace.md +++ b/迭代/04.runtime_workspace/04.runtime_workspace.md @@ -1,30 +1,35 @@ # LineUp App 迭代定义:Runtime 工作区与 Pomodoro MiniApp **迭代编号:** 04.runtime_workspace -**状态:** 草稿,待设计评审 +**状态:** 设计已冻结,待实施 **日期:** 2026-08-06 **前置基线:** [03.sdk_and_coreapp.md](../03.sdk_and_coreapp/03.sdk_and_coreapp.md) **总体计划:** [plan.md](../plan.md) **权威架构:** [APP架构设计.md](../../设计/APP架构设计.md) +**冻结说明:** 本定义以 [03.design_review.md](03.design_review.md) 关闭后的决议为实施基线;后续若调整 +Pomodoro Tool、Runtime 终态、状态快照或本迭代范围,须通过新的设计评审记录确认。 + ## 1. 迭代目标 前三次迭代已经完成 Runtime、Interact、MiniApp SDK v1 和参考 MiniApp 的基础契约。本迭代不再扩展 -复杂的业务应用,而是把这些基础接到真实的 Web/Tauri 工作区界面,证明用户可以启动、切换、后台化、 -关闭、恢复一个 MiniApp,并在 Interact 中查看对应的子会话。 +复杂的业务应用,而是把这些基础接到真实的 Web/Tauri 工作区界面,证明用户可以启动、运行、关闭、恢复 +一个 MiniApp,并在 Interact 中查看对应的子会话。 -本迭代选择一个极简的 **Pomodoro 番茄时钟 MiniApp** 作为 Runtime 验证应用。番茄时钟只负责倒计时和 -自己的完成提醒,不负责任务、项目或复杂业务管理。 +本迭代选择一个极简的 **Pomodoro 番茄时钟 MiniApp** 作为 Runtime 验证应用。它服务于写作业、看书、 +冥想等单次专注活动:第 04 次中用户通过 **IM** 请求 Agent 立即开始或停止一段固定时长的专注;未来 Voice +复用相同意图与 Tool 语义,但不属于本轮实现或验收。Pomodoro 只负责低干扰显示,不负责任务、项目、统计或 +通用烹饪/家务倒计时。 ```text Interact IM → Agent 请求启动 Pomodoro - → Runtime 创建 App instance 和 App 子会话 + → Runtime 创建 App instance、App 子会话和 deadline operation → Pomodoro 进入前台,Interact 进入后台 - → 计时在后台继续运行 - → 用户暂停、继续、取消或回到 Interact - → 到时间显示完成提醒并回传结果 - → 用户关闭 App,Runtime 收口并恢复 Interact + → 用户专注;自动暗屏或锁屏不改变本轮计时 + → 到时间完成,或用户主动离开专注工作区而中断 + → Runtime 回传结果并立即收口关闭 App,恢复 Interact + → Interact / Agent 呈现本次专注完成结果 → 子会话在 IM 中折叠保存 → 重启后按规则恢复或继续处理 ``` @@ -35,7 +40,7 @@ Interact IM Interact 仍然是系统级 `system` MiniApp,负责人与 Agent 的交互: -- Agent 请求启动或控制番茄钟; +- Agent 通过 `pomodoro.start` 请求立即开始一段专注,也可以通过 `pomodoro.interrupt` 结束当前专注; - Agent 向用户询问是否开始下一轮; - Agent 需要用户确认时使用 `notice / choice / confirm / input`; - 交互记录属于 Interact 和对应会话,不属于 Pomodoro 的内部 UI。 @@ -47,7 +52,8 @@ Runtime 负责最终裁决和可靠性: - 创建和管理 Pomodoro App instance; - 管理前台、后台、挂起、关闭和恢复; - 校验 Tool、作用域、实例和会话上下文; -- 保存计时状态和工作区快照; +- 为每个 App instance 保存 schema 校验、revision 控制的业务状态快照; +- 管理通用 deadline operation:持久化 `ends_at`、裁决完成/中断唯一终态并写唯一 outbox; - 处理刷新、断线和重启恢复; - 关闭 App 时停止新 Tool 投递,并收口未完成操作; - 通过 outbox 可靠回传结果; @@ -60,11 +66,10 @@ Capability 规则。 它只负责: -- 显示倒计时; -- 响应暂停、继续和取消; -- 根据 Runtime 提供的状态刷新界面; -- 到时间显示自己的完成提醒; -- 通过 SDK 报告状态和结果。 +- 显示活动名称、倒计时和低干扰专注界面; +- 根据 Runtime 投影的 operation 与 instance state 刷新界面; +- 接收 Runtime 路由的 Agent Tool 和状态投影;不以界面按钮直接改变专注业务状态; +- 通过 SDK 保存自身展示所需的业务状态快照; 它不能: @@ -75,46 +80,159 @@ Capability 规则。 ## 3. Pomodoro 最小模型 -第一版只保留以下状态: +Pomodoro App instance 和一次专注 operation 不是同一个对象:`app_session_id` 表示 Interact 中围绕该 +instance 的子会话,`instance_id` 表示 MiniApp 实例,`operation_id` 才表示一次实际专注。第一版由 +`pomodoro.start` 同时创建它们;它们不在数据模型上互为同义词。 + +一次专注 operation 只保留以下状态: ```text -idle 尚未开始 -running 正在计时 -paused 已暂停 -completed 已完成 -cancelled 已取消 +focusing 正在专注 +completed 到达约定时长 +interrupted 用户明确离开、切换工作区或关闭 App 后中断 ``` -最小数据: +最小 operation 数据: ```text -timer_id +operation_id +source_tool_call_id +activity? duration_seconds started_at ends_at -remaining_seconds state +ended_at? +interruption_reason? ``` -建议的最小 Tool: +运行中只以 `ends_at` 作为时间事实;`remaining_seconds` 是 Pomodoro UI 从 `ends_at - now` 派生的显示值, +不单独持久化。Pomodoro 的 instance snapshot 可以保存活动名称、`operation_id`、`started_at`、`ends_at` +与展示状态;它不替代 Runtime operation 的 Tool 终态和 outbox。 + +Pomodoro 在 Manifest 中声明以下两个由 Agent 调用的 Tool。MiniApp 是 Agent 的业务工具:用户在 IM 中以 +自然语言表达开始、停止等意图(未来同样适用于 Voice),由 Agent 决定并调用 Tool;Pomodoro 的界面不提供 +暂停、继续或“结束专注”的业务按钮。 ```text -pomodoro.start -pomodoro.pause -pomodoro.resume -pomodoro.cancel -pomodoro.status +pomodoro.start({ duration_seconds, activity? }) +pomodoro.interrupt({}) ``` -Runtime 不解释“番茄钟”业务,只管理一个有生命周期的计时操作。计时完成后,Pomodoro 可以显示普通 -完成提示;如果 Agent 要询问用户是否开始下一轮,必须回到 Interact 的标准交互。 +`pomodoro.start` 是长期 Tool operation:Agent 调用成功后立即进入 `focusing`,只在 `completed` 或 +`interrupted` 时回传一次业务结果。`pomodoro.interrupt({})` 是短 Tool:Tool Router 只从该 Agent 调用所在的 +`conversation_id` 中绑定当前唯一的 `focusing` Pomodoro operation;调用者不能传入或伪造 `instance_id`、 +`app_scope`、`operation_id` 或其他会话目标。Runtime 将调用路由给该 Pomodoro instance,并在同一原子裁决中 +将 `pomodoro.start` 收口为 `interrupted`。若 Surface 已卸载,Runtime 仍必须完成裁决;Surface 存在时只接收 +最终状态投影。`pomodoro.interrupt` 的稳定回执为 `interrupted`、`no_active_focusing_operation` 或 +`operation_already_final`;重复或迟到调用不得重写终态或新增 outbox。用户不需要在 App 内再次点击“开始”, +也没有暂停、继续或恢复入口。 + +用户切回 Interact、切到其他 MiniApp 或关闭 Pomodoro 时,属于 Runtime 生命周期事件:Runtime 以同一原子 +中断规则收口,但不伪造 Agent Tool。自动暗屏、锁屏和 Surface 重载不等于退出。到点、Agent 调用 +`pomodoro.interrupt` 与生命周期关闭竞争同一个终态,先成功者生效;后续调用只得到上述稳定回执。到点取得 +`completed` 后,Runtime 先原子写入唯一结果/outbox,再立即关闭 Pomodoro、结束子会话并恢复 Interact; +Pomodoro 不显示独立完成提示,完成结果由 Interact / Agent 呈现。如果 Agent 要询问用户是否开始下一轮, +必须回到 Interact 的标准交互。 + +两个 Tool 的 v1 Descriptor 如下。Runtime 只发布通过 Manifest、Inventory revision、会话作用域和输入 schema +校验的 Tool;校验失败、过期 Inventory 或不存在的目标均稳定拒绝,且不启动或唤醒 Pomodoro。 + +| Tool | 调用模型与启动策略 | 输入 | 输出 / 稳定拒绝 | 幂等、超时与路由顺序 | +|---|---|---|---|---| +| `pomodoro.start` v1 | 长期 `operation`;Runtime 创建 instance、App 子会话和 deadline operation 后将 Pomodoro 前台化。 | `{ duration_seconds: 正整数, activity?: 字符串 }`;拒绝额外字段。 | 最终成功结果:`{ state: "completed" \| "interrupted", operation_id, started_at, ends_at, ended_at, interruption_reason? }`;若同一会话已有其他 `focusing` 专注,稳定拒绝 `active_focusing_operation`。 | `source_tool_call_id` 是幂等键:同一调用重试返回既有 operation/既有最终结果;不同调用由同一个 conversation 的活动专注记录作原子 compare-and-set。没有 Host/UI 临时超时,运行期限以持久化 `ends_at` 为准,重启后继续恢复或结算。 | +| `pomodoro.interrupt` v1 | 短 Tool;不启动、唤醒或等待 Pomodoro Surface。 | 空对象 `{}`;不接受 `instance_id`、`operation_id`、`app_scope` 或其他目标字段。 | `{ status: "interrupted" \| "no_active_focusing_operation" \| "operation_already_final", operation_id? }`。 | `source_tool_call_id` 是幂等键。Runtime 从调用的 `conversation_id` 绑定当前唯一 `focusing` operation,原子写其 `interrupted` 终态与长期 start 的唯一结果/outbox,再返回短 Tool 回执;Surface 存在时才接收终态投影,不能以回执决定或补写终态。 | + +上表中的输入/输出 schema 冻结为以下 JSON Schema;Runtime 在将 Descriptor 发布到 Agent Inventory 前验证它们。 + +```json +{ + "pomodoro.start": { + "input_schema": { + "type": "object", + "additionalProperties": false, + "required": ["duration_seconds"], + "properties": { + "duration_seconds": {"type": "integer", "minimum": 1}, + "activity": {"type": "string", "maxLength": 256} + } + }, + "result_schema": { + "type": "object", + "additionalProperties": false, + "required": ["state", "operation_id", "started_at", "ends_at", "ended_at"], + "properties": { + "state": {"enum": ["completed", "interrupted"]}, + "operation_id": {"type": "string"}, + "started_at": {"type": "string"}, + "ends_at": {"type": "string"}, + "ended_at": {"type": "string"}, + "interruption_reason": {"type": "string"} + } + } + }, + "pomodoro.interrupt": { + "input_schema": { + "type": "object", + "additionalProperties": false + }, + "result_schema": { + "type": "object", + "additionalProperties": false, + "required": ["status"], + "properties": { + "status": { + "enum": ["interrupted", "no_active_focusing_operation", "operation_already_final"] + }, + "operation_id": {"type": "string"} + } + } + } +} +``` + +同一 `conversation_id` 第一版只允许一个 `focusing` Pomodoro operation。不同 `source_tool_call_id` 的第二次 +`pomodoro.start` 必须稳定拒绝为 `active_focusing_operation`,且不得创建 instance、子会话、deadline operation、 +焦点变更或 outbox;Agent 必须先调用 `pomodoro.interrupt({})`,或等待旧 operation 已成为终态后再开始。 + +Pomodoro 必须声明 `app.instance-state.v1`,其最小 Manifest 契约如下。该快照只服务界面恢复和关闭后的历史 +展示,不替代 operation 的终态、deadline 或 outbox 事实。 + +```json +{ + "features": ["app.instance-state.v1"], + "instance_state": { + "state_schema_version": 1, + "state_schema": { + "type": "object", + "additionalProperties": false, + "required": ["operation_id", "started_at", "ends_at", "display_state"], + "properties": { + "operation_id": {"type": "string"}, + "activity": {"type": "string"}, + "started_at": {"type": "string"}, + "ends_at": {"type": "string"}, + "ended_at": {"type": "string"}, + "display_state": {"enum": ["focusing", "completed", "interrupted"]}, + "interruption_reason": {"type": "string"} + } + }, + "max_bytes": 4096, + "retention": "retain_readonly" + } +} +``` + +关闭后的旧 snapshot 只能和只读 App 子会话一起用于历史展示:旧 instance 不可再次写入、不可恢复为 +`focusing`,也不能作为新一轮专注的运行状态;“继续处理”始终创建新的 instance、子会话和 operation。 ## 4. 工作内容 ### 4.1 工作区界面 - 显示当前前台 App; -- 支持 Interact 和 Pomodoro 之间切换; +- 支持由 `pomodoro.start` 从 Interact 切入 Pomodoro,以及用户显式返回 Interact;返回/切换会中断当前 + 专注 operation,而不是把它作为通用后台计时器继续运行; - 显示 App 前台、后台、挂起、关闭和恢复状态; - App 启动失败时恢复 Interact; - 刷新或重启后恢复工作区快照。 @@ -122,38 +240,51 @@ Runtime 不解释“番茄钟”业务,只管理一个有生命周期的计时 ### 4.2 生命周期接入 - 将 `AppLifecycleManager`、`AppFocusManager` 和 `AppOrchestrator` 接入真实 Host; -- 区分后台、挂起和真正关闭; -- 后台运行时保持计时; +- 区分自动暗屏/锁屏、Surface 重载、工作区切换和真正关闭:前两者不终止专注,后两者中断专注; +- Runtime 用持久化 `ends_at` 而非 MiniApp UI 定时器管理 deadline;到点、用户中断和关闭竞争同一唯一终态; +- deadline 取得 `completed` 后,Runtime 依次提交唯一结果/outbox、立即关闭 Pomodoro、结束子会话并恢复 + Interact;完成提示由 Interact / Agent 呈现,不保留 Pomodoro 完成展示窗口; - 关闭后停止向该 instance 投递新 Tool; -- 将未终态普通 Tool 收敛为 `cancelled(app_closed)`; +- 用户退出专注时先将 `pomodoro.start` 终结为 `interrupted` 并提交结果;随后关闭不会将该已终态 Tool 改写为 + `cancelled(app_closed)`。其他未终态普通 Tool 仍按 `cancelled(app_closed)` 收口; - 已提交结果继续通过 outbox 发送; - 关闭后恢复前一个有效前台 App。 ### 4.3 子会话和 Interact 交互 -- 创建 Pomodoro instance 时创建或恢复 App 子会话; +- 创建 Pomodoro instance 时创建 App 子会话;只有恢复同一个未结束 instance 时才恢复同一个子会话; - 在主 IM 中显示可折叠的 Pomodoro 子会话; - 子会话只记录人与 Agent 围绕该番茄钟的交互和结果; -- 倒计时、暂停按钮、继续按钮等 App 内部操作不写入 IM; +- 倒计时和退出按钮等 App 内部操作不写入 IM; - App 前台时,Interact 的标准交互可以显示在其上方; - 展示位置变化不改变交互归属; -- 已结束子会话只读;“继续处理”创建新的 instance 和新的子会话。 +- 已结束子会话只读;“继续处理”创建新的 instance 和新的子会话;关闭时仍 pending 的 Interact 标准交互 + 不自动取消,保留在主 IM / Interact 中等待回答、dismiss、超时或 Runtime 失败,且不能再追加到已结束子会话。 ### 4.4 恢复和提醒 -- 重启后根据 `ends_at` 重新计算剩余时间; +- 重启后根据 `ends_at` 重新计算剩余时间;若已到点,Runtime 原子转为 `completed` 并只写一次结果/outbox; - 不允许重启后无条件重新开始完整时长; -- 明确后台、暂停、关闭和完成的区别; -- 第一版只实现 App 内完成提示; +- 不承诺 Runtime / Host 完全未运行时的即时系统提醒;下次恢复时必须按已到期状态结算,不能重置或重复回传; +- 来电等外部打断预留 `host_interruption` 语义,但第 04 次不实现电话检测、免打扰或静音能力; +- 明确自动暗屏/锁屏、工作区离开、关闭、完成和中断的区别; +- 第一版不实现 Pomodoro App 内完成提示;完成结果由 Interact / Agent 呈现; - 系统通知作为后续 Capability 验证,不允许 MiniApp 直接调用系统 API。 ### 4.5 自动化和真实验收 -- 覆盖 Pomodoro 状态机和 Tool 调用; -- 覆盖前后台切换、关闭和恢复; -- 覆盖重启后剩余时间恢复; +- 覆盖 `focusing → completed / interrupted` 状态机、`pomodoro.start` 长 Tool、Agent 调用的 + `pomodoro.interrupt` 与 Runtime 生命周期中断; +- 覆盖 Tool Descriptor 的合法/非法输入、Inventory 过期、同 call 重试、同会话重复/并发 start、start 与 + interrupt/deadline 并发,以及 Surface 缺席时 Runtime 仍可收口; +- 覆盖自动暗屏/锁屏不终止、工作区切换/关闭中断、重复/迟到退出和到点与中断并发时只产生一个终态; +- 覆盖重启前未到点恢复、重启后已到点结算和一次 outbox; +- 覆盖 `completed` 后立即关闭 Pomodoro、子会话只读、Interact 呈现完成结果,以及完成与关闭并发时终态不可改写; +- 覆盖 `app.instance-state.v1` schema、4 KiB 配额与 `retain_readonly`:关闭后的快照只读,不能复活为 + `focusing` 或写入新状态; - 覆盖 App 子会话创建、折叠、只读和继续处理; -- 覆盖 Interact 标准交互在 Pomodoro 前台时仍归 Interact; +- 覆盖 Interact 标准交互在 Pomodoro 前台时仍归 Interact,以及 Pomodoro 关闭后 pending 交互仍可在主 IM + 回答但不改写只读子会话; - 通过 Web Reference Host 完成完整浏览器验收; - 在 Tauri Desktop Host 完成至少一条代表性流程; - 检查生命周期、Tool、交互、恢复和拒绝路径日志。 @@ -165,23 +296,37 @@ Runtime 不解释“番茄钟”业务,只管理一个有生命周期的计时 - 多 Agent 连接和 Agent 切换; - Audio Mode 和 Video Mode 的真实媒体能力; - Pomodoro 统计报表、多个计时器、日历和复杂提醒计划; +- 通用倒计时(如烹饪、停车、家务)以及用户预设后手动开始的 timer 模式; +- 电话检测、系统专注模式、免打扰、静音和系统通知; - Task Dashboard 的任务、项目、优先级、标签和历史管理; -- Whiteboard 的多人协作、云端同步和复杂绘图工具。 +- Whiteboard 的工作区接入、切换、完整用户流程、多人协作、云端同步和复杂绘图工具;第 04 次只保持第 + 03 次已有的 SDK / 隔离回归。 +- 将 MiniApp SDK 或 Runtime SDK 的实现迁入 Rust;本轮保持现有 TypeScript Runtime / Web Bridge 基线, + 未来仅在 profiling 证明 Runtime Core 存在性能热点后另行评估。 ## 6. 验收目标 ```text -1. 用户可以从 Interact 启动 Pomodoro。 -2. Runtime 创建真实 App instance、焦点记录和 App 子会话。 -3. Pomodoro 进入前台后,Interact 可以退到后台。 -4. Pomodoro 在后台仍然正确计时。 -5. 用户可以暂停、继续和取消计时。 -6. 到时间后 Pomodoro 显示完成提示,并通过 Runtime 回传结果。 -7. 用户关闭 Pomodoro 后,未完成操作正确取消,Interact 恢复前台。 -8. 子会话在主 IM 中折叠保存,已结束内容只读。 -9. 用户选择继续处理时,Runtime 创建新的 instance 和新的 App 子会话。 -10. 刷新或重启后,剩余时间、App 状态、焦点和子会话按规则恢复。 -11. Agent 的标准交互始终由 Interact 负责,Pomodoro 不伪造交互组件。 +1. 用户可以在 **IM** 中请求 Agent 立即开始或停止一段固定时长的写作业、看书或冥想专注;未来 Voice 必须 + 复用同一意图和 Tool 语义,但不属于本轮实现或验收。 +2. `pomodoro.start` 创建真实 App instance、焦点记录、App 子会话和 deadline operation,并直接进入 `focusing`。 +3. Pomodoro 进入前台后,Interact 可以退到后台;自动暗屏、锁屏和 Surface 重载不终止专注。 +4. 用户以 IM / Voice 要求 Agent 停止时,Agent 调用 `pomodoro.interrupt({})`;用户返回 Interact、切到其他 + MiniApp 或关闭 Pomodoro 时,Runtime 直接处理生命周期中断。两类路径的本轮唯一终态均为 `interrupted`, + 随后 Interact 恢复前台;Pomodoro 不作为通用后台计时器继续运行。 +5. 到 `ends_at` 时,本轮唯一终态为 `completed`;Runtime 提交唯一结果/outbox 后立即关闭 Pomodoro、结束子会话 + 并恢复 Interact,由 Interact / Agent 呈现完成结果;到点和中断并发时只接受第一个原子终态。 +6. Runtime 只回传一次 `pomodoro.start` 的 `completed` 或 `interrupted` 结果;`pomodoro.interrupt` 不可伪造 + 目标、只能绑定当前会话中活动的 `focusing` operation,并对重复/迟到调用给出稳定回执;关闭流程不得将已提交 + 终态改写。 +7. 同一 `conversation_id` 不会同时存在两轮 `focusing` Pomodoro;不同调用的第二次 `pomodoro.start` 稳定拒绝为 + `active_focusing_operation`,不创建任何新的 App 或 operation 资源。 +8. 刷新或重启后,未到点的本轮按 `ends_at` 恢复;已到点的本轮结算一次而不重置完整时长或重复回传。 +9. 子会话在主 IM 中折叠保存,关闭后只读;新的“继续处理”创建新的 instance / 子会话,pending Interact + 交互仍可在主 IM 回答但不能向已关闭子会话追加记录。 +10. Agent 的标准交互始终由 Interact 负责,Pomodoro 不伪造交互组件。 +11. Pomodoro 的 `app.instance-state.v1` 快照符合 schema v1 与 4 KiB 配额;关闭后保留为只读历史,不能写入或 + 复活旧 instance。 12. MiniApp 无法访问 Transport、Store、Agent、Host DOM、Tauri 或任意系统 API。 13. `npm test -- --run`、`npm run build` 和 Web/Tauri 代表性验收全部通过。 14. P0~P3 设计和验收问题清零。 diff --git a/迭代/plan.md b/迭代/plan.md index f33ec83..a258533 100644 --- a/迭代/plan.md +++ b/迭代/plan.md @@ -47,7 +47,7 @@ Runtime 已具备应用实例、焦点和生命周期的核心模型: `03.sdk_and_coreapp` 已完成并通过自动化测试和浏览器验收: - Interact 是唯一的 `system` MiniApp; -- 番茄时钟、Task Dashboard 和 Whiteboard 都是普通 `bundled` MiniApp; +- Task Dashboard 和 Whiteboard 都是普通 `bundled` MiniApp 的参考实现; - MiniApp 通过 SDK 接收 Inbox、Tool、进度、结果、生命周期、Surface 和 Capability 请求; - 标准 `notice / choice / confirm / input` 属于 Interact 的人与 Agent 交互,不是 MiniApp 内部表单; - App 子会话、交互归属、关闭收口、继续处理和重启恢复已有明确规则; @@ -57,6 +57,10 @@ Runtime 已具备应用实例、焦点和生命周期的核心模型: ## 3. 第四次迭代:Runtime 工作区与应用闭环 +**设计状态:** 已冻结,待实施;冻结基线为 +[04.runtime_workspace.md](04.runtime_workspace/04.runtime_workspace.md) 及其 +[03.design_review.md](04.runtime_workspace/03.design_review.md)。 + 建议迭代目录: ```text @@ -70,12 +74,11 @@ Runtime 已具备应用实例、焦点和生命周期的核心模型: ```text 进入 Interact IM → Agent 请求启动 Pomodoro 番茄时钟 - → Runtime 创建 App instance 和 App 子会话 + → Runtime 创建 App instance、App 子会话和 deadline operation → 番茄时钟进入前台,Interact 进入后台 - → 计时在后台继续运行 - → 用户可以暂停、继续、取消或回到 Interact - → 到时间后显示完成提醒并回传结果 - → 用户关闭 App,Runtime 收口未完成操作 + → 用户进行写作业、看书或冥想等固定时长专注;自动暗屏/锁屏不终止本轮 + → 到时间完成,或用户主动离开 Pomodoro 工作区而中断 + → Runtime 回传唯一结果并关闭 App → 子会话结束并在 IM 中折叠保存 → Interact 恢复 → 用户查看历史或继续开始下一轮 @@ -85,16 +88,21 @@ Runtime 已具备应用实例、焦点和生命周期的核心模型: 1. **接通 App 工作区界面** - 显示当前前台 App; - - 支持 Interact、Pomodoro 番茄时钟、Whiteboard 的切换; + - 支持由 `pomodoro.start` 从 Interact 进入 Pomodoro,以及用户明确返回 Interact; + 返回/切换会中断当前专注,不把 Pomodoro 作为通用后台计时器继续运行; - 显示后台、挂起、关闭和恢复状态; - App 启动失败时恢复 Interact; - 刷新或重启后恢复工作区。 + Whiteboard 在第 04 次只保持第 03 次已有的 SDK/隔离回归,不接入本轮工作区切换或新的完整流程。 + 2. **接通 App 生命周期** - 将 `AppLifecycleManager`、`AppFocusManager` 和 `AppOrchestrator` 接入真实 Host; - - 区分后台、挂起和真正关闭; + - 区分自动暗屏/锁屏、Surface 重载、工作区切换和真正关闭;前两者不终止专注,后两者中断专注; + - Runtime 使用持久化 `ends_at` 的 deadline operation,而不是 MiniApp UI 定时器; - 关闭后停止新 Tool 投递; - - 将未终态普通 Tool 收敛为 `cancelled(app_closed)`; + - 用户退出专注时先将 `pomodoro.start` 终结为 `interrupted`;其他未终态普通 Tool 才收敛为 + `cancelled(app_closed)`; - 已提交结果继续通过 outbox 发送; - 关闭后恢复前一个有效前台 App。 @@ -113,39 +121,46 @@ Runtime 已具备应用实例、焦点和生命周期的核心模型: - Agent 可以通过 `interaction.dismiss` 远程取消交互。 5. **让 Pomodoro 成为第一条完整用户流程** - - Agent 请求启动一个番茄钟; - - Runtime 创建计时实例和 App 子会话; - - 用户可以查看剩余时间、暂停、继续和取消; - - 番茄钟切到后台后仍能继续计时; - - 到时间后显示完成提醒并回传结果; - - 用户关闭番茄钟时,Runtime 正确取消未完成操作; - - 刷新或重启后根据 `ends_at` 恢复剩余时间; + - 用户在 IM 中以自然语言要求 Agent 开始或结束写作业、看书或冥想等专注;MiniApp 是 Agent 的业务工具, + 而不是由用户自行操作业务状态的独立应用。未来 Voice 复用相同意图和 Tool 语义,但不属于本轮实现或验收; + - Agent 以 `pomodoro.start({ duration_seconds, activity? })` 创建长期 Tool operation,并以 + `pomodoro.interrupt({})` 请求结束当前专注; + - 同一会话已有 `focusing` 专注时,不同调用的第二次 `pomodoro.start` 稳定拒绝为 + `active_focusing_operation`,不创建新的 App、子会话或 deadline operation; + - Runtime 创建 Pomodoro instance、App 子会话和 deadline operation,Pomodoro 直接进入专注界面; + - 到时间得到 `completed`;Agent 调用 `pomodoro.interrupt`,或用户切回 Interact、切到其他 MiniApp、关闭 + Pomodoro 时得到 `interrupted`;后者由 Runtime 的生命周期处理,不伪造 Agent Tool;不支持暂停、继续或恢复; + - 自动暗屏、锁屏与 Surface 重载不终止专注;刷新或重启后根据 `ends_at` 恢复或结算一次; + - 到时间后 Runtime 回传一次结果并立即关闭 Pomodoro,完成结果由 Interact / Agent 呈现; - 完成记录和子会话可以在 Interact 中查看。 第一版只保留这些状态和数据: ```text - state = idle | running | paused | completed | cancelled - timer_id + state = focusing | completed | interrupted + operation_id + source_tool_call_id + activity? duration_seconds started_at ends_at - remaining_seconds + ended_at? + interruption_reason? ``` - 建议的最小 Tool: + 最小 Agent Tool: ```text - pomodoro.start - pomodoro.pause - pomodoro.resume - pomodoro.cancel - pomodoro.status + pomodoro.start({ duration_seconds, activity? }) + pomodoro.interrupt({}) ``` - 番茄钟到时间后的普通完成提醒属于 MiniApp 自己的业务提示;如果以后需要系统通知,再通过 - Runtime 的 Capability 请求,不能让 MiniApp 直接调用 Tauri 或操作系统接口。Agent 询问用户是否 - 开始下一轮时,仍然必须使用 Interact 的标准交互。 + Pomodoro 使用 `app.instance-state.v1` 保存严格 schema v1、最大 4 KiB 的展示快照;关闭后采用 + `retain_readonly`,旧快照只用于历史展示,不能再次写入或恢复为运行中的专注。 + + 番茄钟到点后不保留 App 内完成提醒:Runtime 关闭 Pomodoro 并恢复 Interact,由 Interact / Agent 呈现 + 完成结果。如果以后需要系统通知,再通过 Runtime 的 Capability 请求,不能让 MiniApp 直接调用 Tauri + 或操作系统接口。Agent 询问用户是否开始下一轮时,仍然必须使用 Interact 的标准交互。 6. **补充端到端验收** - Web Reference Host 完整验收; @@ -160,20 +175,23 @@ Runtime 已具备应用实例、焦点和生命周期的核心模型: - 多 Agent 连接、切换和多 Agent 会话列表; - Audio Mode 的真实媒体能力; - Video Mode 的真实媒体能力; -- Whiteboard 的多人协作和复杂绘图能力; +- Whiteboard 的工作区接入、切换、多人协作和复杂绘图能力;第 04 次只保持其已有回归; - Task Dashboard 的完整项目管理功能; - Pomodoro 的统计报表、多个计时器、日历和复杂提醒计划。 ### 3.4 完成标准 ```text -用户可以从 Interact 启动 Pomodoro 番茄时钟; -Runtime 可以正确创建、切换、后台化、关闭和恢复 App; +用户可以在 IM 中请求 Agent 立即开始或停止 Pomodoro 专注;未来 Voice 只复用相同 Tool 语义; +Runtime 可以正确创建、运行、中断、关闭和恢复 Pomodoro App; App 子会话能在 IM 中折叠、展开和继续处理; 标准交互在不同前台 App 下仍归 Interact; -关闭 App 时 Tool 和 outbox 收口正确; -刷新/重启后 App、焦点、子会话、计时剩余时间和待处理交互按规则恢复; +到点/中断竞争唯一终态,Tool 和 outbox 收口正确; +到点后立即关闭 Pomodoro 并由 Interact / Agent 呈现完成结果; +Pomodoro 关闭后的业务快照保留为只读历史,不能复活旧 instance; +刷新/重启后 App、焦点、子会话、`ends_at` 专注状态和待处理交互按规则恢复; Web/Tauri 代表性流程通过端到端验收; +Whiteboard 保持第 03 次回归,但不属于第 04 次工作区闭环或完成范围; 所有 P0~P3 评审问题清零。 ``` @@ -242,6 +260,24 @@ Task Dashboard 仍然保留为后续普通 `bundled` MiniApp,但不作为 Runt ## 7. 更后面的方向 +### Runtime Core 的 Rust 演进 + +在 Runtime 工作区已跑通并积累 Web / Tauri 两个 Host 的性能数据后,单独评估是否将部分**无 UI 的 Runtime +Core** 下沉到 Rust,以改善状态处理和原子收口的效率与一致性。这不是第 04 次迭代的工作,也不等于把整个 +MiniApp SDK 改写成 Rust:Web Reference Host、sandbox iframe、postMessage Bridge、DOM 和 UI 仍须保持在 +TypeScript / 浏览器侧。 + +潜在的 Rust 候选范围: + +- instance state 的持久化、schema 校验和 revision 比较; +- deadline operation 调度; +- Tool / outbox 的原子终态裁决; +- lifecycle 收口和审计。 + +只有在 profiling 显示上述路径存在明确热点时,才建立独立设计与迁移迭代。评估必须同时测量状态快照读写、 +deadline / Tool 吞吐、主线程阻塞、Tauri IPC 往返、JSON 序列化成本,以及 Web / Tauri Host 的实际差异; +并证明收益覆盖跨语言实现、测试、调试和错误边界所增加的复杂度。 + ### 多 Agent 在单 Agent 工作区稳定后再设计: