mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 17:20:22 +00:00
fix: stabilize Telegram draft boundaries and suppress NO_REPLY lead leaks (#33169)
* fix: stabilize telegram draft stream message boundaries * fix: suppress NO_REPLY lead-fragment leaks * fix: keep underscore guard for non-NO_REPLY prefixes * fix: skip assistant-start rotation only after real lane rotation * fix: preserve finalized state when pre-rotation does not force * fix: reset finalized preview state on message-start boundary * fix: document Telegram draft boundary + NO_REPLY reliability updates (#33169) (thanks @obviyus)
This commit is contained in:
@@ -410,7 +410,7 @@ describe("runReplyAgent typing (heartbeat)", () => {
|
||||
shouldType: false,
|
||||
},
|
||||
{
|
||||
partials: ["NO_", "NO_RE", "NO_REPLY"],
|
||||
partials: ["NO", "NO_", "NO_RE", "NO_REPLY"],
|
||||
finalText: "NO_REPLY",
|
||||
expectedForwarded: [] as string[],
|
||||
shouldType: false,
|
||||
|
||||
@@ -74,7 +74,8 @@ describe("stripSilentToken", () => {
|
||||
});
|
||||
|
||||
describe("isSilentReplyPrefixText", () => {
|
||||
it("matches uppercase underscore prefixes", () => {
|
||||
it("matches uppercase token lead fragments", () => {
|
||||
expect(isSilentReplyPrefixText("NO")).toBe(true);
|
||||
expect(isSilentReplyPrefixText("NO_")).toBe(true);
|
||||
expect(isSilentReplyPrefixText("NO_RE")).toBe(true);
|
||||
expect(isSilentReplyPrefixText("NO_REPLY")).toBe(true);
|
||||
@@ -84,9 +85,17 @@ describe("isSilentReplyPrefixText", () => {
|
||||
it("rejects ambiguous natural-language prefixes", () => {
|
||||
expect(isSilentReplyPrefixText("N")).toBe(false);
|
||||
expect(isSilentReplyPrefixText("No")).toBe(false);
|
||||
expect(isSilentReplyPrefixText("no")).toBe(false);
|
||||
expect(isSilentReplyPrefixText("Hello")).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps underscore guard for non-NO_REPLY tokens", () => {
|
||||
expect(isSilentReplyPrefixText("HE", "HEARTBEAT_OK")).toBe(false);
|
||||
expect(isSilentReplyPrefixText("HEART", "HEARTBEAT_OK")).toBe(false);
|
||||
expect(isSilentReplyPrefixText("HEARTBEAT", "HEARTBEAT_OK")).toBe(false);
|
||||
expect(isSilentReplyPrefixText("HEARTBEAT_", "HEARTBEAT_OK")).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects non-prefixes and mixed characters", () => {
|
||||
expect(isSilentReplyPrefixText("NO_X")).toBe(false);
|
||||
expect(isSilentReplyPrefixText("NO_REPLY more")).toBe(false);
|
||||
|
||||
@@ -56,15 +56,34 @@ export function isSilentReplyPrefixText(
|
||||
if (!text) {
|
||||
return false;
|
||||
}
|
||||
const normalized = text.trimStart().toUpperCase();
|
||||
const trimmed = text.trimStart();
|
||||
if (!trimmed) {
|
||||
return false;
|
||||
}
|
||||
// Guard against suppressing natural-language "No..." text while still
|
||||
// catching uppercase lead fragments like "NO" from streamed NO_REPLY.
|
||||
if (trimmed !== trimmed.toUpperCase()) {
|
||||
return false;
|
||||
}
|
||||
const normalized = trimmed.toUpperCase();
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
if (!normalized.includes("_")) {
|
||||
if (normalized.length < 2) {
|
||||
return false;
|
||||
}
|
||||
if (/[^A-Z_]/.test(normalized)) {
|
||||
return false;
|
||||
}
|
||||
return token.toUpperCase().startsWith(normalized);
|
||||
const tokenUpper = token.toUpperCase();
|
||||
if (!tokenUpper.startsWith(normalized)) {
|
||||
return false;
|
||||
}
|
||||
if (normalized.includes("_")) {
|
||||
return true;
|
||||
}
|
||||
// Keep underscore guard for generic tokens to avoid suppressing unrelated
|
||||
// uppercase words (e.g. HEART/HE with HEARTBEAT_OK). Only allow bare "NO"
|
||||
// because NO_REPLY streaming can transiently emit that fragment.
|
||||
return tokenUpper === SILENT_REPLY_TOKEN && normalized === "NO";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user