Files
app/迭代/04.runtime_workspace/04.runtime_workspace.md
T

19 KiB
Raw Blame History

LineUp App 迭代定义:Runtime 工作区与 Pomodoro MiniApp

迭代编号: 04.runtime_workspace 状态: 设计已冻结,待实施 日期: 2026-08-06 前置基线: 03.sdk_and_coreapp.md 总体计划: plan.md 权威架构: APP架构设计.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 只负责低干扰显示,不负责任务、项目、统计或 通用烹饪/家务倒计时。

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 只保留以下状态:

focusing     正在专注
completed    到达约定时长
interrupted  用户明确离开、切换工作区或关闭 App 后中断

最小 operation 数据:

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_idstarted_atends_at 与展示状态;它不替代 Runtime operation 的 Tool 终态和 outbox。

Pomodoro 在 Manifest 中声明以下两个由 Agent 调用的 Tool。MiniApp 是 Agent 的业务工具:用户在 IM 中以 自然语言表达开始、停止等意图(未来同样适用于 Voice),由 Agent 决定并调用 Tool;Pomodoro 的界面不提供 暂停、继续或“结束专注”的业务按钮。

pomodoro.start({ duration_seconds, activity? })
pomodoro.interrupt({})

pomodoro.start 是长期 Tool operationAgent 调用成功后立即进入 focusing,只在 completedinterrupted 时回传一次业务结果。pomodoro.interrupt({}) 是短 ToolTool Router 只从该 Agent 调用所在的 conversation_id 中绑定当前唯一的 focusing Pomodoro operation;调用者不能传入或伪造 instance_idapp_scopeoperation_id 或其他会话目标。Runtime 将调用路由给该 Pomodoro instance,并在同一原子裁决中 将 pomodoro.start 收口为 interrupted。若 Surface 已卸载,Runtime 仍必须完成裁决;Surface 存在时只接收 最终状态投影。pomodoro.interrupt 的稳定回执为 interruptedno_active_focusing_operationoperation_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 长期 operationRuntime 创建 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_idoperation_idapp_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 前验证它们。

{
  "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、 焦点变更或 outboxAgent 必须先调用 pomodoro.interrupt({}),或等待旧 operation 已成为终态后再开始。

Pomodoro 必须声明 app.instance-state.v1,其最小 Manifest 契约如下。该快照只服务界面恢复和关闭后的历史 展示,不替代 operation 的终态、deadline 或 outbox 事实。

{
  "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 生命周期接入

  • AppLifecycleManagerAppFocusManagerAppOrchestrator 接入真实 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. 验收目标

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 和生命周期稳定后,再单独定义任务管理业务模型。