mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 10:31:24 +00:00
60 lines
1.7 KiB
TypeScript
60 lines
1.7 KiB
TypeScript
import {
|
|
DEFAULT_HEARTBEAT_ACK_MAX_CHARS,
|
|
stripHeartbeatToken,
|
|
} from "../../auto-reply/heartbeat.js";
|
|
import { truncateUtf16Safe } from "../../utils.js";
|
|
|
|
type DeliveryPayload = {
|
|
text?: string;
|
|
mediaUrl?: string;
|
|
mediaUrls?: string[];
|
|
};
|
|
|
|
export function pickSummaryFromOutput(text: string | undefined) {
|
|
const clean = (text ?? "").trim();
|
|
if (!clean) return undefined;
|
|
const limit = 2000;
|
|
return clean.length > limit ? `${truncateUtf16Safe(clean, limit)}…` : clean;
|
|
}
|
|
|
|
export function pickSummaryFromPayloads(
|
|
payloads: Array<{ text?: string | undefined }>,
|
|
) {
|
|
for (let i = payloads.length - 1; i >= 0; i--) {
|
|
const summary = pickSummaryFromOutput(payloads[i]?.text);
|
|
if (summary) return summary;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Check if all payloads are just heartbeat ack responses (HEARTBEAT_OK).
|
|
* Returns true if delivery should be skipped because there's no real content.
|
|
*/
|
|
export function isHeartbeatOnlyResponse(
|
|
payloads: DeliveryPayload[],
|
|
ackMaxChars: number,
|
|
) {
|
|
if (payloads.length === 0) return true;
|
|
return payloads.every((payload) => {
|
|
// If there's media, we should deliver regardless of text content.
|
|
const hasMedia =
|
|
(payload.mediaUrls?.length ?? 0) > 0 || Boolean(payload.mediaUrl);
|
|
if (hasMedia) return false;
|
|
// Use heartbeat mode to check if text is just HEARTBEAT_OK or short ack.
|
|
const result = stripHeartbeatToken(payload.text, {
|
|
mode: "heartbeat",
|
|
maxAckChars: ackMaxChars,
|
|
});
|
|
return result.shouldSkip;
|
|
});
|
|
}
|
|
|
|
export function resolveHeartbeatAckMaxChars(agentCfg?: {
|
|
heartbeat?: { ackMaxChars?: number };
|
|
}) {
|
|
const raw =
|
|
agentCfg?.heartbeat?.ackMaxChars ?? DEFAULT_HEARTBEAT_ACK_MAX_CHARS;
|
|
return Math.max(0, raw);
|
|
}
|