|
@@ -119,7 +119,38 @@
|
|
|
| 1 | Matrix adapter 的 `send()` | `metadata.get("_interim_send")` 为真 → `content["fmode.kind"] = "process"`,否则 `"final"` |
|
|
| 1 | Matrix adapter 的 `send()` | `metadata.get("_interim_send")` 为真 → `content["fmode.kind"] = "process"`,否则 `"final"` |
|
|
|
| 2 | 工具进度行的产出链路(`_progress_build_message` 之后) | 同样写入 `content["fmode.kind"] = "process"` |
|
|
| 2 | 工具进度行的产出链路(`_progress_build_message` 之后) | 同样写入 `content["fmode.kind"] = "process"` |
|
|
|
|
|
|
|
|
-**改造纪律**:
|
|
|
|
|
|
|
+##### 🔴 实测结论:这个改造**不是所有容器都能做**(2026-11-11 在 lj-test 上验证)
|
|
|
|
|
+
|
|
|
|
|
+在 `agent-node-lj-test`(Hermes 架构)上实测,改造**受阻于两个硬约束**:
|
|
|
|
|
+
|
|
|
|
|
+**约束 1:`/opt/hermes` 是只读的**
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+HERMES_WRITE_SAFE_ROOT=/opt/data
|
|
|
|
|
+→ [patch] Write denied: '/opt/hermes/plugins/platforms/matrix/adapter.py'
|
|
|
|
|
+ is outside HERMES_WRITE_SAFE_ROOT
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+容器可以**备份**和**写补丁脚本**到 `/opt/data`,但**执行不了**。
|
|
|
|
|
+需要宿主机侧给 `/opt/hermes` 写权限,或由 root 执行补丁。
|
|
|
|
|
+
|
|
|
|
|
+**约束 2:工具进度行拿不到 `_interim_send`**
|
|
|
|
|
+
|
|
|
|
|
+进度行走 `_send_progress_text` → `adapter.send(metadata=ctx._progress_metadata)`,
|
|
|
|
|
+而 `_progress_metadata` 在非 Discord 平台**原样返回(可能 None)**,不带 `_interim_send`。
|
|
|
|
|
+需要在 `run_turn_runner.py` 里**额外显式塞标记键** —— 这是第二处改动,同样在只读区。
|
|
|
|
|
+
|
|
|
|
|
+插件层也绕不开:钩子白名单只有 `post_tool_call` / `transform_llm_output` /
|
|
|
|
|
+`pre_gateway_dispatch` 等**入站/工具/LLM** 钩子,**没有出站 send 钩子**。
|
|
|
|
|
+
|
|
|
|
|
+**因此**:
|
|
|
|
|
+
|
|
|
|
|
+| 情况 | 结论 |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| 容器有 `/opt/hermes` 写权限 | 可以两处都改,效果好 |
|
|
|
|
|
+| 容器只读(如 lj-test) | **改不了**,只能靠 App 侧启发式兜底 |
|
|
|
|
|
+
|
|
|
|
|
+**改造纪律**(若你有写权限,按此执行):
|
|
|
1. 先备份目标文件(如 `adapter.py.bak-fmodekind-<ts>`)
|
|
1. 先备份目标文件(如 `adapter.py.bak-fmodekind-<ts>`)
|
|
|
2. 改完**重启 gateway**(adapter 是进程内加载的)
|
|
2. 改完**重启 gateway**(adapter 是进程内加载的)
|
|
|
3. **用真实房间事件回读验证**:直读 `/_matrix/client/v3/rooms/{roomId}/messages`,
|
|
3. **用真实房间事件回读验证**:直读 `/_matrix/client/v3/rooms/{roomId}/messages`,
|
|
@@ -127,7 +158,15 @@
|
|
|
—— **不能靠看日志或看自己发的文本判断**
|
|
—— **不能靠看日志或看自己发的文本判断**
|
|
|
4. 如某处确实做不到,**如实说明原因,不要假装做了**
|
|
4. 如某处确实做不到,**如实说明原因,不要假装做了**
|
|
|
|
|
|
|
|
-##### 已知瑕疵(改造会暴露,需评估是否接受)
|
|
|
|
|
|
|
+> 🔴 **给接入方的强烈提醒**:lj-test 曾口头回答「改完了」,
|
|
|
|
|
+> 但实际回读发现 `fmode.kind` **出现 0 次** —— 它的文件修改被系统拒绝了。
|
|
|
|
|
+> **任何「已完成」的声称都必须用回读事件验证**,不能采信。
|
|
|
|
|
+>
|
|
|
|
|
+> 可复用的现成产物(lj-test 已备好,仍在该容器里):
|
|
|
|
|
+> - 备份:`/opt/data/backups/fmode-kind-20261111-161903/`
|
|
|
|
|
+> - 补丁脚本:`/opt/data/work/tmp-apply-fmode-kind.py`
|
|
|
|
|
+
|
|
|
|
|
+##### 已知瑕疵(若能改造会暴露)
|
|
|
|
|
|
|
|
`create_handoff_thread()` 的种子消息会被标成 `final`(它没有 `_interim_send`),
|
|
`create_handoff_thread()` 的种子消息会被标成 `final`(它没有 `_interim_send`),
|
|
|
按语义它不算正文。可在该处显式跳过写入。
|
|
按语义它不算正文。可在该处显式跳过写入。
|
|
@@ -256,12 +295,16 @@ API_SERVER_KEY
|
|
|
- [ ] 心跳周期 ≤ 80 分钟
|
|
- [ ] 心跳周期 ≤ 80 分钟
|
|
|
- [ ] 上报 `status` / `currentTask` / `channels` / `extra.currentSessions`
|
|
- [ ] 上报 `status` / `currentTask` / `channels` / `extra.currentSessions`
|
|
|
|
|
|
|
|
-### 结构化标记(强烈推荐,App 侧已就绪)
|
|
|
|
|
|
|
+### 结构化标记(可选 —— 取决于容器是否有写权限)
|
|
|
|
|
+- [ ] **先确认容器能写 `/opt/hermes`(或对应 adapter 目录)** —— 只读则本项无法做
|
|
|
- [ ] Matrix adapter 的 `send()` 落 `fmode.kind: "process" | "final"`
|
|
- [ ] Matrix adapter 的 `send()` 落 `fmode.kind: "process" | "final"`
|
|
|
-- [ ] **工具进度行的产出链路**也落 `fmode.kind: "process"`(不走 `send()`,需单独改)
|
|
|
|
|
|
|
+- [ ] **工具进度行的产出链路**也落 `fmode.kind: "process"`(不走 `send()`,需单独改 `run_turn_runner.py`)
|
|
|
- [ ] 重启 gateway 后用真实房间事件回读,确认字段确实出现在 `content` 里
|
|
- [ ] 重启 gateway 后用真实房间事件回读,确认字段确实出现在 `content` 里
|
|
|
|
|
|
|
|
-> 只做第一处是不够的:`send()` 只覆盖旁白,**工具进度行包含「日志吞正文」的主体**。
|
|
|
|
|
|
|
+> 只做第一处是不够的:`send()` 只覆盖旁白,**工具进度行才是「日志吞正文」的主体**。
|
|
|
|
|
+>
|
|
|
|
|
+> **做不了也没关系**:App 侧已实现自动回退 —— 字段缺失时走 emoji 启发式,
|
|
|
|
|
+> 老容器照常可用。本项**不是接入的硬前提**。
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|