# 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 Evidence(2026-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 Evidence(2026-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 Recheck(2026-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 Evidence(2026-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 Evidence(2026-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 Recheck(2026-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 Evidence(2026-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 Evidence(2026-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,再重新立项验收。