beeos-claw 0.5.28 → 0.5.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{HEARTBEAT_TOKEN,isSilentReplyPrefixText,stripHeartbeatToken}from"./auto-reply-tokens.js";const TIMESTAMP_PREFIX_RE=/^\s*\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+\d{4}-\d{2}-\d{2}\s+\d{1,2}:\d{2}(?::\d{2})?\s+GMT[+-]\d{1,2}(?::?\d{2})?]\s*/i,WORKDIR_PREFIX_RE=/^\s*\[working directory:[^\]]*]\s*/i,MESSAGE_ID_PREFIX_RE=/^\s*\[message_id:[^\]]*]\s*/i,USER_MSG_PREFIX_RE=/^\s*User Message From \w+:\s*/i,HEARTBEAT_OK_LINE=HEARTBEAT_TOKEN,HEARTBEAT_OK_LINE_RE=/^[\s\p{P}\p{S}]*HEARTBEAT_OK[\s\p{P}\p{S}]*$/u,METADATA_PREFIX_PATTERNS=[TIMESTAMP_PREFIX_RE,WORKDIR_PREFIX_RE,MESSAGE_ID_PREFIX_RE,USER_MSG_PREFIX_RE],UNTRUSTED_METADATA_BLOCK_RE=/^Conversation info \(untrusted metadata\):[\s\S]*?Current time:\s*.*?$/i,UNTRUSTED_BLOCK_MARKER="Conversation info (untrusted metadata):",SENDER_UNTRUSTED_MARKER="Sender (untrusted metadata):",SENDER_UNTRUSTED_BLOCK_RE=/Sender\s*\(untrusted[^)]*\)\s*:\s*```[a-z]*\s*[\s\S]*?```/i;function stripMetadataPrefixesFromLine(e){let t=e,r=!1,n=!0;for(;n;){n=!1;for(const e of METADATA_PREFIX_PATTERNS)e.test(t)&&(t=t.replace(e,""),r=!0,n=!0)}return{line:t,changed:r}}export function stripTransportMetadata(e){const t=e.includes(UNTRUSTED_BLOCK_MARKER),r=e.includes(SENDER_UNTRUSTED_MARKER);if(!(e.includes("[")||e.includes(HEARTBEAT_OK_LINE)||e.includes("User Message From")||t||r))return e;if(r){const t=e.replace(SENDER_UNTRUSTED_BLOCK_RE,"").replace(/^\n+/,"").trim();if(t!==e)return t?stripTransportMetadata(t):"";const r=e.indexOf(SENDER_UNTRUSTED_MARKER),n=e.indexOf("\n\n",r),i=-1===n?e.length:n,s=e.slice(i).replace(/^\n+/,"").trim();return s?stripTransportMetadata(s):""}if(t){const t=e.indexOf(UNTRUSTED_BLOCK_MARKER),r=/\nCurrent time:[^\n]*/i.exec(e.slice(t));if(r){const n=t+r.index+r[0].length,i=e.slice(n).replace(/^\n+/,"").trim();return i?stripTransportMetadata(i):""}const n=e.indexOf("\n\n",t),i=-1===n?e.length:n,s=e.slice(i).replace(/^\n+/,"").trim();return s?stripTransportMetadata(s):""}const n=e.split(/\r?\n/),i=[];let s=!1;for(const e of n){const t=stripMetadataPrefixesFromLine(e);HEARTBEAT_OK_LINE_RE.test(t.line.trim())?s=!0:t.changed&&(s=!0,!t.line.trim())||i.push(t.line)}return s?i.join("\n").replace(/^\n+/,"").replace(/\n+$/,""):e}export function normalizeTransportText(e){if(!e)return;const t=stripTransportMetadata(e);return t.length>0?t:void 0}export function isHeartbeatOnlyText(e){const t=e.trim();if(!t)return!1;if(HEARTBEAT_OK_LINE_RE.test(t))return!0;if(t.includes(HEARTBEAT_OK_LINE)){if(stripHeartbeatToken(t,{mode:"message"}).shouldSkip)return!0;if(0===stripTransportMetadata(t).trim().length)return!0}return!1}export function isHeartbeatPrefixText(e){if(!e)return!1;const t=e.trim();return!!t&&(!(t.length>=HEARTBEAT_OK_LINE.length)&&isSilentReplyPrefixText(t,HEARTBEAT_OK_LINE))}export function isHeartbeatFullOrPrefix(e){return"none"!==classifyHeartbeatFilter(e).reason}export function previewFilteredText(e,t=120){if(!e)return"";const r=e.replace(/\\/g,"\\\\").replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t").replace(/\s+/g," ").trim();if(r.length<=t)return r;const n=Math.floor((t-1)/2),i=t-1-n;return`${r.slice(0,n)}…${r.slice(-i)}`}export function classifyHeartbeatFilter(e){if(!e)return{reason:"none",filtered:!1};const t=e.trim();if(!t)return{reason:"none",filtered:!1};if(HEARTBEAT_OK_LINE_RE.test(t))return{reason:"full",filtered:!0};if(t.includes(HEARTBEAT_OK_LINE)){if(stripHeartbeatToken(t,{mode:"message"}).shouldSkip)return{reason:"full-stripped",filtered:!0};if(0===stripTransportMetadata(t).trim().length)return{reason:"full-metadata-residue",filtered:!0}}return isHeartbeatPrefixText(t)?{reason:"prefix",filtered:!0}:{reason:"none",filtered:!1}}const HEARTBEAT_PROMPT_PREFIX="read heartbeat.md if it exists";export function isHeartbeatPromptText(e){return e.slice(0,80).toLowerCase().startsWith(HEARTBEAT_PROMPT_PREFIX)}const CRON_PROMPT_PREFIX_RE=/^\[cron:([0-9a-f-]{8,})\s/i;export function isCronPromptText(e){return CRON_PROMPT_PREFIX_RE.test(e.trimStart())}export function extractCronJobId(e){return CRON_PROMPT_PREFIX_RE.exec(e.trimStart())?.[1]}function isRecord(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function sanitizeNode(e){if("string"==typeof e){const t=stripTransportMetadata(e);return{value:t,changed:t!==e}}if(Array.isArray(e)){let t=!1;const r=e.map(e=>{const r=sanitizeNode(e);return r.changed&&(t=!0),r.value});return{value:t?r:e,changed:t}}if(!isRecord(e))return{value:e,changed:!1};let t=!1,r=e;if("string"==typeof e.type&&"text"===e.type&&"string"==typeof e.text){const n=stripTransportMetadata(e.text);n!==e.text&&(r={...r,text:n},t=!0)}if(Array.isArray(e.content)){const n=sanitizeNode(e.content);n.changed&&(t||(r={...r}),r.content=n.value,t=!0)}return{value:t?r:e,changed:t}}export function sanitizePromptPayload(e){return sanitizeNode(e).value}
1
+ import{HEARTBEAT_TOKEN,isSilentReplyPrefixText,stripHeartbeatToken}from"./auto-reply-tokens.js";const TIMESTAMP_PREFIX_RE=/^\s*\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+\d{4}-\d{2}-\d{2}\s+\d{1,2}:\d{2}(?::\d{2})?\s+GMT[+-]\d{1,2}(?::?\d{2})?]\s*/i,WORKDIR_PREFIX_RE=/^\s*\[working directory:[^\]]*]\s*/i,MESSAGE_ID_PREFIX_RE=/^\s*\[message_id:[^\]]*]\s*/i,USER_MSG_PREFIX_RE=/^\s*User Message From \w+:\s*/i,HEARTBEAT_OK_LINE=HEARTBEAT_TOKEN,HEARTBEAT_OK_LINE_RE=/^[\s\p{P}\p{S}]*HEARTBEAT_OK[\s\p{P}\p{S}]*$/u,UNSUPPORTED_BEEOS_MEDIA_DIRECTIVE_RE=/^\s*MEDIA\s*:\s*beeos-file:\/\/[^\s)]+[^\S\r\n]*$/i,METADATA_PREFIX_PATTERNS=[TIMESTAMP_PREFIX_RE,WORKDIR_PREFIX_RE,MESSAGE_ID_PREFIX_RE,USER_MSG_PREFIX_RE],UNTRUSTED_METADATA_BLOCK_RE=/^Conversation info \(untrusted metadata\):[\s\S]*?Current time:\s*.*?$/i,UNTRUSTED_BLOCK_MARKER="Conversation info (untrusted metadata):",SENDER_UNTRUSTED_MARKER="Sender (untrusted metadata):",SENDER_UNTRUSTED_BLOCK_RE=/Sender\s*\(untrusted[^)]*\)\s*:\s*```[a-z]*\s*[\s\S]*?```/i;function stripMetadataPrefixesFromLine(e){let t=e,r=!1,n=!0;for(;n;){n=!1;for(const e of METADATA_PREFIX_PATTERNS)e.test(t)&&(t=t.replace(e,""),r=!0,n=!0)}return{line:t,changed:r}}export function stripTransportMetadata(e){const t=e.includes(UNTRUSTED_BLOCK_MARKER),r=e.includes(SENDER_UNTRUSTED_MARKER);if(!(e.includes("[")||e.includes(HEARTBEAT_OK_LINE)||/MEDIA\s*:/i.test(e)||e.includes("User Message From")||t||r))return e;if(r){const t=e.replace(SENDER_UNTRUSTED_BLOCK_RE,"").replace(/^\n+/,"").trim();if(t!==e)return t?stripTransportMetadata(t):"";const r=e.indexOf(SENDER_UNTRUSTED_MARKER),n=e.indexOf("\n\n",r),i=-1===n?e.length:n,s=e.slice(i).replace(/^\n+/,"").trim();return s?stripTransportMetadata(s):""}if(t){const t=e.indexOf(UNTRUSTED_BLOCK_MARKER),r=/\nCurrent time:[^\n]*/i.exec(e.slice(t));if(r){const n=t+r.index+r[0].length,i=e.slice(n).replace(/^\n+/,"").trim();return i?stripTransportMetadata(i):""}const n=e.indexOf("\n\n",t),i=-1===n?e.length:n,s=e.slice(i).replace(/^\n+/,"").trim();return s?stripTransportMetadata(s):""}const n=e.split(/\r?\n/),i=[];let s=!1;for(const e of n){const t=stripMetadataPrefixesFromLine(e);HEARTBEAT_OK_LINE_RE.test(t.line.trim())?s=!0:UNSUPPORTED_BEEOS_MEDIA_DIRECTIVE_RE.test(t.line)?s=!0:t.changed&&(s=!0,!t.line.trim())||i.push(t.line)}return s?i.join("\n").replace(/^\n+/,"").replace(/\n+$/,""):e}export function normalizeTransportText(e){if(!e)return;const t=stripTransportMetadata(e);return t.length>0?t:void 0}export function isHeartbeatOnlyText(e){const t=e.trim();if(!t)return!1;if(HEARTBEAT_OK_LINE_RE.test(t))return!0;if(t.includes(HEARTBEAT_OK_LINE)){if(stripHeartbeatToken(t,{mode:"message"}).shouldSkip)return!0;if(0===stripTransportMetadata(t).trim().length)return!0}return!1}export function isHeartbeatPrefixText(e){if(!e)return!1;const t=e.trim();return!!t&&(!(t.length>=HEARTBEAT_OK_LINE.length)&&isSilentReplyPrefixText(t,HEARTBEAT_OK_LINE))}export function isHeartbeatFullOrPrefix(e){return"none"!==classifyHeartbeatFilter(e).reason}export function previewFilteredText(e,t=120){if(!e)return"";const r=e.replace(/\\/g,"\\\\").replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t").replace(/\s+/g," ").trim();if(r.length<=t)return r;const n=Math.floor((t-1)/2),i=t-1-n;return`${r.slice(0,n)}…${r.slice(-i)}`}export function classifyHeartbeatFilter(e){if(!e)return{reason:"none",filtered:!1};const t=e.trim();if(!t)return{reason:"none",filtered:!1};if(HEARTBEAT_OK_LINE_RE.test(t))return{reason:"full",filtered:!0};if(t.includes(HEARTBEAT_OK_LINE)){if(stripHeartbeatToken(t,{mode:"message"}).shouldSkip)return{reason:"full-stripped",filtered:!0};if(0===stripTransportMetadata(t).trim().length)return{reason:"full-metadata-residue",filtered:!0}}return isHeartbeatPrefixText(t)?{reason:"prefix",filtered:!0}:{reason:"none",filtered:!1}}const HEARTBEAT_PROMPT_PREFIX="read heartbeat.md if it exists";export function isHeartbeatPromptText(e){return e.slice(0,80).toLowerCase().startsWith(HEARTBEAT_PROMPT_PREFIX)}const CRON_PROMPT_PREFIX_RE=/^\[cron:([0-9a-f-]{8,})\s/i;export function isCronPromptText(e){return CRON_PROMPT_PREFIX_RE.test(e.trimStart())}export function extractCronJobId(e){return CRON_PROMPT_PREFIX_RE.exec(e.trimStart())?.[1]}function isRecord(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function sanitizeNode(e){if("string"==typeof e){const t=stripTransportMetadata(e);return{value:t,changed:t!==e}}if(Array.isArray(e)){let t=!1;const r=e.map(e=>{const r=sanitizeNode(e);return r.changed&&(t=!0),r.value});return{value:t?r:e,changed:t}}if(!isRecord(e))return{value:e,changed:!1};let t=!1,r=e;if("string"==typeof e.type&&"text"===e.type&&"string"==typeof e.text){const n=stripTransportMetadata(e.text);n!==e.text&&(r={...r,text:n},t=!0)}if(Array.isArray(e.content)){const n=sanitizeNode(e.content);n.changed&&(t||(r={...r}),r.content=n.value,t=!0)}return{value:t?r:e,changed:t}}export function sanitizePromptPayload(e){return sanitizeNode(e).value}
@@ -1 +1 @@
1
- import{describe,it,expect}from"vitest";import{isCronPromptText,isHeartbeatPromptText,isHeartbeatOnlyText,isHeartbeatPrefixText,isHeartbeatFullOrPrefix,classifyHeartbeatFilter,previewFilteredText}from"./message-filter.js";describe("isCronPromptText",()=>{it("matches canonical upstream cron prefix",()=>{expect(isCronPromptText("[cron:3f50c57c-e9ee-420c-acb4-cc80c1dbb15d daily-summary] please summarize")).toBe(!0)}),it("matches case-insensitively and tolerates leading whitespace",()=>{expect(isCronPromptText("\n [CRON:abcdef12-3456-7890-abcd-ef0123456789 job] run")).toBe(!0)}),it("rejects text without the cron prefix",()=>{expect(isCronPromptText("hello, please run my task")).toBe(!1),expect(isCronPromptText("[cron] missing id")).toBe(!1),expect(isCronPromptText("")).toBe(!1)}),it("does not collide with heartbeat prompts",()=>{const e="Read HEARTBEAT.md if it exists (workspace context). Follow it strictly.";expect(isHeartbeatPromptText(e)).toBe(!0),expect(isCronPromptText(e)).toBe(!1)})}),describe("isHeartbeatOnlyText",()=>{it("detects bare token",()=>{expect(isHeartbeatOnlyText("HEARTBEAT_OK")).toBe(!0)}),it("detects token with surrounding whitespace",()=>{expect(isHeartbeatOnlyText(" HEARTBEAT_OK ")).toBe(!0),expect(isHeartbeatOnlyText("\n\nHEARTBEAT_OK\n\n")).toBe(!0)}),it("detects token with trailing punctuation (OpenClaw rule: up to 4 non-word chars)",()=>{expect(isHeartbeatOnlyText("HEARTBEAT_OK.")).toBe(!0),expect(isHeartbeatOnlyText("HEARTBEAT_OK!")).toBe(!0),expect(isHeartbeatOnlyText("HEARTBEAT_OK!!!")).toBe(!0),expect(isHeartbeatOnlyText("HEARTBEAT_OK ✅")).toBe(!0)}),it("detects token wrapped in markdown markup",()=>{expect(isHeartbeatOnlyText("**HEARTBEAT_OK**")).toBe(!0),expect(isHeartbeatOnlyText("`HEARTBEAT_OK`")).toBe(!0),expect(isHeartbeatOnlyText("_HEARTBEAT_OK_")).toBe(!0)}),it("detects token sitting next to stripped transport metadata",()=>{expect(isHeartbeatOnlyText("HEARTBEAT_OK\n[working directory: /home/node]\n[message_id: abc-123]")).toBe(!0)}),it("rejects text that merely mentions the token inline",()=>{expect(isHeartbeatOnlyText("Use the sentinel HEARTBEAT_OK in your ack handler.")).toBe(!1),expect(isHeartbeatOnlyText("The response should be HEARTBEAT_OK always.")).toBe(!1)}),it("rejects empty and pure-whitespace input",()=>{expect(isHeartbeatOnlyText("")).toBe(!1),expect(isHeartbeatOnlyText(" \n\t ")).toBe(!1)})}),describe("isHeartbeatPrefixText",()=>{it("flags the real-world prefix that leaked to staging",()=>{expect(isHeartbeatPrefixText("HEARTBEAT_")).toBe(!0)}),it("flags every meaningful uppercase prefix of HEARTBEAT_OK that includes '_'",()=>{expect(isHeartbeatPrefixText("HEARTBEAT_O")).toBe(!0)}),it("does NOT flag prefixes without '_' (upstream trade-off, avoids eating real words)",()=>{expect(isHeartbeatPrefixText("HEART")).toBe(!1),expect(isHeartbeatPrefixText("HEARTBEAT")).toBe(!1)}),it("rejects length < 2",()=>{expect(isHeartbeatPrefixText("H")).toBe(!1),expect(isHeartbeatPrefixText("")).toBe(!1)}),it("rejects mixed case (avoids eating conversational text)",()=>{expect(isHeartbeatPrefixText("heartbeat_")).toBe(!1),expect(isHeartbeatPrefixText("Heartbeat_")).toBe(!1)}),it("rejects text with non-[A-Z_] characters",()=>{expect(isHeartbeatPrefixText("HEARTBEAT!")).toBe(!1),expect(isHeartbeatPrefixText("HEART-BEAT")).toBe(!1),expect(isHeartbeatPrefixText("HEARTBEAT_OK TRAILING")).toBe(!1)}),it("does NOT flag the full token (that's isHeartbeatOnlyText's job)",()=>{expect(isHeartbeatPrefixText("HEARTBEAT_OK")).toBe(!1)}),it("tolerates leading whitespace",()=>{expect(isHeartbeatPrefixText(" HEARTBEAT_")).toBe(!0)})}),describe("isHeartbeatFullOrPrefix — exactly matches OpenClaw semantics",()=>{it("catches the full token",()=>{expect(isHeartbeatFullOrPrefix("HEARTBEAT_OK")).toBe(!0),expect(isHeartbeatFullOrPrefix("HEARTBEAT_OK.")).toBe(!0),expect(isHeartbeatFullOrPrefix("**HEARTBEAT_OK**")).toBe(!0)}),it("catches prefixes that contain '_'",()=>{expect(isHeartbeatFullOrPrefix("HEARTBEAT_")).toBe(!0),expect(isHeartbeatFullOrPrefix("HEARTBEAT_O")).toBe(!0)}),it("intentionally does NOT catch prefixes without '_' (upstream trade-off)",()=>{expect(isHeartbeatFullOrPrefix("HEART")).toBe(!1),expect(isHeartbeatFullOrPrefix("HEARTBEAT")).toBe(!1),expect(isHeartbeatFullOrPrefix("HE")).toBe(!1)}),it("lets real user content through",()=>{expect(isHeartbeatFullOrPrefix("Let's design a HEARTBEAT_OK-like ack for our API.")).toBe(!1),expect(isHeartbeatFullOrPrefix("Hello, how are you?")).toBe(!1),expect(isHeartbeatFullOrPrefix("Heart of darkness is a novel")).toBe(!1)})}),describe("classifyHeartbeatFilter — structured drop reasons for logs",()=>{it("reports `full` on canonical tokens",()=>{expect(classifyHeartbeatFilter("HEARTBEAT_OK")).toEqual({reason:"full",filtered:!0}),expect(classifyHeartbeatFilter("HEARTBEAT_OK.").reason).toBe("full"),expect(classifyHeartbeatFilter("**HEARTBEAT_OK**").reason).toBe("full"),expect(classifyHeartbeatFilter("`HEARTBEAT_OK`").reason).toBe("full")}),it("reports `full-stripped` as a defensive fallback when regex misses but stripHeartbeatToken catches",()=>{expect(classifyHeartbeatFilter("HEARTBEAT_OK ✅").reason).toBe("full")}),it("reports `full-metadata-residue` when the token is surrounded by transport metadata",()=>{expect(classifyHeartbeatFilter("HEARTBEAT_OK\n[working directory: /home/node]\n[message_id: abc-123]").reason).toBe("full-metadata-residue")}),it("reports `prefix` on uppercase prefixes that contain '_' (upstream rule)",()=>{expect(classifyHeartbeatFilter("HEARTBEAT_").reason).toBe("prefix"),expect(classifyHeartbeatFilter("HEARTBEAT_O").reason).toBe("prefix")}),it("reports `none` for uppercase prefixes WITHOUT '_' (upstream trade-off — avoids eating real words)",()=>{expect(classifyHeartbeatFilter("HEART").reason).toBe("none"),expect(classifyHeartbeatFilter("HEARTBEAT").reason).toBe("none"),expect(classifyHeartbeatFilter("HE").reason).toBe("none")}),it("reports `none` for real content",()=>{expect(classifyHeartbeatFilter("Hello").reason).toBe("none"),expect(classifyHeartbeatFilter("Use HEARTBEAT_OK to reply.").reason).toBe("none"),expect(classifyHeartbeatFilter("").reason).toBe("none"),expect(classifyHeartbeatFilter(" ").reason).toBe("none")}),it("keeps `filtered` in sync with `reason` (never drifts)",()=>{for(const e of["HEARTBEAT_OK","**HEARTBEAT_OK**","HEARTBEAT_","HE","HEART","hello",""]){const t=classifyHeartbeatFilter(e);expect(t.filtered).toBe("none"!==t.reason)}})}),describe("previewFilteredText",()=>{it("returns empty string on empty input",()=>{expect(previewFilteredText("")).toBe("")}),it("escapes newlines / tabs and collapses runs of whitespace",()=>{expect(previewFilteredText("HEARTBEAT_OK\n[meta]")).toBe("HEARTBEAT_OK\\n[meta]"),expect(previewFilteredText("a\t\tb")).toBe("a\\t\\tb"),expect(previewFilteredText("a b c")).toBe("a b c")}),it("returns the full text when within maxLen",()=>{expect(previewFilteredText("HEARTBEAT_OK",20)).toBe("HEARTBEAT_OK")}),it("keeps head + tail with … when longer than maxLen",()=>{const e="A".repeat(50)+"HEARTBEAT_OK"+"B".repeat(50),t=previewFilteredText(e,40);expect(t.length).toBeLessThanOrEqual(40),expect(t).toContain("…"),expect(t.startsWith("A")).toBe(!0),expect(t.endsWith("B")).toBe(!0)})});
1
+ import{describe,it,expect}from"vitest";import{isCronPromptText,isHeartbeatPromptText,isHeartbeatOnlyText,isHeartbeatPrefixText,isHeartbeatFullOrPrefix,classifyHeartbeatFilter,normalizeTransportText,previewFilteredText}from"./message-filter.js";describe("isCronPromptText",()=>{it("matches canonical upstream cron prefix",()=>{expect(isCronPromptText("[cron:3f50c57c-e9ee-420c-acb4-cc80c1dbb15d daily-summary] please summarize")).toBe(!0)}),it("matches case-insensitively and tolerates leading whitespace",()=>{expect(isCronPromptText("\n [CRON:abcdef12-3456-7890-abcd-ef0123456789 job] run")).toBe(!0)}),it("rejects text without the cron prefix",()=>{expect(isCronPromptText("hello, please run my task")).toBe(!1),expect(isCronPromptText("[cron] missing id")).toBe(!1),expect(isCronPromptText("")).toBe(!1)}),it("does not collide with heartbeat prompts",()=>{const e="Read HEARTBEAT.md if it exists (workspace context). Follow it strictly.";expect(isHeartbeatPromptText(e)).toBe(!0),expect(isCronPromptText(e)).toBe(!1)})}),describe("isHeartbeatOnlyText",()=>{it("detects bare token",()=>{expect(isHeartbeatOnlyText("HEARTBEAT_OK")).toBe(!0)}),it("detects token with surrounding whitespace",()=>{expect(isHeartbeatOnlyText(" HEARTBEAT_OK ")).toBe(!0),expect(isHeartbeatOnlyText("\n\nHEARTBEAT_OK\n\n")).toBe(!0)}),it("detects token with trailing punctuation (OpenClaw rule: up to 4 non-word chars)",()=>{expect(isHeartbeatOnlyText("HEARTBEAT_OK.")).toBe(!0),expect(isHeartbeatOnlyText("HEARTBEAT_OK!")).toBe(!0),expect(isHeartbeatOnlyText("HEARTBEAT_OK!!!")).toBe(!0),expect(isHeartbeatOnlyText("HEARTBEAT_OK ✅")).toBe(!0)}),it("detects token wrapped in markdown markup",()=>{expect(isHeartbeatOnlyText("**HEARTBEAT_OK**")).toBe(!0),expect(isHeartbeatOnlyText("`HEARTBEAT_OK`")).toBe(!0),expect(isHeartbeatOnlyText("_HEARTBEAT_OK_")).toBe(!0)}),it("detects token sitting next to stripped transport metadata",()=>{expect(isHeartbeatOnlyText("HEARTBEAT_OK\n[working directory: /home/node]\n[message_id: abc-123]")).toBe(!0)}),it("rejects text that merely mentions the token inline",()=>{expect(isHeartbeatOnlyText("Use the sentinel HEARTBEAT_OK in your ack handler.")).toBe(!1),expect(isHeartbeatOnlyText("The response should be HEARTBEAT_OK always.")).toBe(!1)}),it("rejects empty and pure-whitespace input",()=>{expect(isHeartbeatOnlyText("")).toBe(!1),expect(isHeartbeatOnlyText(" \n\t ")).toBe(!1)})}),describe("normalizeTransportText",()=>{it("strips unsupported standalone BeeOS MEDIA directives",()=>{expect(normalizeTransportText("MEDIA:beeos-file://abc")).toBeUndefined(),expect(normalizeTransportText("media: beeos-file://abc")).toBeUndefined()}),it("removes MEDIA directive lines while preserving normal assistant text",()=>{expect(normalizeTransportText("Here's the screenshot from your device:\n\nMEDIA:beeos-file://abc")).toBe("Here's the screenshot from your device:")}),it("does not strip inline discussion of MEDIA directives",()=>{expect(normalizeTransportText("The invalid form is MEDIA:beeos-file://abc.")).toBe("The invalid form is MEDIA:beeos-file://abc.")})}),describe("isHeartbeatPrefixText",()=>{it("flags the real-world prefix that leaked to staging",()=>{expect(isHeartbeatPrefixText("HEARTBEAT_")).toBe(!0)}),it("flags every meaningful uppercase prefix of HEARTBEAT_OK that includes '_'",()=>{expect(isHeartbeatPrefixText("HEARTBEAT_O")).toBe(!0)}),it("does NOT flag prefixes without '_' (upstream trade-off, avoids eating real words)",()=>{expect(isHeartbeatPrefixText("HEART")).toBe(!1),expect(isHeartbeatPrefixText("HEARTBEAT")).toBe(!1)}),it("rejects length < 2",()=>{expect(isHeartbeatPrefixText("H")).toBe(!1),expect(isHeartbeatPrefixText("")).toBe(!1)}),it("rejects mixed case (avoids eating conversational text)",()=>{expect(isHeartbeatPrefixText("heartbeat_")).toBe(!1),expect(isHeartbeatPrefixText("Heartbeat_")).toBe(!1)}),it("rejects text with non-[A-Z_] characters",()=>{expect(isHeartbeatPrefixText("HEARTBEAT!")).toBe(!1),expect(isHeartbeatPrefixText("HEART-BEAT")).toBe(!1),expect(isHeartbeatPrefixText("HEARTBEAT_OK TRAILING")).toBe(!1)}),it("does NOT flag the full token (that's isHeartbeatOnlyText's job)",()=>{expect(isHeartbeatPrefixText("HEARTBEAT_OK")).toBe(!1)}),it("tolerates leading whitespace",()=>{expect(isHeartbeatPrefixText(" HEARTBEAT_")).toBe(!0)})}),describe("isHeartbeatFullOrPrefix — exactly matches OpenClaw semantics",()=>{it("catches the full token",()=>{expect(isHeartbeatFullOrPrefix("HEARTBEAT_OK")).toBe(!0),expect(isHeartbeatFullOrPrefix("HEARTBEAT_OK.")).toBe(!0),expect(isHeartbeatFullOrPrefix("**HEARTBEAT_OK**")).toBe(!0)}),it("catches prefixes that contain '_'",()=>{expect(isHeartbeatFullOrPrefix("HEARTBEAT_")).toBe(!0),expect(isHeartbeatFullOrPrefix("HEARTBEAT_O")).toBe(!0)}),it("intentionally does NOT catch prefixes without '_' (upstream trade-off)",()=>{expect(isHeartbeatFullOrPrefix("HEART")).toBe(!1),expect(isHeartbeatFullOrPrefix("HEARTBEAT")).toBe(!1),expect(isHeartbeatFullOrPrefix("HE")).toBe(!1)}),it("lets real user content through",()=>{expect(isHeartbeatFullOrPrefix("Let's design a HEARTBEAT_OK-like ack for our API.")).toBe(!1),expect(isHeartbeatFullOrPrefix("Hello, how are you?")).toBe(!1),expect(isHeartbeatFullOrPrefix("Heart of darkness is a novel")).toBe(!1)})}),describe("classifyHeartbeatFilter — structured drop reasons for logs",()=>{it("reports `full` on canonical tokens",()=>{expect(classifyHeartbeatFilter("HEARTBEAT_OK")).toEqual({reason:"full",filtered:!0}),expect(classifyHeartbeatFilter("HEARTBEAT_OK.").reason).toBe("full"),expect(classifyHeartbeatFilter("**HEARTBEAT_OK**").reason).toBe("full"),expect(classifyHeartbeatFilter("`HEARTBEAT_OK`").reason).toBe("full")}),it("reports `full-stripped` as a defensive fallback when regex misses but stripHeartbeatToken catches",()=>{expect(classifyHeartbeatFilter("HEARTBEAT_OK ✅").reason).toBe("full")}),it("reports `full-metadata-residue` when the token is surrounded by transport metadata",()=>{expect(classifyHeartbeatFilter("HEARTBEAT_OK\n[working directory: /home/node]\n[message_id: abc-123]").reason).toBe("full-metadata-residue")}),it("reports `prefix` on uppercase prefixes that contain '_' (upstream rule)",()=>{expect(classifyHeartbeatFilter("HEARTBEAT_").reason).toBe("prefix"),expect(classifyHeartbeatFilter("HEARTBEAT_O").reason).toBe("prefix")}),it("reports `none` for uppercase prefixes WITHOUT '_' (upstream trade-off — avoids eating real words)",()=>{expect(classifyHeartbeatFilter("HEART").reason).toBe("none"),expect(classifyHeartbeatFilter("HEARTBEAT").reason).toBe("none"),expect(classifyHeartbeatFilter("HE").reason).toBe("none")}),it("reports `none` for real content",()=>{expect(classifyHeartbeatFilter("Hello").reason).toBe("none"),expect(classifyHeartbeatFilter("Use HEARTBEAT_OK to reply.").reason).toBe("none"),expect(classifyHeartbeatFilter("").reason).toBe("none"),expect(classifyHeartbeatFilter(" ").reason).toBe("none")}),it("keeps `filtered` in sync with `reason` (never drifts)",()=>{for(const e of["HEARTBEAT_OK","**HEARTBEAT_OK**","HEARTBEAT_","HE","HEART","hello",""]){const t=classifyHeartbeatFilter(e);expect(t.filtered).toBe("none"!==t.reason)}})}),describe("previewFilteredText",()=>{it("returns empty string on empty input",()=>{expect(previewFilteredText("")).toBe("")}),it("escapes newlines / tabs and collapses runs of whitespace",()=>{expect(previewFilteredText("HEARTBEAT_OK\n[meta]")).toBe("HEARTBEAT_OK\\n[meta]"),expect(previewFilteredText("a\t\tb")).toBe("a\\t\\tb"),expect(previewFilteredText("a b c")).toBe("a b c")}),it("returns the full text when within maxLen",()=>{expect(previewFilteredText("HEARTBEAT_OK",20)).toBe("HEARTBEAT_OK")}),it("keeps head + tail with … when longer than maxLen",()=>{const e="A".repeat(50)+"HEARTBEAT_OK"+"B".repeat(50),t=previewFilteredText(e,40);expect(t.length).toBeLessThanOrEqual(40),expect(t).toContain("…"),expect(t.startsWith("A")).toBe(!0),expect(t.endsWith("B")).toBe(!0)})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beeos-claw",
3
- "version": "0.5.28",
3
+ "version": "0.5.29",
4
4
  "description": "OpenClaw plugin that bridges the BeeOS platform with the local OpenClaw Gateway, providing file transfer, ACP bridge, and observability.",
5
5
  "license": "MIT",
6
6
  "author": "BeeOS <dev@beeos.ai>",