fix(agents): preserve messaging dedupe thread ids

This commit is contained in:
Vincent Koc
2026-05-03 18:10:55 -07:00
parent dfadf03e1f
commit 642e1dfcdf
3 changed files with 17 additions and 5 deletions

View File

@@ -49,6 +49,7 @@ Docs: https://docs.openclaw.ai
- Gateway/logging: expand leading `~` in `logging.file` before creating the file logger, preventing startup crash loops for home-relative log paths. Fixes #73587.
- Channels/CLI: keep `openclaw channels list --json` usable when provider usage fetching fails, and report per-provider usage errors without aborting the channel list. Refs #67595.
- Agents/messaging: deliver distinct final commentary after same-target `message` tool sends while still deduping text/media already sent by the tool, so short closing remarks are no longer silently dropped. Fixes #76915. Thanks @hclsys.
- Agents/messaging: preserve string thread IDs when matching message-tool reply dedupe routes, avoiding precision loss on numeric-looking topic IDs before channel plugin comparison. Thanks @vincentkoc.
- OpenAI Codex: let SSRF-guarded provider requests inherit OpenClaw's undici IPv4/IPv6 fallback policy, so ChatGPT-backed Codex runs recover on IPv4-working hosts when DNS still returns unreachable IPv6 addresses. Fixes #76857. Thanks @jplavoiemtl and @SymbolStar.
- Gateway/systemd: preserve operator-added secrets in the Gateway env file across re-stage while clearing OpenClaw-managed keys (such as `OPENCLAW_GATEWAY_TOKEN`) so a fresh staging value is never shadowed by a stale env-file copy; operator secrets are also retained when the state-dir `.env` is empty. Fixes #76860. Thanks @hclsys.
- Plugin updates: do not short-circuit trusted official npm updates as unchanged when the default/latest spec still resolves to an already-installed prerelease that the installer should replace with a stable fallback. Thanks @vincentkoc.

View File

@@ -88,11 +88,7 @@ function normalizeProviderForComparison(value?: string): string | undefined {
}
function normalizeThreadIdForComparison(value?: string): string | undefined {
const normalized = stringifyRouteThreadId(value);
if (!normalized) {
return undefined;
}
return /^-?\d+$/.test(normalized) ? String(Number.parseInt(normalized, 10)) : normalized;
return stringifyRouteThreadId(value);
}
function resolveTargetProviderForComparison(params: {

View File

@@ -197,6 +197,21 @@ describe("shouldDedupeMessagingToolRepliesForRoute", () => {
).toBe(true);
});
it("preserves string thread ids before plugin reply-suppression matching", () => {
installTelegramSuppressionRegistry();
const largeThreadId = "9007199254740993";
expect(
shouldDedupeMessagingToolRepliesForRoute({
messageProvider: "telegram",
originatingTo: `telegram:group:-100123:topic:${largeThreadId}`,
messagingToolSentTargets: [
{ tool: "message", provider: "telegram", to: "-100123", threadId: largeThreadId },
],
}),
).toBe(true);
});
it("does not match telegram topic-origin replies when explicit threadId differs", () => {
expect(
shouldDedupeMessagingToolRepliesForRoute({