mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 01:58:26 +00:00
Ignore up to 4 non-word characters when stripping HEARTBEAT_OK token … (#15847)
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: dc03ce5005
Co-authored-by: Spacefish <375633+Spacefish@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
This commit is contained in:
@@ -16,6 +16,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
|
|
||||||
- Agents/Heartbeat: stop auto-creating `HEARTBEAT.md` during workspace bootstrap so missing files continue to run heartbeat as documented. (#11766) Thanks @shadril238.
|
- Agents/Heartbeat: stop auto-creating `HEARTBEAT.md` during workspace bootstrap so missing files continue to run heartbeat as documented. (#11766) Thanks @shadril238.
|
||||||
- CLI: lazily load outbound provider dependencies and remove forced success-path exits so commands terminate naturally without killing intentional long-running foreground actions. (#12906) Thanks @DrCrinkle.
|
- CLI: lazily load outbound provider dependencies and remove forced success-path exits so commands terminate naturally without killing intentional long-running foreground actions. (#12906) Thanks @DrCrinkle.
|
||||||
|
- Auto-reply/Heartbeat: strip sentence-ending `HEARTBEAT_OK` tokens even when followed by up to 4 punctuation characters, while preserving surrounding sentence punctuation. (#15847) Thanks @Spacefish.
|
||||||
- Clawdock: avoid Zsh readonly variable collisions in helper scripts. (#15501) Thanks @nkelner.
|
- Clawdock: avoid Zsh readonly variable collisions in helper scripts. (#15501) Thanks @nkelner.
|
||||||
- Discord: route autoThread replies to existing threads instead of the root channel. (#8302) Thanks @gavinbmoore, @thewilloftheshadow.
|
- Discord: route autoThread replies to existing threads instead of the root channel. (#8302) Thanks @gavinbmoore, @thewilloftheshadow.
|
||||||
- Discord/Agents: apply channel/group `historyLimit` during embedded-runner history compaction to prevent long-running channel sessions from bypassing truncation and overflowing context windows. (#11224) Thanks @shadril238.
|
- Discord/Agents: apply channel/group `historyLimit` during embedded-runner history compaction to prevent long-running channel sessions from bypassing truncation and overflowing context windows. (#11224) Thanks @shadril238.
|
||||||
|
|||||||
@@ -107,6 +107,62 @@ describe("stripHeartbeatToken", () => {
|
|||||||
didStrip: true,
|
didStrip: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("strips trailing punctuation only when directly after the token", () => {
|
||||||
|
// Token with trailing dot/exclamation/dashes → should still strip
|
||||||
|
expect(stripHeartbeatToken(`${HEARTBEAT_TOKEN}.`, { mode: "heartbeat" })).toEqual({
|
||||||
|
shouldSkip: true,
|
||||||
|
text: "",
|
||||||
|
didStrip: true,
|
||||||
|
});
|
||||||
|
expect(stripHeartbeatToken(`${HEARTBEAT_TOKEN}!!!`, { mode: "heartbeat" })).toEqual({
|
||||||
|
shouldSkip: true,
|
||||||
|
text: "",
|
||||||
|
didStrip: true,
|
||||||
|
});
|
||||||
|
expect(stripHeartbeatToken(`${HEARTBEAT_TOKEN}---`, { mode: "heartbeat" })).toEqual({
|
||||||
|
shouldSkip: true,
|
||||||
|
text: "",
|
||||||
|
didStrip: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("strips a sentence-ending token and keeps trailing punctuation", () => {
|
||||||
|
// Token appears at sentence end with trailing punctuation.
|
||||||
|
expect(
|
||||||
|
stripHeartbeatToken(`I should not respond ${HEARTBEAT_TOKEN}.`, {
|
||||||
|
mode: "message",
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
shouldSkip: false,
|
||||||
|
text: `I should not respond.`,
|
||||||
|
didStrip: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("strips sentence-ending token with emphasis punctuation in heartbeat mode", () => {
|
||||||
|
expect(
|
||||||
|
stripHeartbeatToken(
|
||||||
|
`There is nothing todo, so i should respond with ${HEARTBEAT_TOKEN} !!!`,
|
||||||
|
{
|
||||||
|
mode: "heartbeat",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
shouldSkip: true,
|
||||||
|
text: "",
|
||||||
|
didStrip: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("preserves trailing punctuation on text before the token", () => {
|
||||||
|
// Token at end, preceding text has its own punctuation — only the token is stripped
|
||||||
|
expect(stripHeartbeatToken(`All clear. ${HEARTBEAT_TOKEN}`, { mode: "message" })).toEqual({
|
||||||
|
shouldSkip: false,
|
||||||
|
text: "All clear.",
|
||||||
|
didStrip: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("isHeartbeatContentEffectivelyEmpty", () => {
|
describe("isHeartbeatContentEffectivelyEmpty", () => {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { escapeRegExp } from "../utils.js";
|
||||||
import { HEARTBEAT_TOKEN } from "./tokens.js";
|
import { HEARTBEAT_TOKEN } from "./tokens.js";
|
||||||
|
|
||||||
// Default heartbeat prompt (used when config.agents.defaults.heartbeat.prompt is unset).
|
// Default heartbeat prompt (used when config.agents.defaults.heartbeat.prompt is unset).
|
||||||
@@ -65,6 +66,9 @@ function stripTokenAtEdges(raw: string): { text: string; didStrip: boolean } {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const token = HEARTBEAT_TOKEN;
|
const token = HEARTBEAT_TOKEN;
|
||||||
|
const tokenAtEndWithOptionalTrailingPunctuation = new RegExp(
|
||||||
|
`${escapeRegExp(token)}[^\\w]{0,4}$`,
|
||||||
|
);
|
||||||
if (!text.includes(token)) {
|
if (!text.includes(token)) {
|
||||||
return { text, didStrip: false };
|
return { text, didStrip: false };
|
||||||
}
|
}
|
||||||
@@ -81,9 +85,19 @@ function stripTokenAtEdges(raw: string): { text: string; didStrip: boolean } {
|
|||||||
changed = true;
|
changed = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (next.endsWith(token)) {
|
// Strip the token when it appears at the end of the text.
|
||||||
const before = next.slice(0, Math.max(0, next.length - token.length));
|
// Also strip up to 4 trailing non-word characters the model may have appended
|
||||||
text = before.trimEnd();
|
// (e.g. ".", "!!!", "---"). Keep trailing punctuation only when real
|
||||||
|
// sentence text exists before the token.
|
||||||
|
if (tokenAtEndWithOptionalTrailingPunctuation.test(next)) {
|
||||||
|
const idx = next.lastIndexOf(token);
|
||||||
|
const before = next.slice(0, idx).trimEnd();
|
||||||
|
if (!before) {
|
||||||
|
text = "";
|
||||||
|
} else {
|
||||||
|
const after = next.slice(idx + token.length).trimStart();
|
||||||
|
text = `${before}${after}`.trimEnd();
|
||||||
|
}
|
||||||
didStrip = true;
|
didStrip = true;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user