|
@@ -153,3 +153,40 @@ const live = !closedBlocks && !waitingTooLong
|
|
|
- [ ] 把 `m.typing` ephemeral 这个坑写进《容器接入规范提示词》(已完成)
|
|
- [ ] 把 `m.typing` ephemeral 这个坑写进《容器接入规范提示词》(已完成)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 四、容器会话级状态修复(2026-11-11 晚)
|
|
|
|
|
+
|
|
|
|
|
+### 4.1 问题链
|
|
|
|
|
+
|
|
|
|
|
+| # | 问题 | 根因 | 修法 |
|
|
|
|
|
+|---|---|---|---|
|
|
|
|
|
+| 1 | `extra.currentSessions` 为空 | 心跳只报 `heartbeatAt`+`balance`,不带会话表 | 容器:心跳带上 `currentSessions` |
|
|
|
|
|
+| 2 | 报了也匹配不上 | `thread_id` 大量为 NULL(实测 46%)→ 容器只能报 Hermes 内部 id | 容器:改报 roomId;App:加 roomId 退路匹配 |
|
|
|
|
|
+| 3 | `_extra_base` 空字典覆盖 | GET 失败时把 `{}` 永久缓存,之后每小时拿空底写 | 容器:失败不缓存,跳过本次 extra 写入 |
|
|
|
|
|
+
|
|
|
|
|
+### 4.2 App 侧三级匹配(`agent-state.ts::sessionStateOf`)
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+① sessionKey 含 thread 根($approot_xxx) ← 最精确,一房多会话不串
|
|
|
|
|
+② sessionKey 含 roomId(!xxx) ← 退路,DM 无 thread 时靠房间认
|
|
|
|
|
+③ 都不中 → undefined ← 回落全局状态
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**依据**:容器实测 `chat_id`(roomId)100% 稳定——212 条会话全是 `!` 开头,
|
|
|
|
|
+同一房间跨 22 天、83 条会话不变。
|
|
|
|
|
+
|
|
|
|
|
+**代价**(已知接受):roomId 粒度比 thread 粗,同房间多条会话时「任一在跑」会被认为
|
|
|
|
|
+「这条在跑」。相比「完全匹配不上、状态永远不显示」,这个误判可接受。
|
|
|
|
|
+
|
|
|
|
|
+### 4.3 一次误判(记录以免重犯)
|
|
|
|
|
+
|
|
|
|
|
+容器和我都把「`extra` 只剩 `currentSessions` + `extraUpdatedAt`」判成「业务键被覆盖丢失」。
|
|
|
|
|
+
|
|
|
|
|
+**实际不是**:回读 agent 记录的全部字段,`botName` / `host` / `channels` / `balance` / `usage`
|
|
|
|
|
+全在**顶层**,一个没少;App 也只从 `extra` 读 `currentSessions` 一项。
|
|
|
|
|
+`extra` 只有 2 个键本来就是正常状态。
|
|
|
|
|
+
|
|
|
|
|
+**教训**:判断「数据是否丢失」要看**全字段**,不能只看某一个字段的子集;
|
|
|
|
|
+容器塞的探测键消失 ≠ 业务键消失。
|