Skip to content

fix(chat): 去重翻页边界消息,避免 message list 无限循环 (#430)#787

Open
weiconghe wants to merge 1 commit into
DingTalk-Real-AI:mainfrom
weiconghe:fix/chat-message-boundary-dedup
Open

fix(chat): 去重翻页边界消息,避免 message list 无限循环 (#430)#787
weiconghe wants to merge 1 commit into
DingTalk-Real-AI:mainfrom
weiconghe:fix/chat-message-boundary-dedup

Conversation

@weiconghe

@weiconghe weiconghe commented Jul 24, 2026

Copy link
Copy Markdown

Summary

  • What changed? Re-implement the chat message list pagination boundary de-duplication on the current static chat handler (internal/helpers/chat.go), so that paginating by feeding the previous page's boundary createTime back as the next --time no longer loops forever. The de-dup drops entries in result.messages whose createTime equals the --time anchor, applied to chat message list, chat message list-direct, and chat message list-topic-replies. The shared MCP-call core gains a nil-safe post text hook (callMCPToolInternalOptsPost) so the response is filtered before rendering, preserving DryRun / --format json|table|raw / error-classification behavior exactly.
  • Why is this change needed? Closes [Bug] chat message list 文档推荐的 forward=true 翻页方式会无限循环(v1.0.34) #430. The upstream IM API uses an inclusive time boundary (>= for newer/forward=true, <= for older/forward=false). The skill docs (written by fix: resolve real-machine QA findings across CLI, scripts and skill docs #572) tell the caller to reuse the boundary createTime as the next --time; that exact-boundary message is then re-served on every subsequent page, so chat message list --direction older (or --forward) pagination loops, returning the same message again and again. fix: resolve real-machine QA findings across CLI, scripts and skill docs #572 mitigated this by recommending --direction older, but the strict boundary de-dup still had to be (re-)done on the static handler.

This re-submits the intent of the closed #461, whose implementation lived on internal/compat/chat_hooks.go / dynamic_commands.go — those files were removed by the #565 static-runtime refactor, so that PR could no longer merge. The dedup logic itself is relocated onto the current handler (internal/helpers/chat.go) and the new shared post hook, with no dependency on the deleted compat package.

Verification

Exact CHANGELOG.md-only check: N/A (this is a code change).

  • make buildscripts/dev/build.sh (go build -o dws ./cmd) succeeds; dws binary builds. Also ran go build ./... (whole module) clean.
  • make lintgofmt clean on all changed files; go vet ./internal/helpers/ clean. (staticcheck is not installed in my local env; left to CI.)
  • [~] make testpartial locally; full suite left to CI. I ran the targeted set on Windows + Go 1.25.11:
    • 12 new tests in chat_dedup_test.go (de-dup logic + end-to-end via fake caller): all PASS
    • core callMCPToolInternalOpts coverage in helpers_core_test.go / helpers_coverage_more_test.go (json/table/raw/devdoc, DryRun, unescape, error classification): all PASS
    • all Chat/Message/ChatRecord-related tests in internal/helpers/: PASS (3.77s)
    • The full go test ./... was not completed locally — the internal/helpers package alone exceeds my local run budget on Windows (some pre-existing tests are slow). CI (Linux) runs the full suite.
  • [~] make policy — not run as a whole locally. ./scripts/policy/check-command-surface.sh --strict PASSes (command surface unchanged: no new commands/flags/help text).
  • ./scripts/policy/check-generated-drift.shpre-existing failure on clean origin/main, not introduced here. On a clean checkout of origin/main (my changes stashed) this check already reports internal/cli/schema_agent_metadata is stale across all products (aisearch/aitable/.../chat/contact/wiki…), so it is unrelated to this PR, which changes no CommandRegistry/command definition. Likely a Windows CRLF/path artifact or genuinely stale committed schemas; expecting Linux CI to match its current main behavior.
  • ./scripts/policy/check-command-surface.sh --strict — PASS (covered above).

Notes

  • Scope: CLI handlers only. The shortcut layer (internal/shortcut/chat, internal/shortcut/smart) calls these same tools via rt.CallMCPData (a separate path, not callMCPTool) and only reshapes a single page — it does not loop internally — so it is intentionally out of scope for [Bug] chat message list 文档推荐的 forward=true 翻页方式会无限循环(v1.0.34) #430 (which is about the dws chat message list CLI command). internal/helpers/connect_chatrecord.go calls list_conversation_message_v2 only to resolve records by id, not as a user-facing pagination surface.
  • Why de-dup both directions. --direction newer (forward=true) is fundamentally not paginatable toward older history (it always re-serves the newest N at/after T); the de-dup's real value is on --direction older (forward=false), where pagination is valid except for the single exact-boundary repeat. The de-dup is applied uniformly (harmless on newer), and is an exact-string match (createTime == anchor, after trim) so it only ever drops the verbatim-boundary message — i.e. precisely the loop case.
  • Safe by construction. dedupChatMessageBoundary only inspects top-level result.messages (never recurses into nested forwardMessages, whose createTime is a forwarded body, not a pagination boundary) and returns the original text verbatim on any parse/shape mismatch, so it is safe to apply unconditionally.
  • Skill-doc note deferred. I did not edit skills/.../chat.md in this PR: the generated internal/cli/schema_agent_metadata/index.json carries a source_hash over the skill markdown, so even a one-line doc change forces a make generate-schema regeneration, and that hash is not reliably reproducible on Windows (raw-byte hash, no CRLF normalization). The existing docs (from fix: resolve real-machine QA findings across CLI, scripts and skill docs #572) already recommend --direction older; the new auto-dedup behavior is documented in the code comments and this PR description. Happy to follow up with the doc line (+ regenerated schema) if you'd like it included here.

@weiconghe
weiconghe force-pushed the fix/chat-message-boundary-dedup branch from becf12a to ffef434 Compare July 24, 2026 06:30
上游 IM 接口使用含等号的时间边界(newer 为 >=、older 为 <=),按文档把
边界 createTime 作为下一页 --time 传入时,该消息会在之后每一页被重复
返回,导致 chat message list 无限翻页循环(DingTalk-Real-AI#430)。

在静态 chat handler 上对 list / list-direct / list-topic-replies 的响应做
边界去重:丢弃 result.messages 中 createTime 等于 --time 锚点的消息;
shape 不匹配时原样返回,安全 no-op。

Closes DingTalk-Real-AI#430
@weiconghe
weiconghe force-pushed the fix/chat-message-boundary-dedup branch from ffef434 to 5fd5fda Compare July 24, 2026 07:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] chat message list 文档推荐的 forward=true 翻页方式会无限循环(v1.0.34)

1 participant