Skip to content

Commit 8a7d1aa

Browse files
authored
fix(gateway): preserve route inheritance for legacy channel session keys (#33919) thanks @Takhoffman
Verified: - pnpm build - pnpm check - pnpm test src/gateway/server-methods/chat.directive-tags.test.ts - pnpm test:macmini Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
1 parent f74a04e commit 8a7d1aa

3 files changed

Lines changed: 73 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Docs: https://docs.openclaw.ai
2222
- Config/heartbeat legacy-path handling: auto-migrate top-level `heartbeat` into `agents.defaults.heartbeat` (with merge semantics that preserve explicit defaults), and keep startup failures on non-migratable legacy entries in the detailed invalid-config path instead of generic migration-failed errors. (#32706) thanks @xiwan.
2323
- Plugins/SDK subpath parity: add channel-specific plugin SDK subpaths for Discord, Slack, Signal, iMessage, WhatsApp, and LINE; migrate bundled plugin entrypoints to scoped subpaths/core with CI guardrails; and keep `openclaw/plugin-sdk` root import compatibility for existing external plugins. (#33737) thanks @gumadeiras.
2424
- Routing/session duplicate suppression synthesis: align shared session delivery-context inheritance, channel-paired route-field merges, and reply-surface target matching so dmScope=main turns avoid cross-surface duplicate replies while thread-aware forwarding keeps intended routing semantics. (from #33629, #26889, #17337, #33250) Thanks @Yuandiaodiaodiao, @kevinwildenradt, @Glucksberg, and @bmendonca3.
25+
- Routing/legacy session route inheritance: preserve external route metadata inheritance for legacy channel session keys (`agent:<agent>:<channel>:<peer>` and `...:thread:<id>`) so `chat.send` does not incorrectly fall back to webchat when valid delivery context exists. Follow-up to #33786.
2526
- Security/auth labels: remove token and API-key snippets from user-facing auth status labels so `/status` and `/models` do not expose credential fragments. (#33262) thanks @cu1ch3n.
2627
- Auth/credential semantics: align profile eligibility + probe diagnostics with SecretRef/expiry rules and harden browser download atomic writes. (#33733) thanks @joshavant.
2728
- Security/audit denyCommands guidance: suggest likely exact node command IDs for unknown `gateway.nodes.denyCommands` entries so ineffective denylist entries are easier to correct. (#29713) thanks @liquidhorizon88-bot.

src/gateway/server-methods/chat.directive-tags.test.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,75 @@ describe("chat directive tag stripping for non-streaming final payloads", () =>
448448
);
449449
});
450450

451+
it("chat.send inherits routing metadata for legacy channel-peer session keys", async () => {
452+
createTranscriptFixture("openclaw-chat-send-legacy-channel-peer-routing-");
453+
mockState.finalText = "ok";
454+
mockState.sessionEntry = {
455+
deliveryContext: {
456+
channel: "telegram",
457+
to: "telegram:6812765697",
458+
accountId: "default",
459+
},
460+
lastChannel: "telegram",
461+
lastTo: "telegram:6812765697",
462+
lastAccountId: "default",
463+
};
464+
const respond = vi.fn();
465+
const context = createChatContext();
466+
467+
await runNonStreamingChatSend({
468+
context,
469+
respond,
470+
idempotencyKey: "idem-legacy-channel-peer-routing",
471+
sessionKey: "agent:main:telegram:6812765697",
472+
expectBroadcast: false,
473+
});
474+
475+
expect(mockState.lastDispatchCtx).toEqual(
476+
expect.objectContaining({
477+
OriginatingChannel: "telegram",
478+
OriginatingTo: "telegram:6812765697",
479+
AccountId: "default",
480+
}),
481+
);
482+
});
483+
484+
it("chat.send inherits routing metadata for legacy channel-peer thread session keys", async () => {
485+
createTranscriptFixture("openclaw-chat-send-legacy-thread-channel-peer-routing-");
486+
mockState.finalText = "ok";
487+
mockState.sessionEntry = {
488+
deliveryContext: {
489+
channel: "telegram",
490+
to: "telegram:6812765697",
491+
accountId: "default",
492+
threadId: "42",
493+
},
494+
lastChannel: "telegram",
495+
lastTo: "telegram:6812765697",
496+
lastAccountId: "default",
497+
lastThreadId: "42",
498+
};
499+
const respond = vi.fn();
500+
const context = createChatContext();
501+
502+
await runNonStreamingChatSend({
503+
context,
504+
respond,
505+
idempotencyKey: "idem-legacy-thread-channel-peer-routing",
506+
sessionKey: "agent:main:telegram:6812765697:thread:42",
507+
expectBroadcast: false,
508+
});
509+
510+
expect(mockState.lastDispatchCtx).toEqual(
511+
expect.objectContaining({
512+
OriginatingChannel: "telegram",
513+
OriginatingTo: "telegram:6812765697",
514+
AccountId: "default",
515+
MessageThreadId: "42",
516+
}),
517+
);
518+
});
519+
451520
it("chat.send does not inherit external delivery context for shared main sessions", async () => {
452521
createTranscriptFixture("openclaw-chat-send-main-no-cross-route-");
453522
mockState.finalText = "ok";

src/gateway/server-methods/chat.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -875,14 +875,16 @@ export const chatHandlers: GatewayRequestHandlers = {
875875
const isChannelScopedSession = sessionPeerShapeCandidates.some((part) =>
876876
CHANNEL_SCOPED_SESSION_SHAPES.has(part),
877877
);
878+
const hasLegacyChannelPeerShape =
879+
!isChannelScopedSession && typeof sessionScopeParts[1] === "string";
878880
// Only inherit prior external route metadata for channel-scoped sessions.
879881
// Channel-agnostic sessions (main, direct:<peer>, etc.) can otherwise
880882
// leak stale routes across surfaces.
881883
const canInheritDeliverableRoute = Boolean(
882884
sessionChannelHint &&
883885
sessionChannelHint !== INTERNAL_MESSAGE_CHANNEL &&
884886
!isChannelAgnosticSessionScope &&
885-
isChannelScopedSession,
887+
(isChannelScopedSession || hasLegacyChannelPeerShape),
886888
);
887889
const hasDeliverableRoute =
888890
canInheritDeliverableRoute &&

0 commit comments

Comments
 (0)