Merge d19e028754a97b28529e4c243115c5cbefb037db into 5e417b44e1540f528d2ae63e3e20229a902d1db2

This commit is contained in:
Andy Tien 2026-03-21 10:05:11 +08:00 committed by GitHub
commit 802ac8ab22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 89 additions and 0 deletions

View File

@ -1,6 +1,63 @@
import { describe, expect, it } from "vitest";
import { resolveLastChannelRaw, resolveLastToRaw } from "./session-delivery.js";
describe("session delivery bound channel routing", () => {
it("delivers to bound external channel when persistedLastTo is set", () => {
// Session has deliveryContext bound to Telegram channel
const sessionKey = "agent:main:telegram:default:channel:C123456";
expect(
resolveLastChannelRaw({
originatingChannelRaw: "INTERNAL_MESSAGE_CHANNEL",
persistedLastChannel: "telegram",
sessionKey,
}),
).toBe("telegram");
expect(
resolveLastToRaw({
originatingChannelRaw: "INTERNAL_MESSAGE_CHANNEL",
originatingToRaw: "session:inter-session-123",
persistedLastChannel: "telegram",
persistedLastTo: "C123456",
sessionKey,
}),
).toBe("C123456");
});
it("falls back to originatingToRaw when persistedLastTo is not set", () => {
// Session bound to external channel but lastTo not yet persisted
const sessionKey = "agent:main:telegram:default:channel:C123456";
expect(
resolveLastToRaw({
originatingChannelRaw: "INTERNAL_MESSAGE_CHANNEL",
originatingToRaw: "session:inter-session-123",
persistedLastChannel: "telegram",
persistedLastTo: undefined,
sessionKey,
}),
).toBe("session:inter-session-123");
});
it("does not drop delivery when persistedLastTo is falsy", () => {
// Regression test: ensure we don't silently drop messages
const sessionKey = "agent:main:telegram:default:channel:C123456";
const result = resolveLastToRaw({
originatingChannelRaw: "INTERNAL_MESSAGE_CHANNEL",
originatingToRaw: "session:inter-session-123",
persistedLastChannel: "telegram",
persistedLastTo: undefined,
sessionKey,
});
// Should NOT return undefined (which would drop the message)
expect(result).not.toBeUndefined();
expect(result).toBe("session:inter-session-123");
});
});
describe("session delivery direct-session routing overrides", () => {
it.each([
"agent:main:direct:user-1",

View File

@ -151,6 +151,16 @@ export function resolveLastToRaw(params: {
}
}
// Fix #34308: When channel is INTERNAL but session has a persisted external channel
// (deliveryContext) AND a persisted lastTo address, use the persisted to address for reply delivery.
if (
originatingChannel === INTERNAL_MESSAGE_CHANNEL &&
isExternalRoutingChannel(persistedChannel) &&
params.persistedLastTo
) {
return params.persistedLastTo;
}
return params.originatingToRaw || params.toRaw || params.persistedLastTo;
}

View File

@ -17,11 +17,33 @@ async function copyTextToClipboard(text: string): Promise<boolean> {
return false;
}
// Try Clipboard API first (works in secure contexts/HTTPS)
try {
await navigator.clipboard.writeText(text);
return true;
} catch {
// Fallback for non-secure contexts (HTTP on Windows/localhost)
return copyViaExecCommand(text);
}
}
function copyViaExecCommand(text: string): boolean {
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.style.position = "fixed";
textarea.style.left = "-9999px";
textarea.style.top = "-9999px";
document.body.appendChild(textarea);
try {
textarea.focus();
textarea.select();
return document.execCommand("copy");
} catch {
return false;
} finally {
// Always clean up the textarea element to prevent DOM leak
document.body.removeChild(textarea);
}
}