初始化 agent_ops 文档治理体系

This commit is contained in:
2026-08-07 16:56:51 +08:00
commit 10840909ab
75 changed files with 15750 additions and 0 deletions
@@ -0,0 +1,120 @@
# 迭代工作流与四步法
**版本:** 2026-08-07
**适用范围:** 所有后续 LineUp 迭代
## 1. 为什么要统一四步法
`04A.agent_tool_route` 开始,一次迭代不再只是某个前端仓的局部改动,而可能同时联动:
- Runtime
- Adapter
- AppServer
- 协议文档
- Host 验收
- 联调与证据
如果没有统一方法,很容易出现:
- 规划写在一个地方;
- 评审写在另一个地方;
- 技术规范散落在实现仓;
- 跨项目边界没有统一判断依据。
因此,从现在开始,每次迭代都至少经过四个固定步骤。
## 2. 固定四步
### 第一步:规划
目标:
- 说明为什么做这个迭代;
- 冻结范围边界;
- 说明涉及哪些项目和端;
- 给出完成标准和不在范围内的内容。
标准产物:
- `01.迭代规划.md`
### 第二步:设计评审
目标:
- 用独立视角检查规划中的产品边界、架构边界、状态机和风险;
- 清理 P0P3 问题;
- 把必须回填的结论反映回主规划文档。
标准产物:
- `02.设计评审.md`
- 如有多轮,可继续编号:`03.设计评审(二).md``04.设计评审(三).md`
### 第三步:技术实施规范
目标:
- 把已经冻结的产品/架构结论映射到具体模块、协议、状态、存储、测试和验收实现;
- 明确每个项目各自承担什么修改;
- 说明跨仓联调和验证方式。
标准产物:
- `05.技术实施规范.md`
### 第四步:技术实施规范评审
目标:
- 检查技术规范是否真实覆盖冻结边界;
- 识别实现层遗漏、跨仓接口风险、测试缺口和不可实施部分;
- 清理 P0P3 技术问题。
标准产物:
- `06.技术实施规范评审.md`
- 如有第二轮,可继续编号
## 3. 实施后的补充步骤
虽然用户当前强调的是四步法,但一个完整迭代通常还需要:
- 实施
- 验收评审
- 证据归档
因此建议默认继续补齐:
- `08.验收评审.md`
- `09.验收评审(二).md`
- `evidence/`
## 4. 进入实现的门槛
每个迭代在开始大规模实现前,至少应满足:
1. 主规划文档已写明范围与完成标准;
2. 设计评审中的 P0P3 已清零;
3. 技术实施规范已把跨项目责任拆开;
4. 技术实施规范评审中的 P0~P3 已清零。
## 5. 状态定义
建议统一使用以下阶段状态:
- `规划中`
- `设计评审中`
- `技术规范中`
- `技术评审中`
- `开发实施中`
- `待验收`
- `已完成`
- `已归档`
## 6. 这套方法的核心收益
- 让每次迭代都有统一入口;
- 让跨项目修改不再只挂在某一个代码仓名下;
- 让设计结论、技术规范和验收标准之间形成可追溯链路;
- 让后续 agent 可以稳定接手迭代文档工作。
@@ -0,0 +1,62 @@
# 迭代目录与命名规范
**版本:** 2026-08-07
**适用范围:** `agent_ops/03.迭代规划/` 下的新迭代目录
## 1. 迭代目录命名
迭代目录统一使用:
```text
<迭代编号>.<简短名称>/
```
例如:
- `04.runtime_workspace/`
- `04A.agent_tool_route/`
- `05.first_isolated_miniapp/`
## 2. 每个迭代目录的推荐结构
```text
<迭代目录>/
├── 01.迭代规划.md
├── 02.设计评审.md
├── 03.设计评审(二).md # 按需要增加
├── 05.技术实施规范.md
├── 06.技术实施规范评审.md
├── 07.技术实施规范评审(二).md # 按需要增加
├── 08.验收评审.md
├── 09.验收评审(二).md # 按需要增加
└── evidence/
```
## 3. 命名原则
- 主规划始终用 `01.迭代规划.md`
- 第一轮设计评审固定用 `02.设计评审.md`
- 技术实施规范固定用 `05.技术实施规范.md`
- 技术实施规范评审固定用 `06.技术实施规范评审.md`
- 验收评审从 `08` 开始,避免和前四步混淆
## 4. 为什么保留编号空位
保留 `03 / 04 / 07 / 09` 等位置,是为了支持:
- 多轮设计评审;
- 多轮技术规范评审;
- 多轮验收评审;
- 不打乱主文件的稳定编号。
## 5. 跨项目迭代的写法
`04A` 开始,主规划和技术规范都必须明确写出涉及项目,例如:
- `lineup-app`
- `lineup-adapter/hermes`
- `lineup-app-server`
- `infra/wukongim-v3`
- 文档目录本身
不能再把跨项目迭代只写成某一个仓的局部事项。
@@ -0,0 +1,65 @@
# 跨项目迭代边界与协作方式
**版本:** 2026-08-07
**适用范围:**`04A.agent_tool_route` 开始的跨项目迭代
## 1. 当前现实
`04A` 开始,一次迭代已经不再只作用于 `lineup-app/`
一个完整闭环可能同时涉及:
- `lineup-app/`Runtime、Host、UI、SDK
- `lineup-adapter/hermes/`:协议转换、Agent 兼容层
- `lineup-app-server/`:必要时的传输边界
- `agent_ops/`:规划、设计、协议与验收文档
因此,迭代必须以“能力闭环”为单位,而不是以“某个仓库”命名。
## 2. 规划时必须写清的三件事
### 2.1 涉及哪些项目
每份 `01.迭代规划.md` 都必须列清:
- 主要改动项目;
- 次要联动项目;
- 只需验证、不需修改的项目。
### 2.2 哪个项目负责什么
规划和技术规范都要写清:
- Runtime 责任;
- Adapter 责任;
- 服务端责任;
- Host / 验收责任;
- 文档与协议责任。
### 2.3 哪些层不应被修改
跨项目迭代最常见的问题,不是“漏改一个文件”,而是“把不该动的边界动了”。
所以规划里必须明确:
- 哪一层是执行权威;
- 哪一层只做兼容;
- 哪一层视为透明传输;
- 哪些旧语义在本轮保持不变。
## 3. 评审时的检查顺序
跨项目迭代建议按以下顺序评审:
1. 先看产品与架构边界是否清楚;
2. 再看跨仓责任是否分清;
3. 再看协议与状态机是否闭环;
4. 最后看实施和验收能否真正跨端跑通。
## 4. 当前统一入口
从现在开始:
- 迭代规划、设计评审、技术规范、技术评审都在 `agent_ops/03.迭代规划/` 下组织;
- 具体实现仍分布在各代码仓;
- 但“本次迭代到底在做什么”只在这里定义和冻结。
@@ -0,0 +1,54 @@
# LineUp App 迭代文档
每个迭代使用一个独立目录,目录名固定为“迭代编号.简短名称”。目录内至少保留该迭代的主定义文档;
每进行一次设计、架构或实现评审,都必须新增一份独立评审记录,不覆盖之前的记录。
```text
迭代/
├── plan.md # 当前阶段之后的总体路线和排期原则
├── 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. 迭代完成后的实现验收、发布复盘等文档也保留在对应目录内,并使用清晰的递增编号。
## 后续总计划
当前阶段之后的路线、迭代拆分、范围边界和完成标准见 [plan.md](plan.md)。该文件记录已确认的整体方向;
每个具体迭代开始后,仍需在自己的目录中创建主定义文档和独立评审记录。
## 评审优先级与遗留问题
评审问题使用 `P0``P5` 标示处理优先级。优先级表达的是“最晚何时必须解决”,而不是问题描述的
修辞强弱。
| 级别 | 含义 | 当前迭代的处理规则 |
|---|---|---|
| P0 | 根本性阻塞:安全边界、数据正确性或总体架构不能成立。 | 立即处理;在解决前不得进入相关实现。 |
| P1 | 关键规则未定:不解决会让不同实现互相冲突或明显返工。 | 在开始相关模块实现前处理。 |
| P2 | 交付或验收缺口:核心方向正确,但缺失会使恢复、安全验证或验收不完整。 | 必须在本迭代验收前处理。 |
| P3 | 局部行为、一致性或质量问题:存在安全默认行为,但相关模块完成前仍需收敛。 | 必须在本迭代结束前处理。 |
| P4 | 优化、可读性或维护性建议。 | 可以登记为遗留问题,不阻塞当前迭代。 |
| P5 | 观察记录或未来机会,当前没有足够的产品需求或证据进入排期。 | 可以登记为遗留问题,不纳入当前迭代。 |
因此,**P0~P3 必须在当前迭代处理完毕,P4~P5 可以作为遗留问题延期处理。**
延期不是删除:评审中出现 P4/P5 时,必须在该评审文件的 Outline 或“遗留问题”段落中保留编号、问题、
延期原因和建议重新评估的阶段/条件。只有在对应迭代主定义文档或新的评审记录中明确重新纳入后,才将
其提升为当前迭代的 P0~P3 问题。
@@ -0,0 +1,32 @@
# 迭代总览与阶段结论
**版本:** 2026-08-07 整合版
**范围:** `lineup-app/迭代/` 的主定义、计划、设计评审与验收评审
## 1. 总体判断
当前迭代体系已经从“客户端内部阶段记录”演进成“跨项目能力闭环推进”。
最重要的阶段事实是:
- `00.base` 已完成并验证最小客户端闭环;
- `01.kernel` 明确了应用工作区与运行时编排方向;
- `03.sdk_and_coreapp` 已完成并冻结 MiniApp SDK v1 的关键边界;
- `04.runtime_workspace` 已于 2026-08-06 完成最终验收;
- `04A.agent_tool_route` 已于 2026-08-07 完成当前定义下的联合验收。
## 2. 当前阶段地图
| 阶段 | 状态 | 作用 |
|---|---|---|
| `00.base` | 已完成 | 最小 Runtime 闭环基线 |
| `01.kernel` | 目标设计 | 应用工作区与编排骨架 |
| `03.sdk_and_coreapp` | 已完成 | SDK v1 与内置参考 MiniApp |
| `04.runtime_workspace` | 已完成 | 工作区与 Pomodoro 主闭环 |
| `04A.agent_tool_route` | 已完成 | Agent Tool 通路与 Hermes 兼容层 |
## 3. 当前主优先级
1.`04.runtime_workspace``04A.agent_tool_route` 为已完成样板,启动下一轮跨项目迭代。
2. 继续按统一四步法推进后续阶段。
3. 逐步清理旧来源目录和历史入口。
@@ -0,0 +1,28 @@
# 后续路线与阶段优先级
**版本:** 2026-08-07 整合版
**依据:** `lineup-app/迭代/plan.md`
## 1. 当前北极星
LineUp 的目标是成为面向 Agent 的协议化交互运行时,而不是某个固定 channel 的 UI 外壳。
## 2. 当前推荐路线
```text
已完成 Runtime 工作区闭环
→ 已补齐 Agent Tool 通路
→ 下一步推进下一类真正可用的隔离 MiniApp
```
## 3. 当前不宜提前拉入主线的事项
- 应用市场
- 第三方远程下载与发布
- 多 Agent 连接与切换
- 真实音视频能力
- 复杂任务管理膨胀
## 4. 与新迭代主目录的关系
今后这份路线文档在 `agent_ops/03.迭代规划/` 维护,作为所有跨项目迭代的总路线入口,而不再只放在 `lineup-app/迭代/plan.md`
@@ -0,0 +1,422 @@
# LineUp App 后续迭代计划
**更新时间:** 2026-08-07
**计划状态:** 已确认,作为后续迭代拆分和排期依据
## 1. 产品北极星
LineUp 的最终目标,不是做一个“把 Agent 接到某个聊天 channel 上”的客户端,也不是做一个只能展示固定卡片格式的
Agent IM 外壳;它要成为一个 **面向 Agent 的协议化交互运行时**
这意味着:
- Agent 不只输出文本,还能基于标准协议选择更合适的交互形态;
- 用户与 Agent 的核心交互方式仍然是 `IM``voice``video conference` 等主交互模式;
- Runtime、SDK、Tool、MiniApp、Surface、Capability 和标准交互原语,是这些主交互模式在任务过程中可调用、可组合、可恢复的工具与组件;
- LineUp 的核心资产,不是某一个具体小程序,而是“Agent 如何知道该用什么形式表达任务,以及宿主如何稳定执行这种表达”的标准协议与运行时。
可以将目标结构收敛为:
```text
主交互模式
= IM / Voice / Video Conference / 未来其他模式
任务过程中的可用工具
= 标准交互原语 + Runtime Tool + MiniApp Workspace + Host Capability
LineUp 的核心职责
= 让 Agent 基于统一协议,在这些交互模式中选择、调用、编排合适的工具和组件
```
因此,LineUp 与当前常见 Agent channel 方案的本质区别在于:
- 普通 channel 方案主要解决“Agent 内容如何适配某个平台已有的固定展示格式”;
- LineUp 要解决的是“Agent 如何在一个统一运行时里,按协议动态组织文本、卡片、工具调用和微型工作区,与用户完成真实任务协作”。
这里的 MiniApp 往往不需要很大。像 Pomodoro 这样的能力,本质上不是一个独立产品线,而是 Agent 在任务过程中临时拉起的、
最适合当前目标的一个受控交互载体。未来这类载体可以是番茄钟、白板、选择器、安排器、表单、确认器或其他小型工作区。
本计划后续所有迭代,都应围绕这条北极星判断优先级:
- 是否在强化协议和运行时,而不是回退成某个 channel 的格式适配;
- 是否在增强 Agent 对“该用什么交互形态”的可选择能力;
- 是否在让 IM / Voice / Video 等主交互模式能够共享同一套 Runtime、SDK 和 Tool 语义;
- 是否在让工具与组件成为交互过程中的标准能力,而不是散落的页面特判。
## 2. 计划结论
前三个阶段已经建立了 LineUp App 的基础:Runtime 托管 IM、Runtime 具备应用编排能力、MiniApp SDK v1
和两个内置 MiniApp 参考实现已经完成验证。
下一步不建设应用市场,也不马上扩展多 Agent、语音或视频。下一阶段先把已有的 Runtime、SDK 和 MiniApp
契约做成用户真正可以使用的“应用工作区”。
当前推荐路线:
```text
Runtime 工作区与应用闭环
→ 第一个真正可用的 MiniApp(番茄时钟)
→ 本地 App 管理与签名 Bundle
→ 再评估应用分发和市场
```
## 3. 已完成的基础
### 3.1 Runtime 托管 IM
`00.base` 已建立客户端基础闭环:
- Runtime 独占网络连接、同步循环、消息存储、outbox 和 App Inbox
- 登录、同步、本地回显、Markdown 和 Agent 状态保持稳定;
- 入站消息经过作用域校验、去重和恢复;
- Chat 只能通过 SDK 和 Runtime Action 工作。
### 3.2 Runtime 应用编排基础
Runtime 已具备应用实例、焦点和生命周期的核心模型:
- App Registry
- App Instance Manager
- App Focus Manager
- App Lifecycle Manager
- Tool Router 和 App Orchestrator
- App 子会话、前后台切换、关闭和恢复的契约。
### 3.3 MiniApp SDK v1 与参考实现
`03.sdk_and_coreapp` 已完成并通过自动化测试和浏览器验收:
- Interact 是唯一的 `system` MiniApp
- Task Dashboard 和 Whiteboard 都是普通 `bundled` MiniApp 的参考实现;
- MiniApp 通过 SDK 接收 Inbox、Tool、进度、结果、生命周期、Surface 和 Capability 请求;
- 标准 `notice / choice / confirm / input` 属于 Interact 的人与 Agent 交互,不是 MiniApp 内部表单;
- App 子会话、交互归属、关闭收口、继续处理和重启恢复已有明确规则;
- 当前单 Agent MVP 边界保持不变。
需要注意:当前完成主要证明了 Runtime 契约和参考实现正确,用户可见的完整应用工作区仍需下一阶段收敛。
## 4. 第四次迭代:Runtime 工作区与应用闭环
**设计状态:** 已冻结,待实施;冻结基线为
[04.runtime_workspace.md](04.runtime_workspace/04.runtime_workspace.md)、
[05.technical_implementation_spec.md](04.runtime_workspace/05.technical_implementation_spec.md) 及其
[03.design_review.md](04.runtime_workspace/03.design_review.md)、[06.technical_implementation_spec_review.md](04.runtime_workspace/06.technical_implementation_spec_review.md)、
[07.技术实施规范评审(二).md](04.runtime_workspace/07.技术实施规范评审(二).md)。当前没有未解决的 P0P3
TIS2-06 是未来多入口 session data 协作写入的 P4 延续项,不阻塞第 04 次实施。
建议迭代目录:
```text
迭代/04.runtime_workspace/
```
### 4.1 目标
把 Runtime 的生命周期和会话模型接到真实 Web/Tauri 界面,跑通一条完整的用户流程:
```text
进入 Interact IM
→ Agent 请求启动 Pomodoro 番茄时钟
→ Runtime 创建 App instance、App 子会话和 deadline operation
→ 番茄时钟进入前台,Interact 进入后台
→ 用户进行写作业、看书或冥想等固定时长专注;自动暗屏/锁屏不终止本轮
→ 到时间完成,或用户主动离开 Pomodoro 工作区而中断
→ Runtime 回传唯一结果并关闭 App
→ 子会话结束并在 IM 中折叠保存
→ Interact 恢复
→ 用户查看历史或继续开始下一轮
```
### 4.2 主要工作
1. **接通 App 工作区界面**
- 显示当前前台 App
- 支持由 `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 投递;
- 用户退出专注时先将 `pomodoro.start` 终结为 `interrupted`;其他未终态普通 Tool 才收敛为
`cancelled(app_closed)`
- 已提交结果继续通过 outbox 发送;
- 关闭后恢复前一个有效前台 App。
3. **完成 App 子会话展示**
- 主 IM 显示 App 子会话折叠卡片;
- 支持展开已结束子会话;
- 已结束子会话只读;
- “继续处理”创建新 instance 和新子会话;
- 不把 App 内部按钮、表单和编辑动作写入 IM。
4. **完成 Interact 交互覆盖层**
- Interact 前台时,在 IM 中以内联卡片显示标准交互;
- bundled MiniApp 前台时,可以在其上方显示 Interact 的交互层;
- 展示位置变化不改变交互归属;
- App 关闭不自动取消未回答的标准交互;
- Agent 可以通过 `interaction.dismiss` 远程取消交互。
5. **让 Pomodoro 成为第一条完整用户流程**
- 用户在 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 operationPomodoro 直接进入专注界面;
- 到时间得到 `completed`Agent 调用 `pomodoro.interrupt`,或用户切回 Interact、切到其他 MiniApp、关闭
Pomodoro 时得到 `interrupted`;后者由 Runtime 的生命周期处理,不伪造 Agent Tool;不支持暂停、继续或恢复;
- 自动暗屏、锁屏与 Surface 重载不终止专注;刷新或重启后根据 `ends_at` 恢复或结算一次;
- 到时间后 Runtime 回传一次结果并立即关闭 Pomodoro,完成结果由 Interact / Agent 呈现;
- 完成记录和子会话可以在 Interact 中查看。
第一版只保留这些状态和数据:
```text
state = focusing | completed | interrupted
operation_id
source_tool_call_id
activity?
duration_seconds
started_at
ends_at
ended_at?
interruption_reason?
```
最小 Agent Tool
```text
pomodoro.start({ duration_seconds, activity? })
pomodoro.interrupt({})
```
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 完整验收;
- Tauri Desktop Host 至少完成一条代表性流程;
- 验证刷新、断线、重启、关闭、恢复和重复提交;
- 检查 Runtime 生命周期、交互、Tool 和恢复日志。
### 4.3 不在本迭代范围
- 应用市场和服务端 App Catalog
- 远程下载、第三方发布和在线更新;
- 多 Agent 连接、切换和多 Agent 会话列表;
- Audio Mode 的真实媒体能力;
- Video Mode 的真实媒体能力;
- Whiteboard 的工作区接入、切换、多人协作和复杂绘图能力;第 04 次只保持其已有回归;通用 mutation queue 与
Agent Tool、UI Action 共同修改同一 App 子会话数据的协作协议同样延期,首次实现此类双入口写入功能前必须专项设计;
- Task Dashboard 的完整项目管理功能;
- Pomodoro 的统计报表、多个计时器、日历和复杂提醒计划。
### 4.4 完成标准
```text
用户可以在 IM 中请求 Agent 立即开始或停止 Pomodoro 专注;未来 Voice 只复用相同 Tool 语义;
Runtime 可以正确创建、运行、中断、关闭和恢复 Pomodoro App
App 子会话能在 IM 中折叠、展开和继续处理;
标准交互在不同前台 App 下仍归 Interact
到点/中断竞争唯一终态,Tool 和 outbox 收口正确;
到点后立即关闭 Pomodoro 并由 Interact / Agent 呈现完成结果;
Pomodoro 关闭后的业务快照保留为只读历史,不能复活旧 instance;
刷新/重启后 App、焦点、子会话、`ends_at` 专注状态和待处理交互按规则恢复;
Web/Tauri 代表性流程通过端到端验收;
Whiteboard 保持第 03 次回归,但不属于第 04 次工作区闭环或完成范围;
所有 P0P3 评审问题清零。
```
## 5. 第五次迭代:第二个真正可用的隔离 MiniApp
建议迭代目录:
```text
迭代/05.first_isolated_miniapp/
```
第四次迭代完成工作区和 Pomodoro 后,再把另一个 MiniApp 从“参考实现”推进为“真实隔离运行的应用”。
### 5.1 推荐顺序
优先选择 Whiteboard 作为第二条安全和 Surface 验证流程;Task Dashboard 留到后续,避免任务管理业务影响 Runtime 核心设计。
### 5.2 Whiteboard 方向
- 受限 Surface 中的画布状态;
- 状态 patch
- Artifact 导出;
- Artifact 元数据校验;
- 重启恢复;
- 验证不能访问 Host DOM、Tauri、认证状态、任意网络和其他 MiniApp 数据。
暂时不做多人协作、云端同步和复杂绘图工具。
### 5.3 Task Dashboard 的后续定位
Task Dashboard 仍然保留为后续普通 `bundled` MiniApp,但不作为 Runtime 的第一条验证流程。
等工作区、SDK、生命周期和 Surface 边界稳定后,再单独定义任务、项目、状态和历史等业务模型。
## 6. 第六次迭代:Agent 平台适配与原生命令兼容层
建议迭代目录:
```text
迭代/06.agent_platform_adapter/
```
`04A.agent_tool_route` 已经证明:Hermes 这类 Agent 平台与 LineUp 之间,真正需要稳定下来的不是某个平台的单次补丁,而是一个可扩展的“平台私有命令 / 交互 -> LineUp 标准能力”兼容机制。
这一轮的目标不是把 Runtime 变成各家 Agent 的命令执行器,而是冻结三层分工,并为 Hermes、未来 Open Claw 等平台提供统一承接面:
```text
Agent Native Command / Prompt
-> Agent Adapter Compatibility Layer
-> LineUp Standard Action / Interaction
-> Runtime / App / Host Capability
```
### 6.1 分层原则
1. **LineUp Standard Ability**
- 由 LineUp 自己定义并长期稳定维护;
- 只包含 Runtime Tool、标准交互与 Host capability 等通用协议:
- `lineup.v1.tool.invoke`
- `lineup.v1.tool.call`
- `lineup.v1.tool.result`
- `lineup.v1.app.call`
- `lineup.v1.app.result`
- 不直接暴露 Hermes、Open Claw 等平台私有 slash / prompt 语义。
2. **Agent Adapter Compatibility Layer**
- 位于各平台自己的 adapter / plugin
- 负责把平台私有 command、approval、clarify、confirm、update prompt 等交互,映射到 LineUp 标准协议;
- 维护平台内部 request / callback token 与 LineUp `call_id` / option id 的受控映射;
- 未来 Hermes、Open Claw 等都复用同一套分层原则,而不是继续把兼容逻辑下沉到 Runtime。
3. **Runtime / App Implementation Layer**
- 只实现 LineUp 自己的业务能力和受控宿主能力;
- 例如 `pomodoro.start`、`pomodoro.interrupt`、打开 surface、文件选择、剪贴板、artifact 保存等;
- 不直接理解 `/model`、`/reload-mcp`、`/reset`、`/approve` 之类 Agent-native command。
### 6.2 本迭代拟解决的问题
- 冻结“Agent Native Command 不等于 LineUp Runtime Ability”的架构边界;
- 抽象统一的 `command-to-action` 与 `prompt-to-interaction` 转换模型;
- 为多个 Agent 平台定义一致的 adapter 承接面,而不是每个平台都重新发明一套私有兼容逻辑;
- 明确哪些交互由 adapter 收口,哪些能力才允许进入 Runtime / Host
- 为 `slash_confirm`、`update_prompt` 这类当前在 ACP 路径下缺少稳定触发源的 contract,定义后续 bridge / trigger 承接方案。
### 6.3 不在本迭代范围
- 把各家 Agent 平台的原生命令直接下沉到 Runtime;
- 让 `lineup-app-server` 理解平台私有 command / prompt 语义;
- 一次性实现所有第三方 Agent 平台;
- 扩展多 Agent 会话编排或平台市场能力。
### 6.4 完成标准
```text
形成一份权威分层设计:Agent Native Command、Adapter Compatibility Contract、LineUp Standard Action 三层边界清晰;
Hermes 适配结论可被抽象为平台无关的兼容模型,而不是 Hermes 特判;
未来 Open Claw 等新平台能够按同一 adapter contract 接入,不要求先改 Runtime
明确 slash / confirm / approval / clarify / update prompt 等 contract 的归属与映射原则;
为需要 bridge / trigger 的平台原生交互,给出单独的小迭代承接口径。
```
## 7. 第七次迭代:本地 App 管理和签名 Bundle
建议迭代目录:
```text
迭代/07.local_app_management/
```
这一阶段仍然不建设在线应用市场,只解决本机的应用管理和安全运行边界:
- App Registry 管理界面;
- 本地安装记录;
- App enable / disable / remove
- 版本记录和回滚;
- 本地签名 Bundle
- Bundle 完整性和签名校验;
- 验证失败不污染当前可运行缓存;
- Inventory 更新和 Agent 可见性变化;
- App 删除时实例、焦点、Inbox、Surface 和私有数据的收口。
## 8. 第八次迭代之后:再评估应用分发和市场
只有在 Runtime 工作区、本地 App 管理、SDK、Surface 和签名 Bundle 都稳定之后,才评估:
- 服务端 App Catalog
- 应用搜索和详情;
- 下载和更新;
- 发布者身份;
- 第三方 MiniApp
- 审核、撤回和安全策略。
应用市场不是当前阶段的基础设施,而是建立在前面几层都稳定之后的分发能力。
## 7. 更后面的方向
### Runtime Core 的 Rust 演进
在 Runtime 工作区已跑通并积累 Web / Tauri 两个 Host 的性能数据后,单独评估是否将部分**无 UI 的 Runtime
Core** 下沉到 Rust,以改善状态处理和原子收口的效率与一致性。这不是第 04 次迭代的工作,也不等于把整个
MiniApp SDK 改写成 RustWeb 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 工作区稳定后再设计:
- Agent 列表和切换;
- 多 Agent 会话;
- 多 Agent outbox
- App 子会话归属;
- Inventory 和权限隔离。
### Audio Mode 与 Video Mode
语音和视频应复用当前 Runtime SDK、Tool、Capability、Artifact 和会话模型,不建立独立的 Agent
通信链路。它们需要单独处理媒体权限、设备选择、实时连接、中断和恢复,因此不进入当前两次迭代。
## 8. 当前排期原则
```text
先完成“真实可用的应用工作区”
再完成“一个真正隔离的 MiniApp”
再完成“本地安装和签名管理”
最后才评估“远程分发和应用市场”
```
任何新增功能都应先回答三个问题:
1. 它是否依赖 Runtime 已经稳定的生命周期和会话模型?
2. 它是否能通过现有 MiniApp SDK、Surface、Capability 和 Artifact 边界实现?
3. 它是否会把应用业务逻辑、网络通信或系统权限重新塞回 Interact 或 `main.ts`
如果前两个问题没有准备好,或者第三个问题答案为“会”,就不应提前进入应用市场或新的 App 类型建设。
@@ -0,0 +1,16 @@
# 00.base 阶段摘要
**状态:** 已完成
**来源:** `lineup-app/迭代/00.base/00.base.md`
## 1. 结论
`00.base` 建立了后续所有迭代不可倒退的基线:
- Runtime 独占 Transport、Store、sync loop、outbox 和 App Inbox
- Chat 只能通过 SDK 与 Runtime Action 工作;
- Tauri Desktop Host + Web Reference Host 是统一客户端基线。
## 2. 现在如何看待这个阶段
它现在不是未来新迭代的讨论现场,而是所有后续阶段的底层约束来源。
@@ -0,0 +1,124 @@
# LineUp App 迭代基线:Runtime 托管 Chat
**迭代编号:** 00.base
**状态:** 已实现、已验证
**日期:** 2026-08-04
**定位:** 后续迭代必须保持的客户端基础能力
## 1. 基线说明
本迭代建立了 LineUp Runtime 承载第一个 Core App 的最小闭环。当前实现中的 `chat`
图文 IM 形态的应用;在后续设计中,它将演进为 `Interaction App` 的 IM 模式,但现阶段
仍保留 `app_scope = "chat"` 作为代码和协议兼容名称。
当前客户端基线是:
```text
Tauri 2 Desktop Host + Web Reference Host
```
两种 Host 运行同一份 TypeScript Runtime 和 Core AppTauri 用于正式桌面交付,Web 用于
浏览器开发、Tailscale 联调和自动化验证。它们不是两套客户端,也不各自实现通信、同步或
消息存储。
## 2. 已实现的 Runtime 边界
`LineUpRuntime` 是以下对象的唯一所有者:
```text
Transport
Conversation Store
sync loop
outbox
App Inbox
作用域筛选
旧 lineup.v1 兼容
去重与恢复
```
Chat 不能直接访问 AppServer、Transport、Store、登录态或 Host 特权能力,只能使用
`ChatRuntimeSDK`
```text
Runtime
→ 校验、持久化和筛选 Agent 消息
→ 投递 app_scope + conversation_id 匹配的消息
→ 通过 SDK 提供订阅、Inbox、ACK 和 Runtime Action
→ 接收 Chat 的用户动作并进入 outbox
```
## 3. 已实现的应用加载关系
当前已经将“默认应用选择”和“应用具体挂载”分开:
```text
CoreAppRegistry
→ 选择默认 app_scope = chat
CoreAppHostRegistry
→ 找到 chat 对应的 Host 装配器
chat-app-host.ts
→ 加载 Chat Shell、样式、Renderer、DOM Context 和 SDK
```
`main.ts` 现在只负责启动 Runtime、创建 Host 绑定和请求挂载默认 App,不再直接导入
Chat 的样式、Renderer 或 Shell。
相关实现:
- `tauri/src/runtime/coordination/lineup-runtime.ts`
- `tauri/src/runtime/app-management/app-registry.ts`
- `tauri/src/runtime/app-management/runtime-app-host.ts`
- `tauri/src/core-apps/chat/chat-app-host.ts`
- `tauri/src/main.ts`
## 4. 已实现的消息和交互能力
当前基线已经覆盖:
- 登录、退出和会话恢复;
- HTTP 增量同步与包含式 cursor
- 用户消息本地回显、发送状态和 outbox;
- Agent Markdown 消息和安全 DOM 渲染;
- Agent status、progress、error 和执行摘要;
- `choice``confirm``input` Tool Call 的状态机和可信交互卡片;
- Tool Result / Tool Cancel 回声;
- Surface 生命周期的基础管理;
- Capability Registry、确认、受限 Host 执行和审计;
- Artifact 元数据校验与 Host 临时内容缓存;
- App Inbox 的持久化、恢复、ACK 和 `message_id` 去重。
所有入站内容必须先经过 Runtime 协议和作用域校验。非法或不匹配的消息不得进入 Chat,
未知内容只能安全降级,不能执行远端脚本或系统能力。
## 5. 基线验证
```text
npm test -- --run
21 个测试文件通过
94 个测试通过
npm run build
TypeScript 检查通过
Vite production build 通过
```
## 6. 本基线的边界
以下能力尚未作为本迭代的完整实现:
```text
动态 App Registry
App 安装、更新、回滚和移除
App Instance Manager
App Focus Manager
Runtime Tool Router
Interaction App 的 IM / Audio / Video 模式统一运行时
扩展 App 的前后台切换和恢复
完整应用市场
```
这些内容属于 [01.kernel.md](../01.kernel/01.kernel.md) 定义的下一阶段目标。`00.base` 的意义是保护
已经完成的通信、可靠投递、作用域和安全边界,后续重构不得让这些能力回到 App 或
`main.ts` 中。
@@ -0,0 +1,19 @@
# 01.kernel 阶段摘要
**状态:** 目标设计
**来源:** `lineup-app/迭代/01.kernel/01.kernel.md`
## 1. 结论
`01.kernel` 把 LineUp 从“Runtime 加载一个页面”推进为“Runtime 管理应用工作区”的设计骨架。
它明确了:
- App Registry
- Instance Manager
- Focus Manager
- Lifecycle Manager
- Tool Router
- App Orchestrator
这些能力是后续 `03``04``04A` 的共用骨架。
@@ -0,0 +1,293 @@
# LineUp App 迭代目标:Runtime Kernel 与应用编排
**迭代编号:** 01.kernel
**状态:** 目标设计,待实现
**日期:** 2026-08-04
**前置基线:** [00.base.md](../00.base/00.base.md)
## 1. 迭代目标
本迭代要把 LineUp 从“Runtime 加载一个 Chat 页面”推进为“Runtime 管理一个应用工作区”。
Runtime 不仅负责网络、消息和存储,还要负责应用实例、工具路由、前后台焦点和恢复;
`Interaction App` 则负责用户与 Agent 的核心交互体验。
目标结构:
```text
LineUp Runtime
├── Runtime Shell / Desktop
│ ├── 应用启动
│ ├── 应用切换
│ ├── 前后台与焦点
│ ├── 通知和安全恢复
│ └── Host 挂载协调
├── Interaction AppCore App
│ ├── IM Mode:文字、图片、短消息
│ ├── Audio Mode:实时语音
│ ├── Video Mode:实时视频
│ └── 标准交互原语:choice / confirm / input
└── Installed / Extension Apps
├── Whiteboard
├── Draw-and-Guess
├── Task Dashboard
└── 其他可安装应用
```
当前代码中的 `chat` 继续作为兼容实现名称,产品概念上将其定位为:
```text
Interaction App 的 IM 实现
```
后续是否将目录和作用域从 `chat` 正式迁移到 `interaction`,另行作为命名迁移任务处理,
不与本迭代的运行时编排重构混在一起。
## 2. 核心职责分工
### 2.1 LineUp Runtime
Runtime 是整个应用工作区的底层协调中心,拥有最终调度和安全决策权:
- 管理 App Registry、App Instance 和焦点栈;
- 校验 Agent Tool Call、Manifest、Inventory、参数和作用域;
- 决定调用应直接执行、启动 App、切换前台、等待用户交互还是创建异步任务;
- 维护 App 的启动、运行、后台、挂起、恢复、关闭和失败状态;
- 管理 Conversation、Store、App Inbox、outbox、权限和审计;
- 在扩展 App 结束或启动失败时恢复原来的前台实例。
### 2.2 Runtime Shell / Desktop
Runtime Shell 是一个拥有特殊权限的内置工作区。它在产品体验上类似桌面或 Launcher,
但最终的生命周期和安全决策仍由 Runtime Core 管理。
它负责:
- 把 App 实例挂载到 Tauri 或 Web Host
- 显示当前前台 App
- 管理应用切换、恢复和关闭;
- 提供全局连接状态、通知和安全恢复入口;
- 向 App 传递受限的 Host 能力。
普通 App 不能伪造 Runtime Shell,也不能直接修改焦点栈。
### 2.3 Interaction App
Interaction App 是默认的人与 Agent 交互应用,但不是整个应用生态的调度器。
它负责:
- 当前 Conversation 的主要交互体验;
- IM、Audio、Video 等交互模式;
- 选择框、确认框、输入框和进度卡片等标准交互原语;
- 显示普通 Agent 消息、任务、Tool 结果和扩展 App 的结果;
- 通过 SDK 提交用户动作。
它不负责:
- 直接连接 AppServer 或 Agent
- 决定其他 App 是否启动;
- 管理其他 App 的前后台状态;
- 直接执行未经 Runtime 授权的 Tool 或 Capability。
### 2.4 Extension App
扩展 App 与 Interaction App 是 Runtime 上的平级应用。画板、你画我猜和任务面板拥有
自己的页面、状态、Tool、Surface 和实例生命周期,但必须使用 Runtime SDK。
扩展 App 可以请求:
```text
启动自己
创建 Surface
进入前台
进入后台
提交结果
请求关闭
```
最终是否允许、如何持久化、是否需要用户确认,由 Runtime 决定。
## 3. 应用实例与焦点模型
App Registry 管理“有哪些应用”,App Instance Manager 管理“哪些应用正在运行”。两者
不能混为一个状态。
```ts
type AppInstanceRecord = {
instance_id: string;
app_scope: string;
conversation_id?: string;
state:
| "starting"
| "foreground"
| "background"
| "suspended"
| "stopping"
| "stopped"
| "failed";
parent_instance_id?: string;
started_at: string;
stopped_at?: string;
error?: string;
};
```
Runtime 需要保存当前会话的焦点栈:
```text
focus_stack:
interaction:audio-001
draw-and-guess:game-001
foreground:
draw-and-guess:game-001
background:
interaction:audio-001
```
应用切换不会自动删除原实例。原实例可能进入 `background``suspended`,在新应用关闭、
失败或用户返回时恢复。
## 4. Tool 调度模型
Tool 调度权在 Runtime,不在 Interaction App。
```text
Agent Tool Call
→ Runtime 校验 Envelope / Inventory / Manifest / 参数 / Scope
→ Tool Router 判断处理方式
→ App Orchestrator 创建或切换 App Instance
→ Focus Manager 调整前后台
→ Interaction App / Mode / Extension App 承接
→ App 通过 SDK 返回 progress / result / error
→ Runtime 校验、持久化、审计并回传 Agent
```
Tool 的处理方式至少包括:
```text
direct 直接由 Runtime 或受信 App 处理
interactive 交给 Interaction App 等待用户选择/确认/输入
launch 启动或唤醒一个扩展 App
foreground 要求目标 App 进入前台
operation 创建长时间运行的异步任务
```
Interact 可以请求 Runtime 启动扩展 App,但不能直接加载 Bundle、切换其他 App 或执行
系统能力。
## 5. 典型场景:语音切换到你画我猜
开始时:
```text
Runtime Shell
└── Interaction App
└── Audio Modeforeground
```
用户说:“我们来玩一局你画我猜吧。” Agent 发来启动请求后:
```text
Agent launch(draw-and-guess)
→ Runtime 检查 App 是否已安装、启用和兼容
→ 校验 Tool、Manifest、Inventory、参数和权限
→ 创建 draw-and-guess:game-001
→ 保存 interaction:audio-001 的焦点位置
→ Audio Mode 进入 background / suspended
→ Draw-and-Guess 进入 starting → foreground
```
游戏完成后:
```text
Draw-and-Guess App
→ 通过 SDK 提交 game.result
→ Runtime 持久化并回传 Agent
→ 游戏实例关闭或挂起
→ Runtime 恢复焦点栈中的 Audio Mode
→ Interaction App 显示游戏结果
```
游戏和原来的语音交互使用同一个 `conversation_id`,但拥有不同的 `instance_id`。这样既
能把结果归还给同一个 Agent 会话,也能独立管理每个应用实例。
## 6. 标准交互原语与扩展 App 的边界
以下内容属于 Interaction App 的内建能力,不需要安装独立 App:
```text
choice
confirm
input
progress
error
```
例如 Agent 请求选择框:
```text
Agent tool.call(choice)
→ Runtime 校验并持久化 pending Tool Call
→ Interaction App 在 IM 中显示 Choice Card
→ 用户选择
→ Runtime 校验 call_id 并写入 outbox
→ Choice Card 变为 submitted / completed,不再可操作
→ IM 时间线显示“你选择了……”
```
选择框可以从界面上退出可操作状态,但交互记录不能从 Store 中删除。这样才能支持刷新
恢复、Agent 回声、幂等去重和审计。
画板、游戏和复杂任务面板则属于可安装扩展 App,拥有自己的 Tool 和 Surface。
## 7. 目标模块
```text
runtime/app-management/
├── app-registry.ts
├── app-instance-manager.ts
├── app-focus-manager.ts
├── app-lifecycle-manager.ts
└── runtime-app-host.ts
runtime/coordination/
├── tool-router.ts
├── app-orchestrator.ts
└── interaction-orchestrator.ts
core-apps/interaction/(当前由 core-apps/chat 兼容实现)
├── interaction-runtime.ts
├── interaction-shell.ts
├── interaction-mode-registry.ts
├── modes/im/
├── modes/audio/
├── modes/video/
└── primitives/
installed-apps/
├── whiteboard/
├── draw-and-guess/
└── task-dashboard/
```
`RuntimeAppHost` 负责实际挂载和卸载;App Instance、Focus、Lifecycle 和 Tool Router 负责
状态和决策。这样不会把所有业务规则重新堆回 `main.ts`
## 8. 本迭代验收目标
```text
1. Runtime 能从 App Registry 选择默认 Interaction App。
2. Runtime 能创建、前台化、后台化、挂起、恢复和关闭 App Instance。
3. Agent 启动扩展 App 时,当前前台 App 能安全进入后台。
4. 扩展 App 结束或失败后,Runtime 能恢复原来的焦点和交互模式。
5. Tool 调用必须经过 Runtime 的 Inventory、Manifest、参数、作用域和权限校验。
6. Interaction App 能处理 choice / confirm / input 等标准交互原语。
7. 扩展 App 只能通过 SDK 返回结果,不能直接访问 Agent、Host DOM 或系统特权。
8. 断线或重启后,App Instance、Tool Call、App Inbox、outbox 和焦点栈可以有界恢复。
9. 00.base 中已经通过的 21 个测试文件、94 个测试和生产构建不能退化。
```
@@ -0,0 +1,18 @@
# 03.sdk_and_coreapp 阶段摘要
**状态:** 已完成
**来源:** `lineup-app/迭代/03.sdk_and_coreapp/03.sdk_and_coreapp.md`
## 1. 结论
这一阶段已经确认:
- `Interact` 是唯一 `system` MiniApp
- `Task Dashboard``Whiteboard``bundled` MiniApp
- 标准交互属于 Interact
- App 子会话、关闭收口、只读历史和继续处理规则已固定;
- MiniApp SDK v1 已成为当前主契约。
## 2. 当前角色
这是后续所有工作区与 Tool 迭代的契约支柱,不再只是客户端内部参考阶段。
@@ -0,0 +1,353 @@
# `03.sdk_and_coreapp` 第 1 次设计评审记录
> 评审日期:2026-08-05
> 评审编号: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` 只支持 26 项单选;`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. 总体结论
这份文件保留了首次评审时提出的问题和建议,方便追溯讨论过程;其中涉及 `sdk.ui`、MiniApp 发起
标准交互、owner 注入等早期设想,均已被本文件顶部的“后续决议”和问题清单中的最终结论取代,不能作为
实施依据。
当前已经收敛的核心结论是:
- 产品层级是 **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 信任模型中明确:
- `Interact / System MiniApp` 可使用可信内建 DOM 组件;
- Installed MiniApp 必须经隔离 iframe / Surface Bridge 运行。
而 [03.sdk_and_coreapp.md](03.sdk_and_coreapp.md) 同时写了:
```text
Task Dashboard 可由受信 Host adapter 挂载;Whiteboard 必须走受限 Surface
```
并将 Task Dashboard 定义为:
```text
kind = bundled-reference
```
且说明它可使用“随 LineUp 发布的受信 Host adapter”。
**问题:** 当前权威架构没有明确授权 `bundled-reference` 获得与 System MiniApp 相同的可信 DOM 容器。若 Host adapter 可执行或挂载 Task Dashboard 的 UI,而未经过受限 Surface,隔离边界、SDK 受限视图和“禁止访问 Host DOM”就无法以同一安全模型证明。
**需要冻结的决策(二选一):**
1. **保守方案,且最符合当前权威基线:** Task Dashboard 和 Whiteboard 都是非 System MiniApp,必须经隔离 Surface / Bridge 运行。Host adapter 仅作为 Runtime/Shell 的不可见装配层,不向 MiniApp 提供可直接操作的可信 Host DOM。
2. **若产品确实需要 Task Dashboard 使用可信原生 DOM** 将其升级为 `kind = system`,并在架构主文档新增或明确“bundled trusted system MiniApp”信任层、发布来源、可用 DOM 权限和审计边界;不能继续称其为普通 `bundled-reference`
**建议验收:**
- 若维持 `bundled-reference`Task Dashboard 无法获得 Host 根 DOM、Tauri invoke 或未授权 Host adapter。
- 若改为 `system`,仍要验证其特权只限 Runtime SDK,不能取得 Transport、Store 或 Agent 访问权。
## 3. 重要问题
### I1. `bundled-development` 与 `bundled-reference` 的术语、Manifest 枚举未建立映射
迭代文档把“内置”解释为 `bundled-development`,但冻结的 `MiniAppManifest.kind` 只有:
```ts
kind: "system" | "bundled-reference";
```
架构主文档也使用了“参考 MiniApp 在此阶段可以是 `bundled-development`”的表述。
这两个词可以共存,但必须明确它们不是互相竞争的 `kind` 枚举值。
**建议补充最小映射表:**
| 概念 | 建议语义 |
|---|---|
| `bundled-development` | 本迭代的交付/加载方式:随开发 Host 预置,不下载、不安装、不经服务端 Catalog。 |
| `kind = bundled-reference` | Manifest 身份:参考 MiniApp,决定 SDK / Surface 信任策略。 |
| `kind = system` | 随产品发布、代码可信的系统 MiniApp,例如 Interact。 |
并明确:`bundled-development` **不是** `MiniAppManifest.kind` 的第三个取值。
### I2. `sdk.ui` 缺少 Renderer 如何安全提交用户结果给 Runtime 的闭环契约
当前公开 API 只有:
```ts
request(...)
get(...)
subscribe(...)
cancel(...)
```
但未定义:
- Runtime 如何把被 Policy 选中、可呈现的 interaction record 交给 renderer
- renderer 如何提交 `submitted``result``cancelled`
- Runtime 如何校验 `interaction_id`、owner、renderer binding、状态迁移、一次性提交和结果 schema;
- 在 opaque-origin iframe / Surface Bridge 内,呈现和回传采用何种受限消息协议;
- renderer 与 owner 是同一 MiniApp 时,是否通过 `sdk.ui` 提交,还是只能通过 Runtime/Shell 私有 Renderer API 提交。
**建议冻结 Runtime 私有 Renderer Contract / 最小 Bridge Contract**
```text
Runtime 分配 interaction_id + renderer_binding + presentation container/surface
→ renderer 仅接收 presentation-safe request projection
→ renderer 向 Runtime-owned endpoint 提交 action/result
→ Runtime 校验:
- 被分配的 renderer / container
- interaction_id
- owner binding
- 当前状态与一次性提交
- result schema
- expiry
→ Runtime 持久化状态迁移,并向 owner-scoped ui 订阅发出更新
```
隔离 Surface 至少还要限定消息白名单、`surface_id` / `instance_id` 绑定和 nonce 或等效关联方式;不得只凭 origin 信任。
### I3. 标准交互与 `RuntimeToolCallRecord` 的关系不清晰
当前文档同时规定:
- MiniApp 可调用 `sdk.ui.request(...)`
- owner 的 source 可为 `agent_tool | miniapp_tool | runtime_policy`
- 所有标准交互“必须映射为 Runtime 统一的 `interactive` Tool Call / `StandardInteractionRecord`”。
因此会产生问题:Task Dashboard 在没有 Agent Tool Call、例如用户点击“关闭未完成任务”时调用 `sdk.ui.request(confirm)`,Runtime 是否必须凭空创建 Agent-facing `RuntimeToolCallRecord`
**建议明确拆成三条路径:**
| 来源 | Runtime 记录 | 与 Agent Tool 的关系 |
|---|---|---|
| Agent / legacy `lineup.v1.tool.call(choice/confirm/input)` | `RuntimeToolCallRecord` + `StandardInteractionRecord` | 交互终态受控驱动该 Tool 的后续处理。 |
| MiniApp `sdk.ui.request(...)` | owner-bound `StandardInteractionRecord` | 不自动创建新的 Agent Tool;若提供合法 `parent_call_id`,只建立关联。父 Tool 的完成由 owner 经 `sdk.tools.*` 决定。 |
| Runtime Policy 自发提示 | `runtime_policy` owner 的 interaction record | 需明确其是否绑定某一 Capability / lifecycle request。 |
没有 parent Tool 的交互只改变 MiniApp 本地工作流,不应制造 Inventory、Agent 回传或 outbox 语义。
### I4. 结果投递位置与公开 API 不一致
流程写“结果仅投递回 owner MiniApp 的 Tool/Inbox”,但 SDK 已公开 `sdk.ui.get/subscribe`
否则实现可能分叉成:
1. 结果通过 `sdk.ui.subscribe` 的 record update 返回;
2. 结果作为 Inbox 消息;
3. 结果通过 parent Tool 的 SDK Tool event 返回。
**建议冻结为:**
```text
sdk.ui.get / sdk.ui.subscribe
= owner 获取标准交互状态与结果的唯一 UI-domain 通道
sdk.inbox
= Agent / Runtime 入站消息;不复用为 interaction result transport
sdk.tools.complete / fail / cancel
= owner 根据 UI result 自行决定是否推进关联 parent Tool
```
还需定义:订阅是否立即回放 owner 当前 pending records、终态保留/清理策略、断线重连后的重放及去重语义。
### I5. Schema、状态机、幂等规则不足以支持安全验收
已有四类 request 及基础状态,但还需要明确:
- `StandardInteractionResult``InteractionReceipt` 的类型;
- `choice.actions[].id` 的唯一性、最大数量、空数组是否允许,以及 single/multi/button 的选择基数;
- `input.fields[].id` 的唯一性、字段数上限、必填、数值解析、空字符串、长度和范围的适用规则;
- `notice` 的“只读”与 `acknowledged` 的精确定义:是否需要用户确认、是否自动完成、是否可超时;
- `request``submit``cancel`、超时、恢复之间的合法状态迁移;
- 并发提交、重放、重复取消、跨 Surface 重放的确定性返回;
- 交互专用稳定拒绝码。
建议至少增加:
```text
interaction_not_found
interaction_owner_mismatch
interaction_state_invalid
interaction_expired
interaction_result_invalid
interaction_renderer_mismatch
parent_call_invalid
```
### I6. 持久化、审计与敏感输入的日志最小化约束尚未衔接
`input` 可以含用户自由文本;标准交互要求持久化、恢复和审计,但架构文档又要求日志不得包含身份、会话、消息正文、Tool 参数、token 或 artifact 内容。
**建议明确:**
- 恢复所需的受保护 operational state、审计最小元数据、日志/Telemetry 三者的分层;
- `input` 草稿和结果的保留期、终态清理策略,以及是否加密;
- 审计仅记录 interaction ID、kind、受保护 owner 标识、状态迁移和错误码;
- title、prompt、字段值和 action label 不进入日志、指标、错误详情或普通审计明文;
- 验收增加日志负向断言。
## 4. 建议完善项
### S1. 扩大 `parent_call_id` 校验条件
除“属于当前实例且状态允许关联”外,Runtime 至少应校验:
```text
同 app_scope
同 instance_id
同 conversation_id
父 Tool 非终态
不存在跨 owner 关联
```
还要定义父 Tool 被取消或超时时关联 interaction 的处理:自动取消、保留为独立操作,或交由 Policy 决定。否则恢复后容易遗留孤儿卡片。
### S2. 将 presentation policy 写成可判定矩阵
至少应覆盖:
| owner 状态 / 容器 | 建议行为 |
|---|---|
| Agent IM / Interact 有效 | Interact IM timeline/card。 |
| owner 前台且存在合法 renderer | owner 容器或已绑定 Surface 的标准 modal/sheet/card。 |
| owner 后台或 suspended | 保持 pending;通知/唤醒仅经 Policy,不得抢占焦点。 |
| Surface 已关闭或失效 | 不向旧 Surface 投递;恢复、降级或安全失败。 |
| renderer 不可用 | 保持 pending 或返回稳定拒绝码;不能把 payload 注入 Host DOM。 |
| 系统能力确认 | 走 Capability Gateway,禁止降级为普通 `confirm`。 |
### S3. Interact 呈现与 Agent Tool 回传的分层
**已确认(2026-08-05):** 用户在 Interact 中回答时,Interact 只负责显示问题、收集用户动作并把
答案交给 Runtime。它不是 Agent 的消息发送端,也不负责判断答案属于哪个 Agent、哪个会话或哪个 App
子会话。
```text
Interact / Shell 显示问题
→ 用户作答
→ Interact 向 Runtime 提交「interaction_id + 用户动作/答案」
→ Runtime 从已保存的交互记录取得 agent_id、conversation、App 子会话和 Agent Tool 归属
→ Runtime 核对 Interact 实例、展示位置、状态、期限、答案格式和首次提交
→ Runtime 持久化会话记录与终态,并写入可靠 outbox
→ Runtime 向当前唯一 Agent 回传一次结果
```
提交端不能传递或覆盖 `agent_id``conversation_id``call_id``app_session_id` 等归属信息;这些
只能由 Runtime 创建交互时保存。重复点击、刷新后的旧页面、无效展示位置、过期或已结束问题的提交都
必须被拒绝,且不能改变已经保存的结果或再次通知 Agent。Task Dashboard、Whiteboard 等 bundled
MiniApp 始终不参与这条链路,也读不到问题或答案。
### S4. 统一阶段编号
**已确认(2026-08-05):** Task Dashboard 与 Whiteboard 是 `03.sdk_and_coreapp` 中用来验证 SDK 的
bundled 参考 MiniApp,本阶段就完成其端到端路径。因此删除重复的 `03.reference-miniapps`;后续阶段
保持为 `04.app-delivery-registry`,不需要整体重编号。
### S5. 在实施步骤中拆出标准交互服务和 renderer/Bridge 契约
**已确认(2026-08-05):** 实施顺序已调整为:
1. 冻结契约与测试样本;
2. 实现 Runtime 通用 Tool 闭环;
3. 实现 Runtime 独占的 Agent interaction service,以及 Runtime ↔ Interact 的最小呈现/提交契约;
4. 再适配 Interact IM 和 App 子会话;
5. 最后通过 Task Dashboard、Whiteboard 验证一般 bundled MiniApp 路径,并进行端到端验收。
这样标准交互不会先被做成 Chat 专用或 MiniApp SDK 能力;Interact 只是第一个使用 Runtime 私有交互
契约的系统级呈现者,两个 bundled MiniApp 则只验证各自的 Tool、Surface 和业务 UI 边界。
## 5. 建议新增的最小验收场景
1. A 实例提交属于 B 实例、不同 conversation 或已终态 Tool 的 `parent_call_id` 时,Runtime 拒绝且不创建 record。
2. A 创建 interaction 后,B 无法 `get`、订阅、取消、提交,或通过伪造 `interaction_id` 获得任何 payload/result。
3. 同一 interaction 的两次 renderer submit 只有一次成功;第二次得到稳定终态错误且不改变已持久化结果。
4.`lineup.v1.tool.call(choice|confirm|input)` 创建关联 Tool Call 和 owner 为 Interact IM context 的 interaction;用户结果先由 Runtime 落库,再由 Interact 以 owner 身份完成 Tool。
5. Task Dashboard 无 parent Tool 的 `sdk.ui.request(confirm)` 不创建 Agent-facing Tool Call、不写 Agent outbox;带合法 parent Tool 时仅关联,不自动完成该 Tool。
6. Task Dashboard 前台时,标准 modal/sheet/card 在其合法容器显示,焦点栈与 Interact background 状态不被改写。
7. Whiteboard 在 Surface reload、关闭、旧 iframe 重放提交时,`surface_id` 与 renderer binding 都被验证,旧 iframe 消息被拒绝。
8. 非法 input、重复/未知 choice action、过期 interaction、跨 owner cancel 都被拒绝且不写错误结果。
9. 标准交互输入值、提示正文、选择标签不出现在日志、Telemetry 或 Audit 明文中;重启恢复仅保留设计允许的最小数据。
10. 若维持 `bundled-reference`Task Dashboard 不能直接挂载可信 Host DOM;若改为 `system`,必须有对应的 manifest/source-trust 回归用例。
## 6. 建议的阅读和决策顺序
明天继续时,建议按以下顺序决策和回填:
1. 先决定 **B1Task Dashboard 的信任级别与 UI 容器**
2. 冻结 **I2Renderer → Runtime 的提交/Bridge 契约**
3. 冻结 **I3/I4StandardInteractionRecord 与 Tool Call 的关系,以及 UI result 的唯一返回通道**
4. 细化 **I5/I6:schema、状态机、幂等、错误码、持久化与日志最小化**
5. 把 S1~S5 与第 5 节的验收场景回填入 [03.sdk_and_coreapp.md](03.sdk_and_coreapp.md) 和必要的 [APP架构设计.md](../../设计/APP架构设计.md)。
在上述收敛前,不建议开始 `standard-interaction-service` 或参考 MiniApp 的实现,以免重新形成 Chat / Interact 专用旁路。
@@ -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 InboxRuntime 私有契约交给 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 交给 RuntimeRuntime 保存交互归属、校验、持久化并可靠回传当前唯一 Agentbundled MiniApp 不参与。
- App 子会话正确区分了人与 Agent 的记录和 MiniApp 内部业务操作;前后台、关闭、历史只读和“继续处理”的总体规则一致。
- MVP 单 Agent 边界明确,没有提前引入多 Agent 连接、切换或路由。
## P0:阻塞问题
无。
## P1:开始编码前需要确认的事项
### P1-1Tool 完成不应自动关闭 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-2interactive 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` MiniAppTask 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-2Agent `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-2Whiteboard 的完成后关闭表述与统一生命周期规则冲突
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` 也重复出现一次,应一并删除重复行,避免错误引导目录改动。
## P4P5:遗留问题
本次没有新增 P4/P5。第 2 次评审已登记的 `P4-1`(历史提案可读性)和 `P4-2`(未来 `password-input`
秘密输入原语)继续作为不阻塞当前迭代的遗留问题,其延期原因与重新评估条件以
[02.design_review.md](02.design_review.md) 为准。
@@ -0,0 +1,105 @@
# `03.sdk_and_coreapp` 第 4 次验收评审记录
> 评审编号:04
> 评审日期: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)、[03.design_review.md](03.design_review.md)
> 评审方式:主 agent 交叉检查 + 独立子 agent 只读验收;检查构建、自动化测试、Runtime/MiniApp/Host 代码、SDK 契约、Manifest 和真实浏览器路径。
> 总体结论:本轮 P0~P3 问题已完成修复,并通过代码、自动化测试和真实浏览器闭环检查;独立验收 agent 已确认本迭代可以标记为“已完成”。
## 问题清单(Outline
> **状态标记:** 🔴 未解决,必须处理;🟡 已提出修复方向,尚未实现;✅ 已解决;⚪ 可延期但必须保留记录。
>
> P0~P3 必须在当前迭代处理;P4~P5 可以延期,但要写清延期原因和重新评估条件。
| 状态 | 优先级 | 编号 | 问题 | 证据 | 当前结论 / 下一步 |
|---|---:|---|---|---|---|
| ✅ | P1 | A1 | MiniApp 的 Capability 请求绕过 Runtime 的 Manifest、Policy 和审计边界 | `tauri/src/runtime/coordination/lineup-runtime.ts``capability-audit.ts` | 已统一经过 Manifest 声明、Capability Registry/Policy、输入 schema、前台要求和审计记录,再进入普通 Agent 确认请求;拒绝原因也由 Runtime 返回。 |
| ✅ | P1 | A2 | `bundled` MiniApp 的业务逻辑仍在可信 Host JS 中运行,没有真正的受限执行边界 | `tauri/src/main.ts``isolated-surface-host.ts`、agent-browser | 已移除 Host 直接实例化;Task Dashboard/Whiteboard 业务逻辑运行在 opaque `sandbox="allow-scripts"` iframe 内,只能通过 Runtime Bridge 请求能力;真实浏览器已复验。 |
| ✅ | P1 | A3 | SDK 暴露完整 workspaceMiniApp 可看到其他 App 实例和全局焦点栈 | `lineup-runtime.ts``miniapp-sdk.ts` | 已从 `LineUpMiniAppSDK` 删除 `workspace()`,不再把全局实例和焦点栈交给 bundled MiniApp。 |
| ✅ | P1 | A4 | 同一个 App 的多个 instance 之间 Inbox 订阅没有隔离 | `lineup-runtime.ts` | 已按 `app_scope + conversation_id + instance_id` 过滤实时订阅,并补充双实例回归测试。 |
| ✅ | P1 | A5 | Interact 使用专用 `ChatRuntimeSDK` 旁路,没有和其他 MiniApp 共用 SDK v1 语义 | `tauri/src/runtime/app-management/app-sdk.ts``lineup-runtime.ts``main.ts` | 已明确为“通用 Runtime 操作 + 可信 UI 投影”:Interact 保留可信 DOM 和 IM 展示,但行为统一走 `sdk.runtime.*`,展示走 `sdk.ui.*`;旧扁平方法仅作兼容别名。 |
| ✅ | P1 | A6 | 标准交互的超时、dismiss、提交和答案 schema 没有由 Runtime 统一裁决 | `lineup-runtime.ts``agent-interaction-service.ts``standard-interaction-contract.ts` | 已让本地提交、取消、过期和 Agent dismiss 同步推进 Interaction 与 Kernelnotice 也会完成 Kernel;四种答案在 Runtime 按固定 schema 校验,默认有效期和重启过期均只写一条 outbox。 |
| ✅ | P2 | A7 | 迭代文档中的 Tool 名称和代码 Manifest 不一致 | 主文档 §5.2/§5.3;`reference-miniapps.ts` | 已统一为 `task-dashboard.open``task-dashboard.update``whiteboard.open``whiteboard.submit`Manifest schema、MiniApp 实现、sandbox iframe、fixture 和测试均已同步。 |
| ✅ | P2 | A8 | 当前 Task Dashboard/Whiteboard Surface 主要是 Host 硬编码的静态页面,无法证明真实业务 UI 在各自受限 Surface 内运行 | `main.ts``isolated-surface-host.ts`、agent-browser Console/IM 结果 | 已修复 Bridge 方法校验不接受 SDK 的 camelCase `tools.reportProgress` 的问题。真实浏览器中分别注入并完成 `task-dashboard.open``whiteboard.open`:两者均经过 `tools.list → tools.reportProgress → surface.patch(如适用)→ tools.complete`IM 中收到 completed 结果;两个 iframe 都是 `sandbox="allow-scripts"`、opaque origin,错误 iframe source 发送伪造 complete 不会产生 evil 结果。 |
| ✅ | P2 | A9 | MiniApp progress 没有进入可恢复的 Tool 状态记录 | `miniapp-tool-state.ts``lineup-runtime.test.ts` | 已在 Tool 记录保存最后一次 `percent/status/detail`ConversationStore 随记录持久化;Runtime 重启后可恢复,且补充状态机和重启回归测试。 |
| ✅ | P3 | A10 | App session 记录和 opened/closed 事件没有显式保存 `agent_id` / `conversation_id` 字段 | `lineup-runtime.ts``lineup-runtime.test.ts` | opened/closed 事件现在都带 `agent_id``conversation_id``app_session_id``instance_id``app_scope`;并补充生命周期回归测试。 |
| ✅ | P3 | A11 | 旧的 `openExtensionApp` 公开入口仍提供较宽的旁路能力 | `lineup-runtime.ts``app-sdk.ts` | 已删除 `openExtensionApp()``ExtensionRuntimeSDK`bundled MiniApp 只能通过统一的 `openBundledMiniApp()` SDK 和 Runtime Bridge 访问能力。旧测试已改为验证统一生命周期路径。 |
## 1. 已验证通过的部分
- `npm run build` 通过。
- `npm test -- --run` 通过:29 个测试文件、135 个测试(含 Bridge camelCase 方法、Tool 契约、默认有效期、重启过期和答案 schema 回归)。
- `git diff --check` 通过。
- MiniApp progress 回归通过:状态机保存最新进度,Runtime 重启后恢复 `percent/status/detail`
- 使用独立 `agent-browser` 会话登录本地测试账号成功;AppServer `/login``/messages/send``/messages/sync` 均可用。
- 主 IM 页面、同步状态、应用子会话区域和“启用任务面板”入口可见。
- 用户消息可以发送并显示“已发送”。
- AppServer 频道中的其他用户消息已能被 Runtime 安全忽略;真实页面不再出现此前大量的 `scope_mismatch`
这些证据与后续真实 Tool 闭环日志共同证明 MiniApp 隔离、统一 SDK 和状态机契约满足本轮架构要求。
## 2. P1 问题说明
### A1Capability 请求没有经过 Runtime 的完整裁决(已修复)
现在 `sdk.capabilities.request(name, reason, input)` 只会进入 Runtime 的统一入口。Runtime 依次检查当前实例
对应的 bundled Manifest 是否声明该能力、Capability Registry/Policy 是否可用、输入是否符合 schema,以及
能力要求的前台状态;拒绝会返回明确原因,并写入不含敏感参数的审计记录。
通过检查后,Runtime 生成唯一 `call_id`,记录 pending 审计项,再进入普通 `lineup.v1.app.call` Agent 确认流程。
MiniApp 不会拿到 Host handler、Transport 或权限对象。
### A2bundled MiniApp 实际仍是可信代码(已修复)
已删除 `main.ts` 中对 `TaskDashboardMiniApp``WhiteboardMiniApp` 的直接实例化。开发版 Surface 现在把
Task Dashboard/Whiteboard 的业务脚本放进 opaque sandbox iframeiframe 只能发送经过校验的
`lineup.miniapp.v1.request`,由 Runtime 执行 Inbox、Tool、生命周期和 Surface 操作;Host 不再把 Runtime 对象、
Store 或 Transport 注入 MiniApp。
这与架构文档要求一致:`bundled` MiniApp 也必须使用受限 Surface/Bridge,不能因为随 Host 内置就取得
System MiniApp 的可信 DOM 或其他特权。Task Dashboard 和 Whiteboard 已按这一边界运行,真实浏览器验收也确认
它们的 Bridge 请求会经过 Runtime 的 instance/source 校验。
### A3/A4SDK 数据边界没有做到 instance 级别(已修复)
`LineUpMiniAppSDK` 已不再提供 `workspace()`,因此 MiniApp 不能查看其他 instance 或 Runtime 焦点栈。
`inbox.list()`、ACK 和 `inbox.subscribe()` 现在都按 `app_scope + conversation_id + instance_id` 过滤;新增回归
测试验证同一 App 的两个 instance 不会互收消息。
### A5Interact 的 SDK 语义没有统一(已修复)
Interact 仍然可以使用可信 DOM,但它的 SDK 已明确拆成两层:`sdk.runtime.*` 提供 Runtime 行为入口,
`sdk.ui.*` 提供 IM 和子会话的可信展示投影。标准交互、消息发送、草稿、任务操作仍由 Runtime 完成;UI
投影没有 Transport、Store、Agent 原始 Envelope 或 Host 特权。旧的扁平 `ChatRuntimeSDK` 方法只保留为
兼容别名,避免把 Interact 的 UI 适配误解成另一套协议。
### A6:标准交互有两套状态,可能互相打架(已修复)
已统一处理以下路径:
- 本地提交、取消、过期和 Agent dismiss 都同时更新 Interaction 与 Kernel Tool;任一侧不能迁移时会恢复另一侧的旧快照。
- notice 在呈现完成时也会同步完成 Kernel Tool,避免重启后出现 Interaction 已完成而 Tool 仍 pending。
- `choice` 只接受 `{ action_id }`,且 action 必须来自请求;`confirm` 只接受 `{ approved: boolean }``input` 只接受 `{ text: string }`,并校验必填和长度。
- 终态只创建一次对应的 Tool result/cancel outbox;重复提交、重复 dismiss 和已过期提交不会再次写出站消息。
现在 UI、持久化记录和 Agent 看到的最终结果由 Runtime 统一推进,重复终态不会再次写出站消息。
## 3. 验收结论
当前结论为(完成本轮 P1-1~P1-4,并收敛 A7 后):
```text
功能回归:通过
基础构建与自动化测试:通过
真实登录和消息发送:通过
App 架构边界:通过(A1/A2/A5/A6 已实现,A2 已完成真实 iframe 复验)
MiniApp 隔离与 SDK 规范:A1A11 已通过。
标准交互终态契约:通过
迭代整体验收:通过代码、自动化测试、真实浏览器闭环和独立验收;主迭代文档已标记为“已完成”。
```
本轮独立验收由子 agent `acceptance_round_08` 完成:29 个测试文件、135 个测试通过,构建和 `git diff --check` 通过;A8 的共享 agent-browser 日志确认 Task Dashboard 与 Whiteboard 均完成 `tools.list → tools.reportProgress → surface.patch(适用时)→ tools.complete`,错误 source/instance 请求未产生伪造结果。A1~A11 全部关闭,没有 P0~P3 遗留问题。
@@ -0,0 +1,94 @@
# `03.sdk_and_coreapp` 第 5 次验收评审记录
> 评审编号:05
> 评审日期:2026-08-06
> 评审对象:[03.sdk_and_coreapp.md](03.sdk_and_coreapp.md)
> 架构基线:[APP架构设计.md](../../设计/APP架构设计.md)
> 参考评审:[04.acceptance_review.md](04.acceptance_review.md)
> 评审方式:独立子 agent 只读验收 + 主 agent 逐项复核;检查当前代码、Manifest、SDK 契约、自动化测试、构建和文档证据。
> 总体结论:本轮发现的 P3 契约/证据问题均已逐项修复并分别提交;当前没有遗留 P0~P3 问题。
## 问题清单(Outline
> **状态标记:** 🔴 未解决,必须处理;🟡 已提出修复方向,尚未实现;✅ 已解决;⚪ 可延期但必须保留记录。
> P0~P3 必须在当前迭代处理;P4~P5 可以延期,但必须保留问题、延期原因和重新评估条件。
| 状态 | 优先级 | 编号 | 问题 | 当前结论 / 证据 |
|---|---:|---|---|---|
| ✅ | P3 | A12 | Registry 安装输入仍接受旧的 `core` / `extension` kind 别名,和冻结的 `system \| bundled` 契约不一致 | `CoreAppRecordInput` 已收紧为 `system \| bundled`;旧测试 fixture 已迁移;提交 `44b6a83`。 |
| ✅ | P3 | A13 | 主迭代文档中的测试数量过期 | 主文档已更新为当前 `30 个测试文件、137 个测试`;提交 `94817f7` 后随新增回归测试再次更新,提交 `ca9e8b9`。 |
| ✅ | P3 | A14 | Manifest Tool 的多项权限原先只保留第一项,可能造成后续权限漏检 | `requires_permissions[]` 已完整保存并逐项校验;Manifest/Registry 也拒绝未声明或重复权限;新增回归测试;提交 `e91d07b`。 |
| ✅ | P3 | A15 | 当前冻结约束未硬性保证只有 Interact 能使用 `kind = system` | Registry 现在拒绝除 `chat`Interact 兼容 scope)之外的 system MiniApp,并有回归测试;提交 `fec04a9`。 |
## 1. 本轮基础门禁
当前执行结果:
```text
npm test -- --run 30 个测试文件、137 个测试通过
npm run build 通过
git diff --check 通过
git status 工作树干净
```
本轮独立子 agent 的只读审计确认:
- Interact 仍使用 `sdk.runtime.*``sdk.ui.*` 两层适配,没有第二套 Transport、Store、Tool 或 Capability 协议;
- Task Dashboard 和 Whiteboard 仍是 `kind = bundled`,运行于 `sandbox="allow-scripts"` 的 opaque iframe
- MiniApp 只能通过 Runtime Bridge 请求 Inbox、Tool、Lifecycle、Surface 和 Capability
- `workspace()``openExtensionApp()``ExtensionRuntimeSDK` 等旧旁路没有恢复;
- A1~A11 的已修复约束没有发现回退。
## 2. P3 修复说明
### A12:冻结 kind 词汇
主文档规定 `MiniAppManifest.kind` 只有 `system | bundled`。本轮发现 Runtime 的注册输入仍允许旧的
`core | extension`,虽然最终会转换,仍会让调用方继续依赖已废弃词汇。
现在 `CoreAppRecordInput` 只接受 `system | bundled`,Registry 不再负责旧名称转换;受影响的测试 fixture
已全部迁移为 `bundled`。这样 Manifest、Registry、Inventory 和 Tool Router 使用同一套名称。
### A13:更新验收证据
新增回归测试后,测试总数已经变化。本轮把主迭代文档中的旧统计更新为当前实际值 `30/137`,避免完成定义引用过期数字。
### A14:完整检查 Tool 权限
一个 Tool 可能声明多个权限。Runtime 现在保留完整的 `requires_permissions[]`,注册时要求这些权限都在 Manifest
`permissions` 中且没有重复,路由时逐项确认 App 当前确实拥有每一项;缺任何一项都不会把调用交给 MiniApp。
### A15:限制 system MiniApp 范围
本迭代只有 Interact 是系统级 MiniApp,当前兼容 scope 是 `chat`。Registry 在安装边界拒绝其他 scope 的
`kind = system`,并通过回归测试固定这一不变量。Task Dashboard 和 Whiteboard 仍只能作为受限 `bundled` MiniApp。
## 3. 浏览器证据说明
本轮独立 agent 检查时,当前环境没有正在监听的 Web Host/AppServer 进程,因此没有把本轮称为“重新触发的真实
浏览器闭环”。Task Dashboard / Whiteboard 的真实 Bridge 闭环证据沿用上一轮共享会话记录:
```text
tools.list
→ tools.reportProgress
→ surface.patchTask Dashboard 适用)
→ tools.complete
→ IM 收到 completed 结果
```
上一轮还验证了 iframe 的 `sandbox="allow-scripts"`、opaque origin、CSP、`event.source + instance_id` 校验,
以及错误 source/instance 伪造消息不会改变 Runtime 状态。本轮新增修改没有触及这条浏览器路径;代码门禁和回归
测试均通过。若下一轮需要重新取得独立浏览器证据,应先启动 Web Host 和 AppServer,再按相同路径复验。
## 4. 结论
```text
P0:无
P1:无
P2:无
P3A12、A13、A14、A15 均已解决
P4P5:无新增遗留
```
本轮没有需要留到下一迭代的 P0~P3 问题。主迭代文档仍可保持“已完成”状态;后续若增加新的 system MiniApp
必须先重新评审并修改本迭代冻结的唯一 system 约束,而不能绕过 Registry 安装边界。
@@ -0,0 +1,20 @@
# 04.runtime_workspace 阶段摘要
**状态:** 已完成
**来源:** `lineup-app/迭代/04.runtime_workspace/04.runtime_workspace.md`
## 1. 结论
本阶段目标是把 Runtime 生命周期和会话模型真实接到 Web/Tauri Host,跑通 Pomodoro 工作区闭环。
最重要的冻结结论包括:
- Pomodoro 是 `bundled` MiniApp
- `ends_at` 是唯一时间事实;
- Runtime 是唯一终态裁决者;
- 子会话关闭后只读;
- 到点、关闭、中断竞争同一唯一终态。
## 2. 当前角色
该阶段已于 2026-08-06 完成最终验收,现作为后续工作区类迭代的已完成样板保留。
@@ -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 直接启动的单次专注 operationRuntime 托管 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 / WhiteboardPomodoro 在第 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 schemaMiniApp 通过一个最小、受 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-06SDK 是否迁入 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 都迁入 Rustbundled 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-01D04-05 已形成决议并回填到 [04.runtime_workspace.md](04.runtime_workspace.md) 与
[plan.md](../plan.md)。所有 P0~P3 设计问题已清零;D04-06 作为 P5 carryover 保留,不阻塞本迭代。
@@ -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-01D04-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-C1instance 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、改变
焦点或绕过 lifecyclePomodoro 的到期/中断和唯一 Tool result 必须由 Runtime deadline operation 裁决。
这也没有把静态 IM/App 子会话误当作 MiniApp 业务存储:第 04 次主定义仍只让子会话记录人与 Agent 的交互和
结果,符合第 03 次“App 子会话不是 App 操作日志”的规则。
### D04-C2Agent 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-C4App 子会话和 pending Interact 已一致
主定义限制“仅恢复同一个未结束 instance 时复用同一个子会话”;结束后子会话只读;关闭时仍 pending 的
Interact 标准交互留在主 IM/Interact 中,回答不得追加到旧子会话。该规则与第 03 次的 instance 生命周期、
`pending_interaction_call_ids` 和“关闭不自动取消 Interact 交互”规则一致。前台 Pomodoro 上的 Interact
覆盖层也没有改变交互 owner。
### D04-C5Whiteboard 范围已消除冲突
`04.runtime_workspace.md` 第 5 节与 `plan.md` 第 3 节均明确:Whiteboard 只保持第 03 次已完成的 SDK/隔离
回归,不接入第 04 次工作区切换或完整用户流程;Pomodoro 才是本轮唯一新增的工作区闭环验证应用。
### D04-C6Rust 为正确的 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 原子完成后立即按一般收口关闭 instanceSurface 会先被
卸载,Pomodoro 无法兑现“App 内完成提示”;若 UI 本地自行显示后再关闭,则可能在重启、旧 Surface 或重复
回调下与已提交 outbox 脱节。第 03 次的既有规则还区分“Tool 完成不自动关闭 App/子会话”和
`AppLifecycleManager.close` 才结束子会话;第 04 次为 Pomodoro 采用不同的自动关闭策略是允许的,但必须
明确这是 Pomodoro 的特例及其顺序。
建议二选一并写入工作内容、恢复规则和验收:
1. **保留完成展示窗口。** deadline operation 原子写 `completed` 和唯一 result/outboxRuntime 将终态投影给
PomodoroSurface 在一个明确的受控窗口内显示完成提示;窗口结束、用户确认或用户离开后由 Runtime 发起
`AppLifecycleManager.close`,再结束子会话并恢复 Interact。重启落在窗口内时须能按持久化终态恢复到同一
收口路径,而不得再写结果。
2. **立即收口。** Runtime 完成后立刻关闭 App、结束子会话、恢复 Interact;删除“Pomodoro 显示自己的完成
提示”,或将完成提示明确改为 Interact 的可信呈现而非已关闭 MiniApp 的 UI。
无论选择哪种,验收都应覆盖 `completed` 与 close 并发、已提交 outbox 的重试、终态 UI 不产生第二次 Tool
完成,以及完成前/后 pending Interact 的归属。
## D04-09Pomodoro 的 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-07D04-09 已回填本轮实施权威 [04.runtime_workspace.md](04.runtime_workspace.md) 和
[plan.md](../plan.md);实施时须依照已冻结的 Tool、完成收口和 Manifest 契约提供 fixture 与自动化验收;
- D04-C6 继续作为 P5 carryover 留在本记录与第一轮记录中;没有 profiling 证据前,不把 Rust 迁移纳入当前
实现范围;
- 本评审只记录发现和建议,未修改第 04 次主定义、正式方案、计划或实现。
@@ -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 commandAgent 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 声明的 ToolPomodoro 本身只显示和接收 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` 的唯一结果 / outboxPomodoro 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
或 outboxAgent 必须先调用 `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-10D04-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. 本评审只记录发现,未修改主定义、计划、正式方案或实现。
@@ -0,0 +1,382 @@
# 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)
**实施规范:** [05.technical_implementation_spec.md](05.technical_implementation_spec.md)
**冻结说明:** 本定义以 [03.design_review.md](03.design_review.md)、[06.technical_implementation_spec_review.md](06.technical_implementation_spec_review.md)
和 [07.技术实施规范评审(二).md](07.技术实施规范评审(二).md) 已关闭的 P0~P3 决议为实施基线;后续若调整
Pomodoro Tool、Runtime 终态、状态快照、session data 同步语义或本迭代范围,须通过新的设计评审记录确认。
本文定义产品与验收边界;[05.technical_implementation_spec.md](05.technical_implementation_spec.md) 只将这些
已冻结规则映射到模块、存储、协议和测试,不改变本迭代的产品决议。当前没有未解决的 P0~P3;
TIS2-06(多入口 session data mutation queue)是具有明确触发条件的 P4 延续项,不属于本轮实现。
## 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 创建 Pomodoro App 子会话并进入启动中
→ Host 确认 Pomodoro 已进入前台,Interact 才进入后台
→ Runtime 创建 deadline operation,专注正式开始
→ 用户专注;自动暗屏或锁屏不改变本轮计时
→ 到时间完成,或用户主动离开专注工作区而中断
→ 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、作用域、实例和会话上下文;
- 为当前 MiniApp App 子会话保存通用、revision 控制的 JSON 业务数据;Runtime 不理解其中的业务字段;
- 管理所有 MiniApp 统一的启动状态:`starting → ready | failed | cancelled`,并按每个 Agent Tool 声明的就绪条件
判断是否可执行;将该状态作为与触发调用的 `call_id` 关联的受控协议进度通知 Agent;
- 管理通用 deadline operation:持久化 `ends_at`、裁决完成/中断唯一终态并写唯一 outbox;
- Runtime 的 operation 提交协调器先一次持久化 operation、MiniApp session data、子会话 / workspace 目标、Tool receipt 和 outbox
只有提交成功后才驱动 Lifecycle、Host 与 Surface 对账。提交失败不改变任何层;提交后的 UI / Host 失败不回滚
已提交业务事实,而按已保存状态恢复、重试或收口;
- 处理刷新、断线和重启恢复;
- 关闭 App 时停止新 Tool 投递,并收口未完成操作;
- 通过 outbox 可靠回传结果;
- 需要系统通知时通过 Capability Gateway 处理。
对需要由 MiniApp 处理的 Agent ToolRuntime 只依据已验证的 `app_scope`、method、schema、当前会话和幂等键
进行路由:它知道“调用哪一个 App 的哪一个方法、参数是否合格”,但不理解参数值的业务含义,也不直接修改
MiniApp 的业务数据。Runtime 先解析或建立该 App 当前可用的 App 子会话,等待其 activation 成为 `ready`,再把调用
投递给 MiniApp;MiniApp 自己执行业务方法、写自己的 session data,并把受控结果交回 Runtime。
`activation_ready` 是“这个 App 已经能接收后续 `app_ready` Tool”的 Runtime 事实,Agent 收到它后再连续编排相关
指令是推荐方式,但不是 Runtime 接收合法后续指令的前置条件。若后续 `app_ready` / `foreground_required` 调用在同一
App 的 activation 仍为 `starting` 时先到,Runtime 持久化并等待该 activation,ready 后按到达顺序投递;失败或取消则
回传 `app_activation_failed` / `app_activation_cancelled`,不会执行 MiniApp handler。`activation_not_required` 调用不等
ready,例如启动中的 `pomodoro.interrupt` 必须立即取消启动。所有 ready / failed / cancelled 通知由 Runtime 发给 Agent
MiniApp、Surface 和 Host 都不能直接与 Agent 通信。
### 2.3 Pomodoro MiniApp
Pomodoro 是普通 `bundled` MiniApp,必须使用与其他非系统级 MiniApp 相同的 SDK、受限 Surface 和
Capability 规则。
它只负责:
- 显示活动名称、倒计时和低干扰专注界面;
- 通过自身的 session data 显示活动名称、倒计时和展示状态;
- 接收 Runtime 路由的受限 Tool / 生命周期事件;不以界面按钮直接改变专注业务状态;
- 通过 SDK 读取和保存自己定义的通用 session data
它不能:
- 直接访问 AppServer、Transport、Conversation Store 或 Agent
- 直接调用 Tauri、系统通知或其他 Host 能力;
- 发起 Interact 标准交互;
- 修改 Runtime 的焦点、Registry 或其他 App 数据。
## 3. Pomodoro 最小模型
Pomodoro App instance 和一次专注 operation 不是同一个对象:`app_session_id` 表示 Interact 中围绕该
MiniApp 的子会话,`instance_id` 表示 MiniApp 实例,`operation_id` 才表示一次实际专注。`pomodoro.start`
会先建立一个处于 `starting` 的 App 子会话 / instance;只有 Host 确认它已经成为当前前台界面后,才创建
`focusing` operation。因此它们不在数据模型上互为同义词。
一次专注 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 可以在自己的通用 session data 中保存活动名称、`operation_id``started_at`
`ends_at` 与展示状态;这些字段的结构和可变性由 Pomodoro 自己决定,Runtime 不解释它们,也不以其替代
Runtime operation 的 Tool 终态和 outbox。
Pomodoro 在 MiniApp SDK 源码中声明以下两个由 Agent 调用的 Tool;构建时自动生成 Manifest 中对应的不可执行
Tool 描述,开发者不再手写第二份 Tool 清单。MiniApp 是 Agent 的业务工具:用户在 IM 中以自然语言表达开始、停止等
意图(未来同样适用于 Voice),由 Agent 决定并调用 Tool;Pomodoro 的界面不提供暂停、继续或“结束专注”的业务按钮。
```text
pomodoro.start({ duration_seconds, activity? })
pomodoro.interrupt({})
```
`pomodoro.start` 是要求前台就绪的长期 Tool operation:它进入 `starting` 后,只有 Host 确认 Pomodoro 已经
成为当前前台界面时才进入 `focusing` 并开始计算 `ends_at`;最终只在 `completed``interrupted` 时回传一次
业务结果。若前台启动最终失败或被取消,则返回“本次计时未开始”的启动失败结果,而不创建 operation 或专注
中断记录。`pomodoro.interrupt({})` 是短 ToolTool Router 只从该 Agent 调用所在的
`conversation_id` 中绑定当前唯一的 `starting``focusing` Pomodoro;调用者不能传入或伪造 `instance_id`
`app_scope``operation_id` 或其他会话目标。Runtime 将调用路由给该 Pomodoro instance,并在同一原子裁决中
`starting` 时取消启动、在 `focusing` 时将 `pomodoro.start` 收口为 `interrupted`。若 Surface 已卸载,Runtime
仍必须完成已经开始的专注裁决;Surface 只接收通用 lifecycle 关闭通知。`pomodoro.interrupt` 的稳定回执为
`focus_start_cancelled``interrupted``no_active_focusing_operation``operation_already_final`;重复或迟到调用
不得重写终态或新增 outbox。用户不需要在 App 内再次点击“开始”,也没有暂停、继续或恢复入口。
长期 `pomodoro.start` 的“过程消息”和“业务结果”必须分开。Runtime 在接受调用并提交 `starting` activation 后,持久化并
回传一次协议回执 `accepted / starting`;它只表示“正在打开 Pomodoro”,Agent 不得据此宣称已经开始计时。Host 确认
前台、Runtime 创建 `focusing` operation 与 `ends_at` 后,再持久化并回传一次 `started` 进度;只有此时 Agent 才能
对用户说“已开始计时”。随后 `completed``interrupted``not_started` 才是符合 `pomodoro.start` result schema 的
唯一最终业务结果。相同 `source_tool_call_id` 的重放只返回已持久化的最新回执、进度或最终结果,不创建新 operation,
也不重复追加 outbox。
对于 Pomodoro`started` 已经包含 `activation_ready`:它不仅表示 MiniApp 可以接收调用,还表示 Host 已确认前台、
Runtime 已创建 operation,倒计时已真实开始。因此第 04 次不额外发送一条独立的 `activation_ready`,避免 Agent 收到
两个含义重叠的“已经好了”消息;启动失败或取消则用最终 `not_started` 结果收口。
启动中尚未前台激活时,用户 / Agent 要求停止、用户离开该启动中的工作区或 Host 最终报告无法激活,都只取消
启动尝试;它们不产生 `interrupted` operation。已经进入 `focusing` 后,用户切回 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 只发布通过由 SDK 声明生成的 Manifest、Inventory revision、会话作用域和
输入 schema 校验的 Tool;校验失败、过期 Inventory 或不存在的目标均稳定拒绝,且不启动或唤醒 Pomodoro。
| Tool | 调用模型与启动策略 | 输入 | 输出 / 稳定拒绝 | 幂等、超时与路由顺序 |
|---|---|---|---|---|
| `pomodoro.start` v1 | 长期 `operation``foreground_required`。Runtime 先创建处于 `starting` 的 instance 与 App 子会话,持久化 `accepted / starting` 回执;Host 确认前台激活后才创建 deadline operation,并持久化 `started` 进度。 | `{ duration_seconds: 正整数, activity?: 字符串 }`;拒绝额外字段。 | 唯一最终业务结果:`{ state: "completed" \| "interrupted", operation_id, started_at, ends_at, ended_at, interruption_reason? }`;启动最终失败或取消时返回 `not_started`(原因是 `focus_start_failed` / `focus_start_cancelled`),且没有 `operation_id`;若同一会话已有 `focusing``starting` 专注,稳定拒绝 `active_focusing_operation` / `focus_start_in_progress`。 | `source_tool_call_id` 是幂等键:同一调用重试返回既有的最新回执、进度或最终结果;不同调用由同一个 conversation 的活动 / 启动槽位作原子 compare-and-set。开始倒计时的时刻只能是前台激活确认时刻,运行期限才以持久化 `ends_at` 为准。 |
| `pomodoro.interrupt` v1 | 短 Tool`activation_not_required`,不为此调用启动、唤醒或等待 Pomodoro Surface。 | 空对象 `{}`;不接受 `instance_id``operation_id``app_scope` 或其他目标字段。 | `{ status: "focus_start_cancelled" \| "interrupted" \| "no_active_focusing_operation" \| "operation_already_final", operation_id? }`。 | `source_tool_call_id` 是幂等键。Runtime 从调用的 `conversation_id` 绑定当前唯一 `starting``focusing` 对象;前者只取消启动,后者原子写 `interrupted` 终态与长期 start 的唯一结果/outbox。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": {
"oneOf": [
{
"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"}
}
},
{
"type": "object",
"additionalProperties": false,
"required": ["status", "reason"],
"properties": {
"status": {"enum": ["not_started"]},
"reason": {"enum": ["focus_start_failed", "focus_start_cancelled"]}
}
}
]
}
},
"pomodoro.interrupt": {
"input_schema": {
"type": "object",
"additionalProperties": false
},
"result_schema": {
"type": "object",
"additionalProperties": false,
"required": ["status"],
"properties": {
"status": {
"enum": ["focus_start_cancelled", "interrupted", "no_active_focusing_operation", "operation_already_final"]
},
"operation_id": {"type": "string"}
}
}
}
}
```
同一 `conversation_id` 第一版只允许一个 `focusing``starting` Pomodoro。不同 `source_tool_call_id` 的第二次
`pomodoro.start`:若已有 `focusing`,稳定拒绝为 `active_focusing_operation`;若已有 `starting`,稳定拒绝为
`focus_start_in_progress`。两种拒绝都不得创建第二个 instance、子会话、deadline operation、焦点变更或 outbox。
若用户要修改尚未开始的时长,Agent 必须先调用 `pomodoro.interrupt({})` 取消原启动尝试,再发起新的 start。
所有 MiniApp 都通过 SDK 获得按 `app_scope + app_session_id` 隔离的通用 session data。其内容是 MiniApp 自己
定义的任意嵌套 JSON 字典:Pomodoro 可以把当前展示信息或历史写在其中;未来 To-do 可以把待办业务数据写在
其中。Runtime 只保证隔离、revision、持久化、恢复、原子提交和通用安全配额;它不声明、校验或冻结业务 schema,
也不把关闭后的业务数据统一变成只读。已发生的专注结果写入 Interact / IM 时,才由 Runtime 生成独立的、不可
篡改的静态记录。
第 04 次的 session data 同步规则冻结为:不存在数据时 `get()` 返回 `{ data: null, revision: 0 }`;每次
`subscribe()` 成功注册后,Runtime 必须先投递当前完整快照,再按严格递增的 revision 投递后续完整快照。订阅注册
`replace()` 在同一 App 子会话的串行顺序中处理,因此 `get()``subscribe()` 之间发生的更新不会丢失。SDK 忽略
重复或更旧 revision;发现 revision 跳跃、或 Web / Tauri Bridge 重连时,重新 `get()` 并建立新的订阅,以 Runtime
当前快照为准。`expected_revision` 冲突仍只返回 `session_data_revision_conflict`,本轮不自动合并业务数据;多入口
协作写入见已延期的 mutation queue 专项设计。
## 4. 工作内容
### 4.1 工作区界面
- 显示当前前台 App
- 支持由 `pomodoro.start` 从 Interact 切入 Pomodoro,以及用户显式返回 Interact;返回/切换会中断当前
专注 operation,而不是把它作为通用后台计时器继续运行;
- 显示 App 前台、后台、挂起、关闭和恢复状态;
- App 启动失败时恢复 Interact;对于需要前台就绪的 Tool,失败表示业务尚未开始;
- 刷新或重启后恢复工作区快照。
### 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.start` 时创建 `starting` Pomodoro instance 和 App 子会话;只有 Host 确认前台激活后才创建
focusing operation。只有恢复同一个未结束 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 并发;只有已经 focusing 的专注才可由 Surface 缺席时 Runtime 收口;
- 覆盖自动暗屏/锁屏不终止、工作区切换/关闭中断、重复/迟到退出和到点与中断并发时只产生一个终态;
- 覆盖重启前未到点恢复、重启后已到点结算和一次 outbox;
- 覆盖 `completed` 后立即关闭 Pomodoro、子会话只读、Interact 呈现完成结果,以及完成与关闭并发时终态不可改写;
- 覆盖通用 session data 的 App / 子会话隔离、JSON / 配额限制、revision CAS、原子写入与重启恢复;验证 Runtime
不认识 Pomodoro 的 `current` / `history` 等业务字段;
- 覆盖 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 / 隔离回归。
- 通用 mutation queue,以及 Agent Tool、UI Action 与 Runtime 动作共同修改同一 App 子会话数据时的有序协作协议;
当前 Pomodoro 没有修改型 UI Action,继续使用通用 session data 的 revision CAS。首次实现手动编辑 To-do 或
Whiteboard 的交互式绘制流程前,必须先完成该通用机制的专项设计。
- 将 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 子会话并进入 `starting`;仅在 Host 确认 Pomodoro 已成为当前
前台后,才创建 deadline operation 并进入 `focusing`。前台激活最终失败或被取消时,本次计时未开始。
3. Pomodoro 进入前台并开始后,Interact 可以退到后台;自动暗屏、锁屏和 Surface 重载不终止专注。
4. 用户以 IM 要求 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` 结果;若尚未前台激活即失败或
取消,则回传一次 `not_started` 结果。`pomodoro.interrupt` 不可伪造目标、可绑定当前会话的 `starting` 或
`focusing` Pomodoro,并对重复/迟到调用给出稳定回执;关闭流程不得将已提交终态改写。
7. 同一 `conversation_id` 不会同时存在两轮 `focusing` 或 `starting` Pomodoro;不同调用的第二次 `pomodoro.start`
稳定拒绝为 `active_focusing_operation` 或 `focus_start_in_progress`,不创建任何新的 App 或 operation 资源。
8. 刷新或重启后,未到点的本轮按 `ends_at` 恢复;已到点的本轮结算一次而不重置完整时长或重复回传。
9. 子会话在主 IM 中折叠保存,关闭后只读;新的“继续处理”创建新的 instance / 子会话,pending Interact
交互仍可在主 IM 回答但不能向已关闭子会话追加记录。
10. Agent 的标准交互始终由 Interact 负责,Pomodoro 不伪造交互组件。
11. 任意 MiniApp 都只能经 SDK 操作当前 `app_scope + app_session_id` 的通用 session dataRuntime 保证 JSON、
配额、revision、原子持久化和恢复,但不理解或限制其 `current` / `history` 等业务字段。不存在数据时为 revision 0;
每个订阅首帧都是当前完整快照,之后 revision 严格递增,断线、跳跃时 SDK 重拉并重订阅。IM 完成记录是独立的
不可变静态快照。
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 和生命周期稳定后,再单独定义任务管理业务模型。
@@ -0,0 +1,682 @@
# 04.runtime_workspace 技术实施规范
**状态:** 开发实施基线
**日期:** 2026-08-06
**实施权威:** [04.runtime_workspace.md](04.runtime_workspace.md)
**评审基线:** [03.design_review.md](03.design_review.md)
**前置实现:** [03.sdk_and_coreapp.md](../03.sdk_and_coreapp/03.sdk_and_coreapp.md)
## 1. 目的、边界与强制约束
本规范将第 04 次迭代已冻结的设计映射为开发任务。实现必须以本文件和主迭代定义共同为准;若两者出现
冲突,以主迭代定义为准,并先补充设计评审,不能在代码中自行选择另一种产品语义。
本轮交付的是通过 **IM** 由 Agent 启动、停止和观察的单次专注 Pomodoro。Voice 未来复用相同 Tool 语义,
但本轮不实现音频采集、识别或语音端到端路径。
以下约束不可违反:
1. `pomodoro.start``pomodoro.interrupt` 都是 Agent ToolPomodoro Surface 不提供开始、暂停、继续或
结束专注的业务按钮。
2. Runtime 是 `ends_at`、operation 终态、Tool result、outbox、焦点和生命周期的唯一裁决者。Surface 不能
`complete``fail``cancel` 这两个 Tool,也不能发送 Agent 协议消息。
3. 每个 `conversation_id` 同时至多存在一个 `focusing` 的 Pomodoro operation。
4. deadline、Agent interrupt、返回 Interact、切换其他 MiniApp 和关闭 App 竞争同一个原子终态;第一个成功者
生效,所有后到者不得改写终态或新增长期 Tool outbox。
5. `remaining_seconds` 只由 `ends_at - now` 派生,绝不持久化为第二个时间事实。
6. completed 后立即关闭 Pomodoro、结束 App 子会话、恢复 Interact;完成结果由 Interact / Agent 呈现。
7. 所有 MiniApp 都有 Runtime 管理的启动状态 `starting → ready | failed | cancelled`。启动状态不是 MiniApp
的业务数据,也不是 Pomodoro operation;每个 Agent Tool 声明其成功执行前要求的就绪条件。
8. MiniApp 只能通过 SDK 操作当前 `app_scope + app_session_id` 的通用 session data。Runtime 只管理隔离、JSON
安全限制、revision、原子持久化和恢复,不理解或校验 `current``history` 等 MiniApp 业务字段。
## 2. 现有基线与改动边界
下表列出当前仓库已存在的模块与第 04 次必须补齐的责任。新增业务逻辑不得堆入 `src/main.ts`;该入口只能
装配 Runtime、Host 和可信 Surface。
| 现有模块 | 当前责任 | 第 04 次改动 |
|---|---|---|
| `src/runtime/app-management/miniapp-manifest.ts` | Manifest、Tool Descriptor 的静态校验 | 增加 Tool 的通用启动就绪条件和 Runtime 托管 deadline operation 的声明类型及校验。 |
| `src/runtime/app-management/reference-miniapps.ts` | 内置 bundled App 的 Manifest | 新增 `POMODORO_MANIFEST`,并纳入默认 bundled manifests。 |
| `src/runtime/coordination/tool-router.ts` | 校验 Agent Tool、Inventory revision、schema、scope | 保持首层校验;为通过校验的 Pomodoro Tool 返回 Runtime-managed operation 路由,不能直接投递给 Surface。 |
| `src/runtime/coordination/lineup-runtime.ts` | Runtime 装配、持久化、outbox、事件 | 接入 Pomodoro operation 服务、deadline 调度、恢复、关闭编排和 Host 投影。 |
| `src/runtime/coordination/miniapp-tool-state.ts` | 普通 MiniApp Tool 的 Surface 回执状态机 | 保持普通 Tool 语义;Pomodoro 的 Runtime-managed Tool 不得走 Surface `sdk.tools.complete` 路径。 |
| `src/runtime/persistence/conversation-store.ts` | 会话、本地 outbox、Tool、App workspace 持久化 | 升级版本并持久化 operation、App 启动状态、通用 session data、Tool 去重及 IM 静态结果。 |
| `src/runtime/app-management/app-lifecycle-manager.ts``app-orchestrator.ts` | instance、焦点、关闭和恢复 | 提供由 Pomodoro operation 服务调用的受控 close,避免普通 `app_closed` 取消覆盖已提交的终态。 |
| `src/core-apps/pomodoro/`(新增) | 不存在 | 实现受限 Pomodoro SDK Surface;从通用 Tool / lifecycle 事件和自身 session data 渲染,不读取 Runtime operation。 |
| `src/main.ts` | Web / Tauri Host 装配 | 注册 Pomodoro 的本地 Surface 与工作区投影;不放入 Tool、计时、状态机或存储逻辑。 |
## 3. Manifest、Tool 与 Runtime operation 契约
### 3.1 Manifest 扩展
`miniapp-manifest.ts` 增加以下结构;名称可保持一致,字段语义不得改变。`runtime_operation` 是通用的
Runtime deadline operation 声明,不允许 MiniApp 注入代码或回调。`activation_requirement` 说明一个 Agent Tool
在业务处理或创建 Runtime operation 前,需要 Runtime 将目标 MiniApp 准备到什么程度;这不是 UI Action。
```ts
type ToolActivationRequirement = "activation_not_required" | "foreground_required" | "app_ready";
type RuntimeDeadlineOperationDeclaration = Readonly<{
kind: "deadline";
duration_input: "duration_seconds";
concurrency_scope: "conversation_and_app";
execution_owner: "runtime";
}>;
type MiniAppToolDescriptor = Readonly<{
// 由 MiniApp SDK 的 defineAgentTools(...) 声明,并在构建时生成到 Manifest;不是手写的第二份文件。
id: string;
version: 1;
handling: "direct" | "interactive" | "launch" | "foreground" | "operation";
delivery: "miniapp_sdk" | "runtime";
input_schema: JsonObject;
output_schema: JsonObject;
timeout_ms?: number;
activation_requirement: ToolActivationRequirement;
runtime_operation?: RuntimeDeadlineOperationDeclaration;
runtime_execution?: RuntimeToolExecutionRoute;
}>;
type MiniAppManifestV1 = Readonly<{
// 保留既有字段;不声明 MiniApp 的业务数据 schema。
// tools 为 SDK 构建期生成的不可执行 Tool 契约。
}>;
```
开发者只在 MiniApp 代码中调用 SDK 的 `defineAgentTools(...)`:每一项同时声明公开 method、说明、输入 / 输出 schema、
activation requirement 和本地 handler。构建工具必须从这些 SDK 声明生成 Manifest 的 tools 投影;开发者不维护第二份
手写 Manifest。生成物只包含描述数据,绝不包含 handler 函数、私有函数名、URL 或回调;`delivery = miniapp_sdk`
时 Bundle 内的 SDK 保留 `method → handler` 的本地映射。构建必须拒绝重复 method、schema 不合法、miniapp_sdk Tool
缺 handler 或 handler 类型与 schema 不一致;`delivery = runtime` 则必须匹配 Runtime 内置、受控注册的 execution。
下面是开发者实际维护的唯一一处 Tool 声明的形态(字段名称可以按 SDK 最终 API 微调,语义不得变化):
```ts
export const agentTools = defineAgentTools({
"todo.add": {
description: "向当前待办列表添加一项",
activation_requirement: "app_ready",
delivery: "miniapp_sdk",
input_schema: todoAddInputSchema,
output_schema: todoAddOutputSchema,
handler: async (input, context) => {
// 只有 MiniApp 理解 input 的业务含义,并使用 context.sessionData 更新自己的数据。
return addTodoToSessionData(input, context.sessionData);
},
},
});
```
构建从这一个声明同时得到两件不同的东西:Bundle 内 SDK 的 `method → handler` 映射,以及安装包 Manifest 的 Tool
描述。后者是 Runtime 的安装、校验与发布输入,而不是要求开发者另外同步维护的图纸;App 身份、版本、签名等安装包
基础元数据仍按打包配置提供,但不重复定义 Tool。
静态校验规则:
- 每一项 Agent Tool 都必须声明 `activation_requirement`;仅接受 `activation_not_required`
`foreground_required``app_ready`
- `activation_not_required` 表示 Tool 只操作已有 Runtime 事实,不能为此调用创建或唤醒 MiniApp;
- `app_ready` 表示 Runtime 已解析或建立目标 App 子会话,该 MiniApp 已 active / ready、可接收受限 Tool 调用;
它不要求创建或显示 Surface。Runtime 不理解或处理此调用参数的业务含义,而是将方法与已验证参数投递给 MiniApp。
- `foreground_required` 表示在满足 `app_ready` 的基础上,Host 还必须确认该 MiniApp 已成为当前前台 App 后,Tool
才可进入业务执行;前台是该 Tool 的业务前提,而不是通用路由要求。
- `delivery = "miniapp_sdk"` 的普通 Tool 一律投递到当前 ready App session 的统一 SDK Tool 接收入口;Runtime
不读取或保存其私有函数 target。
- `runtime_operation` 仅允许 `handling = "operation"``delivery = "runtime"`,且 `execution_owner` 必须为
`runtime`
- Manifest 是声明,不授予 Transport、Tauri、系统通知或 Agent 权限。
### 3.1.1 Agent Tool 的 Registry 投影与内部执行路由
本规范中的 MiniApp Tool 都是向远端 Agent 开放的 Agent Tool 声明;它们不是用户 UI 的公共操作入口。每个
已安装 MiniApp 必须在安装时由 Runtime 验证 Manifest,并把可路由的 Manifest 投影写入 App Registry。Runtime
不能依赖 MiniApp Surface 已启动、临时读取 Bundle 源码,或在收到调用后按 Tool 名字散落地猜测其行为。
现有 Manifest 的 tools 数组仍可沿用原字段名,但其中的每一项在本规范中都按 Agent Tool 处理;UI Action 与
Host 生命周期意图不写入该数组,也不进入 Agent Inventory。
Registry 中每个已验证 MiniApp 至少保存以下事实:
~~~ts
type RegisteredMiniAppRecord = Readonly<{
app_scope: string;
installed_version: string;
enabled: boolean;
verified_manifest_digest: string;
agent_tools: readonly RegisteredAgentTool[];
}>;
type RuntimeToolExecutionRoute = Readonly<{
owner: "runtime";
handler_key:
| "pomodoro.start_deadline.v1"
| "pomodoro.interrupt_current.v1";
}>;
type RegisteredAgentTool = Readonly<{
tool_key: {
app_scope: string;
method: string;
contract_version: string;
};
handling: "direct" | "interactive" | "launch" | "foreground" | "operation";
delivery: "miniapp_sdk" | "runtime";
activation_requirement: ToolActivationRequirement;
input_schema: JsonObject;
output_schema: JsonObject;
visible_to_agent: boolean;
runtime_execution?: RuntimeToolExecutionRoute;
}>;
~~~
这里的 handler_key 只是 `delivery = "runtime"` 时 Runtime 内部的受控执行路由,不是发布给 Agent 的第二个
Runtime Tool。它不得出现在 Agent Inventory、Agent Tool 参数或 MiniApp SDK context 中。对于
`delivery = "miniapp_sdk"`Runtime 一律投递到 SDK 固定的 Tool 接收入口,由 MiniApp Bundle 内部按 method
分发 handlerManifest 不传递 JavaScript、URL、回调、私有函数名或其他可执行内容。
Runtime 只接受自己在 RuntimeOperationHandlerRegistry 中预注册、且与 app_scope、method、版本精确匹配的
handler_key。未知 key、与 Tool 不匹配的 key,或未由当前安装版本验证过的声明,均以 manifest_denied 拒绝。
第一版至少预注册两个映射:
~~~text
pomodoro.start v1
→ pomodoro.start_deadline.v1
pomodoro.interrupt v1
→ pomodoro.interrupt_current.v1
~~~
Runtime 启动、安装、启用、禁用、升级、卸载、Host 能力变化或策略变化时,必须按以下方式更新动态 Inventory:
~~~text
读取 App Registry
→ 只选择已安装、已启用、Manifest digest 与版本匹配的 MiniApp
→ 读取每个 Record 的 agent_tools
→ 与 Host、权限、策略、当前 Agent 和 conversation scope 求交
→ 重建 Runtime Tool Registry
→ 生成新的 Inventory revision
→ 在连接可用时同步给远端 Agent
~~~
因此,Pomodoro 尚未启动、没有前台 Surface 或 Surface 暂时卸载时,只要 App 仍已启用,Agent 仍可看到并调用
pomodoro.start 和 pomodoro.interrupt。禁用或卸载流程必须先从 Tool Registry 和 Agent Inventory 移除新调用入口,
再按生命周期规则收口现有 instance 和 operation。
ToolRouter 只能读取 RegisteredAgentTool,而不能读取原始 Manifest 后自行推断。它必须返回显式 Route:
~~~ts
type ToolRoute =
| { kind: "runtime"; tool: RegisteredAgentTool; execution: RuntimeToolExecutionRoute }
| { kind: "miniapp_sdk"; tool: RegisteredAgentTool };
~~~
当 ToolRoute 为 runtime 时,LineUpRuntime 从 RuntimeOperationHandlerRegistry 取得已注册服务后执行。Pomodoro
的 start 调用 PomodoroOperationService.startAfterForegroundActivationinterrupt 调用
PomodoroOperationService.interruptCurrent。两者都不得以 tool_id 的字符串特判散落在 Runtime 的多个分支中;
其中 start 必须等待 Host 的前台激活确认,interrupt 则不要求 Surface 存在。
当 ToolRoute 为 miniapp_sdk 时,Runtime 解析或建立已 ready 的 App session,向该 session 的统一 SDK Tool
入口投递 `{ call_id, method, params }` 及受限 context。SDK 根据构建时的 `defineAgentTools(...)` 本地映射分发
handlerMiniApp 自己理解业务、写 session data,并通过 SDK 返回 receipt、progress、result 或 error。Runtime
只验证、持久化和回传这些受控数据,不调用 MiniApp 私有函数。
### 3.2 Pomodoro Manifest
新增 `POMODORO_MANIFEST`,固定 `app_scope = "pomodoro"``kind = "bundled"``version = "1.0.0"`
`host.surface_required = true``requested_capabilities = []`。Manifest 不声明 Pomodoro 的业务数据 schema
它与其他 MiniApp 一样,只使用当前 App 子会话的通用 session data。
两项 Tool 的实现 Descriptor
| Tool | `handling` / delivery | Runtime operation | Host 行为 |
|---|---|---|---|
| `pomodoro.start` | `operation``delivery = runtime``activation_requirement = foreground_required``restore_previous_focus = true` | `{ kind: "deadline", duration_input: "duration_seconds", concurrency_scope: "conversation_and_app", execution_owner: "runtime" }` | 先进入 `starting` 并请求前台化;只有 Host 确认已成为当前前台后,才创建 deadline operation。 |
| `pomodoro.interrupt` | `direct``delivery = runtime``activation_requirement = activation_not_required``restore_previous_focus = false` | 不声明 deadline;由 `PomodoroOperationService.interruptCurrent` 处理。 | 不挂载、不唤醒、不等待 Surface;已有 starting 时取消启动,已有 focusing 时中断 operation。 |
两个 Descriptor 都必须写入 3.1.1 定义的 runtime_executionpomodoro.start 固定使用
pomodoro.start_deadline.v1pomodoro.interrupt 固定使用 pomodoro.interrupt_current.v1。该投影只保存于
Registry 和 Runtime Tool Registry;面向 Agent 发布的 Inventory 保留 Tool 名称、业务说明、schema、调用模型、
可见性和版本,不暴露内部 handler_key。
输入和最终业务结果 schema 直接使用主定义第 3 节的 JSON Schema。协议回执 / 进度不是业务 result,不能拿
activation 或 operation 对象去冒充 `pomodoro.start` 的 result。路由层拒绝使用现有通用错误码:
`invalid_request``inventory_revision_mismatch``scope_mismatch``not_installed``disabled`
`manifest_denied``permission_denied`。通过路由后,Pomodoro 业务稳定回执为:
```text
pomodoro.start
active_focusing_operation
focus_start_in_progress
focus_start_failed
focus_start_cancelled
pomodoro.interrupt
interrupted
focus_start_cancelled
no_active_focusing_operation
operation_already_final
```
`source_tool_call_id` 是每个 Tool 的幂等键。相同 `call_id` 的重放必须返回先前持久化的**最新协议回执、进度或最终
业务结果**,不得再次创建 instance、子会话、deadline、焦点记录、Tool result 或 outbox。
## 4. 持久化模型与原子提交
### 4.1 新增记录
`src/runtime/coordination/pomodoro-operation-state.ts` 定义纯状态机和数据类型;在
`src/runtime/coordination/pomodoro-operation-service.ts` 定义编排服务。服务不得依赖 DOM、`window` 或 Tauri。
```ts
type PomodoroOperationState = "focusing" | "completed" | "interrupted";
type PomodoroOperationRecord = Readonly<{
operation_id: string;
source_tool_call_id: string;
conversation_id: string;
app_scope: "pomodoro";
instance_id: string;
app_session_id: string;
activity?: string;
duration_seconds: number;
started_at: string;
ends_at: string;
state: PomodoroOperationState;
ended_at?: string;
interruption_reason?: "agent_interrupt" | "user_exit" | "workspace_switch" | "app_closed";
result_outbox_id?: string;
close_committed_at?: string;
}>;
type MiniAppActivationState = "starting" | "ready" | "failed" | "cancelled";
type MiniAppActivationRecord = Readonly<{
app_scope: string;
instance_id: string;
app_session_id: string;
conversation_id: string;
source_tool_call_id: string;
activation_requirement: ToolActivationRequirement;
state: MiniAppActivationState;
created_at: string;
ready_at?: string;
terminal_reason?: "surface_activation_failed" | "cancelled_before_ready";
attempt_count: number;
// 通过 Router 校验、但必须等待本 activation ready 后才能投递的 call_id,按持久化到达顺序保存。
pending_tool_call_ids: readonly string[];
}>;
type MiniAppSessionDataRecord = Readonly<{
app_scope: string;
app_session_id: string;
revision: number;
data: JsonObject | null;
updated_at: string;
}>;
type ToolProtocolReceipt = Readonly<{
call_id: string;
// activation_* 是通用 App 启动进度;started 是像 Pomodoro 一样更强的业务开始进度。
phase:
| "accepted"
| "activation_ready"
| "activation_failed"
| "activation_cancelled"
| "started";
activation: Readonly<{
app_scope: string;
state: MiniAppActivationState;
}>;
reason?: "surface_activation_failed" | "cancelled_before_ready";
operation_id?: string;
started_at?: string;
ends_at?: string;
}>;
type PersistedAgentToolCall = Readonly<{
call_id: string;
tool_key: { app_scope: string; method: string; contract_version: string };
// 最后一个可重放的过程事实;终态存在时 final_result 优先于它返回。
latest_receipt?: ToolProtocolReceipt;
final_result?: JsonObject;
}>;
```
`ConversationStore` 从当前版本 14 升级到 **15**。新增 `pomodoro_operations``miniapp_activations`
`miniapp_session_data`,并实现以下最小 API
```ts
replacePomodoroOperations(records: readonly PomodoroOperationRecord[]): void;
replaceMiniAppActivations(records: readonly MiniAppActivationRecord[]): void;
replaceMiniAppSessionData(records: readonly MiniAppSessionDataRecord[]): void;
runtimeWorkspaceSnapshot(): {
operations: readonly PomodoroOperationRecord[];
activations: readonly MiniAppActivationRecord[];
sessionData: readonly MiniAppSessionDataRecord[];
};
```
迁移要求:v1~v14 的既有数据保持现有兼容路径;缺失的新数组按空数组恢复。不得因旧记录无法拥有 Pomodoro
字段而丢弃既有 IM、outbox、Tool、workspace 或 App 子会话数据。
### 4.2 必须保持的唯一性
所有比较均在同一 `conversation_id` 内进行:
| 约束 | 行为 |
|---|---|
| `source_tool_call_id` 唯一 | 相同调用重放先返回已持久化的最终业务结果;尚未终态时返回最后一个协议回执 / 进度。 |
| `(conversation_id, app_scope = pomodoro, state = starting \| ready)` 启动槽位唯一 | 第二个不同 start 返回 `focus_start_in_progress`;不会创建第二个启动中的 instance。 |
| `(conversation_id, app_scope = pomodoro, state = focusing)` operation 唯一 | 第二个不同 start 返回 `active_focusing_operation`,不产生任何资源。 |
| `(app_scope, app_session_id)` session data 唯一 | 只能以正确 `expected_revision` 整体替换;revision 每次成功写入加一。 |
| `result_outbox_id` 唯一 | 终态首次成功时创建;后续 deadline / interrupt / close 不能追加第二条长期 Tool result。 |
### 4.3 原子提交接口
由 LineUpRuntime 拥有唯一的 RuntimeOperationCommitCoordinator。PomodoroOperationService、deadline scheduler、
Tool Router 和 Lifecycle 事件只能向该协调器提交业务变更请求;它们不能各自直接写 ConversationStore、改变
AppLifecycleManager、发送 outbox 或通知 Surface。
协调器在内存副本中完成校验、幂等和 compare-and-set,并以一次 ConversationStore.persist 写入完整的下一份
Runtime 事实。一次提交至少同时包含 operation、activation、MiniApp session data、App 子会话 / workspace 的目标状态、Tool
调用记录或 receipt,以及需要回传的 outbox。若任一校验或持久化失败,持久化事实、Lifecycle 内存状态、Host 和
Surface 均不得改变。
提交成功后,协调器按固定顺序执行派生动作:
~~~text
1. ConversationStore.persist(next_runtime_snapshot) 成功
2. AppLifecycleManager 以已保存 workspace 对账;它只更新内存投影,不再次持久化业务事实
3. Runtime 发出 lifecycle / state / workspace 投影
4. Host 挂载、卸载、前后台切换等副作用执行
5. outbox 网络发送异步重试;网络成功与否不改变已提交 operation
~~~
AppLifecycleManager 不再拥有与 Store 平行的业务事实。它必须支持从已提交 workspace 重建或对账自己的 instance /
focus 内存投影。若提交成功后对账失败,Runtime 不得回滚 operation、session data、Tool receipt 或 outbox;它必须停止
向 Surface 发新投影,标记需要重新对账,并立即或在下次恢复时从已保存 workspace 重建 Lifecycle。Host / Surface
副作用失败同样不得回滚已提交 operation,后续按第 5.2 节的挂载重试和最终失败收口规则处理。
以下操作必须各自在一次事务内完成:
1. foreground-required start 的第一阶段:检查幂等与启动 / active slot → 创建 instance / 子会话 / `starting`
activation / workspace → 写 `accepted` protocol receipt 及其唯一 outbox → 请求 Host 前台化;此阶段不得创建
Pomodoro operation 或倒计时。
2. Host 确认前台激活:以同一事务将 activation 置为 `ready`,创建 operation / `focusing`、写 workspace、更新为
`started` protocol receipt 及其唯一 progress outbox,并投递通用 lifecycle 事件;`started_at``ends_at` 以该
确认时刻计算。
3. interrupt:若存在 starting activation,写 `cancelled`、短 Tool receipt 和启动取消结果,关闭子会话;若存在
focusing operation,写 `interrupted`、长期 Tool result、短 Tool receipt、outbox → 标记待关闭。
4. deadline:定位 operation → 写 `completed`、长期 Tool result、outbox → 标记待关闭。
5. lifecycle close:若 activation 仍 `starting`,取消启动;若 operation 尚在 `focusing`,写 `interrupted` 和唯一
result;若已终态,只执行尚未完成的 close。
当 interrupt 找到 focusing operation 时,短 Tool receipt、长期 start 的终态 result、outbox 与
workspace 的待关闭目标必须属于同一次提交,并引用同一 operation。没有 focusing operation 的短 Tool receipt
不改变业务 operation,但仍必须按 call_id 持久化以支持幂等重放。Host 的前台激活确认只能报告前台事实;由
Runtime 根据该事实在同一提交中写 operation、`started` protocol receipt 和 outboxHost 本身不能直接写这些记录。
## 5. 状态机、路由和收口顺序
### 5.1 Runtime deadline operation 状态机
```text
start accepted
→ focusing
├── now >= ends_at → completed
├── Agent pomodoro.interrupt → interrupted(agent_interrupt)
├── 返回 Interact → interrupted(user_exit)
├── 切换其他 MiniApp → interrupted(workspace_switch)
└── AppLifecycle close → interrupted(app_closed)
completed / interrupted
→ 仅允许幂等读取、outbox 重试和一次 close 收口
→ 不可返回 focusing
```
在同一事务内,deadline、Agent interrupt、lifecycle close 使用 `operation_id + state = focusing`
compare-and-set。CAS 失败时读取既有终态:不改写 record、不新增长期 result/outboxAgent Tool 返回对应的稳定
receiptlifecycle 继续执行安全 close。
### 5.2 通用 MiniApp 启动与 `pomodoro.start` 顺序
每个 MiniApp instance / App 子会话的启动都进入 Runtime 管理的 activation 状态;一个 Tool 是否需要新建或
等待 activation,则由 Descriptor 的 `activation_requirement` 决定,而不是由某个 MiniApp 的业务名称决定。
Runtime 在这一层只做“找到 App、方法和已验证参数,并确认目标 App session 已 ready”的路由工作;它不解释参数
字段的业务含义,也不直接修改 MiniApp session data
```text
Tool 调用已通过 Router 校验
├── activation_not_required
│ → 不创建或唤醒 MiniApp,直接在已有 Runtime 事实范围内执行
├── foreground_required
│ → 创建 app_session / instance / activation(starting)
│ → Host 挂载并确认该 instance 已成为当前前台
│ → activation(ready) → 执行 Tool 的业务效果
└── app_ready
→ 创建 app_session / instance / activation(starting)
→ MiniApp 报告自身可接收 Tool 的 readyRuntime 取得其当前 app_session
→ Runtime 将 method + 已验证参数投递给 MiniApp
→ MiniApp 自己执行业务效果、写 session data 并返回结果;无需显示 Surface
```
#### 5.2.1 activation 进度通知与等待队列
一次 activation 的启动、就绪、失败和取消都必须由 Runtime 在持久化后,以与原始 `call_id` 关联的 Tool protocol
receipt / progress 通知该 Agent`accepted` 表示 `starting` 已提交,`activation_ready` 表示目标 App session 已可接收
`app_ready` Tool`activation_failed` / `activation_cancelled` 表示启动已经不能继续。公开 payload 只能包含 call_id、
app_scope、状态与受控 reason`instance_id``app_session_id` 仅保留在 Runtime 内部记录,不得作为 Agent 可指定的
路由参数或公开的 activation 控制柄。
收到合法 Tool 后,Router 不要求 Agent 先收到 `activation_ready` 才能提交下一条调用。若目标 conversation / app scope
存在 `starting` activation,且调用要求 `app_ready``foreground_required`,Runtime 必须在同一提交中持久化 Tool Call
record,将它的 call_id 追加到该 activation 的 `pending_tool_call_ids`,并回传 accepted receipt。activation 进入 ready 后,
Runtime 按数组中的持久化顺序向统一 SDK 接收入口投递这些调用;不得因重新挂载、恢复或 outbox 重试改变顺序或重复投递。
若 activation 失败或取消,Runtime 为每个尚未投递的调用持久化且回传 `app_activation_failed` / `app_activation_cancelled`
不调用 handler。`activation_not_required` 不得进入该队列,仍立即由 Runtime 在已有事实范围内处理。
`pomodoro.start`,前台确认的同一提交既使 activation ready,又创建 deadline operation;因此只发送 `started`
progress,不额外发送语义重复的 `activation_ready`。若启动失败或取消,`pomodoro.start` 发送其唯一的
`not_started(focus_start_failed | focus_start_cancelled)` business result;不再附加重复的 activation 终态通知。
这使未来的 `todo.add` 可以在 To-do 已 active / ready、但页面未前台显示时处理自己的 session data 并回报结果;只有像
`pomodoro.start` 一样把“用户已进入前台专注模式”作为业务前提的 Tool,才使用 `foreground_required`
`pomodoro.interrupt` 则为 `activation_not_required`,以保证它永不因自身调用而唤醒 Pomodoro。
启动状态属于 Runtime workspace / lifecycle 事实,不写入 MiniApp 的业务 JSON 数据,也不等于 Pomodoro operation。
Pomodoro 的具体顺序如下:
```text
Agent envelope
→ ToolRouterEnvelope / conversation / Inventory / Manifest / input schema 校验
→ Runtime:按 call_id 查重
→ Runtime:检查同会话 focusing / starting slot
├─ 已有 focusing:持久化稳定拒绝 active_focusing_operation,结束
├─ 已有 starting:持久化稳定拒绝 focus_start_in_progress,结束
└─ 无:一次事务创建 instance、app_session、activation(starting)、workspace、accepted receipt/outbox
→ AppOrchestrator:请求前台化 Pomodoro
→ Host:确认 Surface 已挂载且该 instance 已成为当前前台
→ Runtime:一次事务 activation(ready) + 创建 deadline operation/focusing + started receipt/progress outbox
→ Runtime:投递通用 app_session.ready lifecycle 事件;Pomodoro 自行把需要的展示数据写入 session data
```
Host 的确认不能只是 iframe / 页面对象已创建,而必须表示该 instance 已是用户当前看到的前台 App。在此确认前,
Pomodoro 没有 `operation_id`、没有 `focusing`、没有 `ends_at`,也不产生专注的 IM 静态记录。
Host 可以按 Runtime 配置进行有限重试;每次尝试和最终失败都只改变 activation 事实。重启或 Host 重连发现
`starting` activation 时,继续同一 `instance_id` 的激活尝试,不能新建第二个专注。当 Host 最终报告
`surface_activation_failed`Runtime 原子写 `failed`、持久化 start 的唯一 `not_started(focus_start_failed)` business
result / Agent result outbox、关闭 App 子会话并恢复 Interact;不创建 `interrupted` operation 或专注结果记录。
### 5.3 `pomodoro.interrupt` 顺序
```text
Agent envelope
→ ToolRouter 完成通用校验
→ Runtime 仅用 envelope.conversation_id 查找当前 starting / focusing Pomodoro
→ 有 starting:原子写 cancelled(cancelled_before_ready) + 长期 start 的 not_started result/outbox + interrupt 自己的短 result,关闭子会话,返回 focus_start_cancelled
→ 无 starting 且无 focusing:返回 no_active_focusing_operation
→ 有 focusing:原子写 interrupted(agent_interrupt) + 长 Tool result/outbox + 短 Tool receipt
→ Runtime 关闭实例、结束子会话、恢复 Interact
→ 若 Surface 仍存在,只投影终态后卸载;不等待其回执
```
Agent 不可提供 instance、operation、app scope 或其他目标 ID。`operation_already_final` 只用于已经绑定到同一
operation 的迟到/重放调用;找不到当前 `starting``focusing` 对象时返回 `no_active_focusing_operation`
### 5.4 deadline、恢复和关闭
新增 `DeadlineOperationScheduler`,由 `LineUpRuntime` 创建并只接收注入的 `now()` 与调度器适配器,便于测试。
- Runtime 打开会话、恢复 workspace、Host 重新可用和每次 scheduler tick 时,扫描当前会话所有 `focusing`
operation`now >= ends_at` 则调用同一终态 CAS。
- UI 的 `setInterval` 只用于倒计时重绘;不得触发 completion。
- 自动暗屏、锁屏、iframe Surface 重载只重挂载或重绘,不调用 interrupt。
- 返回 Interact、切换其他 App、显式 close:若 activation 仍 `starting`,调用
`PomodoroOperationService.cancelStartForLifecycle(reason)`;若已 `focusing`,调用
`PomodoroOperationService.interruptForLifecycle(reason)`;不得从 Host 直接删除 instance。
- completed 的收口顺序固定为:终态与长期 Tool result/outbox → close instance → 结束子会话 → 恢复 Interact
→ Interact 显示由 Runtime 投影的完成结果。outbox 的网络投递可以稍后重试,不阻塞 close。
- 进程完全未运行期间不承诺提醒;下一次 Runtime 恢复只按 `ends_at` 结算一次。
## 6. MiniApp SDK、Surface 与工作区
### 6.1 通用 MiniApp session data API
实现 `sdk.sessionData`。Runtime 必须从不可伪造的 SDK context 推导 `app_scope + app_session_id`MiniApp 不传入
目标 scope / session,也不能读取其他 App、其他会话、Runtime operation、outbox 或 Conversation Store。
```ts
interface MiniAppSessionDataAPI<Data extends JsonObject = JsonObject> {
get(): Promise<{ data: Data | null; revision: number }>;
replace(request: { data: Data; expected_revision: number }): Promise<{ revision: number }>;
subscribe(listener: (snapshot: { data: Data | null; revision: number }) => void): Unsubscribe;
}
```
Bridge 使用同名的稳定方法 `session_data.get``session_data.replace`,以及 Runtime → Surface 事件
`session_data.changed``get` 无参数,返回 `{ data, revision }``replace` 仅接受 `{ data, expected_revision }`
成功返回 `{ revision }`;变化事件携带新的 `{ data, revision }`。Web 与 Tauri 必须复用完全相同的请求、响应与事件
golden fixture。
同步语义按每个 `app_scope + app_session_id` 串行冻结,不能由 Web / Tauri 各自决定:
1. 从未保存过数据时,`get()` 与订阅首帧均返回 `{ data: null, revision: 0 }`
2. `subscribe(listener)` 在该子会话的串行执行器中先注册 listener、捕获当前完整 snapshot,并将该 snapshot 作为
本订阅的首次 `session_data.changed` 投递;注册成功前不得让订阅者错过已提交的 replace。
3. 每次成功 `replace` 先原子持久化 revision 加一后的完整 data,再向当时已注册的订阅者投递该完整 snapshot。对
同一个订阅,首次 snapshot 与后续事件必须按 revision 严格递增:竞争中的 replace 要么成为首次 snapshot 的版本,
要么成为紧随其后的 change,不能消失或倒序。
4. SDK 收到重复或更旧 revision 时忽略;收到比本地最后 revision 大于 1 的 snapshot,或检测到 Bridge 重连时,必须
取消旧订阅,重新 `get()`,再建立新订阅。重拉后的完整 snapshot 是新的基线。
5. `expected_revision` 不匹配只返回 `session_data_revision_conflict`;第 04 次不对 JSON 做重试、合并或协作写入
队列。本项由 TIS2-06 作为 P4 延续项处理。
因此下面这个竞态必须得到确定结果:Surface 已经得到 revision 3,另一条合法写入提交 revision 4Surface 随后
subscribe 时,其首帧必须为 revision 4;反过来若订阅先注册,则它必须先收到 revision 3、随后收到 revision 4。
`replace` 的固定验证顺序:SDK context 有效且 App 子会话仍可用 → data 是 JSON object → UTF-8 字节长度和安全
解析深度未超过 Runtime 通用配额 → expected revision 相等。稳定错误码为:
```text
session_data_unavailable
session_data_invalid
session_data_quota_exceeded
session_data_revision_conflict
```
Runtime 不校验 MiniApp 的业务 JSON Schema,不识别 `current``history` 或嵌套结构,也不直接写 Pomodoro 的
私有数据字段。Pomodoro 接收已验证的 Tool invocation 输入和通用 `app_session.ready` lifecycle 事件后,自行写入
展示所需数据;`ready_at` 是 Runtime 的激活确认时间,Pomodoro 可据此从 `duration_seconds` 派生界面倒计时。
Runtime 自己保存的 `ends_at` 和终态仍只由 Runtime operation 管理。
### 6.2 Pomodoro Surface
新增 `src/core-apps/pomodoro/pomodoro-miniapp.ts` 及对应测试。Surface 输入只应来自 SDK lifecycle、已验证的
Tool invocation 输入与自身 session data;它不读取 Runtime operation 投影。它显示:活动名称(可缺省)、
`ready_at + duration_seconds - now` 派生的剩余时间和自身展示状态;没有暂停、
继续、开始、停止、系统通知或直接 Agent 通信入口。
当收到 lifecycle `closing` 时,Surface 只停止渲染计时并等待 Runtime 卸载。完成提示属于 Interact,不在
Pomodoro 内显示。Surface 被重载时从 SDK 重新读取 session data;不得从本地 `setTimeout` 推断 operation 终态。
### 6.3 Host 工作区
`main.ts` 的 bundled App 对账逻辑加入 `pomodoro`,但应尽量抽出 Host adapter / workspace renderer,避免继续扩大
入口文件。Host 只根据 Runtime `app-lifecycle` / workspace 事件挂载、更新和卸载隔离 Surface:
- 收到 `foreground_required` activation 后,挂载 Pomodoro;仅在实际成为当前前台 App 时,向 Runtime 发出
`foreground_activated(instance_id)` 确认。iframe 创建、资源加载或后台预载都不能替代此确认;
- Runtime 进入 `focusing` 或投影 lifecycle `closing` 后:显示或卸载 Pomodoro,并恢复 Interact
- 点击/键盘返回 Interact、选择其他 App、关闭当前 App:向 Runtime 发出 host lifecycle intent,由 Runtime 决定
cancel-start 或 interruptHost 不能自行改持久化状态;
- Web Reference Host 与 Tauri Desktop Host 使用同一 Runtime、同一 Manifest、同一 Surface Bridge,不增加
Tauri 专用业务旁路。
## 7. 日志、错误与可观测性
每个状态变化写结构化 Runtime 日志。日志可记录 `conversation_id``operation_id``instance_id``call_id`
旧/新 state、原因、是否首次 CAS 成功、outbox local id;不得记录完整 IM 内容、活动文本或 Agent 推理。
必须可从日志区分:`miniapp_activation_started``miniapp_activation_ready``miniapp_activation_failed`
`miniapp_activation_cancelled``pomodoro_started``pomodoro_start_rejected``pomodoro_deadline_won`
`pomodoro_interrupt_won``pomodoro_lifecycle_won``pomodoro_terminal_lost_race``pomodoro_close_committed`
`pomodoro_recovered_due``miniapp_session_data_rejected`
## 8. 测试与验收矩阵
新增或扩展测试必须以 fake clock、内存 Storage、可控 outbox / scheduler / Surface adapter 执行;禁止依赖真实
等待 20 分钟。建议新增:
```text
src/runtime/coordination/pomodoro-operation-state.test.ts
src/runtime/coordination/pomodoro-operation-service.test.ts
src/runtime/coordination/deadline-operation-scheduler.test.ts
src/runtime/persistence/conversation-store.pomodoro.test.ts
src/core-apps/pomodoro/pomodoro-miniapp.test.ts
```
| 场景 | 必须断言 |
|---|---|
| 合法 foreground-required start | 先创建一个 `starting` instance / app_session,前台激活确认后才创建 operation / focusing / ends_at;确认前没有 result outbox 或专注 IM 记录。 |
| 前台激活最终失败 | `failed(surface_activation_failed)`,恢复 InteractAgent 得到 `focus_start_failed`;没有 operation、deadline、专注 outbox 或专注静态记录。 |
| 启动中 interrupt / 生命周期离开 | `cancelled_before_ready`,返回 `focus_start_cancelled`;没有 operation、deadline 或专注中断记录。 |
| 相同 start call 重放 | 返回原 activation / operation;没有第二个 instance、子会话、deadline 或 outbox。 |
| Runtime 启动后的 Tool 重建 | Pomodoro 未启动时,已启用 Registry Record 的两个 Agent Tool 均进入可见 InventoryInventory 不含内部 handler_key。 |
| 禁用、升级或卸载 Pomodoro | 先撤销旧 Inventory revision 中的两个 Tool;未知或旧版本的 route 被拒绝,不能由旧 Surface 继续承接新调用。 |
| Runtime-owned Tool 路由 | start 和 interrupt 都由 Registry 的显式 runtime_execution 路由到 Runtime 服务;interrupt 在 Surface 缺席时仍可完成,不存在按 Tool 字符串散落特判。 |
| operation 提交持久化失败 | operation、activation、session data、workspace、Tool record、outbox、Lifecycle 内存和 Surface 投影均保持提交前状态;不得发出成功或终态事件。 |
| operation 提交后 Lifecycle 对账失败 | 已提交 operation / receipt / outbox 不回滚、不重复;Runtime 停止新的 Surface 投影并从已保存 workspace 重新对账,Host 最终挂载结果按 TIS-05 处理。 |
| 不同 start 且已有 focusing / starting | 分别为 `active_focusing_operation` / `focus_start_in_progress`;所有持久化集合长度不变。 |
| 非法 schema、过期 Inventory、scope 错误 | 在 Router 拒绝;不创建 operation 或挂载 Surface。 |
| Agent interrupt | starting 时取消启动;focusing 时写长 Tool 一次 interrupted result、短 Tool 一次 receipt、一次 close;后者在 Surface 缺席时同样成立。 |
| deadline / interrupt / lifecycle 并发 | 恰好一个 terminal winner、一个长期 Tool result/outbox;其余为稳定回执。 |
| 自动暗屏、锁屏、Surface reload | operation 仍 focusing`ends_at` 不变,无 outbox。 |
| 返回 Interact / 切换 App / close | 以相应 reason interrupted;恢复 Interact;不得产生 `cancelled(app_closed)` 覆盖。 |
| completed | 先提交 result/outbox,后 close / 子会话只读 / Interact 显示结果;Pomodoro 不显示完成页。 |
| 刷新或重启,未到点 | 以原 `ends_at` 恢复;剩余时间不重置。 |
| 刷新或重启,已到点 | 只结算一次 completed;重复恢复不新增 outbox。 |
| session data | 当前 App / 子会话隔离、JSON / 通用配额、revision CAS、持久化和恢复均被校验;不存在数据固定为 revision 0;每次订阅先收到当前完整快照,之后严格递增,重复 / 旧 revision 忽略,跳跃 / Bridge 重连重拉并重订阅。必须覆盖 `get(revision 3) → replace(revision 4) → subscribe(首帧 revision 4)` 与反向注册顺序。Runtime 不校验 `current` / `history` 等业务字段,其他 scope 不可读写。 |
| app-ready 通用路由回归 | 以测试 MiniApp 验证:页面不在前台时,只要目标 App session 已 active / readyRuntime 就能将已验证的 method + 参数投递给它;MiniApp 自己写 session data 并回传结果。它证明未来 `todo.add` 不会被 Pomodoro 的前台规则限制。 |
| activation Agent 通知与提前调用 | 启动调用按 call_id 收到 `accepted` 与一次 `activation_ready`(失败 / 取消时收到对应受控终态);同 conversation / app scope 的 `app_ready` 调用若提前到达,先持久化排队,ready 后按到达顺序仅投递一次;失败 / 取消时不执行 handler 而各自回传稳定错误。Pomodoro 只收 `started`,不重复收 `activation_ready`;启动中 interrupt 立即处理。 |
| pending Interact | Pomodoro close 后子会话只读;问题仍在主 IM 可回答,回答不写旧子会话。 |
| Host 验收 | Web 完整 IM 流程;Tauri 至少跑 start → interrupt 或 start → deadline 的代表路径。 |
所有现有 `npm test -- --run``npm run build`、前 03 次迭代的 Tool / Surface / App 子会话回归与
`git diff --check` 必须通过。
## 9. 推荐实施顺序与完成门槛
1. 先扩展 Manifest、Tool Descriptor、golden fixture、Inventory 和 schema 测试;未通过前不写 UI。
2. 实现 ConversationStore v15、activation repository、session data repository、Pomodoro operation 状态机和所有原子竞争单元测试。
3. 实现通用 activation coordinator、`PomodoroOperationService``DeadlineOperationScheduler`,接入 ToolRouter / LineUpRuntime / outbox /
AppLifecycleManager;完成恢复与并发测试。
4. 增加 Pomodoro Manifest、受限 Surface、SDK session data Bridge 与 Host 工作区对账及 foreground activation 确认;不在 `main.ts` 放业务逻辑。
5. 接入 Interact 的完成结果投影、折叠子会话、pending interaction 回归。
6. 完成 Web Reference Host 与 Tauri Desktop Host 验收,保存可复现的测试步骤和日志证据。
进入下一项前的门槛:前一项的新增测试、全部既有测试、构建和格式检查均通过。实施完成前,不得以
真实 Audio Mode、系统通知、多个 Pomodoro、Whiteboard 工作区、Rust 迁移或应用市场功能替代本规范中的任一
验收场景。
@@ -0,0 +1,210 @@
# 04.runtime_workspace 技术实施规范评审(一)
**评审编号:** 01
**日期:** 2026-08-06
**评审对象:** [05.technical_implementation_spec.md](05.technical_implementation_spec.md)(以下简称“实施规范”)、[04.runtime_workspace.md](04.runtime_workspace.md)(产品/验收权威)、[03.design_review.md](03.design_review.md)、[03.sdk_and_coreapp.md](../03.sdk_and_coreapp/03.sdk_and_coreapp.md)、[lineup-runtime-sdk-architecture.md](../../设计/02.正式方案/lineup-runtime-sdk-architecture.md),以及当前 `src/runtime/` 的 Manifest、Registry、Tool Router、Runtime、Store、SDK Bridge 与 Lifecycle 实现。
**后续决议依据:** [运行时与智能体工具.md](../../设计/02.正式方案/运行时与智能体工具.md)
**评审方法:** 从程序员实际会触及的入口向内追踪:Manifest → Registry 投影 → Inventory / Tool Router → Runtime 持久化与终态 → SDK / Surface Bridge → Host 与恢复;只记录会让两个开发者产生不兼容实现、或无法证明本轮验收的缺口。
**总体结论:** 实施规范正确继承了已冻结的产品边界:Agent 是业务入口,Runtime 是唯一终态裁决者,Surface 只是投影,完成后立即回到 Interact,完成记录是不可变的 IM 静态快照。随后已确认五项关键决议:已启用 MiniApp 的 Agent Tool 从已验证 Registry 投影主动重建为 Runtime Tool Registry / Agent Inventory;无目标 interrupt 的新调用在没有 focusing operation 时稳定返回 no_active_focusing_operationRuntimeOperationCommitCoordinator 先原子持久化业务事实,再驱动 Lifecycle、Host 和 Surface 对账;Runtime 不向 Pomodoro 或其他 MiniApp 暴露专属 operation / instance-state API,而是向当前 MiniApp 会话提供通用、隔离、版本化的业务数据字典;所有 MiniApp 都经历统一的 `starting → ready | failed | cancelled` 启动过程,而每个 Tool 决定自己只需 App ready、还要求前台,或根本不需要 activation。Runtime 只路由已验证的 App 方法和参数,不理解其业务含义或直接写 MiniApp 数据。**本轮 TIS-01TIS-05 已全部解决。** 后续实现必须按已定契约补齐 SDK / Bridge、Host 激活确认和验收 fixture,不得把实施细节重新解释为产品分歧。
## 问题清单(Outline
状态标记:🔴 未解决,必须处理;🟡 已提出修复方向,尚未确认或未回填实施规范;✅ 已确认通过;⚪ 可延期但必须保留记录。P0~P3 必须在当前迭代处理;P4~P5 可以延期,但须记录原因与重新评估条件。
| 状态 | 优先级 | 编号 | 问题 | 当前结论 / 下一步 |
|---|---:|---|---|---|
| ✅ | P1 | TIS-01 | 已启用 MiniApp 如何将其 Agent Tool 声明注册给 Runtime,并由 Runtime 路由。 | 已确认并回填:安装时保存已验证 Manifest 的 Registry 投影;Runtime 启动和状态变化时主动重建 Tool Registry / Agent InventoryRouter 按受控 runtime_execution 路由,不以 Tool ID 散落特判。 |
| ✅ | P1 | TIS-02 | operation、snapshot、workspace、Tool receipt / outbox 与 Lifecycle 如何避免分裂。 | 已确认并回填:LineUpRuntime 拥有 RuntimeOperationCommitCoordinator;先一次持久化事实,再由 Lifecycle / Host / Surface 对账。持久化失败不改变任何层;提交后的对账或 Host 失败不回滚事实。 |
| ✅ | P1 | TIS-03 | MiniApp 如何经 SDK 读取和保存其业务数据。 | **已确认并回填评审结论:** Runtime 仅向当前 `app_scope + app_session_id` 提供通用、持久化、版本化的 JSON 数据字典;MiniApp 自行定义 `current``history` 等业务结构及其可变性。Runtime 不公开 Pomodoro operation 或专属 instance-state API。SDK / Bridge 的接口、错误码和 Web / Tauri fixture 必须按此已定方案写入实施规范。 |
| ✅ | P1 | TIS-04 | 无目标 interrupt 的新调用如何定位已结束 operation。 | 已确认并回填:同一 call_id 重放返回首次持久化 receipt;不同新调用在没有 focusing operation 时一律返回 no_active_focusing_operation,不自动绑定最近终态。 |
| ✅ | P2 | TIS-05 | 要求前台的 Tool 在 Surface 未成功显示时,是否已经开始业务 operation。 | **已确认并回填评审结论:** Runtime 为所有 MiniApp 管理 `starting → ready | failed | cancelled` activationTool 声明 `app_ready``foreground_required``activation_not_required`。Runtime 只把已验证的 method 与参数路由至 ready MiniApp,不理解其业务或直接写其数据。`pomodoro.start` 必须等 Host 确认已成为当前前台 App 才创建 focusing operation / deadline;最终失败或启动中取消均为“本次计时未开始”,不产生 interrupted operation 或专注 IM 静态记录。未来 `todo.add` 等 Tool 只需 To-do 已 active / ready、无需前台;`pomodoro.interrupt` 不会为自身调用启动或唤醒 App。 |
| ✅ | — | TIS-C1 | Agent、Runtime、Surface 的权限边界 | 规范禁止 Surface 完成 Pomodoro Tool 或直接写 Agent 协议,并保留 Runtime 唯一终态/Outbox 所有权;与正式 SDK 架构一致。 |
| ✅ | — | TIS-C2 | deadline、退出、完成与恢复的产品语义 | 规范正确使用 `ends_at`,区分暗屏/Surface reload 与离开工作区,并定义 completed 后立即 close → Interact。 |
| ✅ | — | TIS-C3 | 单会话专注、MiniApp 业务数据与 IM 静态记录的边界 | 规范正确要求一个 focusing operation、重复 start 稳定拒绝;Runtime 只管理通用数据容器的 revision、配额与隔离,MiniApp 自行决定业务数据结构与可变性;完成记录作为 IM 静态快照保留。 |
| ✅ | — | TIS-C4 | 范围与验证策略 | 当前以 IM 验收,Voice、系统通知、多个计时器、Whiteboard 工作区和 Rust 迁移未被重新纳入;fake clock 和 Web/Tauri 验收方向正确。 |
## 通过项与一致性证据
### TIS-C1:权限边界没有倒退
实施规范第 1、3、5、6 节将 Agent Tool 的输入校验、deadline、operation 终态、outbox、焦点与 close 交给
RuntimePomodoro Surface 只显示投影、保存展示快照,不能调用 `sdk.tools.complete/fail/cancel` 来完成 Pomodoro
Tool。这符合正式 SDK 架构中“instance snapshot 不能完成 Tool、写 outbox、改变焦点或绕过 lifecycle”的规则,
也避免把可信业务逻辑重新塞入 `main.ts` 或 iframe。
### TIS-C2:计时和完成收口与主定义一致
规范使用绝对 `ends_at`,并要求 Runtime 恢复时结算已到期 operation;UI 定时器只重绘。这与主定义的
`remaining_seconds = ends_at - now` 一致。deadline、Agent interrupt 与 lifecycle close 竞争同一终态,completed
后依次提交结果/outbox、关闭 Pomodoro、结束子会话、恢复 Interact,亦没有重新引入 App 内完成页或系统通知。
### TIS-C3Runtime 事实、MiniApp 业务数据与 IM 静态记录各自归位
Runtime 内部的 operation 保存 deadline、终态竞争、Tool receipt、outbox 和恢复所需的可靠性事实;它不是
Pomodoro 需要读取或修改的专属数据模型。Runtime 同时为当前 MiniApp 会话保存一份通用 JSON 数据字典,但不认识
其中的 `current``history` 或其他业务字段。MiniApp 自己决定这些字段的组织、是否追加历史以及业务上是否允许
修改;Runtime 只负责作用域隔离、revision、持久化、恢复和技术安全限制。一次专注完成后写进 Interact / IM 的
记录是已发生事实的静态副本,不会随 MiniApp 后续更新自身业务数据而改变。
## TIS-01Manifest 信息在 Registry / Router 边界丢失
**已解决(2026-08-06,收敛决议)。** 实施规范第 3.1.1 节已经冻结:每个已安装 MiniApp 在安装时将已验证
Manifest 投影保存进 RegistryRuntime 启动以及 App / Host / 策略状态变化时,从全部已启用 Record 主动重建
Runtime Tool Registry 和 Agent Inventory。Router 只读取投影后的 RegisteredAgentTool,并根据受控
runtime_execution 路由至 RuntimeOperationHandlerRegistry 或 MiniApp SDK;内部 handler_key 不会发布给 Agent
也不允许 Manifest 携带可执行代码。
评审初稿曾要求在 `MiniAppManifestV1` 中加入 `instance_state`,并在第 3.2 将 `pomodoro.start` 直接等同于
Runtime-owned deadline operation;这两个前提已被后续 TIS-03 / TIS-05 决议替换为通用 session data 和先 activation、
后创建 operation 的模型。
但当前 `CoreAppRegistry.installMiniAppManifest()` 只把 `app_scope`、version、permissions 和 Tool 的 name、handling、
input/output schema、target、permissions 投影到另一套 `AppManifest/AppToolDefinition`;新增的 feature、state 声明和
`runtime_operation` 都会丢失。`ToolRouter` 只读取后者,并且其 `miniapp` 路由会被 `LineUpRuntime` 交给普通
`MiniAppToolStateMachine`,等待目标 instance / Surface。按现有路径实现,`pomodoro.interrupt` 会退化为普通 direct
Tool,和“不唤醒、不等待 Surface、Runtime 自己收口”的已冻结语义冲突。
以下为原评审识别出的风险和收敛方向;现已采用其中“Registry 保留验证后的运行时元数据,并由显式 Route
分支交给 Runtime 内置服务”的方案:
```text
MiniAppManifestV1
→ RegisteredMiniAppRecord 中保存已验证的 Agent Tool、activation requirement 与 runtime_execution 投影
→ Inventory 只发布 Agent 所需 Tool Descriptor,不泄露内部 execution 或 MiniApp 私有数据
→ ToolRouter 返回显式 runtime Route(包含已验证 descriptor 和内部 execution
→ LineUpRuntime 的 RuntimeOperationHandlerRegistry 按受控 handler_key 调用相应 Runtime 服务
```
Pomodoro 的 start 固定路由到 pomodoro.start_deadline.v1interrupt 固定路由到
pomodoro.interrupt_current.v1。handler_key 只能由 Runtime 内置注册;MiniApp Manifest 不能传递函数、URL
或可执行代码,LineUpRuntime 也不能在多处分支以 tool_id 字符串猜测行为。
## TIS-02:原子提交的拥有者与失败处理未冻结
**已解决(2026-08-06,收敛决议)。** LineUpRuntime 拥有唯一的 RuntimeOperationCommitCoordinator。
PomodoroOperationService、deadline scheduler、Tool Router 和 Lifecycle 事件只提交业务变更请求;协调器在内存副本
校验后一次持久化 operation、snapshot、workspace、Tool receipt 和 outbox,再驱动 Lifecycle / Host / Surface 对账。
持久化失败则所有层保持旧状态;提交后的对账或 Host 失败不回滚业务事实,而是按已保存 workspace 重新对账,并交给
TIS-05 的挂载重试 / 最终失败规则收口。
实施规范第 4.3 要求在一次 `commitPomodoroMutation` 中更新 operation、snapshot、workspace、MiniApp Tool projection
和 outbox。然而当前实现中 `ConversationStore` 保存 `app_workspace` 和 outbox`AppLifecycleManager` 在内存中
保存 instance / focus`LineUpRuntime` 目前的 `orchestrator.launch/close``persistWorkspace()`
`replaceMiniAppToolCalls()``queueProtocolAction()` 是多个独立 mutation / persist。若服务先创建 operation,再
`AppOrchestrator` 前台化失败,或先 close Lifecycle、再写 outbox 失败,恢复时会得到彼此不一致的事实。
本轮已确定由 `LineUpRuntime` 内部的 `RuntimeOperationCommitCoordinator` 取得 Store 快照,并在提交成功后
驱动 `AppLifecycleManager` 对账:
```text
读取 Store snapshot
→ 在副本中校验 CAS 并计算 next operation / snapshot / workspace / receipt / outbox
→ 一次 Store.persist(next runtime snapshot)
→ 用已保存 workspace 对账 Lifecycle 内存投影
→ 才 emit lifecycle / state / surface 事件,并执行 Host 副作用
```
若 Store 持久化失败,Lifecycle 必须仍保持旧 snapshot,且不得 emit 或发送 outbox。提交成功后若 Lifecycle
对账或 Host 操作失败,Runtime 不得回滚已经提交的 operation;它停止新的 Surface 投影、从已保存 workspace
重新对账,并进入 TIS-05 定义的可恢复启动 / 最终失败流程。
## TIS-03MiniApp 如何经 SDK 读取和保存自己的业务数据
**已解决(2026-08-06,收敛决议);实施契约待回填。**
此前问题把 `instanceState``operations` 当作 SDK 的两个能力,其中 `operations` 还要求向 Pomodoro Surface
公开 Runtime 的 operation 投影。这个方向已否决:若 Runtime 为 Pomodoro 的 `current``history` 或 operation
提供专属 API,它就会开始理解并适配每一个 MiniApp 的业务模型,失去通用 Runtime 的边界。
本轮确认的分工如下:
```text
Runtime 内部 operation
= deadline、终态竞争、Tool result / receipt、outbox、恢复等可靠性事实
= 不作为 Pomodoro 专属 SDK 数据模型公开
MiniApp session data
= 当前 MiniApp 自己定义和解释的通用 JSON 数据字典
= 可组织 current、history、统计、UI 所需状态等任意业务结构
IM / Interact record
= 已发生结果的静态、不可变副本
= 不随着 MiniApp 后续修改自身业务数据而变化
```
Runtime 应从不可伪造的 SDK context 取得 `app_scope + app_session_id`,并只向当前 MiniApp 暴露该作用域的一份
通用、持久化、版本化 JSON 数据字典。MiniApp 不能传入或伪造 App / Session 标识,不能读取其他 MiniApp 或其他
会话的数据,也不能读取 Runtime operation Store、outbox 或 Conversation Store。
MiniApp 自己决定数据结构、`current` / `history` 的含义、是否保存历史、是否提供历史给 Agent 分析,以及业务上
哪些内容可变或只追加。Runtime 不校验这些业务语义,但仍应施加通用技术限制:只接受 JSON、数据与单次写入的
大小配额、安全解析深度,以及基于 revision 的 CAS 以避免并发覆盖。
实施规范需要据此提供一个通用的当前会话数据接口;名称暂定可使用更清楚的 `sdk.sessionData`,而不是会与文件或
Artifact 存储混淆的泛称 `sdk.storage`。其最小能力为读取、带 `expected_revision` 的整体替换,以及订阅当前数据
版本的变化。接口和 Web / Tauri Bridge 还须写清方法名、请求响应 JSON、CAS 冲突错误码、初始化数据投影和共用
golden fixture;这些是已确认方案的实施细节,不再是 Runtime 与 MiniApp 的边界选择。
## TIS-04:无目标 interrupt 的 `operation_already_final` 回执不可判定
**已解决(2026-08-06,收敛决议)。** 实施规范第 5.3 节已冻结:同一个 interrupt call_id 的重放返回首次
持久化 receipt;不同的新 interrupt call 在当前 conversation 没有 focusing operation 时,一律返回
no_active_focusing_operation。Runtime 不查询或自动绑定最近的终态 operationoperation_already_final 只适用于
已经绑定到同一 operation 的迟到或重放调用。
实施规范和主定义均要求 `pomodoro.interrupt({})` 不接受 `operation_id`,并列出
`interrupted | no_active_focusing_operation | operation_already_final`。但一个不同的新 interrupt call 到达时,如果
当前 conversation 已不存在 focusing operationRuntime 没有携带目标 ID 的输入,不能知道调用者要指的是最近
completed/interrupted 的哪一个 operation。将任何“最近 operation”自动绑定会让旧自然语言消息误伤或得到不稳定
回执;只按当前 active slot 查找则永远只能返回 `no_active_focusing_operation`
原评审提出的二选一规则如下;现已采用第一项:
1. **推荐:** 同一 interrupt `call_id` 的重放返回首次持久化 receipt;不同的新 call 在没有 focusing operation
时一律返回 `no_active_focusing_operation`,删除或不再承诺新 call 的 `operation_already_final`;或
2. 为 interrupt 增加 Runtime 生成、Agent 可从先前 start result 得到的受控 operation reference,并定义可接受
的历史状态和 scope 校验。这会改变当前空对象输入,需要新的产品/主定义决议。
在没有这项决议前,开发者会在“查询最近终态”和“只查询运行中 slot”之间自行选择,重放、恢复和自动化验收
无法一致。
## TIS-05MiniApp 启动就绪与前台激活
**已解决(2026-08-06,收敛决议)。**
此前实施规范把“Host 挂载失败”错误地只看成 Pomodoro operation 创建后的 UI 副作用,因此同时保留了“回滚”与
“interrupted 收口”两种互斥行为。用户已经确认:是否进入前台并非所有 MiniApp Tool 的统一前提;它是具体 Tool
的业务语义。Runtime 因此必须先管理通用 activation,而不是先假设业务 operation 已开始。
```text
所有 MiniAppstarting → ready | failed | cancelled
foreground_requiredpomodoro.start
= Host 确认 Surface 已成为用户当前前台 App,才 ready
= ready 后才创建 Pomodoro focusing operation 和 deadline
app_ready(未来 todo.add
= To-do 已 active / readyRuntime 可以取得当前 App session 并路由 method + 参数
= 不要求创建或显示 Surface;To-do 自己执行业务并写 session data
activation_not_requiredpomodoro.interrupt
= 不为该调用创建或唤醒 MiniApp,只操作已有 Runtime 事实
```
`pomodoro.start` 在前台确认前只持久化 App 子会话、instance 与 `starting` activation;没有 operation_id、
`focusing``ends_at`、专注 outbox 或 IM 静态记录。Host 的确认必须表示“已成为当前前台 App”,而不只是 iframe
或页面对象已创建。Runtime 可对同一 `instance_id` 有限重试并在重启 / Host 重连后继续;最终失败时写
`failed(surface_activation_failed)`,回传 `focus_start_failed`,关闭子会话并恢复 Interact。用户 / Agent 在启动中
说停止、或生命周期离开时写 `cancelled_before_ready`,回传 `focus_start_cancelled`。两者都表示“本次计时未开始”,
不产生 `interrupted` operation 或专注结果记录。
只有已经 `focusing` 的 Pomodoro 才会因返回 Interact、切换 App、关闭或 Agent interrupt 进入
`interrupted`。这既保留了 Pomodoro 的“前台即专注模式”语义,也使未来 To-do 等后台业务工具不被错误地要求打开 UI。
## 评审关闭条件
1. 已定的通用 session data、activation / foreground confirmation 方案须在 [05.technical_implementation_spec.md](05.technical_implementation_spec.md)
中以 SDK / Bridge 方法、请求响应 JSON、CAS 错误码、Host fixture 与 Web / Tauri 验收落实;并同步主设计中的旧
`instanceState` / operation projection 表述。这是已决方案的实施工作,不再构成未解决评审项。
2. 所有补充都必须保持 TIS-C1~TIS-C4 已通过边界;不得借机加入 Voice、系统通知、多计时器、Whiteboard 工作区
或 Rust 迁移。
@@ -0,0 +1,202 @@
# 04.runtime_workspace 技术实施规范评审(二)
**评审编号:** 02
**日期:** 2026-08-06
**评审对象:** [04.runtime_workspace.md](04.runtime_workspace.md)(产品/验收权威)、[05.technical_implementation_spec.md](05.technical_implementation_spec.md)、[06.technical_implementation_spec_review.md](06.technical_implementation_spec_review.md),以及 [运行时与智能体工具.md](../../设计/02.正式方案/运行时与智能体工具.md)、[LineUp Runtime 与 App SDK 架构方案](../../设计/02.正式方案/lineup-runtime-sdk-architecture.md)、[LineUp App 最终设计方案](../../设计/02.正式方案/app_final_design.md)。
**评审方法:** 由独立子代理只读复核当前文件,不沿用上一轮“已解决”结论;重点检查统一 activation、Agent Tool 路由、SDK session data、Runtime 原子提交,以及 Web / Tauri 是否能按同一契约实现。
**总体结论:** Pomodoro 前台语义、`activation_not_required`、通用 session data 和“先持久化、后对账”已经对齐。本轮进一步确认:Runtime 只按已验证的 App、方法、参数和当前 ready App session 做路由,不理解 MiniApp 的业务参数或直接写其数据;前台只是个别 Tool 的额外业务前提。MiniApp 通过 SDK 声明 Agent Tool 与 handler,构建时自动生成 Manifest 的不可执行 Tool 描述,开发者无需维护第二份手写 Manifest。长期 Tool 已分开协议回执 / 进度与最终业务结果;activation ready 也成为 Runtime 持久化、向 Agent 可见的进度事实,同时不把提前到达的合法调用丢给 Agent 处理。session data 已冻结 revision 0、订阅首帧、严格递增和重连对齐语义。因此 TIS2-01TIS2-05 已解决;TIS2-06 是有明确触发条件的 P4 延续项。**本轮没有剩余 P0~P3,第 04 次技术规范可以进入实现阶段。**
## 问题清单(Outline
状态标记:🔴 未解决,必须处理;🟡 已提出修复方向,尚未确认或未回填实施规范;✅ 已确认通过;⚪ 可延期但必须保留记录。P0~P3 必须在当前迭代处理;P4~P5 可以延期,但须记录原因与重新评估条件。
| 状态 | 优先级 | 编号 | 问题 | 证据与当前结论 / 下一步 |
|---|---:|---|---|---|
| ✅ | P1 | TIS2-01 | Runtime 如何在页面未前台显示时调用 MiniApp 的方法。 | **已确认并回填:** Runtime 只验证并路由 `app_scope + method + 参数`,解析或建立当前 App session 并等待 MiniApp active / ready;随后将调用投递给 MiniApp。MiniApp 自己理解方法和参数、修改 session data、返回结果。`app_ready` 不要求前台;`foreground_required` 在其上额外要求前台;`activation_not_required` 不启动 App。具体无界面执行容器是后续实现选择,不改变这条 Runtime / MiniApp 契约。 |
| ✅ | P1 | TIS2-02 | MiniApp 如何声明面向 Agent 的方法,并让 Runtime 路由到正确的处理逻辑。 | **已确认并回填:** MiniApp 在 SDK 中用 `defineAgentTools(...)` 声明公开 method、schema、activation requirement 和 handler;构建自动生成 Manifest 的不可执行 Tool 描述,开发者不写第二份 Manifest。Runtime 只按已验证 Tool 路由到 ready App session 的统一 SDK 接收入口;SDK 在 Bundle 内按 method 分发本地 handler。Registry 不保存私有函数 target。 |
| ✅ | P1 | TIS2-03 | 长期 `pomodoro.start``starting` / `focusing` 时的 Agent 回执与冻结 result schema 不一致。 | **已确认并回填:** `accepted / starting``started` 是持久化、可重放的协议回执 / 进度;只有 `completed``interrupted``not_started` 是符合 output schema 的唯一最终业务结果。Agent 只能在 `started` 后说“已开始计时”。同一 call_id 返回最新已持久化回执,终态后返回最终结果,不追加 outbox。 |
| ✅ | P1 | TIS2-05 | Agent 如何得知 MiniApp 已启动就绪,以及是否必须等待该通知后才能发送后续调用。 | **已确认并回填:** Runtime 将 activation 的 `starting / ready / failed / cancelled` 作为与触发 call_id 关联的受控协议进度通知 Agent;Agent 收到 `activation_ready` 后再编排依赖调用是推荐方式,但不是硬门槛。Runtime 持久化并排队提前到达的 `app_ready` / `foreground_required` 调用,ready 后按顺序投递;失败 / 取消则受控拒绝。`activation_not_required` 仍立即执行。Pomodoro 的 `started` 包含 ready,不重复发 `activation_ready`。 |
| ✅ | P2 | TIS2-04 | `sessionData.get()``subscribe()` 之间可能丢失一次更新,首帧与 revision 语义未冻结。 | **已确认并回填:** 空数据固定 revision 0;订阅在同一 App 子会话的串行顺序中注册并先收到当前完整快照,后续 revision 严格递增;因此读与订阅之间的 replace 要么成为首帧、要么紧随其后。重复 / 旧 revision 忽略,跳跃 / Bridge 重连重新 get 并订阅;Web / Tauri 共享 fixture。revision CAS 不做业务自动合并,TIS2-06 延续。 |
| ⚪ | P4 | TIS2-06 | 多入口同时修改 session data 时,是否建立 Runtime 通用 mutation queue。 | **延续项,不属于第 04 次实现:** 当前 Pomodoro 没有修改型 UI Action,保持 `get / replace / subscribe + revision CAS` 即可。首次实现“Agent Tool 与 UI Action 都可修改同一 App 子会话”的 MiniApp(例如可手动修改的 To-do 或 Whiteboard)前,必须先冻结统一 mutation queue、操作顺序、幂等恢复与业务冲突边界;离线多端协同 / CRDT 另行设计。 |
| ✅ | — | TIS2-C1 | Pomodoro 的前台即专注语义 | 前台确认后才创建 operation;仅挂载 / 预载不等于开始,失败 / 取消不产生 operation 或 IM 静态记录。 |
| ✅ | — | TIS2-C2 | `pomodoro.interrupt` 不唤醒 App | `activation_not_required` 可取消已有 starting 或中断已有 focusing,但不会因停止指令新开 App。 |
| ✅ | — | TIS2-C3 | session data、operation 与 IM 记录的职责边界 | session data 按 `app_scope + app_session_id` 隔离并由 MiniApp 自解释;operation / outbox 是 Runtime 事实;IM 是不可变静态结果。 |
| ✅ | — | TIS2-C4 | 原子提交和三条入口 | Runtime 先持久化 operation、activation、session data、workspace、receipt / outbox,再让 Lifecycle / Host / Surface 对账;Agent Tool、UI Action、Host 生命周期意图不混同。 |
## 本轮已通过的边界
### TIS2-C1Pomodoro 只有进入前台才真正开始
`pomodoro.start` 被接受后,Runtime 可以创建 instance、App 子会话和 `starting` activation;但 Host 必须明确确认该 instance 已成为用户当前看到的前台 App,Runtime 才创建 `focusing` operation 与 `ends_at`。iframe 创建、资源加载或后台预载都不是这个确认。最终激活失败、启动中取消或用户在启动中离开,都表示“本次计时未开始”,不产生 `operation_id`、deadline、专注中断记录或专注 IM 静态记录。
### TIS2-C2:停止指令不会反而打开 Pomodoro
`pomodoro.interrupt` 只查询调用所在 conversation 已有的 `starting``focusing` Pomodoro:前者取消启动,后者中断 operation;两者都不会为这条停止指令创建 instance、Surface 或前台焦点。
### TIS2-C3:通用 session data 不泄露 Runtime 业务事实
SDK 从不可伪造的 context 推导 `app_scope + app_session_id`,MiniApp 只能读取和替换自己的 JSON 数据字典。Pomodoro 可自行组织活动、显示信息、current 或 historyRuntime 不识别这些字段,也不直接向字典写 Pomodoro 私有业务数据。deadline、终态竞争、Tool receipt、outbox 和恢复继续属于 Runtime operation;一次完成后写入 Interact / IM 的记录是独立的不可变静态副本。
### TIS2-C4:持久化仍先于 Host 副作用
`RuntimeOperationCommitCoordinator` 的职责没有被 activation 新模型削弱:持久化失败时 activation、operation、session data、workspace、receipt、outbox、Lifecycle 和 Surface 均保持旧状态;持久化成功后 Host / Surface 对账失败时,Runtime 从已保存事实恢复或重试,不能倒写或伪造终态。
## TIS2-01Runtime 如何调用未前台显示的 MiniApp 方法
**已解决(2026-08-06,收敛决议)。**
此前问题错误地把重点放在“后台执行容器究竟是 Worker、iframe 还是别的实现”。用户已明确 Runtime 的架构职责:
它接收 Agent Tool 后只根据 schema 验证调用结构,找到 `app_scope` 下的 method 与参数,确认目标 MiniApp 当前
session 是否 active / ready,然后把调用路由给 MiniApp。Runtime 不理解参数字段和数值的业务意义,也不直接写
MiniApp 的数据;MiniApp 才知道 `todo.add` 表示新增待办,并自行通过 session data 完成业务修改。
```text
Agent Tool Call
→ Runtime:验证 Tool、schema、scope、幂等
→ Runtime:解析或建立目标 App session,等待 MiniApp ready
→ Runtime:投递 method + 已验证参数
→ MiniApp:理解业务、修改自己的 session data、返回结果
→ Runtime:验证、持久化 receipt / result,并回传 Agent
```
三个 activation requirement 的意义随之明确:
```text
app_ready
= MiniApp 已 active / readyRuntime 可路由方法调用;不要求前台
= 未来 todo.add
foreground_required
= app_ready,且 Host 已确认 MiniApp 成为当前前台
= pomodoro.start;前台是“开始专注”的业务前提
activation_not_required
= 不为这次调用启动或唤醒 MiniApp,只操作已有 Runtime 事实
= pomodoro.interrupt
```
运行中但不在前台的 MiniApp 如何落到具体 Host 容器,是后续实现层的选择;它不得改变上述统一的 Runtime 路由、
MiniApp 业务处理与 SDK 回传契约。本轮不再把“Runtime 是否直接写 To-do 数据”或“是否前台”误当作这个问题。
Runtime 同时会把 activation 的受控状态回传给触发启动的 Agent:`accepted / starting``activation_ready`,或
`activation_failed / activation_cancelled`。这条通知帮助 Agent 在需要时顺序编排后续调用,但 Runtime 不把正确性
交给 Agent:如果合法的 `app_ready` / `foreground_required` 调用在 ready 前已经到达,它会被持久化到同一 activation
等待队列,ready 后按顺序投递;`activation_not_required` 则保持立即执行。
## TIS2-02SDK 声明 Agent Tool,构建自动生成 Manifest
**已解决(2026-08-06,收敛决议)。**
MiniApp Manifest 的 Tool 区块仍是 Runtime 安装、Registry、Inventory 和 Agent 调用所依赖的能力说明书;但它不应是
开发者手写和维护的第二份图纸。每个 MiniApp 在自身代码中通过 SDK 的 `defineAgentTools(...)` 声明公开 Tool、
输入 / 输出 schema、activation requirement 和 `miniapp_sdk` Tool 的本地 handler。构建阶段从该声明自动生成
Manifest 的不可执行 Tool 投影,并与 Bundle 一同校验、签名和安装。
```text
MiniApp 源码中的 SDK Tool 声明
├── 供 TypeScript 校验 handler、输入和输出
├── 供 Bundle 内 SDK 建立 method → handler 本地映射
└── 构建自动生成 Manifest tools 描述
→ Runtime 安装时验证并投影 Registry
→ Runtime 发布 Agent Inventory
```
运行时对普通 MiniApp Tool 的路径固定为:
```text
Agent 调用 app_scope + method + params
→ Runtime 按生成 Manifest 校验、定位 ready App session
→ Runtime 投递到该 session 的统一 SDK Tool 接收入口
→ SDK 按 method 调用 Bundle 内已声明 handler
→ MiniApp 处理业务、写 session data、返回受控结果
```
Runtime 不保存、更不调用 MiniApp 私有函数 targetManifest 不包含函数、URL、回调或其他可执行内容。`delivery =
miniapp_sdk` 表示上述固定入口;`delivery = runtime` 才使用 Runtime 内部 handler key,例如第 04 次 Pomodoro
的 deadline / interrupt 可靠性操作。构建必须拒绝重复 method、非法 schema、miniapp_sdk Tool 缺少 handler 或
handler 与 schema 不匹配;runtime Tool 则必须匹配 Runtime 内置的受控 execution。
## TIS2-03:长期 Tool 的协议 receipt 与业务 result 分层
**已解决(2026-08-06,收敛决议)。**
`pomodoro.start` 的业务 result schema 只允许最终业务结果:已完成、已中断或未开始。同一个 call_id 在 `starting`
`focusing` 时不能伪造终态,也不能把 activation / operation 对象塞进业务 result。因此这条调用固定分成两层:
```text
Tool protocol receipt / progress
= accepted / startingRuntime 已收到请求,正在把 Pomodoro 打开到前台
= startedHost 已确认前台,Runtime 已创建 operation,计时真实开始
= 对 Agent 只包含 call_id、app_scope、受控状态,以及 started 后必要的时间事实
= 不属于 Tool 的业务 result_schema;每个阶段各自只写一次可重试 outbox
Tool business result
= 已完成、已中断、未开始等业务终态
= 必须符合 Manifest result_schema
= 每次 Tool Call 只持久化并回传一次
```
首次 start 与开始成功的原子提交分别写 `accepted / starting``started` 协议消息。相同 call_id 在 `starting` / `focusing`
时返回已持久化的最新协议回执;仅在进入 completed、interrupted 或 not_started 后返回已持久化的业务 result。`accepted`
只表示 Runtime 接到请求,不代表 Agent 可以对用户说“已开始计时”;真正开始仍以 Host 前台确认和 operation 创建为准。
## TIS2-05activation ready 如何通知 Agent、提前调用由谁负责
**已解决(2026-08-06,收敛决议)。**
所有 MiniApp 都有 `starting → ready | failed | cancelled` activation。Runtime 在持久化状态变化后,通过与原启动
call_id 关联的 protocol receipt / progress 向 Agent 发出 `accepted / starting``activation_ready`
`activation_failed / activation_cancelled`MiniApp、Surface 与 Host 都不得直接通知 Agent。通知只携带 call_id、
app_scope、状态和受控失败原因,不能把 instance / App 子会话变成 Agent 可指定的控制目标。
Agent 在收到 `activation_ready` 后再发送依赖该 App 的后续指令,是推荐的编排方式,但不是请求能否接收的硬门槛。
Runtime 自己持久化并排队在 ready 前到达的合法 `app_ready` / `foreground_required` Tool,ready 后按到达顺序投递;
若启动失败或取消,Runtime 不执行业务 handler,而为每个等待调用回传稳定错误。`activation_not_required` 保持立即处理,
因而启动中的 `pomodoro.interrupt` 可以直接取消当前启动。Pomodoro 的 `started` 同时表达 activation ready、前台确认与
operation 已开始,故不额外向 Agent 发送重复的 `activation_ready`
## TIS2-06:通用 mutation queue 与多入口协作写入
**优先级:P4(延续项,不在第 04 次迭代实现)。**
To-do 若只展示数据、所有修改都经 Agent Tool 进入,实际上天然只有一个业务写入来源;画板若同时允许 Agent Tool
和用户 UI Action 修改同一份 session data,则需要 Runtime 为同一个 `app_scope + app_session_id` 建立统一、持久化、
幂等的 mutation queue,避免两个来源各自基于旧 JSON 快照整体覆盖。该机制应让 Agent Tool、UI Action 与受控
Runtime 动作都以“业务操作”而非完整 JSON 快照进入同一顺序,再由 MiniApp 在最新数据上实现自己的业务效果。
这个方向是通用 Runtime 能力,不应要求开发者预先选择“单点 / 协作”模式;实际有哪些写入入口,是 MiniApp 已经
声明的 Tool / UI Action 自然决定的。但实现它会同时引入写入 handler 上下文、队列持久化与重启恢复、UI Action 写入
协议、顺序 / 幂等规则,以及未来离线多人协作与 CRDT / OT 的清晰边界。第 04 次只验证没有修改型 UI Action 的
Pomodoro,不能为此提前扩大范围。
本迭代继续使用通用 `sdk.sessionData.get / replace / subscribe` 与 revision CASCAS 只防止旧版本覆盖,不承诺
自动合并业务冲突。**重新评估触发条件:** 在开始任何一个“Agent Tool 与 UI Action 都能修改同一 App 子会话”的
功能之前,例如手动编辑 To-do 或 Whiteboard 的首个交互式绘制流程,必须先将本项提升为该迭代的 P1 并完成设计。
## TIS2-04session data 首帧与 revision 同步窗口
**已解决(2026-08-06,收敛决议)。**
本项只处理 Surface 读取自身 session data 时的版本同步,不处理 Agent 与 UI 同时修改数据的业务合并;后者已作为
TIS2-06 的 P4 延续项。若 Surface 先 `get()`、再 `subscribe()`,两次调用之间发生的 replace 不能被遗漏。第 04 次
冻结以下最小且跨 Host 一致的规则:
```text
不存在 session data
→ get() 返回 { data: null, revision: 0 }
subscribe(listener)
→ 在同一 app_scope + app_session_id 串行顺序中注册
→ 注册后先投递当前完整 { data, revision }
→ 之后投递 revision 严格递增的 session_data.changed
```
因此,若 replace 在注册前提交,首帧就是新 revision;若注册先完成,则新 revision 作为后续事件抵达。SDK 收到重复或
旧 revision 时忽略;若发现 revision 跳跃或 Bridge 重连,取消旧订阅、重新 `get()` 并建立新的订阅。Web / Tauri 共享
golden fixture,且必须测试“get(revision 3) 与 subscribe 之间发生 replace(revision 4)”以及反向注册顺序的场景。
`expected_revision` 冲突只返回稳定错误 `session_data_revision_conflict`,第 04 次不自动合并 JSON。
## 本轮关闭条件
1. TIS2-01TIS2-05 已回填主迭代定义、实施规范与正式方案;TIS2-06 是已记录的 P4 延续项,在其重新评估触发条件出现前不阻塞第 04 次验收。
2. 第 04 次进入实现后,必须以本文件和实施规范的 Web / Tauri fixture、单元测试、构建与代表性 Host 流程验证这些决议;实现完成后进行下一轮独立验收评审。
@@ -0,0 +1,117 @@
# 04.runtime_workspace 验收复核(第 08 轮)
**评审轮次:** 08
**日期:** 2026-08-06
**主定义:** [04.runtime_workspace.md](04.runtime_workspace.md)
**实施规范:** [05.technical_implementation_spec.md](05.technical_implementation_spec.md)
**评审方式:** 当前工作树实现核对 + 全量 gate + 新鲜浏览器验收 + 独立只读复核
**独立复核:** `Rawls`fresh read-only acceptance audit
**总体结论:** 当前实现与自动化回归已覆盖主实现路径,`RW-A10` 已关闭;仍有 `1` 个未关闭 `P2`,本轮不能宣布最终验收通过。
## 问题清单(Outline
状态图例:`🔴` 未解决|`🟡` 进行中 / 证据不足|`✅` 已关闭|`⚪` 延续观察
| 状态 | 优先级 | ID | 问题 | 当前结论 / 下一步 |
|---|---|---|---|---|
| ✅ | P2 | RW-A10 | “未到点重启恢复”缺少直接、场景级自动化证明。 | 已关闭。`runtime-workspace-host` 恢复顺序测试、Pomodoro remount 倒计时测试、Runtime overdue restart 测试和组合恢复测试已共同覆盖“未到点 / 已到点”恢复矩阵。 |
| 🔴 | P2 | RW-A11 | Tauri Desktop Host 尚缺一条可审计的代表性业务流验收证据。 | 仍未关闭。当前只有 fresh browser 代表路径证据,以及 prior-round / exploratory desktop window visible evidence;还缺少 Tauri Host 上 `start → interrupt``start → deadline` 的业务闭环证据。 |
## Gate 结果
- `npm test -- --run`:通过,`35` 个文件 / `195` 个测试全部通过。
- `npm run build`:通过。
- `git diff --check`:通过。
- 独立复核聚焦套件:`pomodoro-miniapp.test.ts``runtime-workspace-host.test.ts``lineup-runtime.test.ts` 通过;独立 reviewer 报告其本轮复核为 `3` 个文件 / `56` 个测试通过。
## 本轮确认
### RW-A10 已关闭
本轮重新核对后,`RW-A10` 不再成立。当前代码和测试已直接覆盖规范要求的“刷新或重启,未到点 / 已到点”恢复矩阵:
- 恢复装配顺序已抽到 [tauri/src/runtime/coordination/runtime-workspace-host.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/coordination/runtime-workspace-host.ts:15),先 `applySnapshot`,再挂载已恢复 surface,再 `reconcileBundledMiniApps`,最后恢复执行摘要。
- [tauri/src/runtime/coordination/runtime-workspace-host.test.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/coordination/runtime-workspace-host.test.ts:32) 固定恢复顺序。
- [tauri/src/runtime/coordination/runtime-workspace-host.test.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/coordination/runtime-workspace-host.test.ts:55) 直接覆盖“登录恢复路径 -> Pomodoro remount -> 按持久化 `ends_at` 继续倒计时”。
- [tauri/src/core-apps/pomodoro/pomodoro-miniapp.test.ts](/home/gao/Development/lineup/lineup-app/tauri/src/core-apps/pomodoro/pomodoro-miniapp.test.ts:135) 固定 MiniApp remount 后继续按原 `ends_at` 派生剩余时间。
- [tauri/src/runtime/coordination/lineup-runtime.test.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/coordination/lineup-runtime.test.ts:733) 固定“已到点后重启只结算一次 completed,不重复追加 outbox”。
独立 reviewer `Rawls` 的本轮结论也明确将 `RW-A10` 关闭。
### RW-A11 仍未关闭
规范的验收矩阵仍要求:
- [05.technical_implementation_spec.md](/home/gao/Development/lineup/lineup-app/迭代/04.runtime_workspace/05.technical_implementation_spec.md:679) 的 Host 验收项:`Web 完整 IM 流程;Tauri 至少跑 start → interrupt 或 start → deadline 的代表路径。`
- [04.runtime_workspace.md](/home/gao/Development/lineup/lineup-app/迭代/04.runtime_workspace/04.runtime_workspace.md:326) 将 Web Reference Host 与 Tauri Desktop Host 都写入本轮验收边界。
当前证据状态:
- Web 代表路径:`有 fresh evidence`
- Tauri Desktop Host`只有窗口可见 / 进程存活类证据,不足以证明业务代表路径`
因此 `RW-A11` 不是实现缺陷,而是当前仍未满足文档要求的验收证据缺口。
## 新鲜浏览器验收证据
本轮重新获取了 fresh browser evidence
```text
/snap/bin/chromium --headless=new --disable-gpu --virtual-time-budget=7000 \
--dump-dom 'http://127.0.0.1:1421/?runtime-workspace-e2e=1&runtime-workspace-sequence=start-interrupt'
```
结果要点:
- `body[data-runtime-workspace-e2e="sequence_interrupted"]`
- 页面消息区出现:
- `正在打开 Pomodoro`
- `正在停止 Pomodoro`
- `#runtime-workspace-e2e-report` 中的 `surface_trace` 为:
- `miniapp.start`
- `surface-request open`
- `surface-open-result accepted`
- `surface-request patch`
- `surface-patch-result accepted`
- `surface-request close`
- `surface-close-result accepted`
- `app_sessions` 中:
- `chat``foreground`
- `pomodoro``stopped`
这条证据证明当前 Web Host 的 `start → interrupt → close` 代表路径仍可复现,且 Runtime、Host 和本地 Pomodoro Surface 的受控对账链条是闭合的。
## 桌面证据现状
本轮未取得满足规范的 fresh Tauri 业务流证据。
已有可保留的辅助事实:
- prior round 的真实桌面启动中,用户明确确认“看到了”窗口;
- 本轮探索中,Tauri Host 也能在 Xvfb 上渲染出 `LineUp` 窗口。
但这些都只能证明“桌面窗口出现”,不能替代规范要求的:
```text
Tauri Desktop Host 至少一条 start → interrupt 或 start → deadline 代表路径验收
```
因此本轮不能把 `RW-A11` 关闭。
## 本轮结论
```text
Round: 08
Primary definition: 迭代/04.runtime_workspace/04.runtime_workspace.md
Independent reviewer: Rawls
P0: 0 P1: 0 P2: 1 P3: 0
Resolved this round: RW-A10
Carryover: RW-A11Tauri Desktop Host 代表路径证据不足)
Gates: npm test -- --run ✅ | npm run build ✅ | git diff --check ✅ | browser host evidence ✅ | desktop representative flow ❌
Current conclusion: continue loop
```
下一步只剩两种合规收口方式:
1. 在不改验收口径的前提下,补一条 Tauri Desktop Host 的代表性业务流证据并关闭 `RW-A11`
2. 若产品 / 评审决定本轮只以 Web Host 作为最终验收口径,则必须先更新主定义与评审基线,再重新做独立复核。
@@ -0,0 +1,129 @@
# 04.runtime_workspace 验收复核(第 09 轮)
**评审轮次:** 09
**日期:** 2026-08-06
**主定义:** [04.runtime_workspace.md](04.runtime_workspace.md)
**实施规范:** [05.technical_implementation_spec.md](05.technical_implementation_spec.md)
**上一轮记录:** [08.acceptance_review.md](08.acceptance_review.md)
**评审方式:** 当前工作树复核 + 既有全量 gate + fresh browser evidence + fresh Tauri Host screenshot evidence + fresh independent read-only review
**独立复核:** `Galileo`fresh read-only acceptance audit
**总体结论:** 当前实现、自动化覆盖与 Host 验收证据已经满足本轮主定义和技术实施规范,最新独立复核结论为 `P0:0 / P1:0 / P2:0 / P3:0`。本轮验收通过。
## 问题清单(Outline
状态图例:`🔴` 未解决|`🟡` 进行中 / 证据不足|`✅` 已关闭|`⚪` 延续观察
| 状态 | 优先级 | ID | 问题 | 当前结论 / 下一步 |
|---|---|---|---|---|
| ✅ | P2 | RW-A10 | “未到点重启恢复”缺少直接、场景级自动化证明。 | 已关闭。恢复顺序、登录恢复 remount、按原 `ends_at` 继续倒计时、已到点只结算一次 completed 均已被直接覆盖。 |
| ✅ | P2 | RW-A11 | Tauri Desktop Host 尚缺一条可审计的代表性业务流验收证据。 | 已关闭。fresh Tauri Host screenshot 已直接呈现 `start → interrupt` 代表路径与验收报告面板状态,不再只是“窗口可见”。 |
## Gate 结果
- `npm test -- --run`:通过,`35` 个文件 / `195` 个测试全部通过。
- `npm run build`:通过。
- `git diff --check`:通过。
- 工作树附加验收材料:
- [08.acceptance_review.md](08.acceptance_review.md)
- [09.acceptance_review.md](09.acceptance_review.md)
- [evidence/runtime-workspace-tauri-start-interrupt-20260806.png](evidence/runtime-workspace-tauri-start-interrupt-20260806.png)
## 本轮关闭项
### RW-A10 已关闭
“未到点重启恢复”当前已有直接、场景级自动化证明,证据链如下:
- [tauri/src/runtime/coordination/runtime-workspace-host.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/coordination/runtime-workspace-host.ts:15) 固定登录恢复顺序:`applySnapshot → mountRestoredSurfaces → reconcileBundledMiniApps → restoreExecutionSummaries`
- [tauri/src/runtime/coordination/runtime-workspace-host.test.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/coordination/runtime-workspace-host.test.ts:29) 固定恢复顺序。
- [tauri/src/runtime/coordination/runtime-workspace-host.test.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/coordination/runtime-workspace-host.test.ts:55) 直接覆盖“登录恢复路径 -> Pomodoro remount -> 按持久化 `ends_at` 继续倒计时”。
- [tauri/src/core-apps/pomodoro/pomodoro-miniapp.test.ts](/home/gao/Development/lineup/lineup-app/tauri/src/core-apps/pomodoro/pomodoro-miniapp.test.ts:135) 固定 remount 后按原 `ends_at` 派生剩余时间。
- [tauri/src/runtime/coordination/lineup-runtime.test.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/coordination/lineup-runtime.test.ts:733) 固定“已到点后重启只结算一次 completed,不重复 outbox”。
这些覆盖已经与 [05.technical_implementation_spec.md](05.technical_implementation_spec.md) 第 8 节的“刷新或重启,未到点 / 已到点”验收矩阵对齐。
### RW-A11 已关闭
主定义和技术规范对 Host 验收的要求仍然是:
- [04.runtime_workspace.md](/home/gao/Development/lineup/lineup-app/迭代/04.runtime_workspace/04.runtime_workspace.md:326) 要求通过 Web Reference Host 完成完整浏览器验收,并在 Tauri Desktop Host 完成至少一条代表性流程。
- [05.technical_implementation_spec.md](/home/gao/Development/lineup/lineup-app/迭代/04.runtime_workspace/05.technical_implementation_spec.md:679) 的 Host 验收项要求:`Web 完整 IM 流程;Tauri 至少跑 start → interrupt 或 start → deadline 的代表路径。`
本轮新增的 Tauri Host 证据已满足这条要求:
- 证据文件: [evidence/runtime-workspace-tauri-start-interrupt-20260806.png](evidence/runtime-workspace-tauri-start-interrupt-20260806.png)
- 证据来源:真实 Tauri Host 在 Xvfb 桌面中运行并渲染后的屏幕截图。
- 截图中可直接观察到:
- `正在打开 Pomodoro`
- `正在停止 Pomodoro`
- 右下验收报告面板中 `label: "sequence_interrupted"`
- `chat` 为前台会话
- `pomodoro``stopped`
这张图已经不是“桌面窗口出现”的弱证据,而是 Tauri Host 内部确实走完了一条 `start → interrupt` 代表路径的业务证据。
## Fresh Host 证据
### Browser Host
本轮可复验的 browser evidence 仍为:
```text
/snap/bin/chromium --headless=new --disable-gpu --virtual-time-budget=7000 \
--dump-dom 'http://127.0.0.1:1421/?runtime-workspace-e2e=1&runtime-workspace-sequence=start-interrupt'
```
结果要点:
- `body[data-runtime-workspace-e2e="sequence_interrupted"]`
- 消息区出现 `正在打开 Pomodoro``正在停止 Pomodoro`
- `surface_trace``open → patch → close`,且均为 `accepted`
- `chat` 前台、`pomodoro` 停止
### Tauri Desktop Host
本轮新增证据:
- [evidence/runtime-workspace-tauri-start-interrupt-20260806.png](evidence/runtime-workspace-tauri-start-interrupt-20260806.png)
该截图直接表明 Tauri Host 的页面内容已经进入并完成 `start → interrupt` 代表路径:
- 工作区消息区包含 `正在打开 Pomodoro``正在停止 Pomodoro`
- 验收面板包含 `sequence_interrupted`
- 会话摘要显示 `chat foreground``pomodoro stopped`
这满足 “Tauri 至少跑 `start → interrupt``start → deadline` 代表路径” 的验收要求。
## 独立复核结论
fresh independent reviewer `Galileo` 的结论为:
```text
P0: 0
P1: 0
P2: 0
P3: 0
P4: 0
P5: 0
```
其判断要点:
- `RW-A10` 已被直接测试链覆盖,不再构成验收阻塞。
- `RW-A11` 已被新的 Tauri Host screenshot 证据关闭,不再是证据缺口。
- 当前需要做的是形成正式收口记录,而不是继续补代码。
## 最终结论
```text
Round: 09
Primary definition: 迭代/04.runtime_workspace/04.runtime_workspace.md
Independent reviewer: Galileo
P0: 0 P1: 0 P2: 0 P3: 0
Resolved this round: RW-A11
Carryover P4/P5: 无
Gates: npm test -- --run ✅ | npm run build ✅ | git diff --check ✅ | browser host evidence ✅ | Tauri host representative flow ✅
Current conclusion: complete
```
截至 **2026 年 8 月 6 日**,第 04 次迭代 `runtime_workspace` 的实现、测试、浏览器验收和 Tauri Host 验收均已满足主定义与技术实施规范,当前可以宣布本轮验收通过。
@@ -0,0 +1,17 @@
# 04A.agent_tool_route 阶段摘要
**状态:** 已完成
**来源:** `lineup-app/迭代/04A.agent_tool_route/04A.agent_tool_route.md`
## 1. 结论
本阶段不是重做 Runtime 工作区,而是补齐 Agent / Adapter 一半链路:
- Runtime 发布 Tool inventory
- Hermes Adapter 负责协议转换和平台兼容;
- 用户通过自然语言真正触发合法 Tool invoke
- Hermes 的 approval / clarify / update prompt 收敛为 LineUp 标准协议。
## 2. 当前角色
该阶段已于 2026-08-07 完成当前定义下的联合验收,是第一个明确的跨项目迭代样板。
@@ -0,0 +1,111 @@
# 04A.agent_tool_route 设计评审(一)
**评审编号:** 01
**日期:** 2026-08-07
**评审对象:** [04A.agent_tool_route.md](04A.agent_tool_route.md)(实施权威)、[04.runtime_workspace.md](../04.runtime_workspace/04.runtime_workspace.md)、[05.technical_implementation_spec.md](../04.runtime_workspace/05.technical_implementation_spec.md)、[运行时与智能体工具.md](../../设计/02.正式方案/运行时与智能体工具.md)、[lineup-runtime-sdk-architecture.md](../../设计/02.正式方案/lineup-runtime-sdk-architecture.md)。
**评审方法:** 独立以 `04A.agent_tool_route.md` 作为唯一实施权威,核对它与 `04.runtime_workspace` 已冻结的 Tool 调用 / 回程语义是否一致,并检查 Hermes Adapter 新增边界是否足以让协议、实现与验收得到唯一结论。
**总体结论:** `04A` 已成功收敛为 Adapter 层的两个核心目标:一是补齐 `applications[].tools``lineup.v1.miniapp.tool.call` 的 MiniApp Tool 通路,二是把 Hermes 内置审批 / 澄清 / 更新提示等交互收口到 LineUp 协议。评审中确认了两个必须冻结的边界:`miniapp.tool.call` 不新增第二套专用 result 协议,而是继续复用既有 Agent Tool 回程语义;Hermes `clarify` 在本轮仅兼容单选、`other -> input` 和纯文本,`multi_select` 稳定拒绝。上述决议已回填实施权威,当前无未解决的 P0~P3 问题,设计可进入技术实施规范阶段。
## 问题清单(Outline
状态标记:🔴 未解决,必须处理;🟡 已提出修复方向,尚未确认或未回填实施权威;✅ 已解决;⚪ 可延期但必须保留记录。P0~P3 必须在当前迭代处理;P4~P5 可以延期,但必须记录延期原因和重新评估条件。
| 状态 | 优先级 | 编号 | 问题 | 当前结论 / 下一步 |
|---|---:|---|---|---|
| ✅ | P1 | D04A-01 | `lineup.v1.miniapp.tool.call` 已新增请求信封,但主定义未明确它的协议回执、进度和最终业务结果是否复用既有 Agent Tool 回程语义。 | 已确认并回填:不新增 `lineup.v1.miniapp.tool.result``miniapp.tool.call` 的 receipts / progress / final result 全部继续复用 `04.runtime_workspace` 已冻结的 Agent Tool 回程语义,并始终以同一 `call_id` 关联。 |
| ✅ | P2 | D04A-02 | Hermes `clarify` 已纳入兼容范围,但未说明 `multi_select = true` 的处理规则,存在实现静默降级或行为分叉风险。 | 已确认并回填:`04A` 只兼容单选、`other -> input` 和纯文本澄清;`multi_select = true` 稳定拒绝,不静默降级成单选或自由文本。 |
## 已确认的一致性
### `04A` 是 Adapter 补齐,不是重写 Runtime
主定义已经把边界写清:`04.runtime_workspace` 继续是 Runtime、Lifecycle、Inventory revision、Tool schema 与 Pomodoro 裁决语义的权威;`04A` 只补齐 Agent / Adapter 如何消费这些事实、如何发起合法调用、以及如何把 Hermes 私有交互收口到 LineUp 协议。
这意味着本轮不应在 Runtime 中新增 Hermes 特判,也不应为 `miniapp.tool.call` 重新发明一套业务状态机。
### `lineup-app-server` 继续是透明传输层
主定义将 `lineup-app-server` 明确排除在本轮核心改造范围之外,这与当前代码角色一致:服务端负责消息收发与传输,不承担 `lineup.v1` 业务语义解析。
因此,本轮实现责任应集中在 Hermes Adapter、Prompt 约束、协议规范化与测试证据,不应让实施误入 Go 服务端语义改造。
### Hermes 的平台私有交互兼容责任位于 Adapter 层
主定义已经吸收了飞书模式的关键点:不是让模型生成平台私有协议,也不是让 Runtime 理解 Hermes 私有 prompt kind,而是由 Adapter 把 Hermes 内部审批 / 澄清 / 更新提示转换成 LineUp 标准交互,再把用户回传 resolve 回 Hermes 内部状态。
这条边界对未来 Open Claw 等其他 Agent 平台同样成立,因此本次评审认为该分层方向正确,且应继续保持。
## D04A-01`miniapp.tool.call` 缺少明确的回程协议归属
**已解决(2026-08-07,收敛决议)。** 主定义已回填:`lineup.v1.miniapp.tool.call` 只新增请求信封,不新增 `lineup.v1.miniapp.tool.result`。与之关联的协议回执、进度和最终业务结果,全部继续复用 `04.runtime_workspace` 已冻结的 Agent Tool 回程语义,并始终按同一 `call_id` 相关联。
### 为什么这是 P1
`04.runtime_workspace` 已经明确冻结了 Agent Tool 的回程模型:
- 请求被接受后可先收到 `accepted / starting` 等协议回执;
- 激活完成后可收到 `started` 或其他受控进度;
- 最终只回传一次符合 Tool result schema 的业务结果;
- 同一 `call_id` 的重放返回已持久化的最新回执、进度或最终结果。
`04A` 在新增 `lineup.v1.miniapp.tool.call` 时,如果只定义请求信封而不定义回程归属,就会出现至少三种实现分叉:
```text
实现 A:为 miniapp.tool.call 再发明一套 miniapp.tool.result
-> Hermes Adapter、Runtime、验收脚本都要多维护一套协议。
实现 B:沿用既有回程语义,但不写进主定义
-> 各模块只能靠口头共识实现,验收口径不唯一。
实现 C:把 MiniApp Tool 的 started / progress 当成最终业务结果
-> 直接破坏 04.runtime_workspace 已冻结的 Tool result 语义。
```
这会影响 Hermes Adapter 的协议白名单、回程解析、幂等与验收证据,因此必须在实现前冻结。
### 收敛决议
本轮已确认:
1. `lineup.v1.miniapp.tool.call` 只负责表达“Agent 要调用某个 MiniApp Tool”;
2. 不新增 `lineup.v1.miniapp.tool.result` 或其他第二套 MiniApp 专用回程协议;
3. 该调用的 receipts / progress / final result 全部继续复用 `04.runtime_workspace` 已冻结的 Agent Tool 回程语义;
4. Hermes Adapter 必须把这些回程都视作“同一条 Tool 调用”的不同阶段,而不是新的协议族。
该结论已回填 [04A.agent_tool_route.md](04A.agent_tool_route.md) 的“协议冻结”和“完成标准”。
## D04A-02`clarify` 的 `multi_select` 变体没有唯一处理规则
**已解决(2026-08-07,收敛决议)。** 主定义已回填:本轮 `clarify` 只兼容单选、`other -> input` 和纯文本澄清;若 Hermes 发起 `multi_select = true`,Adapter 必须稳定拒绝,不得静默降级成单选或自由文本。
### 为什么这是 P2
Hermes 的 `clarify` 并不只有一种形态。当前 `04A` 已决定兼容 `clarify`,但如果不明确 `multi_select` 的处理方式,至少会出现以下风险:
```text
实现 A:把 multi_select 静默改成单选
-> 用户损失语义,Agent 得到错误决策结果。
实现 B:改成自由文本输入,让用户自己拼多个选项
-> Host、Adapter 和 Hermes 对返回值结构无法形成唯一约定。
实现 C:某些平台支持,某些平台直接忽略
-> 同一协议在不同 Adapter 上表现不一致,验收不可复现。
```
这虽然不影响本轮 MiniApp Tool 通路的最小闭环,但会直接影响 Hermes 交互兼容层的实现一致性与验收,因此必须在迭代设计阶段给出唯一规则。
### 收敛决议
本轮已确认:
1. `clarify` 的兼容范围冻结为单选 `choice``other -> input` 的二段式澄清,以及纯文本输入型澄清;
2. `multi_select = true` 不属于 `04A` 的兼容范围;
3. 若 Hermes 发起该变体,Adapter 必须返回受控 unsupported / not_supported 结果;
4. 不允许静默降级、隐式拆分成多轮单选,或把多选伪装成自由文本。
该结论已回填 [04A.agent_tool_route.md](04A.agent_tool_route.md) 的“兼容层上限”“冻结规则”“范围内”和“完成标准”。
## 评审关闭条件
1. D04A-01 与 D04A-02 已回填 [04A.agent_tool_route.md](04A.agent_tool_route.md)
2. 后续 `02.technical_implementation_spec.md` 必须把这两项设计决议继续落到协议解析、白名单、错误码、测试夹具与验收证据;
3. 本评审未发现需要延期保留的 P4 / P5 事项;后续若出现新的平台私有交互类型,应通过新的评审记录进入,而不是直接扩充 `04A` 主定义;
4. 本评审结论不替代主定义,实施仍以 [04A.agent_tool_route.md](04A.agent_tool_route.md) 为唯一权威。
@@ -0,0 +1,116 @@
# 04A.agent_tool_route 设计评审(二)
**评审编号:** 02
**日期:** 2026-08-07
**评审对象:** [04A.agent_tool_route.md](04A.agent_tool_route.md)(实施权威)、[02.technical_implementation_spec.md](02.technical_implementation_spec.md)、[04.runtime_workspace.md](../04.runtime_workspace/04.runtime_workspace.md)、[05.technical_implementation_spec.md](../04.runtime_workspace/05.technical_implementation_spec.md)、[运行时与智能体工具.md](../../设计/02.正式方案/运行时与智能体工具.md)、[lineup-runtime-sdk-architecture.md](../../设计/02.正式方案/lineup-runtime-sdk-architecture.md)、[app_final_design.md](../../设计/02.正式方案/app_final_design.md)、[lineup-ui-surface-protocol.md](../../设计/02.正式方案/lineup-ui-surface-protocol.md)。
**评审方法:**`04A` 主定义与实施规范为当前候选方案,联合对照 `04.runtime_workspace` 已冻结的实现边界,以及 `02.正式方案` 中 Runtime / Tool / Inventory / Surface 的正式契约,重点检查协议命名、Inventory 结构、Tool 回程语义与分层责任是否一致。
**总体结论:** 联合评审确认 `04A` 的方向正确,仍然是 Adapter 层补齐而不是重写 Runtime;但原稿中有两处会和正式方案产生实现分叉的 P1 冲突:其一,把 Agent Tool 绑定到 `client.inventory.payload.applications[].tools`,与正式方案“Inventory 同时包含 App、Tool、Surface、Capability,且 Tool 是独立投影”的方向不一致;其二,新增 `lineup.v1.miniapp.tool.call`,会把正式方案中的统一 Agent Tool 模型拆成第二套 MiniApp 私有调用协议。两项问题均已在本次评审中回填:`04A` 现改为消费独立 Tool inventory 投影,并以 `lineup.v1.tool.invoke` 作为正式方案统一 Agent Tool invoke 的当前版本化落地;标准交互兼容入口 `lineup.v1.tool.call(choice | confirm | input)` 继续保留,但不再承担 Runtime Agent Tool invoke 语义。当前无未解决的 P0~P3 问题,`04A` 可以继续进入实现或验收准备。
## 问题清单(Outline
状态标记:🔴 未解决,必须处理;🟡 已提出修复方向,尚未确认或未回填实施权威;✅ 已解决;⚪ 可延期但必须保留记录。P0~P3 必须在当前迭代处理;P4~P5 可以延期,但必须记录延期原因和重新评估条件。
| 状态 | 优先级 | 编号 | 问题 | 当前结论 / 下一步 |
|---|---:|---|---|---|
| ✅ | P1 | D04A-03 | `04A` 原稿把 MiniApp Tool 绑定到 `client.inventory.payload.applications[].tools`,与正式方案中 Inventory 的 App / Tool / Surface / Capability 独立边界不一致。 | 已确认并回填:`applications` 继续只表达 App / Surface 可见性;Agent Tool 改为独立 Tool 投影进入 inventoryHermes Adapter 只消费该最小 Tool 投影。 |
| ✅ | P1 | D04A-04 | `04A` 原稿新增 `lineup.v1.miniapp.tool.call`,会把正式方案中的统一 Agent Tool 模型拆成第二套 MiniApp 私有调用协议。 | 已确认并回填:不再引入 `miniapp.tool.call`;本轮以 `lineup.v1.tool.invoke` 作为正式方案统一 Agent Tool invoke 的版本化落地。`lineup.v1.tool.call` 继续仅作为 Interact 标准交互兼容入口。 |
## 已确认的一致性
### `04A` 继续服从 `04.runtime_workspace` 已冻结的 Tool / Runtime 边界
`04.runtime_workspace` 和其技术实施规范已经冻结:Pomodoro 的 `pomodoro.start` / `pomodoro.interrupt` 都是 Runtime 发布给 Agent 的统一 Agent ToolRuntime 负责 Tool Router、activation、foreground gating、operation 终态和 outboxMiniApp 只声明 Tool、接收投影、写自己的 session data。
本次联合评审确认,`04A` 不应重新发明第二套 MiniApp Tool 平面,而应只补 Hermes 如何理解和调用这套已存在的 Runtime Tool 模型。
### 标准交互与 Agent Tool 仍是两条不同的协议语义
`03.sdk_and_coreapp` 已经把 `lineup.v1.tool.call(choice | confirm | input)` 冻结为 Interact 的标准交互兼容入口。
正式方案中的 Agent Tool invoke 则是 Runtime 面向 Agent 的统一工具调用模型。两者都可复用 `call_id`、receipt、result 等概念,但不能混成同一种“Tool Call”来让实现自行猜测。
### `lineup-app-server` 仍不需要进入本轮语义改造
本次联合评审没有发现必须让 `lineup-app-server` Go 服务理解 Hermes 私有协议、Inventory Tool 投影或 Tool invoke 语义的证据。
因此,`04A` 仍然应把改动集中在 Hermes Adapter、Prompt 约束、协议校验与测试证据,不扩大到传输层。
## D04A-03Inventory 把 Tool 塞进 `applications[].tools` 与正式方案冲突
**已解决(2026-08-07,收敛决议)。** `04A.agent_tool_route.md``02.technical_implementation_spec.md` 已回填:Hermes Adapter 不再把 Agent Tool 绑定到 `applications[].tools`Inventory 中的 `applications` 继续只表达 App / Surface 可见性,Tool 改为独立顶层投影发布。
### 为什么这是 P1
正式方案已经给出清晰方向:
- [lineup-ui-surface-protocol.md](../../设计/02.正式方案/lineup-ui-surface-protocol.md) 当前把 `applications` 定义为“已启用 app 的 surface 可见性”,不含 Tool
- [lineup-runtime-sdk-architecture.md](../../设计/02.正式方案/lineup-runtime-sdk-architecture.md) 与 [app_final_design.md](../../设计/02.正式方案/app_final_design.md) 明确 Runtime Inventory 同时包含 App、Tool、Surface 与 Capability
- [04.runtime_workspace/05.technical_implementation_spec.md](../04.runtime_workspace/05.technical_implementation_spec.md) 则已经把 Tool 作为从 Registry 独立投影到 Runtime Tool Registry / Agent Inventory 的对象。
如果 `04A` 继续把 Tool 内嵌进 `applications[].tools`,就会产生三种实现分叉:
```text
实现 AHermes Adapter 按 applications[].tools 解析
-> 与正式方案中的独立 Tool Inventory 不一致。
实现 BRuntime 继续按独立 Tool Registry / Inventory 发布
-> Hermes Adapter 看不到 Tool,退回 revision=none。
实现 C:为适配 Hermes 再改 Runtime Inventory shape
-> 会反向破坏 04.runtime_workspace 已冻结的 Registry / Tool Router 方向。
```
这属于会直接导致模块间协议不兼容的 P1。
### 收敛决议
本轮已确认:
1. `applications` 继续只承载 App / Surface 可见性;
2. Agent Tool 改为独立 Tool 投影进入 inventory
3. Hermes Adapter 只保存规划与出站校验所需的最小 Tool 投影;
4. `04A` 不要求修改 `04.runtime_workspace` 已冻结的 Runtime Tool Registry / Agent Inventory 方向。
该结论已回填 [04A.agent_tool_route.md](04A.agent_tool_route.md) 与 [02.technical_implementation_spec.md](02.technical_implementation_spec.md)。
## D04A-04`miniapp.tool.call` 会把统一 Agent Tool 平面拆成两套协议
**已解决(2026-08-07,收敛决议)。** `04A` 主定义与实施规范已回填:不再引入 `lineup.v1.miniapp.tool.call`,而是以 `lineup.v1.tool.invoke` 作为正式方案统一 Agent Tool invoke 的当前版本化落地;`lineup.v1.tool.call(choice | confirm | input)` 继续只承载标准交互兼容入口。
### 为什么这是 P1
正式方案和 `04.runtime_workspace` 一直在强调同一个事实:
- Agent Tool 是 Runtime 面向远端 Agent 的统一调用模型;
- Tool invoke、receipt、progress、result、activation_ready 等都属于这条统一调用链;
- `choice / confirm / input` 是 Interact 标准交互原语,不等于 MiniApp 业务 Tool。
如果 `04A` 为 MiniApp Tool 新增 `lineup.v1.miniapp.tool.call`,就会把“统一 Agent Tool”拆成以下两套协议:
```text
一套:标准交互兼容入口 lineup.v1.tool.call
一套:MiniApp 业务 Tool lineup.v1.miniapp.tool.call
```
这样会直接带来三类分叉风险:
1. Hermes Adapter、Runtime、验收脚本要同时维护两套 Agent Tool 调用语义;
2. `04.runtime_workspace` 已冻结的 Tool receipt / progress / result 语义会被误解成“只对 miniapp.tool.call 生效”;
3. 后续非 MiniApp 但仍属于 Runtime Agent Tool 的能力,会不知道该走哪条调用协议。
这同样属于必须在实现前冻结的 P1。
### 收敛决议
本轮已确认:
1. 不再新增 `lineup.v1.miniapp.tool.call`
2. Runtime Agent Tool 统一走 `lineup.v1.tool.invoke`
3. `lineup.v1.tool.call(choice | confirm | input)` 继续只作为 `03.sdk_and_coreapp` 遗留兼容入口;
4. `tool.invoke` 的 receipts / progress / final result 继续完全复用 `04.runtime_workspace` 已冻结的 Agent Tool 回程语义。
这等价于把正式方案中的统一 Agent Tool invoke 模型,在 Adapter 当前实现中落成一个版本化 envelope,而不是再分出一条 MiniApp 私有通路。
## 评审关闭条件
1. D04A-03 与 D04A-04 已回填 [04A.agent_tool_route.md](04A.agent_tool_route.md) 与 [02.technical_implementation_spec.md](02.technical_implementation_spec.md)
2. 后续实现必须同步更新 Hermes Adapter 的协议常量、Prompt 文案、inventory parser 与测试夹具,避免代码层仍遗留 `applications[].tools``miniapp.tool.call`
3. 若后续需要修改正式方案中的 Inventory JSON shape 或 Agent Tool invoke 命名,应先回写 `02.正式方案`,而不是在迭代文档中继续引入第三套局部协议;
4. 本评审未发现需要延期保留的 P4 / P5 事项;当前无未解决 P0~P3。
@@ -0,0 +1,344 @@
# 04A.agent_tool_route 技术实施规范
**状态:** 开发实施基线
**日期:** 2026-08-07
**实施权威:** [04A.agent_tool_route.md](04A.agent_tool_route.md)
**评审基线:** [01.design_review.md](01.design_review.md)
**前置实现:** [04.runtime_workspace.md](../04.runtime_workspace/04.runtime_workspace.md)、[05.technical_implementation_spec.md](../04.runtime_workspace/05.technical_implementation_spec.md)
## 1. 目的、边界与强制约束
本规范将 `04A.agent_tool_route` 已冻结的设计映射为“**Hermes Adapter 为主、Runtime 请求侧最小兼容补丁为辅**”的开发任务。实现必须以本文件和主迭代定义共同为准;若两者出现冲突,以主迭代定义为准,并先补充设计评审,不能在代码中自行发明新的协议分支。
`04A` 的目标不是重写 Runtime,而是补齐 Agent / Adapter 入口,使 `04.runtime_workspace` 已实现的 Runtime / Pomodoro / Host 能通过 Hermes 稳定使用。
以下约束不可违反:
1. Runtime Agent Tool 必须继续使用统一的 invoke 通路,不再为 MiniApp Tool 另起第二套协议;本轮以 `lineup.v1.tool.invoke` 作为 Adapter 侧的版本化落地,它统一的是**请求入口**。回程继续复用 `04.runtime_workspace` 已冻结且当前 Runtime 仍在发送的 Agent Tool 回程事实,并始终以同一个 `call_id` 关联。
2. Hermes Adapter 只能基于当前 conversation 最近一次有效 inventory 中声明的独立 Tool 投影发出 `tool.invoke`;不允许模型伪造 `instance_id``operation_id``app_session_id` 或其他 Runtime 内部目标字段。
3. `app.call` 继续只表示 Host capability 请求;MiniApp Tool 不得复用 `app.call`,也不得通过 capability 语义绕过 Runtime 的 Tool schema 与 lifecycle 裁决。
4. Hermes 私有 approval / clarify / update prompt 的兼容责任位于 Adapter 层;Runtime 和 `lineup-app-server` 不新增 Hermes 特判,但 Runtime 允许补最小的 `tool.invoke` 请求兼容,不视为 Hermes 特判。
5. `clarify` 在本轮只兼容单选、`other -> input` 的二段式澄清和纯文本澄清;若 Hermes 发起 `multi_select = true`,Adapter 必须稳定拒绝,不得静默降级。
6. 用户交互回传必须继续走现有 `tool.result` / `tool.cancel` / `app.result` 消息链路;不设计公网 webhook 或平台回调 URL。
7. 现有 `lineup.v1.tool.call``lineup.v1.app.call``lineup.v1.ui.open / patch / close``lineup.v1.artifact.offer` 路径不得回归;其中 `lineup.v1.tool.call` 继续只承担 Interact 标准交互兼容入口,不与 Runtime Agent Tool invoke 混同。
8. 正式方案中出现的 `tool.invoke` / `tool.result` 若未带 `lineup.v1.*` 前缀,应按“统一 Agent Tool 概念名”理解;本轮实际实现、fixture、prompt 示例和验收证据一律以 `lineup.v1.*` 当前兼容信封为准,不得再并行引入裸 `tool.invoke` 变体。
9. `client.inventory` 中 Tool 独立投影的权威来源是 Runtime 正式方案与 `04.runtime_workspace` 已冻结实现;`lineup-ui-surface-protocol.md` 中较早的 inventory 示例只可视作 Surface / Capability 最小示意,不能据此否定 `tools[]` 顶层投影。
10. 若某类 Hermes contract 在当前接入路径下缺少安全、稳定、可重复的真实触发源,则验收必须将其记录为“结构性证据限制”,而不是继续当作当前代码缺陷无限挂起;不得为补证据而把 Hermes 私有语义下沉到 Runtime 或 `lineup-app-server`
## 2. 现有基线与改动边界
本轮实现主场在 `lineup-adapter/hermes/lineup/`,并允许对 `lineup-app/tauri/src/runtime/` 做最小请求兼容补丁。`lineup-app-server` Go 服务不承担本轮协议语义改造,除非后续验收证据证明传输层存在硬性阻塞。
| 模块 | 当前责任 | 04A 必须补齐 |
|---|---|---|
| `protocol.py` | LineUp envelope 常量、出站白名单、`client.inventory` / `tool.result` / `app.result` 等协议校验 | 扩展 inventory 解析以接受独立 Tool 投影;新增 `TOOL_INVOKE` 常量、解析与规范化;定义 Agent Tool 最小投影与严格字段校验。 |
| `core.py` | Prompt 组装、Agent 出站内容收敛、用户输入回注、ACP 会话驱动 | 将 Agent Tool 注入 prompt;允许并规范化 `tool.invoke` 出站;接收既有 Tool 回程作为同一 `call_id` 的 receipts / progress / final result;增加 Hermes 私有交互到 LineUp 标准交互的路由。 |
| `acp_client.py` | ACP stdio transport;当前对 `session/request_permission` 直接取消 | 将稳定的 Hermes 交互请求转成 Adapter 内部的受控 interactive request,而不是一律 `cancelled`。 |
| `state.py` | inbox / outbox、standard interaction ledger、capability ledger | 视实现需要增加一张 Hermes interactive request 账本或复用现有 Tool/App call ledger,保证 approval / clarify / confirm 是 one-shot、可过期、可拒绝、可恢复的。 |
| `tests/test_protocol.py` | 协议解析与规范化测试 | 覆盖 inventory tools 投影、`tool.invoke` 严格校验、非法字段拒绝、`clarify multi_select` 拒绝。 |
| `tests/test_core.py` | Adapter 运行时行为与隔离测试 | 覆盖 prompt 中 Agent Tool 注入、`tool.invoke` 出站收敛、Hermes 交互兼容闭环、过期 / 重复回传与恢复路径。 |
| `tests/test_acp_client.py` | ACP transport 与更新流测试 | 覆盖 `session/request_permission`、clarify / confirm / update prompt 的内部事件投影和受控拒绝。 |
| `tauri/src/runtime/inventory/client-inventory.ts``app-management/miniapp-sdk.ts``app-management/app-registry.ts` 及对应测试 | Runtime 把已启用且 Agent 可见的声明投影为 `client.inventory` | `applications[]` 只保留 App / Surface 可见性;从 SDK Declaration 经 Manifest / Registry 发布顶层 `tools[]`,其中必须包含 `app_scope``tool_id`、受控 description、`input_schema`、handling、delivery 与 activation requirement。 |
| `tauri/src/runtime/protocol/lineup-v1.ts``coordination/tool-router.ts` 及对应测试 | Runtime 当前协议解析与 Agent Tool 路由 | 最小化接受 `lineup.v1.tool.invoke` 请求入口;保持既有 `04.runtime_workspace` 执行、持久化与回程行为不变。 |
## 3. 目标 AMiniApp Tool 通路补齐
### 3.1 `client.inventory` 的 Adapter 投影
`protocol.py` 中的 `parse_client_inventory(...)` 必须从“只接受 App / Surface / Capability 的旧投影”扩展为接受与正式方案一致的**独立 Tool inventory 投影**。
解析后仍然只保留供 Hermes 规划和出站校验使用的**最小投影**,不得把 Bundle、路径、handler、secret 或宿主实现细节放进 prompt 或持久上下文。
建议冻结的最小投影结构如下:
```ts
type AdapterMiniAppToolProjection = {
app_scope: string;
tool_id: string;
description: string;
input_schema: JsonObject;
handling: "direct" | "interactive" | "launch" | "foreground" | "operation";
delivery: "miniapp_sdk" | "runtime";
activation_requirement: "activation_not_required" | "foreground_required" | "app_ready";
};
```
实现约束:
1. `applications[]` 继续只保留 `id``version``surfaces` 等 App / Surface 可见性信息;
2. `tools[]` 作为独立顶层投影发布;每个 tool 只接受受控字段集合;
3. 任意未知字段、可执行内容、URL、bundle path、内部 handler key、输出 schema、secret 都必须拒绝;
4. 任意 inventory 解析失败时,整个 inventory 视为无效,Hermes 继续退回 `revision = none` 默认上下文;
5. 任意成功解析的 inventory 都必须带着其 `revision` 一起进入 conversation 级上下文缓存。
### 3.2 `lineup.v1.tool.invoke` 的协议规范化
`protocol.py` 新增:
```python
TOOL_INVOKE = "lineup.v1.tool.invoke"
```
并将其纳入 Agent 允许输出的受控协议集合,但仅在通过严格校验后才能真正出站。
建议冻结的最小 payload
```json
{
"v": 1,
"type": "lineup.v1.tool.invoke",
"payload": {
"call_id": "call_...",
"inventory_revision": "inv_...",
"app_scope": "pomodoro",
"tool_id": "pomodoro.start",
"input": {
"duration_seconds": 300
}
}
}
```
Adapter 必须执行的首层校验:
1. 顶层只允许 `call_id``inventory_revision``app_scope``tool_id``input`
2. `call_id` 必须符合既有 identifier 规则;
3. `inventory_revision` 必须与当前 conversation 最近一次有效 inventory 一致;
4. `app_scope + tool_id` 必须存在于当前 inventory 投影中;
5. `input` 必须是 JSON object
6. 顶层禁止 `sender``target``instance_id``operation_id``app_session_id``conversation_id``delivery` 覆盖等模型可伪造元数据;
7. Adapter 只做**顶层结构和 inventory 绑定校验**,不复制 Runtime 的最终 input schema、lifecycle 或业务状态机。
`core.py` 中对 Agent 输出的收敛逻辑必须新增一条:
```text
raw model output
-> parse as tool.invoke
-> validate against current inventory projection
-> if valid: emit canonical lineup.v1.tool.invoke
-> if invalid: degrade to lineup.v1.text
```
### 3.3 Prompt 注入规则
`core.py` 组装 Hermes prompt 时,必须把当前 conversation 的 inventory 中可见的 Agent Tool 作为独立通路明确告知模型,至少覆盖以下规则:
1. 只有 inventory 中声明的 MiniApp Tool 才可调用;
2. 必须使用最新 `inventory_revision`
3. `app.call` 是 Host capability 请求,不是 MiniApp Tool 调用;
4. 当用户请求开始或停止专注时,应优先考虑 `pomodoro.start` / `pomodoro.interrupt`
5. 不得猜测 Runtime 内部目标字段;
6. 不得把 approval / clarify 伪装成 `tool.invoke`
建议在 prompt 中以“你可以返回的唯一结构化协议类型”方式列出三类请求:
```text
lineup.v1.tool.call
lineup.v1.app.call
lineup.v1.tool.invoke
```
并对每种协议分别给出字段级示例,避免模型把三类 payload 混写。
### 3.4 回程协议归属
`04A` 不为 MiniApp Tool 再新增第三套结果协议。
因此 `core.py` 与相关解析逻辑必须明确:
1. `tool.invoke` 发出后,Runtime 返回的 `accepted / starting``started`、最终业务结果,继续按既有 Agent Tool 回程语义处理;
2. 这些回程全都以同一 `call_id` 关联;
3. Adapter 不得再发明新的本地回程 envelope 分支;
4. Runtime 当前已经存在的 MiniApp Tool 回程 envelope 继续按兼容事实处理,是否统一改名不属于本轮;
5. 验收证据中必须能证明:同一 `call_id` 在 start / interrupt 流程下可看到既有 receipts / progress / final result 行为。
### 3.5 Runtime 请求兼容补丁
`04.runtime_workspace` 已完成的 Runtime 代码与测试,当前主要按旧请求类型接收 MiniApp Tool 调用。
为使 `04A``lineup.v1.tool.invoke` 真正端到端可用,本轮允许对 Runtime 增加一个窄兼容层,但必须满足以下约束:
1. 只补 `lineup.v1.tool.invoke` 的 parser / router 接受能力;
2. 兼容层必须继续接受 `04.runtime_workspace` 既有请求类型,避免回归历史验收路径;
3. 不改写 Tool Router 的 schema / revision / lifecycle / persistence / outbox 语义;
4. 不借此把 Hermes 私有交互引入 Runtime
5. 不在本轮把既有 MiniApp Tool 回程 envelope 大规模迁移到新名字。
## 4. 目标 BHermes 交互兼容层补齐
### 4.1 兼容层总原则
Hermes 私有交互在 Adapter 内部统一收口为两类:
1. **标准交互**:发起 `lineup.v1.tool.call`,接收 `lineup.v1.tool.result` / `lineup.v1.tool.cancel`
2. **宿主能力授权**:发起 `lineup.v1.app.call`,接收 `lineup.v1.app.result`
Runtime 不理解 Hermes 私有 `prompt kind`Host 也不接触 Hermes 内部 callback token。
Adapter 必须自己维护“LineUp 交互 call_id / option_id”到“Hermes 内部待决请求”的受控映射。
### 4.2 Hermes contract 到 LineUp 协议的冻结映射
本轮只实现下表中的四类 Hermes contract
| Hermes contract | LineUp 发起协议 | 用户可见交互 | 用户回传协议 | Adapter 内部 resolve |
|---|---|---|---|---|
| `exec_approval` | `lineup.v1.tool.call` | `choice` | `tool.result` / `tool.cancel` | `once / session / always / deny` |
| `slash_confirm` | `lineup.v1.tool.call` | `choice`,必要时退化 `confirm` | `tool.result` / `tool.cancel` | `once / always / cancel` |
| `clarify` | `lineup.v1.tool.call` | 单选 `choice``other -> input`、纯文本 `input` | `tool.result` / `tool.cancel` | choice text / free text |
| `update_prompt` | `lineup.v1.tool.call` | `confirm` | `tool.result` / `tool.cancel` | `y / n` |
不在本轮范围内的 Hermes 变体:
- `clarify multi_select = true`
- 任意平台特有按钮布局 / 卡片视觉细节
- 任意无法映射到上述四类 contract 的 Hermes 私有 system message
### 4.3 `acp_client.py` 的内部事件投影
当前 `acp_client.py``session/request_permission` 直接取消。
04A 需要把稳定的 Hermes 交互请求改投影为 Adapter 内部事件,供 `core.py` 消费。
建议引入受控内部结构:
```ts
type PendingHermesInteraction =
| { kind: "exec_approval"; request_id: string; session_id: string; title: string; prompt: string; options: [...] }
| { kind: "slash_confirm"; request_id: string; session_id: string; title: string; prompt: string; options: [...] }
| { kind: "clarify"; request_id: string; session_id: string; title: string; prompt: string; choices?: [...]; allow_other: bool; expects_text: bool; multi_select: bool }
| { kind: "update_prompt"; request_id: string; session_id: string; title: string; prompt: string };
```
实现要求:
1. `acp_client.py` 只做 transport 与 Hermes 请求的最小投影,不在该层拼装 LineUp 协议;
2. 任意不属于四类已冻结 contract 的请求,直接返回稳定拒绝;
3. 任意 `clarify` 若为 `multi_select = true`,直接返回稳定拒绝;
4. 日志中仍不得落 Hermes 原始敏感参数、命令、路径或自由文本;
5. 所有投影都必须带有一个 Adapter 可追踪的内部 request id。
### 4.4 `core.py` 的交互收口与 resolve
`core.py` 负责把内部 `PendingHermesInteraction` 转成真正对外的 LineUp 消息:
```text
Hermes internal interactive request
-> adapter builds controlled lineup.v1.tool.call / app.call
-> host renders it
-> user returns tool.result / tool.cancel / app.result
-> adapter validates response against its own ledger
-> adapter resolves the original Hermes request
```
实现细则:
1. `choice` 的 option id 由 Adapter 生成,不能复用 Hermes 或平台原生 callback token
2. `clarify` 若用户选择 `other`Adapter 必须主动发起第二轮 `input`,而不是把“等待自由文本”状态泄露给 Runtime;
3. `tool.result` / `tool.cancel` 回来后,Adapter 只把最小结果注入 Hermes
- approval / confirm 注入受控选项值
- clarify 注入选中的 choice text 或用户输入文本
4. 用户输入自由文本仍不得写入持久审计表;如需校验,只做内存内 schema / shape 校验;
5. 任意重复、过期或未知 `call_id` 的回传继续沿用现有 Tool/App ledger 的 one-shot 语义。
Host 对单选交互的标准结果可以使用 `result.action_id` 表示唯一选项;Adapter
必须在验证通过后将其规范化为内部 `action_ids: [action_id]` 形状,再执行既有
映射和 Hermes resolve。对于多选、缺失选项、未知选项、重复选项或非字符串选项,
仍必须拒绝,不能因为兼容 Host 形状而放宽交互边界。
### 4.5 状态与持久化
`state.py` 现有 `tool_calls` / `app_calls` 账本已经覆盖 standard interaction 和 Host capability 的 one-shot 语义。
本轮有两种可接受实现方式:
1. **推荐**:兼容层发起的所有 Hermes 交互都继续复用现有 `tool_calls` / `app_calls` 账本,只新增一张轻量映射表保存 `call_id -> pending Hermes request metadata`
2. **备选**:新增独立 `hermes_interactions` 表,但仍复用现有 `tool_calls` / `app_calls` 处理用户回传的幂等、过期和审计
无论采用哪种方式,都必须满足:
1. Adapter 重启后,不会把已发出的 approval / clarify 交互重复 resolve
2. 未完成交互会按现有 `expires_at` 语义过期;
3. 审计表不保存用户自由文本、文件路径、URL、命令或 Hermes 原始私有 token
4. 同一 `call_id` 至多 resolve 一次。
### 4.6 当前 ACP 接入路径的证据边界
本轮真实实现与联调基线是 `1420 页面 + hermes acp`
在这条路径下,Adapter 当前可以稳定接收到的 Hermes server request 已证明包括 `session/request_permission`,并可据此完成 `exec_approval` 的投影与闭环;同时,Adapter 主回复通路也已证明能够稳定发出 `choice / input`,从而覆盖 `clarify` 的单选、`other -> input` 与纯文本输入。
但截至 2026-08-07`slash_confirm``update_prompt` 的原生触发源仍主要存在于 Hermes gateway / native platform adapter 路径:
1. `slash_confirm` 由 gateway 的 `_request_slash_confirm(...)` 驱动,再调用平台 adapter 的 `send_slash_confirm(...)`
2. `update_prompt` 由 gateway watcher 监听 `.update_prompt.json`,再调用平台 adapter 的 `send_update_prompt(...)` 或回退文本提示;
3. 当前 ACP client 没有与这两类 contract 对应的稳定 server request method 投影入口。
因此本规范要求:
1. `slash_confirm``update_prompt` 继续保留在 Adapter 目标映射表中;
2. 但若当前 ACP 路径缺少稳定触发源,不得为了本轮验收临时向 Runtime 或 `lineup-app-server` 注入 Hermes 私有桥接;
3. 验收应输出结构性限制记录,并给出后续 bridge / trigger 承接建议;
4. 只有在新增可控触发源后,才重新要求这两项的 fresh runtime evidence。
## 5. 测试与验收实现要求
### 5.1 `protocol.py` 自动化测试
至少新增以下用例:
1. `client.inventory` 接受包含独立 `tools[]` 投影的受控定义;
2. inventory 中含未知 tool 字段、handler、bundle/path/secret 时整体拒绝;
3. 合法 `tool.invoke` 能被规范化;
4. `tool.invoke` 缺少 revision、tool 不存在、顶层多字段、伪造内部 target 字段时拒绝;
5. `tool.invoke` 不会升级成 `app.call` 或其他 envelope
6. `clarify multi_select = true` 被稳定拒绝。
### 5.2 `core.py` 自动化测试
至少新增以下用例:
1. prompt 中包含当前 inventory 的 Agent Tool 投影;
2. 用户请求开始 / 停止专注时,模型合法输出可被收敛为 `lineup.v1.tool.invoke`
3. 非法 `tool.invoke` 降级为 `text`
4. approval / slash confirm / clarify / update prompt 能映射成标准 `tool.call`
5. `clarify other -> input` 走二段式闭环;
6. `multi_select` 请求被稳定拒绝;
7. 既有 `tool.call``app.call``ui.*``artifact.offer` 路径不回归。
### 5.3 `acp_client.py` 自动化测试
至少新增以下用例:
1. `session/request_permission` 不再一律直接取消,而是被投影为受控内部交互请求;
2. 不支持的 Hermes 请求会稳定拒绝;
3. ACP reader 线程不会把原始敏感参数写进日志;
4. adapter 关闭、超时、Hermes 退出时,pending request 能稳定收口。
### 5.4 Runtime 兼容测试
至少新增以下用例:
1. Runtime 协议 parser 接受 `lineup.v1.tool.invoke`
2. Tool Router 能把 `lineup.v1.tool.invoke` 路由到与旧请求类型相同的 MiniApp Tool 路径;
3. 至少一条普通 MiniApp Tool 路径和一条 Pomodoro 路径通过 `lineup.v1.tool.invoke` 跑通;
4. 旧请求类型的兼容测试不回归。
### 5.5 Fresh Evidence
`03.acceptance_review.md` 必须至少记录以下 fresh evidence
1. 真实用户语言触发 `pomodoro.start`Hermes 输出 `tool.invoke`Runtime 成功执行;
2. 真实用户语言触发 `pomodoro.interrupt`Hermes 输出 `tool.invoke`Runtime 成功执行;
3. 至少一种 Hermes approval / confirm / clarify 交互通过 LineUp 消息链路完成闭环;
4. 至少一种不在本轮范围内的 Hermes 请求被稳定拒绝,且未形成隐式授权。
补充约束:
- `slash_confirm` / `update_prompt` 只有在当前接入路径存在稳定触发源时,才要求 fresh runtime evidence
- 若当前接入路径不存在稳定触发源,则必须在验收文档中记录源码对照、运行期现象与后续 bridge 建议,作为关闭本轮验收时的正式结论之一。
## 6. 实施顺序建议
建议按以下顺序推进,避免多模块同时漂移:
1. 先改 `protocol.py`,冻结 inventory tools 投影和 `tool.invoke` 校验;
2. 再改 `core.py` prompt 与出站收敛,让 Pomodoro start / interrupt 通路能先跑通;
3. 再补 Runtime 对 `lineup.v1.tool.invoke` 的最小 parser / router 兼容与对应测试;
4. 再改 `acp_client.py``core.py` 的交互兼容层,完成 approval / clarify / confirm 闭环;
5. 最后补 `state.py`、自动化测试和 fresh evidence。
本阶段不要求改动 `lineup-app-server`。若联调时发现传输层对新 envelope 有硬阻塞,应先补设计评审,再决定是否将其升级为 `04A` 范围内改动。
@@ -0,0 +1,163 @@
# 04A.agent_tool_route 验收评审(中间记录,已被后续评审收敛)
**状态:** 中间记录;最终以 [05.acceptance_review.md](05.acceptance_review.md) 为准
**日期:** 2026-08-07
**评审对象:** `lineup-adapter/hermes/lineup/` 当前实现、[04A.agent_tool_route.md](04A.agent_tool_route.md)、[02.technical_implementation_spec.md](02.technical_implementation_spec.md)
**评审口径:**`04A` 主定义和实施规范为准,先记录当时已经拿到的自动化证据与已闭环子能力,再明确尚未满足的最终验收项。本文不是最终完成声明;后续 fresh runtime evidence、结构性限制记录与最终关闭结论,均已收敛到 [05.acceptance_review.md](05.acceptance_review.md)。
## 1. 当前已验证的实现
### 1.1 Runtime Agent Tool invoke 的 Adapter 侧基线已通过自动化测试
已具备并已由自动化测试覆盖的能力:
- `client.inventory` 接受独立 `tools[]` 顶层投影;
- `lineup.v1.tool.invoke` 已进入 Hermes Adapter 允许输出集合;
- `tool.invoke` 会绑定当前 conversation 的最新 `inventory_revision`
- 非法 `tool.invoke` 会被拒绝或降级,不允许伪造 Runtime 内部目标字段。
对应测试命令:
```bash
npm test -- \
src/runtime/app-management/miniapp-sdk.test.ts \
src/runtime/app-management/reference-miniapps.test.ts \
src/runtime/app-management/app-registry.test.ts \
src/runtime/inventory/client-inventory.test.ts \
src/runtime/coordination/tool-router.test.ts \
src/runtime/coordination/lineup-runtime.test.ts
```
2026-08-07 当前结果:
```text
Test Files 6 passed (6)
Tests 71 passed (71)
```
当前已通过自动化测试验证的点:
- Runtime `client.inventory` 以顶层 `tools[]` 发布 Agent Tool,不再把 Tool 塞进 `applications[].tools`
- Tool projection 从 MiniApp SDK Declaration 经 Manifest / Registry 产生,保留 `app_scope``tool_id`、description、`input_schema`、handling、delivery 与 activation requirement
- Pomodoro 的 `pomodoro.start` / `pomodoro.interrupt` 会进入顶层投影;禁用、升级、卸载会递增 revision 并撤销对应 Tool
- inventory 不泄露 `output_schema`、permissions、handler、bundle 或本地实现信息。
### 1.2 Hermes Adapter 已通过自动化测试消费同一顶层 Tool projection
对应测试命令:
```bash
python3 -m unittest \
lineup-adapter/hermes/lineup/tests/test_protocol.py \
lineup-adapter/hermes/lineup/tests/test_core.py \
lineup-adapter/hermes/lineup/tests/test_acp_client.py
```
2026-08-07 当前结果:
```text
Ran 32 tests in 0.022s
OK
```
已具备并已由自动化测试覆盖的能力:
- `client.inventory` 接受独立 `tools[]` 顶层投影;
- `lineup.v1.tool.invoke` 已进入 Hermes Adapter 允许输出集合;
- `tool.invoke` 会绑定当前 conversation 的最新 `inventory_revision`
- 非法 `tool.invoke` 会被拒绝或降级,不允许伪造 Runtime 内部目标字段。
### 1.3 Runtime 已接受 `lineup.v1.tool.invoke` 作为最小请求兼容入口
当前已拿到的 Runtime 自动化证据表明,`04A` 不再只是 Adapter 内部自洽,而是已经把新的请求入口真正接到了 `04.runtime_workspace` 既有执行链路上。
当前已通过自动化测试验证的点:
- Runtime parser 接受 `lineup.v1.tool.invoke`
- Tool Router 会把 `lineup.v1.tool.invoke` 路由到与旧请求类型相同的 MiniApp Tool 路径;
- 至少一条普通 bundled MiniApp Tool 路径已经通过 `lineup.v1.tool.invoke` 跑通;
- `pomodoro.start` 已通过 `lineup.v1.tool.invoke` 跑通 activation、foreground 确认、deadline 结算与最终结果;
- `pomodoro.interrupt` 已通过 `lineup.v1.tool.invoke` 跑通短 Tool 自身结果,以及对长 `pomodoro.start` 的中断收口;
- Runtime 对旧请求类型的兼容测试没有因此回归。
### 1.4 Hermes 交互兼容层的统一状态机已落地
本轮已在 Adapter 内实现并通过自动化测试验证以下兼容闭环:
```text
Hermes ACP session/request_permission
-> Adapter 投影为内部 permission request
-> Adapter 发出 lineup.v1.tool.call(choice)
-> 用户回传 lineup.v1.tool.result
-> Adapter resolve 回 ACP permission outcome
Adapter 内部 interactive request
-> Adapter 发出 lineup.v1.tool.call(choice | confirm | input)
-> 用户回传 lineup.v1.tool.result / tool.cancel
-> Adapter resolve 回 Hermes 内部 interaction outcome
```
当前已通过自动化测试验证的点:
- ACP permission request 会被解析为受控请求,而不是一律 `cancelled`
- Adapter 会为该请求生成标准 `tool.call(choice)`
- `tool.result` 中的受控选项会被映射回 ACP `optionId`
- 该交互会复用现有 `tool_calls` one-shot ledger,并配套持久化 `hermes_interactions` 映射;
- Adapter 重启恢复时,未完成的 Hermes 交互会被过期收口,而不是在新进程里继续假定可 resolve。
- `slash_confirm` 已能通过 `tool.call(choice)` 生成并回收 `once / always / cancel` 一类受控结果;
- `update_prompt` 已能通过 `tool.call(confirm)` 生成并回收 `y / n` 一类受控结果;
- `clarify` 已能覆盖单选 `choice`、纯文本 `input`、以及 `other -> input` 的二段式路径;
- `clarify multi_select = true` 当前会被 Adapter 稳定拒绝,不向 Host 发出不受支持的交互请求。
### 1.5 当前已新增的实现文件
- [acp_client.py](/home/gao/Development/lineup/lineup-adapter/hermes/lineup/acp_client.py)
- [core.py](/home/gao/Development/lineup/lineup-adapter/hermes/lineup/core.py)
- [state.py](/home/gao/Development/lineup/lineup-adapter/hermes/lineup/state.py)
- [test_acp_client.py](/home/gao/Development/lineup/lineup-adapter/hermes/lineup/tests/test_acp_client.py)
- [test_core.py](/home/gao/Development/lineup/lineup-adapter/hermes/lineup/tests/test_core.py)
- [client-inventory.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/inventory/client-inventory.ts)
- [miniapp-sdk.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/app-management/miniapp-sdk.ts)
- [app-registry.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/app-management/app-registry.ts)
- [tool-router.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/coordination/tool-router.ts)
- [lineup-v1.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/protocol/lineup-v1.ts)
- [tool-router.test.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/coordination/tool-router.test.ts)
- [lineup-runtime.test.ts](/home/gao/Development/lineup/lineup-app/tauri/src/runtime/coordination/lineup-runtime.test.ts)
### 1.6 Fresh Runtime / Browser Evidence2026-08-07
在重新安装当前工作树的 Hermes Adapter、重启 Adapter 并对 Host 执行强制刷新后,取得了新的真实运行期证据。该证据不是历史截图或旧消息:
- Host 新发送的 `client.inventory` 消息序号为 `1201``revision = catalog-1`
- 该 inventory 使用独立顶层 `tools[]` 投影,包含 `pomodoro.start``pomodoro.interrupt``task-dashboard.open/update``whiteboard.open/submit`
- 用户消息序号 `1202` 为真实用户输入:`开始一个 5 分钟的专注`
- 同一条启动调用 `call_id = call_5f9c2a7d1e` 收到 `accepted` 进度回执(消息序号 `1207`);
- 随后收到 `started` 进度回执(消息序号 `1209`),其中 `operation_id = pomodoro:operation_msiet305_zwa4ckb9f2`,并绑定到前台 Pomodoro instance
- 用户提供的页面截图显示“专注工作区”“5分钟专注”,倒计时从 `05:00` 进入 `04:56`,证明前台工作区已真实打开并运行;
- 用户消息序号 `1219` 为真实用户输入:`停止当前专注`
- 停止后收到两个 `lineup.v1.miniapp.tool.result` 终态:
- `call_id = call_7d3e9f2a1b`Pomodoro interrupt 短工具结果为 `status = interrupted`
- `call_id = call_5f9c2a7d1e`:原始 start 调用结果为 `state = interrupted`,同一个 `operation_id``pomodoro:operation_msiet305_zwa4ckb9f2``interruption_reason = agent_interrupt`
- 两个终态均由 Runtime 发送并被 Adapter 收到,证明 start / interrupt 的请求、进度、终态和 operation 关联在真实消息链路中闭环。
## 2. 当前尚未证明完成的验收项
以下项目仍未被当前证据证明完成,因此 `04A` 不能视为已验收通过:
- 虽然 `slash_confirm``clarify``update_prompt` 已具备 Adapter 侧自动化测试证据,但 Hermes ACP 当前是否会以可直接消费的真实请求形态发出这些 contract,尚未拿到运行期 fresh evidence
- 至少一种“本轮范围外 Hermes 请求被稳定拒绝”的运行证据尚未补入。
## 3. 当前结论
`04A` 已从“仅完成协议设计和 invoke parser”推进到“Runtime 已发布 Adapter 可消费的顶层 Tool inventoryAdapter 侧 invoke 基线完成,Runtime 已接受 `lineup.v1.tool.invoke` 请求入口,Hermes 交互兼容层已有统一状态机并具备多类自动化证据”的阶段。
但按主定义与实施规范的最终完成标准来看,本轮只能判定为:
```text
Runtime Tool inventory:已从 SDK / Manifest / Registry 端到端发布顶层 tools[],并有 71 个 Runtime 定向测试证据
MiniApp Tool invoke 基线:已具备 Adapter 侧实现与 32 个 Adapter 定向测试证据
Runtime 请求兼容:已具备 lineup.v1.tool.invoke 的 parser / router / 高价值集成测试证据
Hermes 交互兼容层:已完成 exec_approval、slash_confirm、clarify、update_prompt 的 Adapter 侧统一桥接与自动化覆盖
Hermes ACP 真源验证:当前仍需补充除 request_permission / exec_approval 外的 contract,及一种范围外请求的真实拒绝证据
真实 Pomodoro fresh evidence:已完成 start / interrupt;见 1.6
整体 04ARuntime inventory 与 Pomodoro start / interrupt 已完成真实 Host 验证;仍需补齐 Hermes 权限/交互真源和范围外请求拒绝证据,尚未完成最终验收
```
@@ -0,0 +1,87 @@
# 04A.agent_tool_route 联合评审(三)
**评审编号:** 03
**日期:** 2026-08-07
**评审对象:** [04A.agent_tool_route.md](04A.agent_tool_route.md)、[02.technical_implementation_spec.md](02.technical_implementation_spec.md)、[04.runtime_workspace.md](../04.runtime_workspace/04.runtime_workspace.md)、[05.technical_implementation_spec.md](../04.runtime_workspace/05.technical_implementation_spec.md)、[运行时与智能体工具.md](../../设计/02.正式方案/运行时与智能体工具.md)、[lineup-runtime-sdk-architecture.md](../../设计/02.正式方案/lineup-runtime-sdk-architecture.md)、[app_final_design.md](../../设计/02.正式方案/app_final_design.md)、[lineup-ui-surface-protocol.md](../../设计/02.正式方案/lineup-ui-surface-protocol.md)。
**评审方法:**`04A` 主定义与技术实施规范为候选实现基线,联合对照 `04.runtime_workspace` 已冻结实现,以及 `02.正式方案` 中涉及 Runtime、Inventory、Tool invoke、Compatibility Adapter 的条目,重点检查是否仍存在命名分叉、Inventory shape 误读或责任边界回退。
**总体结论:** 本轮联合评审未发现新的 Runtime / Adapter 架构冲突,`04A` 继续可以按 Adapter 侧补齐推进;但正式方案文档中还存在两处“历史表述与当前兼容实现并存”的误导点,需要在 `04A` 文档中明确消歧,否则实现者容易在协议名和 inventory shape 上走回旧分支。两项问题均已在本次评审中回填到 `04A` 主定义与技术实施规范:一是正式方案中的概念名 `tool.invoke` 需要在当前实现中统一落到 `lineup.v1.tool.invoke`;二是较早 `client.inventory` 示例未展示 `tools[]`,不能再被当作当前 Tool 投影的权威 shape。当前无未解决的 P0~P3 问题。
## 问题清单(Outline
状态标记:🔴 未解决,必须处理;🟡 已提出修复方向,尚未确认或未回填实施权威;✅ 已解决;⚪ 可延期但必须保留记录。P0~P3 必须在当前迭代处理;P4~P5 可以延期,但必须记录延期原因和重新评估条件。
| 状态 | 优先级 | 编号 | 问题 | 当前结论 / 下一步 |
|---|---:|---|---|---|
| ✅ | P2 | D04A-05 | 正式方案文档同时出现概念名 `tool.invoke` 与当前兼容信封 `lineup.v1.tool.invoke`,若 `04A` 不主动消歧,Adapter 实现、fixture 与验收可能各自使用不同名字。 | 已回填:`04A` 明确正式方案中的 `tool.invoke` 仅表示统一 Agent Tool 概念名;本轮实现、prompt、fixture、验收一律使用 `lineup.v1.tool.invoke`。 |
| ✅ | P2 | D04A-06 | `lineup-ui-surface-protocol.md` 的较早 `client.inventory` 示例未包含 `tools[]`,容易让实现者误以为 `04A` 的独立 Tool 投影 shape 与正式方案冲突。 | 已回填:`04A` 明确 Tool 独立投影的权威来源是 Runtime 正式方案和 `04.runtime_workspace`;较早 Surface 协议示例只作为 Surface / Capability 最小示意,不再作为 Tool shape 权威。 |
## 已确认的一致性
### `04A` 与 `04.runtime_workspace` 的 Runtime 事实保持一致
`04.runtime_workspace` 已冻结:Runtime 是 Tool Router、activation、lifecycle、outbox、Tool receipt / progress / final result 的唯一裁决者;Adapter 只做 inventory 绑定、顶层结构校验和 Hermes 私有交互收口。
本次复核没有发现 `04A` 重新把业务语义拉回 Adapter 或 `lineup-app-server` 的迹象。
### 正式方案允许当前实现继续使用 `lineup.v1.*` 兼容信封
[app_final_design.md](../../设计/02.正式方案/app_final_design.md) 已明确:当前 Tauri 实现与 golden fixture 仍使用 `lineup.v1.*` 类型,Runtime Compatibility Adapter 负责把旧实现映射到新 Runtime 边界。
因此 `04A``lineup.v1.tool.invoke` 作为当前 Adapter 落地,与正式方案并不冲突;真正需要避免的是在当前实现里再并行制造“裸 `tool.invoke`”第二种 envelope。
### Tool 独立投影与较早 Surface inventory 示例不再矛盾
[lineup-runtime-sdk-architecture.md](../../设计/02.正式方案/lineup-runtime-sdk-architecture.md) 和 [app_final_design.md](../../设计/02.正式方案/app_final_design.md) 都已把 Inventory 收敛为同时包含 App、Tool、Surface、Capability,且 Tool 调用必须携带 `inventory_revision`
[lineup-ui-surface-protocol.md](../../设计/02.正式方案/lineup-ui-surface-protocol.md) 中未展示 `tools[]``client.inventory` 示例,现应视作较早阶段围绕 Surface / Capability 的最小示意,而不是 04A 当前 Tool 投影的最终 JSON shape。
## D04A-05`tool.invoke` 概念名与 `lineup.v1.tool.invoke` 当前信封并存,易造成实现分叉
**已解决(2026-08-07,收敛决议)。** `04A.agent_tool_route.md``02.technical_implementation_spec.md` 已补充说明:正式方案中的 `tool.invoke` / `tool.result` 若未带前缀,应按统一 Agent Tool 概念名理解;`04A` 代码、prompt、fixture、验收证据统一采用当前兼容信封 `lineup.v1.*`
### 为什么这是 P2
如果不消歧,至少会出现以下风险:
```text
实现 AAdapter 代码使用 lineup.v1.tool.invoke
实现 B:测试夹具或 prompt 示例使用裸 tool.invoke
实现 C:验收人员按正式方案字面再补第三套命名解释
```
这不会推翻 Runtime 架构,但会直接影响自动化测试、联调日志、验收口径和后续迁移路径,因此需要在当前迭代关闭。
### 收敛决议
1. 当前实现、prompt、fixture、验收证据统一使用 `lineup.v1.tool.invoke`
2. 正式方案中的 `tool.invoke` 只作为“统一 Agent Tool invoke”概念名引用;
3. 若未来正式迁移到去前缀的新 Runtime Envelope,必须先回写正式方案和兼容迁移计划,不能在 `04A` 期间并行引入第二套可执行名字。
## D04A-06:较早 `client.inventory` 示例未展示 `tools[]`,容易误导 `04A` 的 Tool 投影判断
**已解决(2026-08-07,收敛决议)。** `04A.agent_tool_route.md``02.technical_implementation_spec.md` 已明确:`tools[]` 顶层投影的权威来源是 Runtime 正式方案与 `04.runtime_workspace` 已冻结实现;`lineup-ui-surface-protocol.md` 中较早示例不再作为 Tool shape 权威。
### 为什么这是 P2
当前正式方案文档内部其实存在时间差:
- Runtime 架构和最终方案已经收敛到 “Inventory 同时包含 App、Tool、Surface、Capability”;
- 较早 Surface 协议示例仍只展示了 `standard_components + applications + capabilities`
如果不明确权威顺序,后续实现者很容易错误得出以下结论:
```text
04A 新增 tools[] = 偏离正式方案
```
这会直接干扰 Adapter parser、测试夹具、评审判断和后续 Runtime 文档演进。
### 收敛决议
1. `04A` 当前以独立 `tools[]` 顶层投影为准;
2. `applications[]` 继续只表达 App / Surface 可见性;
3. 若后续要统一正式方案中的 inventory 示例,应在正式方案文档中统一回填,而不是让 `04A` 回退到 `applications[].tools` 或无 `tools[]` 的旧分支。
## 评审关闭条件
1. D04A-05 与 D04A-06 已回填 [04A.agent_tool_route.md](04A.agent_tool_route.md) 和 [02.technical_implementation_spec.md](02.technical_implementation_spec.md)
2. 后续代码实现、测试夹具和验收记录必须统一使用 `lineup.v1.*` 当前兼容信封;
3. 后续若补正式方案文档示例,应把 `client.inventory` 的 Tool 顶层投影同步回填到正式方案,而不是在 `04A` 再引入本地例外;
4. 本评审当前无未解决的 P0P3。
@@ -0,0 +1,103 @@
# 04A.agent_tool_route 联合评审(四)
**评审编号:** 04
**日期:** 2026-08-07
**评审对象:** [04A.agent_tool_route.md](04A.agent_tool_route.md)、[02.technical_implementation_spec.md](02.technical_implementation_spec.md)、[04.runtime_workspace.md](../04.runtime_workspace/04.runtime_workspace.md)、[05.technical_implementation_spec.md](../04.runtime_workspace/05.technical_implementation_spec.md)、[运行时与智能体工具.md](../../设计/02.正式方案/运行时与智能体工具.md)、[lineup-runtime-sdk-architecture.md](../../设计/02.正式方案/lineup-runtime-sdk-architecture.md)、[app_final_design.md](../../设计/02.正式方案/app_final_design.md),以及当前 `tauri/src/runtime/` 中与 Tool invoke / Tool Router / 回程 outbox 相关的实现。
**评审方法:**`04A` 主定义和实施规范为候选基线,联合对照 `04.runtime_workspace` 已冻结的 Runtime 行为、`02.正式方案` 中 Runtime Tool 模型的正式边界,以及当前 Tauri Runtime 的协议 parser / router / outbox 代码,重点检查“文档是否把 04A 的真实改动边界写实”以及“请求侧统一与回程侧兼容是否被错误混写”。
**总体结论:** 本轮联合评审发现两处新的 P2 级口径冲突,都会直接影响实现范围和验收判断,但都不是架构方向错误:第一,`04A` 文档先前把工作描述成“仅 Adapter 补齐”,与当前 Runtime 仍主要按旧请求类型接收 MiniApp Tool 调用的实现事实不符;要让 `lineup.v1.tool.invoke` 真正打通,必须补一个最小 Runtime 请求兼容层。第二,`04A` 文档先前把“统一 Agent Tool invoke”表述得过宽,容易让实现者误以为本轮还要把 Runtime 既有 MiniApp Tool 回程 envelope 一并收敛。结合 `04.runtime_workspace` 已冻结实现与当前代码现状,更准确的结论应是:`04A` 统一的是**请求入口**,回程仍沿用 Runtime 已冻结的兼容事实,并由 Adapter 解释为同一条 `call_id` 的 receipts / progress / final result。两项问题均已在本次评审中回填到主定义与实施规范;当前无未解决的 P0~P3 问题。
## 问题清单(Outline
状态标记:🔴 未解决,必须处理;🟡 已提出修复方向,尚未确认或未回填实施权威;✅ 已解决;⚪ 可延期但必须保留记录。P0~P3 必须在当前迭代处理;P4~P5 可以延期,但必须记录延期原因和重新评估条件。
| 状态 | 优先级 | 编号 | 问题 | 当前结论 / 下一步 |
|---|---:|---|---|---|
| ✅ | P2 | D04A-07 | `04A` 文档把实现口径写成“仅 Adapter 补齐”,但当前 Runtime 仍主要按旧请求类型接收 MiniApp Tool 调用;若不补一层 Runtime 请求兼容,`lineup.v1.tool.invoke` 无法真正端到端生效。 | 已回填:`04A` 改为“Adapter 为主、附带最小 Runtime 请求兼容补丁”;允许仅在 Runtime parser / router / 测试夹具上补 `lineup.v1.tool.invoke` 接受能力,不改写 `04.runtime_workspace` 已冻结的业务语义。 |
| ✅ | P2 | D04A-08 | `04A` 文档把“统一 Agent Tool invoke”描述得过宽,容易误导为本轮还要把 Runtime 既有 MiniApp Tool 回程 envelope 一并统一。 | 已回填:`04A` 当前统一的是请求入口;回程仍沿用 Runtime 已冻结且当前代码仍在发送的兼容事实,Adapter 只按同一 `call_id` 解释,不再新增第三套协议分支。 |
## 已确认的一致性
### `app-server` 仍可保持透明传输层
本次复核没有发现 `lineup-app-server` 需要理解 `lineup.v1.tool.invoke` 业务语义的证据。当前阻塞并不在传输层,而在 Runtime 本地 parser / router 对新请求类型的接受能力。
因此,用户此前关于“是否涉及 app-server”的判断仍成立:`04A` 不应扩大为 Go 服务端语义改造。
### `04A` 的 Runtime 改动属于兼容补丁,不是边界回退
`04.runtime_workspace` 已冻结的权威仍然是 Runtime 对 revision、schema、activation、foreground gating、operation、outbox 与恢复的唯一裁决。
这次要求的 Runtime 变更,只是让它在**请求入口**上接受 `lineup.v1.tool.invoke`;不改变 Tool Router 的核心裁决,也不把 Hermes 私有 contract 拉进 Runtime。
### 正式方案与当前实现可以通过“概念层 / 兼容层”两级解释保持一致
`02.正式方案` 中的 `tool.invoke / tool.result / progress` 描述的是统一 Agent Tool 模型。当前 Tauri Runtime 则仍保留 `lineup.v1.*` 兼容信封和已验收的 MiniApp Tool 回程 envelope。
因此,本轮正确做法不是宣称“现有代码已经和正式方案一字不差”,而是明确:
1. `lineup.v1.tool.invoke` 是当前实现中的统一请求入口;
2. 当前回程仍是 `04.runtime_workspace` 已冻结的兼容事实;
3. 未来若做回程统一迁移,需要单独立项,不应在 `04A` 中顺手扩大。
## D04A-07:若不补 Runtime 请求兼容,`tool.invoke` 只会停留在 Adapter 内部
**已解决(2026-08-07,收敛决议)。** `04A.agent_tool_route.md``02.technical_implementation_spec.md` 已明确:本轮实现主场仍在 Hermes Adapter,但允许在 `lineup-app/tauri/src/runtime/` 中补最小请求兼容层,使 Runtime 接受 `lineup.v1.tool.invoke`
### 为什么这是 P2
当前冲突并不只是“代码还没写”,而是文档口径会直接误导实施范围:
```text
文档口径:04A 仅改 Adapter
实际代码:Runtime 主要仍按旧请求类型接收 MiniApp Tool
结果:Adapter 即使正确发出 lineup.v1.tool.invoke,也无法端到端命中既有 Tool Router
```
这会让实现者在联调时误判为:
1. 需要回退到旧请求类型;
2. 需要让 Adapter 同时发两种请求;
3. 需要扩大到 app-server 语义改造。
这三种方向都偏离了本轮已经收敛的架构结论,因此必须在当前迭代文档里把 Runtime 的最小兼容补丁正名。
### 收敛决议
本轮已确认:
1. `04A` 仍是 Adapter 主导迭代;
2. Runtime 允许补 parser / router / 测试夹具级别的最小请求兼容;
3. 该兼容层必须同时保留 `04.runtime_workspace` 既有请求路径,避免回归历史验收;
4. 该兼容层不改变 Runtime 的 schema / lifecycle / persistence / result 语义;
5. `lineup-app-server` 仍不进入本轮范围。
## D04A-08:请求统一不等于本轮已完成回程统一
**已解决(2026-08-07,收敛决议)。** `04A.agent_tool_route.md``02.technical_implementation_spec.md` 已把描述收窄为:`lineup.v1.tool.invoke` 统一的是请求入口;回程继续沿用 Runtime 已冻结且当前实现仍在发送的兼容事实,Adapter 以同一 `call_id` 解释,不再新增第三套协议分支。
### 为什么这是 P2
如果不收窄这层表述,会出现两个危险后果:
```text
后果 A:实现者误以为 04A 必须顺手重构 Runtime 全部 miniapp.tool.progress/result/cancel
-> 范围扩张到超出本轮验收与回归预算。
后果 B:验收者误以为只要代码里仍出现 miniapp.tool.result,就说明 04A 与正式方案冲突
-> 会把“兼容事实尚在”误判成“架构方向错误”。
```
这不是语义吹毛求疵,而是直接关系到本轮是否还能保持“最小补丁闭环”的实现策略。
### 收敛决议
本轮已确认:
1. `04A` 不新增新的本地回程协议族;
2. Adapter 需要能把现有回程解释为同一条 Agent Tool 调用的 receipts / progress / final result
3. Runtime 既有回程 envelope 是否重命名,不在 `04A` 范围内;
4. 若未来推进回程统一,应以新的迭代和新的设计评审承接,而不是在 `04A` 中隐式扩大。
## 评审关闭条件
1. D04A-07 与 D04A-08 已回填 [04A.agent_tool_route.md](04A.agent_tool_route.md) 与 [02.technical_implementation_spec.md](02.technical_implementation_spec.md)
2. 后续实现与验收必须按“Adapter 主导 + Runtime 最小请求兼容 + app-server 不动”的边界推进;
3. 后续若要推动 Runtime 回程 envelope 统一,必须新增独立设计评审记录,不能在 `04A` 中以顺手修正方式扩容;
4. 本评审当前无未解决的 P0P3。
@@ -0,0 +1,417 @@
# LineUp App 迭代定义:04A Agent Tool 通路补齐
**迭代编号:** 04A.agent_tool_route
**状态:** 开发实施中,核心实现已完成,待最终验收
**日期:** 2026-08-07
**前置基线:** [04.runtime_workspace.md](../04.runtime_workspace/04.runtime_workspace.md)、[05.technical_implementation_spec.md](../04.runtime_workspace/05.technical_implementation_spec.md)、[09.acceptance_review.md](../04.runtime_workspace/09.acceptance_review.md)
**总体计划:** [plan.md](../plan.md)
**权威架构:** [运行时与智能体工具.md](../../设计/02.正式方案/运行时与智能体工具.md)、[lineup-runtime-sdk-architecture.md](../../设计/02.正式方案/lineup-runtime-sdk-architecture.md)
## 1. 迭代定位与目标总览
`04.runtime_workspace` 已经完成了 Runtime、Pomodoro MiniApp、Host 工作区、Lifecycle、Inventory 发布与端到端 Host 验收;
但它证明的是“Runtime 这一侧怎样工作”,并没有真正补齐 “Agent 如何拿到这些 Tool,并把它们变成稳定可执行调用”。
因此 `04A` 不是重做 Pomodoro,也不是继续扩展 Host 工作区,而是补齐 **Agent / Adapter 这一半链路**,让用户能在真实会话中:
- 通过自然语言启动 / 中断 Pomodoro
- 通过标准交互完成 Hermes 需要的确认、澄清与审批;
- 全程只暴露 LineUp 自己定义的协议,不把 Hermes 私有 system message 直接暴露到产品面。
本次会话已将 `04A` 的工作收敛为 **以 Adapter 为主、附带最小 Runtime 请求兼容补丁的两个核心目标**
1. **MiniApp Tool 通路补齐**
- 让 Hermes Adapter 正确消费 Runtime 发布的 Agent Tool inventory
- 让 Agent 能生成合法的统一 Agent Tool invoke 请求;
- 让 Runtime 接受 `lineup.v1.tool.invoke` 这一新的请求入口,并继续基于 `inventory_revision`、schema、scope 与 lifecycle 执行 `pomodoro.start` / `pomodoro.interrupt`
2. **Hermes 交互兼容层补齐**
- 让 Hermes 内置的 approval / clarify / update prompt 等交互意图,不再依赖 Hermes 私有 permission request
- 让它们被 Adapter 收敛为 LineUp 已定义的 `tool.call` / `tool.result` / `app.call` / `app.result`
- 让用户的选择通过 LineUp 自己的可靠消息链路回传给 Hermes Adapter,再 resolve 回 Hermes 内部状态。
为便于后续评审与实施,本文按以下层次展开:
1. 先冻结 `04A` 的架构边界与分层原则;
2. 再分别定义目标 A 与目标 B 的当前缺口、协议结论与责任分界;
3. 最后给出范围、完成标准与交付物。
## 2. 架构边界与分层原则
本迭代后续的设计评审、技术规范与实现,都以以下分层原则为准。
### 2.1 Runtime 仍是执行权威
- `04.runtime_workspace` 已冻结的 `inventory revision``app_scope`、schema、lifecycle 与 Pomodoro 裁决语义继续作为权威;
- `04A` 不重新定义 `pomodoro.start` / `pomodoro.interrupt` 的运行规则;
- Adapter 不复制 Runtime 的业务状态机,只负责把 Agent 侧输入收敛到 Runtime 可接受的协议边界;
- 但为让 `lineup.v1.tool.invoke` 真正打通,本轮允许对 Runtime 做**最小请求兼容补丁**:只补 parser / router / 测试夹具对新请求信封的接受能力,不重写 `04.runtime_workspace` 已冻结的 Tool 执行、持久化或结果语义。
### 2.2 Agent 平台私有协议兼容责任位于 Adapter 层
- Hermes、未来的 Open Claw 或其他 Agent 平台,可能各自带有不同的 system message、approval 流程或工具调用约定;
- 这些差异属于“具体 Agent 平台如何适配 LineUp 协议”的问题,应在对应平台的插件 / Adapter 层完成收敛;
- Runtime 只接收已经进入 LineUp 协议边界的受控消息,不承担理解各家 Agent 私有协议的责任。
这一原则与 Hermes 既有飞书接入经验一致:
- 飞书 approval 不是让模型自己产出飞书协议;
- 而是 Hermes gateway / Feishu adapter 把内部 approval 事件转换成飞书 interactive card
- 再把按钮回调解析为 Hermes 内部 approval 结果。
`04A` 对飞书的借鉴,是 **adapter 负责转换与 resolve 的分层模式**,不是 webhook 或卡片 schema 本身。
### 2.3 LineUp 内部不依赖公网 webhook 回传
- LineUp 当前不是一个可被公网平台回调的 Bot 平台;
- 因此 04A 不设计 “平台回调 URL -> Adapter 收到 HTTP webhook” 这一链路;
- 用户选择应走现有 IM 长连接 / 消息同步链路回传。
对于 LineUp 内部交互,统一采用如下闭环:
```text
Adapter 发起受控协议
-> Host / Interact 渲染
-> 用户选择
-> Host 通过 tool.result / tool.cancel / app.result 回写
-> Adapter 消费结果并 resolve 回 Hermes 内部状态
```
### 2.4 `lineup-app-server` 在本轮默认视为透明传输层
- 当前 `lineup-app-server` Go 服务主要负责消息收发、base64 编解码与 WuKongIM API 对接;
- 它不解析 `lineup.v1` 的业务语义;
- 因此 04A 默认不要求改动 `lineup-app-server` 服务端来适配 Agent Tool invoke 或 Tool inventory 投影。
### 2.5 Runtime 仅补“请求侧兼容”,不在本轮改写回程协议
- `04.runtime_workspace` 已冻结并已验收的 Runtime 代码,当前仍保留一套面向 MiniApp Tool 的兼容回程 envelope
- `04A` 当前要解决的是“Agent 如何以统一入口发起 Runtime Agent Tool 调用”,因此本轮只要求 Runtime 接受 `lineup.v1.tool.invoke` 请求;
- 对于回程,Adapter 必须继续兼容 Runtime 现有的 receipts / progress / final result 事实,并按同一 `call_id` 解释为同一条 Agent Tool 调用;
- 是否将现有回程 envelope 进一步统一为新的 `lineup.v1.tool.*` 族,不属于 `04A` 当前收敛范围,后续若要推进,必须单独立项并补设计评审。
### 2.6 正式方案中的“概念协议名”与当前 `lineup.v1.*` 信封兼容
- `02.正式方案` 中部分 Runtime 文档会用 `tool.invoke` / `tool.result` 这样的概念名描述统一 Agent Tool 模型;
- 但当前 Tauri / Runtime 兼容实现仍以 `lineup.v1.*` 作为真实线上信封,这是 [app_final_design.md](../../设计/02.正式方案/app_final_design.md) 已保留的兼容事实;
- 因此 `04A` 在 Adapter 侧的实际落地继续采用 `lineup.v1.tool.invoke`,其语义对齐正式方案中的统一 Agent Tool invoke,而不是新增第二套协议;
- 本轮不得在代码或验收中同时引入“裸 `tool.invoke`”与 `lineup.v1.tool.invoke` 两条并行通路。
## 3. 目标 AMiniApp Tool 通路补齐
### 3.1 当前缺口
当前与目标 A 直接相关的缺口如下。
1. **Hermes inventory 解析模型落后于 Runtime**
- 正式方案要求 Runtime Inventory 同时包含 App、Tool、Surface 与 Capability
- `04.runtime_workspace` 的具体实现也已明确 Tool 由 Registry 独立投影进入 Runtime Tool Registry / Agent Inventory
- 但 Hermes Adapter 仍主要按旧的 App / Surface 结构理解 `client.inventory`,无法稳定消费 Runtime 发布的 Tool 投影。
2. **Hermes 协议白名单缺少统一 Agent Tool invoke**
- 当前 Hermes 允许的结构主要是:
- `lineup.v1.tool.call`
- `lineup.v1.app.call`
- `lineup.v1.ui.open / patch / close`
- `lineup.v1.artifact.offer`
- 还没有面向 Runtime Agent Tool 的统一 invoke 输出路径。
3. **Prompt 与测试尚未把 MiniApp Tool 变成可规划对象**
- 当前 prompt 只约束 capability request,不指导模型基于 Runtime 发布的 Tool Inventory 生成
`pomodoro.start` / `pomodoro.interrupt`
- Adapter 测试也没有覆盖 “inventory -> tool.invoke -> revision-bound invoke” 这条链路。
### 3.2 目标说明
让 Hermes Adapter 真正具备以下能力:
- 读取由 Runtime 发布的最新 Tool Inventory
- 识别哪些 MiniApp Tool 当前可调用;
- 在用户自然语言触发时生成合法的统一 Agent Tool invoke 请求;
- 携带正确的 `inventory_revision``app_scope``tool_id` 与受 schema 约束的 `input`
- 由 Runtime 正确执行并回传进度 / 最终结果。
首个验收对象固定为 Pomodoro:
```text
“开始一个 5 分钟的专注”
-> Hermes 生成 pomodoro.start
-> Runtime 创建并启动 Pomodoro
-> 用户看到前台专注工作区
“停止当前专注”
-> Hermes 生成 pomodoro.interrupt
-> Runtime 中断当前 Pomodoro
-> 用户回到 Interact 并收到稳定结果
```
### 3.3 协议冻结
本轮不再为 MiniApp Tool 新增第二套独立协议,而是沿用正式方案的统一 Agent Tool 调用模型:
- `app.call` 继续只表示 Host capability 请求;
- Runtime Agent Tool 统一走 `lineup.v1.tool.invoke`
- 旧的 `lineup.v1.tool.call(choice | confirm | input)` 继续保留为 Interact 标准交互兼容入口,不承担 Runtime Agent Tool invoke 语义;
- `tool.invoke` 在本轮只统一**请求入口**;其回程继续复用 `04.runtime_workspace` 已冻结且当前 Runtime 仍在发送的 Agent Tool 回程事实,并始终以同一个 `call_id` 关联。
建议冻结的最小 payload 形态如下:
```json
{
"v": 1,
"type": "lineup.v1.tool.invoke",
"payload": {
"call_id": "call_...",
"inventory_revision": "inv_...",
"app_scope": "pomodoro",
"tool_id": "pomodoro.start",
"input": {
"duration_seconds": 300,
"activity": "冥想"
}
}
}
```
也就是说,对 `lineup.v1.tool.invoke`
- Runtime 仍可先回传 `accepted / starting` 一类协议回执;
- 在启动成功后继续回传 `started` 或其他已冻结的受控进度;
- 最终仍只回传一次符合该 Tool result schema 的业务结果;
- Hermes Adapter 需要把这些回程继续视作“同一条 Agent Tool 调用的 receipts / progress / final result”;
- 本轮不要求 Runtime 把既有 MiniApp Tool 回程 envelope 全量改名;Adapter 只是不再新增第三套协议分支。
### 3.4 Inventory 最小投影
Hermes 侧只保留用于规划与出站校验的最小 Tool 投影。
该投影在 Inventory 中应独立于 `applications` 发布;`applications` 继续只表达 App / Surface 可见性。
每个工具至少保留以下字段:
- `app_scope`
- `tool_id`
- `description`
- `input_schema`
- `handling`
- `delivery`
- `activation_requirement`
这里的权威来源依次是:
- `02.正式方案` 中 Runtime / Tool / Inventory 的正式边界;
- `04.runtime_workspace` 已冻结的 Runtime Tool Registry / Agent Inventory 落地;
- `lineup-ui-surface-protocol.md` 中较早的 `client.inventory` 示例只能视为 Surface / Capability 最小示意,不再作为 Tool 投影 shape 的权威。
以下内容不进入 Hermes prompt 或 Adapter 持久上下文:
- `output_schema`
- 内部 handler
- bundle / path / secret
- 宿主实现细节
### 3.5 Adapter 与 Runtime 的责任分界
Adapter 负责:
- 仅接受当前 conversation 最近一次有效 inventory 中存在的 MiniApp Tool
- 校验 `call_id / inventory_revision / app_scope / tool_id / input` 顶层结构;
- 拒绝模型伪造 `sender / target / instance_id / operation_id / app_session_id` 等字段;
- 把合法 `tool.invoke` 原样交给 Runtime。
Runtime 继续负责:
- 最新 revision 对账;
- schema 校验;
- lifecycle / activation / foreground_required / activation_not_required 语义;
- 幂等、持久化、进度与最终结果。
本轮 Runtime 额外需要补上的,只是让上述执行链路接受 `lineup.v1.tool.invoke` 这一请求入口;它不是一项新的业务责任。
## 4. 目标 BHermes 交互兼容层补齐
### 4.1 当前缺口
当前与目标 B 直接相关的缺口如下。
1. **Hermes 内置 permission / system message 尚未收敛到 LineUp 协议**
- 当前 ACP 通道会向 Adapter 发出 `session/request_permission` 一类 Hermes 内置请求;
- 现有 Adapter 为避免越权,直接返回 `cancelled`
- 用户无法稳定完成这类交互,也没有一个明确的产品化协议边界。
2. **Hermes 的通用 interactive contract 尚未在 LineUp 中落位**
- Hermes 现有平台适配已稳定使用若干通用交互语义:
- `exec_approval`
- `slash_confirm`
- `clarify`
- `update_prompt`
- 但 LineUp 侧还没有把它们统一映射到 `choice / confirm / input / app.call`
### 4.2 目标说明
让 Hermes 内置的确认、澄清与审批意图不再依赖 ACP 私有 permission prompt,而是通过 LineUp 已定义协议完成:
- 标准交互:`lineup.v1.tool.call`
- 标准交互结果:`lineup.v1.tool.result` / `lineup.v1.tool.cancel`
- 宿主能力调用:`lineup.v1.app.call`
- 宿主能力结果:`lineup.v1.app.result`
### 4.3 本轮一次性实现的兼容层上限
参考 Hermes 现有 Feishu / Relay / Telegram / Slack / WhatsApp 适配实现,本轮一次性抽象的“通用交互语义层”限定为四类:
1. `exec_approval`
2. `slash_confirm`
3. `clarify`
4. `update_prompt`
其中 `clarify``04A` 的兼容范围进一步冻结为:
- 单选 `choice`
- 单选后进入 `other -> input` 的二段式澄清
- 纯文本输入型澄清
当前不进入本轮兼容范围的 `clarify` 变体:
- `multi_select = true`
- 任意要求 Host 原生复选组件或一次回收多值的私有交互
不在本轮一起实现的内容:
- 平台特有卡片样式、按钮布局、reaction ack、thread 元数据;
- 任意 Hermes 私有 system message 的泛化透传;
- Runtime 侧对 Hermes 私有 prompt kind 的直接理解。
### 4.4 Hermes -> LineUp 映射表
| Hermes contract | 典型语义 | LineUp 发起协议 | 用户可见交互 | 用户回传协议 | Adapter resolve 目标 |
|---|---|---|---|---|---|
| `exec_approval` | 危险操作审批;典型选项 `once / session / always / deny` | `lineup.v1.tool.call` | `choice` | `lineup.v1.tool.result` / `lineup.v1.tool.cancel` | Hermes 内部 approval 结果:`once` / `session` / `always` / `deny` |
| `slash_confirm` | slash command 或系统级动作确认;典型选项 `once / always / cancel` | `lineup.v1.tool.call` | `choice`,必要时可退化为 `confirm` | `lineup.v1.tool.result` / `lineup.v1.tool.cancel` | Hermes 内部 slash-confirm / confirm resolve |
| `clarify` | 多项澄清选择;可包含 `other` | `lineup.v1.tool.call` | 首轮 `choice`;若选 `other`,再发 `input` | `lineup.v1.tool.result` / `lineup.v1.tool.cancel` | Hermes 内部 clarify resolve`other` 分支进入后续文本 / 输入回填 |
| `update_prompt` | 更新、重载、恢复等 `y / n` 询问 | `lineup.v1.tool.call` | `confirm` | `lineup.v1.tool.result` / `lineup.v1.tool.cancel` | Hermes 内部 update prompt resolve |
| Host capability approval | 打开链接、写剪贴板、选文件、保存 artifact 等宿主能力授权 | `lineup.v1.app.call` | Host 原生确认界面 / 受控授权组件 | `lineup.v1.app.result` | Hermes capability result / approval result |
### 4.5 交互兼容层冻结规则
- `exec_approval``slash_confirm``clarify``update_prompt` 都属于 Adapter 层把 Hermes 交互语义收口到 LineUp 标准协议的范畴;
- 这四类 contract 在 Runtime 看来都只是 LineUp 的标准交互或 capability 调用,不暴露 Hermes 私有 prompt kind
- `choice` 的 option id 必须由 Adapter 生成并受控映射,不能直接把 Hermes 或平台侧的任意 callback token 透传给 Host
- `clarify``other` 路径由 Adapter 显式驱动第二次 `input`,而不是要求 Runtime 理解 Hermes 的“等待自由文本”内部状态;
- Hermes `clarify` 若携带 `multi_select = true`,本轮必须稳定拒绝并返回受控 unsupported / not_supported 结果,不能静默降级成单选或自由文本;
- Host 对单选卡片回传唯一的 `action_id` 时,Adapter 必须将其严格规范化为内部的单元素 `action_ids`,再映射回 Hermes;未知、缺失、重复或多于一个选项仍必须拒绝;
- 若某个 Hermes 私有请求不能落在上表任一项中,则本轮默认稳定拒绝,而不是新增第五种临时协议。
### 4.6 当前接入路径下的验证边界
截至 2026-08-07,本轮真实联调采用的是 LineUp 1420 页面 + `hermes acp` 的接入路径。
在这条路径上,`exec_approval``clarify` 已有可重复的运行期事件源,因此完成了 fresh evidence;但 `slash_confirm``update_prompt` 的原生触发源仍主要位于 Hermes gateway / native platform adapter 路径,而不是当前 ACP server request 流。
因此本轮对这两类 contract 的结论冻结为:
- **设计上保留映射目标**:它们继续属于 Adapter 兼容层应该承接的通用 contract;
- **实现上不放到 Runtime / app-server**:不能为了补证据把 Hermes 私有语义下沉到 Runtime 或 `lineup-app-server`
- **验收上不继续做 1420 盲测**:若当前 ACP 路径没有稳定 trigger source,则记录为结构性证据限制,而不是把它当成 04A 尚未修复的代码缺陷;
- **后续若要补 fresh evidence**:需要单独增加 bridge / trigger path,或引入可控的 gateway-native 事件源再复验。
## 5. 本迭代范围
### 5.1 范围内
1. **扩展 Hermes inventory 解析**
- 接受独立的 Tool inventory 投影;
- 为每个工具保留最小声明信息;
- 收到包含 Tool 投影的有效 inventory 后,不再退回 `revision = none`
2. **补齐统一 Agent Tool invoke**
- 为 Hermes Adapter 增加受控 Agent Tool 输出通路;
- 完成顶层结构校验与 inventory 绑定;
- 禁止伪造 Runtime 内部目标字段。
3. **补全 prompt 约束**
- inventory 中的 MiniApp Tool 才可调用;
- 必须使用最新 revision
- capability 与 MiniApp Tool 是不同通路;
- `pomodoro.start` / `pomodoro.interrupt` 的输入形态与边界明确;
- 不得依赖 Hermes / ACP 私有 permission prompt 或 system message 完成用户授权。
4. **补全 Hermes 交互兼容层**
-`exec_approval``slash_confirm``clarify``update_prompt` 映射到 LineUp 协议;
- 让用户选择通过既有消息流回传,再由 Adapter resolve 回 Hermes
- 明确 `clarify` 当前只兼容单选 / `other -> input` / 纯文本,`multi_select` 稳定拒绝;
- 对当前协议无法安全表达的 Hermes 私有请求,稳定拒绝。
5. **补全联调测试与真实证据**
- inventory 解析测试;
- `tool.invoke` 规范化测试;
- prompt / adapter 输出约束测试;
- Runtime 对 `lineup.v1.tool.invoke` 的 parser / router 兼容测试;
- Hermes 内置 permission request 处置测试;
- approval / clarify / confirm 回传闭环测试;
- Pomodoro 开始 / 停止两条真实链路 fresh evidence。
-`slash_confirm` / `update_prompt`,若当前接入路径无稳定触发源,则必须输出结构性限制记录,而不是继续把它们列为“待盲测补证据”。
6. **补充文档与验收口径**
- 明确 `04A``04.runtime_workspace` 的关系;
- 明确 Adapter 校验边界,避免与 Runtime 的 schema / lifecycle 责任重复;
- 明确“Agent 平台私有协议的兼容责任位于各自 Adapter 层”的架构结论。
### 5.2 不在范围内
- 新的 MiniApp 业务功能;
- 应用市场、远程下载和签名安装链路;
- 多 Agent、语音或视频能力;
- Pomodoro 统计、历史分析、暂停 / 继续;
- 改写 `04.runtime_workspace` 的 Runtime 生命周期语义;
- 为适配 Agent Tool invoke 改造 `lineup-app-server` Go 服务端;
- 在本轮内把 Runtime 既有 MiniApp Tool 回程 envelope 全量重构为新的协议族;
- 为 Web Chat 参考前端增加新的协议可视化,除非后续验收证据明确要求;
- 在 Runtime 中增加面向 Hermes、Open Claw 等具体 Agent 平台的私有协议特判。
## 6. 完成标准
满足以下条件时,`04A` 可视为完成:
```text
Hermes Adapter 能正确解析包含独立 Tool 投影的 Runtime inventory
Hermes 不再把这类 inventory 退回 revision=none 的默认上下文;
模型在用户请求开始/停止专注时,可以合法产出统一 Agent Tool invoke 请求;
Adapter 能基于当前 conversation 的最新 inventory 校验并规范化 tool.invoke,再交给 Runtime
Runtime 能基于最新 inventory revision 执行 pomodoro.start / pomodoro.interrupt
Runtime 已接受 `lineup.v1.tool.invoke` 作为新的请求入口;
tool.invoke 的协议回执、进度与最终业务结果继续复用既有 Agent Tool 回程语义;本轮不再新增第三套协议分支,也不要求立即重构 Runtime 既有回程 envelope
Hermes 不再依赖 ACP 私有 permission/system message 作为 LineUp 产品中的主授权路径;
当前可由 LineUp 协议表达的 Hermes 交互意图,已明确收敛到 tool.call / app.call / tool.invoke 之一;
Hermes clarify 的单选、`other -> input` 与纯文本路径可稳定闭环;`multi_select` 请求会被稳定拒绝,不发生静默降级;
当前不可安全表达的 ACP permission request,会被稳定拒绝且不会形成隐式授权;
若 `slash_confirm` / `update_prompt` 在当前 ACP 接入路径下缺少安全、稳定、可重复的触发源,则需有明确的结构性限制记录与后续 bridge 承接建议,不能再被当作未修复代码缺陷挂起;
真实用户语言 -> Agent tool invoke -> Runtime -> Pomodoro 的“开始专注”和“停止专注”两条链路都至少完成一条 fresh evidence
现有 standard interaction、app.call、ui.* 和 artifact.offer 路径不回归;
所有新增 P0P3 评审问题清零。
```
## 7. 建议交付物
建议至少产生以下文档与实现产物:
```text
迭代/04A.agent_tool_route/
04A.agent_tool_route.md
01.design_review.md
02.technical_implementation_spec.md
03.acceptance_review.md
```
以及代码层面的对应修改:
```text
lineup-adapter/hermes/lineup/protocol.py
lineup-adapter/hermes/lineup/core.py
lineup-adapter/hermes/lineup/acp_client.py
lineup-adapter/hermes/lineup/tests/
lineup-app/tauri/(用于最小 Runtime 请求兼容补丁与对应测试)
```
## 8. 当前结论
`04.runtime_workspace` 没有失败,它已经完成了 Runtime / Host / Pomodoro 这一侧的设计与实现闭环。
`04A.agent_tool_route` 的任务是把这一闭环真正接到 Agent / Adapter 入口上,并把 Hermes 的必要交互收敛到
LineUp 自己定义的协议里,使用户能够通过自然语言稳定使用已实现的 Pomodoro Tool。
@@ -0,0 +1,498 @@
# 04A.agent_tool_route 验收评审(五)
**状态:** 已完成本轮联合验收,A04A-09 / A04A-10 / A04A-11 已通过 fresh runtime 验证;`slash_confirm` / `update_prompt` 已转为当前 ACP 接入路径下的结构性证据限制记录
**日期:** 2026-08-07
**评审对象:** `lineup-adapter/hermes/lineup/` 当前实现、[04A.agent_tool_route.md](04A.agent_tool_route.md)、[02.technical_implementation_spec.md](02.technical_implementation_spec.md)、实际 LineUp Host / Hermes Adapter 运行状态
**评审方法:** 将自动化测试与真实消息链路证据交叉核对;本轮重点复核 Hermes `session/request_permission` 投影后的单选结果是否与 Host 实际发送的 `lineup.v1.tool.result` 形状一致。
**总体结论:** 已发现并修复三个会破坏 04A 交互边界的 P2 缺陷。权限卡片闭环、`clarify other -> input`、纯文本 `clarify``multi_select` 稳定拒绝,以及至少一种范围外稳定拒绝的 fresh evidence 均已通过。结合 `02.正式方案``04.runtime_workspace` 与当前实际接入实现复核后,可以确认 `slash_confirm``update_prompt` 在本轮 1420 + `hermes acp` 接入路径下缺少安全、稳定、可重复的运行期触发源;它们当前不应继续按“未修复缺陷”处理,而应作为后续 bridge / trigger 能力的小迭代承接项记录。
## 问题清单(Outline
状态标记:🔴 未解决,必须处理;🟡 已修复但等待运行期确认;✅ 已解决;⚪ 可延期但必须保留记录。
| 状态 | 优先级 | 编号 | 问题 | 当前结论 / 下一步 |
|---|---:|---|---|---|
| ✅ | P2 | A04A-09 | Host 单选卡片实际回传 `result.action_id`Adapter 只接受 `result.action_ids[]`,导致用户点击允许后结果被拒绝,Hermes 交互保持 pending 并最终超时。 | 已在 `protocol.py` 增加严格单选规范化;真实 Host 验证通过,交互 `call_b9c089f04ec44e19bbffca63ddc66c17``resolved`Hermes 已收到 `allow_once`,目标文件已创建。 |
| ✅ | P2 | A04A-10 | `clarify other -> input` 的第二段输入卡片真实回传 `result.text`Adapter 只接受 `result.values.{field_id}`,导致用户提交后卡片失效但 Hermes 未被解除阻塞。 | 已在 `protocol.py` 增加单字段输入 `text -> values.{field_id}` 严格规范化;新的 1420 页面 fresh 复验已通过,`call_input_task_01``completed`Hermes 已确认收到 `验收 04A other-input`。 |
| ✅ | P2 | A04A-11 | 04A 要求 `clarify multi_select = true` 稳定拒绝,但 Hermes 主回复提示词和 `tool.call` 校验仍允许模型直接发出 `multi-choice` 卡片,导致多选请求被错误放行。 | 已移除 Adapter 出站层对 `multi-choice` 的接受并更新回归测试;新的 1420 页面 fresh 复验已确认“不再出现多选卡片”,Hermes 改为明确提示“LineUp 卡片只支持单选,不支持多选”。 |
## 1. 真实失败证据
测试请求为:
```text
请在 /tmp/lineup-permission-test.txt 写入一行内容:LineUp permission test
```
真实记录:
- 用户请求消息序号:`1267`
- Adapter 创建权限交互:`call_72f3571755a94aff909d40db4ef53936`
- 交互类型:`exec_approval`
- 选项映射:`action_01 -> allow_once``action_02 -> deny`
- 用户点击后的消息序号:`1270`
- Host 实际回传:
```json
{
"call_id": "call_72f3571755a94aff909d40db4ef53936",
"status": "completed",
"result": {
"action_id": "action_01"
}
}
```
修复前该消息被记录为 `invalid hermes interaction response``tool_audit`
`result / rejected`,交互仍为 `pending`,目标文件不存在,原始 Hermes 请求最终为
`Hermes ACP response timeout`。旧交互在 Adapter 重启恢复时已被正确标记为
`aborted`,不能继续重复操作。
## 2. 修复与自动化证据
修复内容:
- `validate_tool_response()``mode = single-choice` 且不存在 `action_ids` 时接受唯一字符串 `action_id`
- 统一规范化为内部 `action_ids = [action_id]`
- 保留 inventory/schema 边界,未知、缺失、非字符串、多选及重复选项继续拒绝;
- Hermes resolve 继续只消费规范化后的内部形状,因此不新增第二套 resolve 分支。
自动化证据:
```bash
PYTHONPATH=lineup-adapter/hermes python3 -m unittest \
lineup-adapter/hermes/lineup/tests/test_protocol.py \
lineup-adapter/hermes/lineup/tests/test_core.py \
lineup-adapter/hermes/lineup/tests/test_acp_client.py \
lineup-adapter/hermes/lineup/tests/test_state.py
```
结果:
```text
Ran 38 tests
OK
```
`git -C lineup-app diff --check` 已通过。修复后的插件已经安装到运行目录
`/home/gao/.hermes/plugins/lineup/`,当前 Adapter PID 为 `3895319`
## 3. 待完成的 fresh 验证
需要重新发起一次低风险 `/tmp` 写入请求,并确认:
1. 新权限卡片可以交互;
2. 点击 `Allow edit` 后,`hermes_interactions.state` 变为 `resolved`
3. ACP responder 收到 `allow_once`
4. 原始 Hermes 请求最终完成,不再出现权限超时;
5. `/tmp/lineup-permission-test.txt` 被创建且内容正确;
6. 同一结果重复回传不会二次 resolve。
上述 fresh evidence 已取得,`A04A-09` 已关闭;04A 整体仍保持未完成,直到其他
强制验收项补齐。
## 4. A04A-09 Fresh Runtime Evidence2026-08-07
修复后的 Adapter 重启为 PID `3895319`。用户重新发起同一低风险写入请求并点击
新权限卡片的 `Allow edit` 后,取得以下证据:
- 新的 `exec_approval` call`call_b9c089f04ec44e19bbffca63ddc66c17`
- `hermes_interactions.state = resolved`
- 对应 `tool_calls.state = completed`
- `tool_audit``result` disposition 为 `accepted`
- Hermes ACP 实际完成了 `write_file`,工具消息记录 `bytes_written = 22`
- Hermes 随后返回完成文本;
- `/tmp/lineup-permission-test.txt` 已创建,文件大小 `22` 字节,内容为:
```text
LineUp permission test
```
这证明真实 Host 的单选 `action_id` 回传已经被 Adapter 规范化并 resolve 回
Hermes,且没有再次出现 `Hermes ACP response timeout`
## 5. 当前验收结论
- `clarify` 单选、`other -> input`、纯文本输入、`multi_select` 稳定拒绝与至少一种范围外稳定拒绝均已具备 fresh evidence
- 当前自动化门禁:Adapter `39 tests`、Runtime 定向测试 `71 tests`、Tauri `tsc --noEmit + Vite build``git -C lineup-app diff --check` 均通过;
- 本轮已发现的 P0-P3 缺陷均已关闭;
- `slash_confirm``update_prompt` 不再作为 04A 当前实现的阻塞项,原因见第 13 节“当前 ACP 接入路径下的结构性证据限制”。
## 6. Clarify Single-Choice Fresh Runtime Evidence2026-08-07
Adapter 重启后,用户在 1420 页面实际发送:
```text
请先让我从“专注工作”和“休息”两个选项中选择一个,再继续后续操作。
```
Hermes 生成了真实标准交互:
- `call_id = call_8f3a2c91d4e6b7a0`
- `tool = choice`
- `mode = single-choice`
- `action_ids = ["focus", "rest"]`
- Host 回传消息序号:`1287`
- 实际回传 `result.action_id = "focus"`
- Adapter `tool_audit``call / accepted``result / accepted`
- 对应 `tool_calls.state = completed`
- Hermes 最终回复已明确收到“专注工作”选择。
这证明真实 Hermes -> LineUp `tool.call`、用户选择 -> `tool.result`
Adapter ledger -> Hermes resolve 的单选路径已闭环,并再次验证 Host 的
`action_id` 形状兼容。
同一次会话的 Hermes 回复指出当前 inventory 为 `revision:none`。这是因为 Adapter
重启后 Host 尚未重新下发新的 `client.inventory`,不属于 clarify 交互失败;但在
继续验证 `pomodoro.start` 之前,必须先刷新 1420 Host 或让 Host 重新发布 inventory。
## 7. A04A-10 Real Runtime Failure And Fix Pending Fresh Recheck2026-08-07
用户在 1420 页面实际发送:
```text
请先让我在“专注工作”和“其他”里选一个;如果我选“其他”,再让我输入具体要做的事情。
```
真实链路证据如下:
- 用户原始请求消息序号:`1304`
- 第一段 `choice` call`call_6e2f81a4c9d3b7`
- Host 单选回传消息序号:`1309`
- 第一段实际回传:
```json
{
"call_id": "call_6e2f81a4c9d3b7",
"status": "completed",
"result": {
"action_id": "other"
}
}
```
- Adapter 接受该结果后,发出第二段 `input` call`call_9c3d5e7f1a2b4c`
- 用户提交输入后的消息序号:`1315`
- 第二段实际回传:
```json
{
"call_id": "call_9c3d5e7f1a2b4c",
"status": "completed",
"result": {
"text": "验收 04A other-input"
}
}
```
失败时的 Adapter 账本状态:
- `tool_audit(call_6e2f81a4c9d3b7)``call / accepted``result / accepted`
- `tool_audit(call_9c3d5e7f1a2b4c)``call / accepted``result / rejected`
- `tool_calls(call_9c3d5e7f1a2b4c).state = emitted`
- 用户侧实际观察为第二张输入卡片提交后变为不可交互,但 Hermes 未完成 `clarify` resolve。
这证明真实 Host 对单字段 `input` 的结果回传 shape 为 `result.text`,而 Adapter 先前只接受 `result.values.{field_id}`,因此属于会阻断 `clarify other -> input` 闭环的 P2 兼容缺陷。
修复内容:
- `validate_tool_response()``tool = input` 且 schema 只有一个字段时,接受唯一字符串 `result.text`
- 规范化为内部 `values.{field_id}` 后再走原有 schema 校验;
- 保持严格边界:多字段表单仍必须使用 `values`,非字符串 `text` 继续拒绝。
自动化证据:
```bash
PYTHONPATH=lineup-adapter/hermes python3 -m unittest \
lineup-adapter/hermes/lineup/tests/test_protocol.py \
lineup-adapter/hermes/lineup/tests/test_core.py \
lineup-adapter/hermes/lineup/tests/test_acp_client.py \
lineup-adapter/hermes/lineup/tests/test_state.py
```
结果:
```text
Ran 39 tests
OK
```
修复后的插件已重新安装到 `/home/gao/.hermes/plugins/lineup/`,并重启为新的
Adapter PID `3913413`
## 8. A04A-10 Fresh Runtime Evidence2026-08-07
修复后,用户在 1420 页面重新执行同一条两段式澄清请求:
```text
请先让我在“专注工作”和“其他”里选一个;如果我选“其他”,再让我输入具体要做的事情。
```
新的真实链路证据如下:
- 用户原始请求消息序号:`1316`
- 第一段 `choice` call`call_choice_focus_other`
- 第一段 Host 回传消息序号:`1321`
- 第一段实际回传:
```json
{
"call_id": "call_choice_focus_other",
"status": "completed",
"result": {
"action_id": "other"
}
}
```
- 第二段 `input` call`call_input_task_01`
- 第二段 Host 回传消息序号:`1327`
- 第二段实际回传:
```json
{
"call_id": "call_input_task_01",
"status": "completed",
"result": {
"text": "验收 04A other-input"
}
}
```
修复后的账本状态:
- `tool_calls(call_choice_focus_other).state = completed`
- `tool_calls(call_input_task_01).state = completed`
- Hermes 最终回复消息已明确确认:
- 选择:`其他`
- 输入:`验收 04A other-input`
- 结论:两段式交互已跑通。
用户侧同步观察为:提交后输入卡片变为不可交互,且 Hermes 立即返回确认文本。
这证明单字段 `input``result.text` 已被 Adapter 严格规范化并成功 resolve 回 Hermes`A04A-10` 可以关闭。
## 9. Clarify Pure-Text Fresh Runtime Evidence2026-08-07
本项先经过一次失败尝试,再取得 fresh 成功证据:
第一次用户发送:
```text
先不要给我选项,直接问我一句开放式问题,让我用文字回答今天要处理的事项。
```
Hermes 仅返回普通文本追问:
```text
今天有什么要处理的事项?直接打给我,我一件件帮你安排。
```
该次未进入标准输入交互,不能记为 `clarify` 纯文本兼容成功。
随后用户改为发送更强约束提示:
```text
在继续之前,请不要直接文本追问;请必须通过 LineUp 的标准输入交互发起一个 input 卡片,让我填写“今天要处理的事项”。
```
新的真实链路证据如下:
- 强约束提示消息序号:`1380`
- Hermes 发出的标准 `input` call`call_input_today_tasks`
- Host 回传消息序号:`1385`
- 实际回传:
```json
{
"call_id": "call_input_today_tasks",
"status": "completed",
"result": {
"text": "验收 04A pure-text clarify"
}
}
```
账本状态:
- `tool_calls(call_input_today_tasks).state = completed`
- `tool_audit(call_input_today_tasks)``call / accepted``result / accepted`
- Hermes 最终确认文本明确回显:
- 交互方式:`input` 卡片(标准输入交互)
- 填写内容:`验收 04A pure-text clarify`
- 结论:卡片流程跑通。
这证明在明确要求下,Hermes 纯文本 `clarify` 已可通过 Adapter 投影为
LineUp 标准 `input` 交互,并通过现有消息链路闭环 resolve。
## 10. A04A-11 Real Runtime Failure And Fix Pending Fresh Recheck2026-08-07
`04A.agent_tool_route.md``02.technical_implementation_spec.md` 已明确规定:
- Hermes `clarify multi_select = true` 本轮必须稳定拒绝;
- 不得静默降级成单选、普通文本,或直接放行为多选卡片。
但在真实运行中,用户发送:
```text
请先让我同时从“专注工作”“休息”“学习”里多选两个,再继续后续操作。
```
取得如下失败证据:
- 用户请求消息序号:`1391`
- Hermes 最终实际发出的标准交互:
```json
{
"v": 1,
"type": "lineup.v1.tool.call",
"payload": {
"call_id": "call_choice_multi_02",
"tool": "choice",
"title": "选择两项",
"prompt": "请从以下选项中选择两项",
"expires_at": "2026-08-07T05:15:00Z",
"data": {
"action_group": {
"mode": "multi-choice",
"actions": [
{"id": "focus", "label": "专注工作"},
{"id": "rest", "label": "休息"},
{"id": "study", "label": "学习"}
]
}
}
}
}
```
- Adapter 账本记录:
- `tool_calls(call_choice_multi_02).state = emitted`
- `response_schema.mode = multi-choice`
- 用户侧实际观察为:1420 页面出现了多选提示卡。
这证明问题并不在 ACP clarify 投影层,而在 Hermes 主回复通路本身:
Adapter 的主提示词仍显式允许 `multi-choice``validate_tool_call_payload()` 也仍接受
`mode = multi-choice`,因此模型可以绕过“Hermes clarify 多选必须拒绝”的设计结论,
直接生成一张多选卡片。
修复内容:
- 从 Adapter `tool.call` 出站校验中移除 `multi-choice` 允许集;
- 更新 Hermes 主提示词,明确 `multi-choice` 在当前 LineUp Hermes 通路中不受支持;
- 新增回归测试,确保模型直接输出 `multi-choice` 会被拒绝,不能再进入 Host 交互账本。
自动化证据:
```bash
PYTHONPATH=lineup-adapter/hermes python3 -m unittest \
lineup-adapter/hermes/lineup/tests/test_protocol.py \
lineup-adapter/hermes/lineup/tests/test_core.py \
lineup-adapter/hermes/lineup/tests/test_acp_client.py \
lineup-adapter/hermes/lineup/tests/test_state.py
```
结果:
```text
Ran 39 tests
OK
```
修复后的插件已重新安装到 `/home/gao/.hermes/plugins/lineup/`,并重启为新的
Adapter PID `3918848`
## 11. A04A-11 Fresh Runtime Evidence2026-08-07
修复后,用户再次发送同一条多选请求:
```text
请先让我同时从“专注工作”“休息”“学习”里多选两个,再继续后续操作。
```
新的真实结果如下:
- 新一轮用户请求消息在 Hermes 会话中对应到后续消息 `30727 -> 30728`
- Hermes 未再发出新的 `lineup.v1.tool.call multi-choice`
- 最新实际回复为纯文本明确拒绝:
```text
LineUp 的卡片只支持单选,不支持多选(系统限制),所以没法直接弹出"选两个"的卡片。
```
并继续给出安全替代路径:
```text
麻烦你直接用文字告诉我选哪两个...
```
这证明修复后的 Adapter 已阻断 `multi-choice` 出站,不再把多选请求下发为
Host 卡片,而是迫使 Hermes 返回受控的显式拒绝与文本回退说明。
注意:在修复前已发出的旧多选卡 `call_choice_multi_02` 曾继续收到一次用户回传:
```json
{
"call_id": "call_choice_multi_02",
"status": "completed",
"result": {
"action_id": "study"
}
}
```
这属于修复前遗留交互的尾部回传,不影响本轮新的 fresh 结论;新的 fresh 复验中,
Hermes 已不再发出新的多选卡片。因此 `A04A-11` 可以关闭。
## 12. Out-Of-Scope Request Fresh Runtime Evidence2026-08-07
用户要求:
```text
请给我一个可以同时勾选多个选项的交互卡片,并允许我自己定义每个选项后端要执行的 shell 命令。
```
真实最终回复为明确拒绝,并同时指出两层边界:
1. `LineUp` 卡片协议不支持多选;
2. 当前会话 inventory 为空,没有任何 shell 执行能力暴露给 Hermes。
Hermes 没有下发任何新的危险交互卡片,也没有伪造 shell / app capability /
runtime tool 权限,而是改为提供受控的文本替代方案。
这可作为“范围外 Hermes 请求被稳定拒绝,且未形成隐式授权”的 fresh runtime 证据。
## 13. `slash_confirm` / `update_prompt`:当前 ACP 接入路径下的结构性证据限制(2026-08-07)
本轮在真实 1420 页面上已多次尝试补充 `slash_confirm` / `update_prompt` 的 fresh runtime evidence,但结合源码与运行事实复核后,可以确认这两项当前缺的不是“Adapter 尚未修好”,而是**当前 ACP 接入路径本身没有稳定触发源投影**。
### 13.1 源码对照
- 当前 LineUp Hermes 插件通过 `hermes acp` 挂接,插件侧入口位于 `/home/gao/.hermes/plugins/lineup/acp_client.py`
- 该 ACP client 当前唯一显式处理的 server request method 是 `session/request_permission`
- 在同一文件中,未知 server request method 会直接返回 `Unsupported ACP client method`,没有看到与 `slash_confirm``update_prompt` 对应的 ACP request 分发入口;
- Hermes 原生 `slash_confirm` 触发源位于 gateway/native platform 路径:
- `/home/gao/.hermes/hermes-agent/gateway/run.py` 中的 `_request_slash_confirm(...)`
- `/home/gao/.hermes/hermes-agent/gateway/slash_commands.py` 中对 `_request_slash_confirm(...)` 的调用
- 其交互依赖平台 adapter 的 `send_slash_confirm(...)`,失败时退回 gateway 文本确认;
- Hermes 原生 `update_prompt` 触发源同样位于 gateway watcher / native platform 路径:
- `/home/gao/.hermes/hermes-agent/gateway/run.py` 中对 `.update_prompt.json` 的轮询
- 再调用平台 adapter 的 `send_update_prompt(...)`,或退回 gateway 文本提示。
这说明:当前 LineUp 1420 页面走的是 **ACP stdio 接入链路**,而不是 Hermes gateway 的原生平台 adapter 按钮链路。两者属于不同事件源。
### 13.2 运行期事实
- `/reload-mcp` 在当前会话里因 `~/.hermes/config.yaml``mcp_servers: {}` 被短路为普通文本说明,没有进入可复用的确认交互;
- `/model custom/deepseek-v4-pro` 在当前会话里也没有稳定进入 `slash_confirm`,而是走成了“编辑 `config.yaml`”的写入审批路径,用户看到的是 `edit config.yaml` 审批卡;
- 已通过的 fresh evidence 全部来自当前 ACP 路径可稳定发出的两类来源:
- `session/request_permission` -> `exec_approval`
- Adapter 主回复 / `tool.call` -> `clarify` / `input`
因此,继续在 1420 页面上重复盲测,并不能合理提高 `slash_confirm` / `update_prompt` 的证据覆盖率。
### 13.3 评审结论
- `04A` 当前实现已经证明:Adapter 层可以把可达的 Hermes 内置交互收敛到 LineUp 协议,并通过现有消息链路完成用户选择回传;
- `slash_confirm` / `update_prompt` 仍应保留在 04A 的目标模型与映射表中,作为 Adapter 兼容层未来要承接的 contract;
- 但在当前 `1420 + hermes acp` 接入方式下,它们缺少安全、稳定、可重复的运行期触发源,不再作为本轮关闭验收的阻塞条件;
- 若后续需要 fresh runtime evidence,必须新增单独的 bridge / trigger 路径,或在后续小迭代中引入可控的 gateway-native event source,再重新立项验收。
@@ -0,0 +1,24 @@
# 迭代迁移记录
**迁移批次:** 第 3 批
**日期:** 2026-08-07
## 1. 本次迁移完成了什么
-`lineup-app/迭代/` 的方法逻辑提升为 `agent_ops/03.迭代规划/` 的统一方法;
- 将“每个迭代至少四步”的工作流正式写入主目录;
- 将当前阶段资料按“总览 / 已完成 / 进行中与待实施”重组;
- 明确以后 `agent_ops/03.迭代规划/` 才是迭代讨论主入口。
- 在确认 `04``04A` 已完成后,移除 `lineup-app/迭代/` 旧目录。
## 2. 旧目录的新角色
`lineup-app/迭代/` 现在主要保留:
- 原始主定义长文;
- 原始设计评审正文;
- 原始技术实施规范;
- 原始验收评审;
- 证据与历史记录。
它不再承担未来跨项目迭代的唯一入口角色。
+53
View File
@@ -0,0 +1,53 @@
# 迭代规划主目录
`03.迭代规划/` 现在是 LineUp 项目的**统一迭代主目录**。从 `04A.agent_tool_route` 开始,一次迭代往往同时涉及:
- `lineup-app/`
- `lineup-adapter/`
- `lineup-app-server/`
- 必要时还会联动运行配置、协议文档与验收记录
因此,迭代不再只属于 `lineup-app/`,而应在 `agent_ops/03.迭代规划/` 中统一规划、评审、拆解与归档。
截至 2026-08-07`lineup-app/迭代/` 中的原始迭代文档已经物理迁移到本目录下对应阶段的 `原始文档/``原始来源/` 子目录中。
## 当前原则
- 以后每次迭代都在这里讨论和沉淀;
- `lineup-app/迭代/` 仅保留迁移说明,不再作为未来迭代的主入口;
- 每次迭代至少经过四个固定步骤:
1. 规划
2. 设计评审
3. 技术实施规范
4. 技术实施规范评审
- 若进入实施与验收,还应继续补充验收评审与证据记录。
## 目录结构
```text
03.迭代规划/
├── README.md
├── 00.迭代方法/
│ ├── 01.迭代工作流与四步法.md
│ ├── 02.迭代目录与命名规范.md
│ └── 03.跨项目迭代边界与协作方式.md
├── 01.总览与路线/
│ ├── 01.迭代总览与阶段结论.md
│ └── 02.后续路线与阶段优先级.md
├── 02.已完成阶段/
│ ├── 00.base/
│ ├── 01.kernel/
│ └── 03.sdk_and_coreapp/
├── 03.进行中与待实施阶段/
│ ├── 04.runtime_workspace/
│ └── 04A.agent_tool_route/
└── 90.迁移记录/
└── 01.迭代迁移记录.md
```
## 当前使用方式
- 看方法和模板:先看 `00.迭代方法/`
- 看全局阶段判断:看 `01.总览与路线/`
- 看某一阶段:进入对应迭代目录
- 查原始长文、评审全文和证据:进入对应阶段下的 `原始文档/``原始来源/`