Files
openclaw/src/cron/isolated-agent/helpers.ts
2026-01-14 01:17:56 +00:00

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);
}