feat: add bounded session and history navigation

Add session scrolling, latest-message navigation and fingerprint location under the shared UI gate; reuse navigation in history reads and provide read-only smoke. Navigation index snapshot passed 124 tests and a full build. Windows 4.1.13.63 read-only validation passed 10/10, including two-scroll history location; no sends. Record scope limits, including next-unread navigation not yet implemented.
This commit is contained in:
2026-09-06 20:09:00 +08:00
parent c49f6a10b2
commit e5548bd471
13 changed files with 563 additions and 24 deletions
+29
View File
@@ -0,0 +1,29 @@
# 导航增量验收 — 2026-09-06
## 本次实现
- `session scroll --direction up|down --pages N`:会话列表滚动;默认不输出联系人名称,`--include-content` 显式开启。
- `chat latest [--session <name>]`:回到指定会话的最新可见消息。
- `chat locate --fingerprint <可见消息指纹> [--session <name>] [--scrolls N]`:有界向上搜索并将匹配行带入视口。
- `chat history` 支持 `--session`;两个历史读取入口复用共享滚动与回到最新逻辑,不再按累计滚轮距离猜测返回位置。
- `chat merged-open` 是可选的卡片窗口导航,仅返回新窗口句柄,**不负责内容提取**。合并内容的正式读取由独立的 `db merged` 实现。
- 增加 `smoke --read-only`,明确返回 `sendSkipped=true`、`confirmed=false`;只读验证不发送标记。
所有 UI 操作使用现有跨进程命令锁、输入桌面检查和会话核验。先使用 UIA Scroll/ScrollItem,缺失时使用集中维护的滚轮兜底;不点击输入框、不发送或转发消息。
## 验证
- 微信 `4.1.13.63`,Windows 真机 `10.1.1.101` 的现有已登录会话。
- 发布目录:`C:\Users\Rogee\wx-agent\releases\navigation-20260906T113948`。
- Linux Core 测试 **124/124**,完整 Release build **0 warnings / 0 errors**。
- `Test-Navigation.ps1` 最终 **10/10** 通过,发送次数 **0**:doctor、inspect-ui、只读 smoke、会话下滚/上滚、回到最新、历史读取、定位、再次回到最新、打开合并卡片。
- 定位测试确实向上滚动 **2 次**后找到指定指纹,不是仅匹配当前页。
- 第一次脚本将 JSON 枚举误作数字,7 步后主动停止;修正为 `Text` / `Merge` 后完成第二次只读验证。未把第一次失败记为通过。
脱敏证据:[`evidence/Navigation-20260906/`](evidence/Navigation-20260906/)。不提交联系人名称、消息正文或完整 UI 树。
## 边界
- 指纹是现有 UIA 可见快照指纹,不是数据库 server ID;相同文本重复消息在不同视口的身份仍有局限,不能据此进行不可逆操作。
- 无原生滚动百分比时,“回到最新”依赖连续稳定视口,不宣称数据库历史完整性。定位达到滚动上限返回 `ResultUnconfirmed`;视口停止变化而未找到返回 `ControlNotFound`。
- 本增量未实现“下一个未读会话”。会话列表缺少已验证的未读标记时,不从预览文本猜测未读状态。
@@ -0,0 +1,10 @@
{
"Scrolls": 2,
"message": {
"Fingerprint": "1e59c51f8086b6ec6d80e34a16daabc0359f0fd4f4633289113e653b9b0ab166",
"Type": "Text",
"length": 61,
"content": null,
"quote": null
}
}
@@ -0,0 +1,56 @@
{
"Scrolls": 2,
"ViewportChanged": true,
"sessions": [
{
"name": null,
"nameId": "sha256:1DC65E74B01A64FA",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:B0E5330510C53A70",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:4DF34FAE92A9BB26",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:2EF8DCA410614184",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:2092711F288D71DA",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:AD8DF8A5CA6DCC46",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:BA4121C435EF5D04",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:563C6AC6EFB52329",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:5F1E8BDE6155432A",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:D93D7F06AE52537F",
"IsCurrent": false
}
]
}
@@ -0,0 +1,56 @@
{
"Scrolls": 2,
"ViewportChanged": true,
"sessions": [
{
"name": null,
"nameId": "sha256:28032CB51B5D5918",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:EC654065E79D4554",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:C7CE04276AC5A9C4",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:9F775FAD499E92FF",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:2B55CD2E01D1E400",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:A5B67004830C26E7",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:8ABDA031D8E42C4E",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:68C6E1577C7F5576",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:BFE36215E3C4FDAA",
"IsCurrent": false
},
{
"name": null,
"nameId": "sha256:1DC65E74B01A64FA",
"IsCurrent": false
}
]
}
@@ -0,0 +1,11 @@
{
"scope": "M1",
"doctorErrors": [],
"output": "C:\\Users\\Rogee\\wx-agent\\releases\\navigation-20260906T113948\\artifacts\\ui-tree.json",
"nodes": 154,
"sanitized": true,
"sentTo": null,
"Fingerprint": null,
"sendSkipped": true,
"confirmed": false
}
@@ -0,0 +1,47 @@
{
"Checks": [
{
"Stage": "doctor",
"ExitCode": 0
},
{
"Stage": "inspect-ui",
"ExitCode": 0
},
{
"Stage": "smoke",
"ExitCode": 0
},
{
"Stage": "sessions-down",
"ExitCode": 0
},
{
"Stage": "sessions-up",
"ExitCode": 0
},
{
"Stage": "latest-before",
"ExitCode": 0
},
{
"Stage": "history",
"ExitCode": 0
},
{
"Stage": "locate",
"ExitCode": 0
},
{
"Stage": "latest-after",
"ExitCode": 0
},
{
"Stage": "merged-open",
"ExitCode": 0
}
],
"SendsAttempted": 0,
"BinarySha256": "F443EC6EF044F66349E55EBEB4ED0109EA13C96D8216079AEAEE3A19DF9A32DA",
"Success": true
}