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
+7 -8
View File
@@ -303,8 +303,10 @@ public static partial class WechatChatClient
public static async Task<IReadOnlyList<ChatMessageSnapshot>> ReadHistoryAsync(
int maxMessages,
int maxScrolls,
CancellationToken cancellationToken)
CancellationToken cancellationToken,
string session = WechatLocators.FileTransferAssistant)
{
ArgumentException.ThrowIfNullOrWhiteSpace(session);
if (maxMessages is < 1 or > 1000 || maxScrolls is < 0 or > 100)
{
throw new WxAgentException(WxAgentErrorCode.InvalidArgument, "History limits are maxMessages 1-1000 and maxScrolls 0-100.");
@@ -315,7 +317,7 @@ public static partial class WechatChatClient
{
using var automation = new UIA3Automation();
var window = AttachWindow(automation, cancellationToken);
await OpenFileTransferAssistantCoreAsync(window, cancellationToken);
await OpenNamedSessionAsync(window, session, cancellationToken).ConfigureAwait(false);
var messages = new List<ChatMessageSnapshot>();
var deduper = new BoundedMessageDeduper(Math.Max(2048, maxMessages * 4));
var unchanged = 0;
@@ -323,24 +325,21 @@ public static partial class WechatChatClient
for (var page = 0; page <= maxScrolls; page++)
{
cancellationToken.ThrowIfCancellationRequested();
window = WechatDoctor.FindWechatWindow(automation) ?? window;
RequireNavigationSession(window, session);
var visible = ReadVisible(window);
var older = visible.Where(message => deduper.TryAdd(message.Fingerprint)).ToArray();
if (older.Length == 0) unchanged++; else unchanged = 0;
if (page == 0) messages.AddRange(older); else messages.InsertRange(0, older);
if (messages.Count >= maxMessages || unchanged >= 2 || page == maxScrolls) break;
var list = FindByAutomationId(window, WechatLocators.MessageList)
?? throw new WxAgentException(WxAgentErrorCode.ControlNotFound, $"Control {WechatLocators.MessageList} was not found.");
MoveToCenter(list);
Mouse.Scroll(5);
ScrollNavigationList(window, WechatLocators.MessageList, WechatScrollDirection.Up);
scrolled++;
await Task.Delay(350, cancellationToken);
}
if (scrolled > 0)
{
Mouse.Scroll(-5d * scrolled);
await GoToLatestCoreAsync(window, session, 100, cancellationToken).ConfigureAwait(false);
}
return messages.Count <= maxMessages ? messages : messages.TakeLast(maxMessages).ToArray();