Files

339 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 中查看对应的子会话。
本迭代选择一个极简的 **Pomodoro 番茄时钟 MiniApp** 作为 Runtime 验证应用。它服务于写作业、看书、
冥想等单次专注活动:第 04 次中用户通过 **IM** 请求 Agent 立即开始或停止一段固定时长的专注;未来 Voice
复用相同意图与 Tool 语义,但不属于本轮实现或验收。Pomodoro 只负责低干扰显示,不负责任务、项目、统计或
通用烹饪/家务倒计时。
```text
Interact IM
→ Agent 请求启动 Pomodoro
→ Runtime 创建 App instance、App 子会话和 deadline operation
→ Pomodoro 进入前台,Interact 进入后台
→ 用户专注;自动暗屏或锁屏不改变本轮计时
→ 到时间完成,或用户主动离开专注工作区而中断
→ Runtime 回传结果并立即收口关闭 App,恢复 Interact
→ Interact / Agent 呈现本次专注完成结果
→ 子会话在 IM 中折叠保存
→ 重启后按规则恢复或继续处理
```
## 2. 产品和架构边界
### 2.1 Interact
Interact 仍然是系统级 `system` MiniApp,负责人与 Agent 的交互:
- Agent 通过 `pomodoro.start` 请求立即开始一段专注,也可以通过 `pomodoro.interrupt` 结束当前专注;
- Agent 向用户询问是否开始下一轮;
- Agent 需要用户确认时使用 `notice / choice / confirm / input`
- 交互记录属于 Interact 和对应会话,不属于 Pomodoro 的内部 UI。
### 2.2 Runtime
Runtime 负责最终裁决和可靠性:
- 创建和管理 Pomodoro App instance
- 管理前台、后台、挂起、关闭和恢复;
- 校验 Tool、作用域、实例和会话上下文;
- 为每个 App instance 保存 schema 校验、revision 控制的业务状态快照;
- 管理通用 deadline operation:持久化 `ends_at`、裁决完成/中断唯一终态并写唯一 outbox;
- 处理刷新、断线和重启恢复;
- 关闭 App 时停止新 Tool 投递,并收口未完成操作;
- 通过 outbox 可靠回传结果;
- 需要系统通知时通过 Capability Gateway 处理。
### 2.3 Pomodoro MiniApp
Pomodoro 是普通 `bundled` MiniApp,必须使用与其他非系统级 MiniApp 相同的 SDK、受限 Surface 和
Capability 规则。
它只负责:
- 显示活动名称、倒计时和低干扰专注界面;
- 根据 Runtime 投影的 operation 与 instance state 刷新界面;
- 接收 Runtime 路由的 Agent Tool 和状态投影;不以界面按钮直接改变专注业务状态;
- 通过 SDK 保存自身展示所需的业务状态快照;
它不能:
- 直接访问 AppServer、Transport、Conversation Store 或 Agent
- 直接调用 Tauri、系统通知或其他 Host 能力;
- 发起 Interact 标准交互;
- 修改 Runtime 的焦点、Registry 或其他 App 数据。
## 3. Pomodoro 最小模型
Pomodoro App instance 和一次专注 operation 不是同一个对象:`app_session_id` 表示 Interact 中围绕该
instance 的子会话,`instance_id` 表示 MiniApp 实例,`operation_id` 才表示一次实际专注。第一版由
`pomodoro.start` 同时创建它们;它们不在数据模型上互为同义词。
一次专注 operation 只保留以下状态:
```text
focusing 正在专注
completed 到达约定时长
interrupted 用户明确离开、切换工作区或关闭 App 后中断
```
最小 operation 数据:
```text
operation_id
source_tool_call_id
activity?
duration_seconds
started_at
ends_at
state
ended_at?
interruption_reason?
```
运行中只以 `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 决定并调用 ToolPomodoro 的界面不提供
暂停、继续或“结束专注”的业务按钮。
```text
pomodoro.start({ duration_seconds, activity? })
pomodoro.interrupt({})
```
`pomodoro.start` 是长期 Tool operationAgent 调用成功后立即进入 `focusing`,只在 `completed`
`interrupted` 时回传一次业务结果。`pomodoro.interrupt({})` 是短 ToolTool 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 SchemaRuntime 在将 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
- 支持由 `pomodoro.start` 从 Interact 切入 Pomodoro,以及用户显式返回 Interact;返回/切换会中断当前
专注 operation,而不是把它作为通用后台计时器继续运行;
- 显示 App 前台、后台、挂起、关闭和恢复状态;
- App 启动失败时恢复 Interact
- 刷新或重启后恢复工作区快照。
### 4.2 生命周期接入
-`AppLifecycleManager``AppFocusManager``AppOrchestrator` 接入真实 Host
- 区分自动暗屏/锁屏、Surface 重载、工作区切换和真正关闭:前两者不终止专注,后两者中断专注;
- Runtime 用持久化 `ends_at` 而非 MiniApp UI 定时器管理 deadline;到点、用户中断和关闭竞争同一唯一终态;
- deadline 取得 `completed` 后,Runtime 依次提交唯一结果/outbox、立即关闭 Pomodoro、结束子会话并恢复
Interact;完成提示由 Interact / Agent 呈现,不保留 Pomodoro 完成展示窗口;
- 关闭后停止向该 instance 投递新 Tool
- 用户退出专注时先将 `pomodoro.start` 终结为 `interrupted` 并提交结果;随后关闭不会将该已终态 Tool 改写为
`cancelled(app_closed)`。其他未终态普通 Tool 仍按 `cancelled(app_closed)` 收口;
- 已提交结果继续通过 outbox 发送;
- 关闭后恢复前一个有效前台 App。
### 4.3 子会话和 Interact 交互
- 创建 Pomodoro instance 时创建 App 子会话;只有恢复同一个未结束 instance 时才恢复同一个子会话;
- 在主 IM 中显示可折叠的 Pomodoro 子会话;
- 子会话只记录人与 Agent 围绕该番茄钟的交互和结果;
- 倒计时和退出按钮等 App 内部操作不写入 IM;
- App 前台时,Interact 的标准交互可以显示在其上方;
- 展示位置变化不改变交互归属;
- 已结束子会话只读;“继续处理”创建新的 instance 和新的子会话;关闭时仍 pending 的 Interact 标准交互
不自动取消,保留在主 IM / Interact 中等待回答、dismiss、超时或 Runtime 失败,且不能再追加到已结束子会话。
### 4.4 恢复和提醒
- 重启后根据 `ends_at` 重新计算剩余时间;若已到点,Runtime 原子转为 `completed` 并只写一次结果/outbox
- 不允许重启后无条件重新开始完整时长;
- 不承诺 Runtime / Host 完全未运行时的即时系统提醒;下次恢复时必须按已到期状态结算,不能重置或重复回传;
- 来电等外部打断预留 `host_interruption` 语义,但第 04 次不实现电话检测、免打扰或静音能力;
- 明确自动暗屏/锁屏、工作区离开、关闭、完成和中断的区别;
- 第一版不实现 Pomodoro App 内完成提示;完成结果由 Interact / Agent 呈现;
- 系统通知作为后续 Capability 验证,不允许 MiniApp 直接调用系统 API。
### 4.5 自动化和真实验收
- 覆盖 `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,以及 Pomodoro 关闭后 pending 交互仍可在主 IM
回答但不改写只读子会话;
- 通过 Web Reference Host 完成完整浏览器验收;
- 在 Tauri Desktop Host 完成至少一条代表性流程;
- 检查生命周期、Tool、交互、恢复和拒绝路径日志。
## 5. 不在本迭代范围
- 应用市场、服务端 App Catalog 和远程下载;
- 第三方 MiniApp 发布和在线更新;
- 多 Agent 连接和 Agent 切换;
- Audio Mode 和 Video Mode 的真实媒体能力;
- Pomodoro 统计报表、多个计时器、日历和复杂提醒计划;
- 通用倒计时(如烹饪、停车、家务)以及用户预设后手动开始的 timer 模式;
- 电话检测、系统专注模式、免打扰、静音和系统通知;
- Task Dashboard 的任务、项目、优先级、标签和历史管理;
- Whiteboard 的工作区接入、切换、完整用户流程、多人协作、云端同步和复杂绘图工具;第 04 次只保持第
03 次已有的 SDK / 隔离回归。
- 将 MiniApp SDK 或 Runtime SDK 的实现迁入 Rust;本轮保持现有 TypeScript Runtime / Web Bridge 基线,
未来仅在 profiling 证明 Runtime Core 存在性能热点后另行评估。
## 6. 验收目标
```text
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 设计和验收问题清零。
```
## 7. 后续定位
本迭代完成后,Whiteboard 可以作为下一条 Surface、Artifact 和隔离边界验证应用;Task Dashboard 保留
为后续普通 `bundled` MiniApp,等 Runtime 工作区、SDK 和生命周期稳定后,再单独定义任务管理业务模型。