UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

184 lines (183 loc) 11.6 kB
import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js"; import { y as truncateUtf16Safe } from "./utils-CCC-BEJH.js"; import { i as getReplyPayloadMetadata } from "./reply-payload-CZJ2cgZc.js"; import { s as hasOutboundReplyContent } from "./reply-payload-D-VMfpYI.js"; import { d as stripHeartbeatToken } from "./heartbeat-CicGJ6Bb.js"; //#region src/cron/heartbeat-policy.ts /** Decides when cron heartbeat acknowledgements should stay out of visible delivery. */ /** Returns whether delivery output contains only heartbeat acknowledgement text. */ function shouldSkipHeartbeatOnlyDelivery(payloads, ackMaxChars) { if (payloads.length === 0) return true; if (payloads.some((payload) => hasOutboundReplyContent({ ...payload, text: void 0 }, { trimText: true }))) return false; return payloads.some((payload) => { return stripHeartbeatToken(payload.text, { mode: "heartbeat", maxAckChars: ackMaxChars }).shouldSkip; }); } //#endregion //#region src/cron/isolated-agent/helpers.ts /** Normalizes isolated cron run output into summaries, delivery payloads, and error state. */ function normalizeCronFailureSignal(signal) { const message = normalizeOptionalString(signal?.message); if (signal?.fatalForCron !== true || !message) return; return { ...signal, message, fatalForCron: true }; } function formatCronFailureSignal(signal) { const kind = normalizeOptionalString(signal.kind) ?? "run"; const code = normalizeOptionalString(signal.code); const source = normalizeOptionalString(signal.toolName) ?? normalizeOptionalString(signal.source); return `cron classifier: ${kind} failure${source ? ` from ${source}` : ""}${code ? ` (${code})` : ""}: ${signal.message}`; } function formatCronRunLevelError(error) { const direct = normalizeOptionalString(error); if (direct) return `cron isolated run failed: ${direct}`; if (!error || typeof error !== "object") return; const record = error; const message = normalizeOptionalString(record.message); if (message) return `cron isolated run failed: ${message}`; const kind = normalizeOptionalString(record.kind); if (kind) return `cron isolated run failed: ${kind}`; return "cron isolated run failed"; } /** Picks a bounded cron run summary from plain text output. */ function pickSummaryFromOutput(text) { const clean = (text ?? "").trim(); if (!clean) return; const limit = 2e3; return clean.length > limit ? `${truncateUtf16Safe(clean, limit)}…` : clean; } /** Picks the last non-error payload text suitable for cron run summaries. */ function pickSummaryFromPayloads(payloads) { for (let i = payloads.length - 1; i >= 0; i--) { if (payloads[i]?.isError) continue; const summary = pickSummaryFromOutput(payloads[i]?.text); if (summary) return summary; } for (let i = payloads.length - 1; i >= 0; i--) { if (isNonTerminalToolErrorWarning(payloads[i])) continue; const summary = pickSummaryFromOutput(payloads[i]?.text); if (summary) return summary; } } /** Picks the last non-empty payload text while ignoring terminal error payloads first. */ function pickLastNonEmptyTextFromPayloads(payloads) { for (let i = payloads.length - 1; i >= 0; i--) { if (payloads[i]?.isError) continue; const clean = (payloads[i]?.text ?? "").trim(); if (clean) return clean; } for (let i = payloads.length - 1; i >= 0; i--) { if (isNonTerminalToolErrorWarning(payloads[i])) continue; const clean = (payloads[i]?.text ?? "").trim(); if (clean) return clean; } } function isDeliverablePayload(payload) { if (!payload) return false; return hasOutboundReplyContent(payload, { trimText: true }); } function payloadHasStructuredDeliveryContent(payload) { if (!payload) return false; return payload.mediaUrl !== void 0 || (payload.mediaUrls?.length ?? 0) > 0 || (payload.presentation?.blocks?.length ?? 0) > 0 || (payload.interactive?.blocks?.length ?? 0) > 0 || Object.keys(payload.channelData ?? {}).length > 0; } /** Picks the last payload with deliverable outbound content, preferring non-error payloads. */ function pickLastDeliverablePayload(payloads) { for (let i = payloads.length - 1; i >= 0; i--) { if (payloads[i]?.isError) continue; if (isDeliverablePayload(payloads[i])) return payloads[i]; } for (let i = payloads.length - 1; i >= 0; i--) if (isDeliverablePayload(payloads[i])) return payloads[i]; } /** Selects deliverable cron payloads while preserving multi-payload successful responses. */ function pickDeliverablePayloads(payloads) { const successfulDeliverablePayloads = payloads.filter((payload) => payload != null && payload.isError !== true && isDeliverablePayload(payload)); if (successfulDeliverablePayloads.length > 0) return successfulDeliverablePayloads; const lastDeliverablePayload = pickLastDeliverablePayload(payloads); return lastDeliverablePayload ? [lastDeliverablePayload] : []; } /** * Check if delivery should be skipped because the agent signaled no user-visible update. * Returns true when any payload is a heartbeat ack token and no payload contains media. */ function isHeartbeatOnlyResponse(payloads, ackMaxChars) { return shouldSkipHeartbeatOnlyDelivery(payloads, ackMaxChars); } /** Resolves the non-negative heartbeat ack length used for heartbeat-only filtering. */ function resolveHeartbeatAckMaxChars(agentCfg) { const raw = agentCfg?.heartbeat?.ackMaxChars ?? 300; return Math.max(0, raw); } function isCronMessagePresentationWarning(text) { const normalized = normalizeOptionalString(text)?.toLowerCase(); return normalized === "⚠️ ✉️ message failed" || normalized?.startsWith("⚠️ ✉️ message failed:") === true; } function isCronToolWarning(text) { return normalizeOptionalString(text)?.startsWith("⚠️ 🛠️ ") === true; } function isNonTerminalToolErrorWarning(payload) { return Boolean(payload && getReplyPayloadMetadata(payload)?.nonTerminalToolErrorWarning); } function isSuccessfulCronPayload(payload) { return payload?.isError !== true && (isDeliverablePayload(payload) || payloadHasStructuredDeliveryContent(payload)); } /** Resolves summary, output text, delivery payloads, and fatal-error state from cron run output. */ function resolveCronPayloadOutcome(params) { const firstText = params.payloads.find((payload) => !isNonTerminalToolErrorWarning(payload))?.text ?? ""; const fallbackSummary = pickSummaryFromPayloads(params.payloads) ?? pickSummaryFromOutput(firstText); const fallbackOutputText = pickLastNonEmptyTextFromPayloads(params.payloads); const deliveryPayload = pickLastDeliverablePayload(params.payloads); const selectedDeliveryPayloads = pickDeliverablePayloads(params.payloads); const deliveryPayloadHasStructuredContent = payloadHasStructuredDeliveryContent(deliveryPayload); const hasErrorPayload = params.payloads.some((payload) => payload?.isError === true); const lastErrorPayloadIndex = params.payloads.findLastIndex((payload) => payload?.isError === true); const lastErrorPayloadText = [...params.payloads].toReversed().find((payload) => payload?.isError === true && Boolean(payload?.text?.trim()))?.text?.trim(); const errorPayloads = params.payloads.filter((payload) => payload?.isError === true); const normalizedFinalAssistantVisibleText = normalizeOptionalString(params.finalAssistantVisibleText); const hasSuccessfulPayloadAfterLastError = !params.runLevelError && lastErrorPayloadIndex >= 0 && params.payloads.slice(lastErrorPayloadIndex + 1).some(isSuccessfulCronPayload); const hasSuccessfulPayloadBeforeLastError = !params.runLevelError && lastErrorPayloadIndex > 0 && params.payloads.slice(0, lastErrorPayloadIndex).some(isSuccessfulCronPayload); const lastErrorPayload = lastErrorPayloadIndex >= 0 ? params.payloads[lastErrorPayloadIndex] : void 0; const hasRecoveringTerminalOutput = normalizedFinalAssistantVisibleText !== void 0 || hasSuccessfulPayloadAfterLastError || hasSuccessfulPayloadBeforeLastError; const hasNonTerminalToolErrorWarning = !params.runLevelError && params.failureSignal?.fatalForCron !== true && hasRecoveringTerminalOutput && isNonTerminalToolErrorWarning(lastErrorPayload); const hasPendingPresentationWarning = !params.runLevelError && params.failureSignal?.fatalForCron !== true && lastErrorPayloadIndex >= 0 && isCronMessagePresentationWarning(lastErrorPayloadText) && (normalizedFinalAssistantVisibleText !== void 0 || hasSuccessfulPayloadBeforeLastError); const hasStructuredDeliveryPayloads = selectedDeliveryPayloads.some((payload) => payloadHasStructuredDeliveryContent(payload)); const hasRecoveredToolWarning = !params.runLevelError && params.failureSignal?.fatalForCron !== true && params.preferFinalAssistantVisibleText === true && normalizedFinalAssistantVisibleText !== void 0 && !hasStructuredDeliveryPayloads && errorPayloads.length > 0 && errorPayloads.every((payload) => isCronToolWarning(payload?.text)); const hasFatalStructuredErrorPayload = hasErrorPayload && !hasSuccessfulPayloadAfterLastError && !hasPendingPresentationWarning && !hasNonTerminalToolErrorWarning && !hasRecoveredToolWarning; const shouldUseFinalAssistantVisibleText = params.preferFinalAssistantVisibleText === true && normalizedFinalAssistantVisibleText !== void 0 && !hasFatalStructuredErrorPayload && !hasStructuredDeliveryPayloads; const summary = shouldUseFinalAssistantVisibleText ? pickSummaryFromOutput(normalizedFinalAssistantVisibleText) ?? fallbackSummary : fallbackSummary; const outputText = shouldUseFinalAssistantVisibleText ? normalizedFinalAssistantVisibleText : fallbackOutputText; const synthesizedText = normalizeOptionalString(outputText) ?? normalizeOptionalString(summary); const resolvedDeliveryPayloads = shouldUseFinalAssistantVisibleText ? [{ text: normalizedFinalAssistantVisibleText }] : selectedDeliveryPayloads.length > 0 ? selectedDeliveryPayloads : synthesizedText ? [{ text: synthesizedText }] : []; const failureSignal = normalizeCronFailureSignal(params.failureSignal); const runLevelError = formatCronRunLevelError(params.runLevelError); const hasFatalErrorPayload = hasFatalStructuredErrorPayload || failureSignal !== void 0 || runLevelError !== void 0; const structuredErrorText = hasFatalStructuredErrorPayload ? lastErrorPayloadText ?? "cron isolated run returned an error payload" : void 0; const shouldUseRunLevelErrorPayload = runLevelError !== void 0 && structuredErrorText === void 0 && failureSignal === void 0; const fatalDeliveryText = structuredErrorText ?? failureSignal?.message ?? (shouldUseRunLevelErrorPayload ? runLevelError : void 0); const fatalDeliveryPayload = fatalDeliveryText ? { text: fatalDeliveryText, isError: true } : void 0; return { summary: fatalDeliveryText ? pickSummaryFromOutput(fatalDeliveryText) ?? summary : summary, outputText: fatalDeliveryText ?? outputText, synthesizedText: fatalDeliveryText ?? synthesizedText, deliveryPayload: fatalDeliveryPayload ?? deliveryPayload, deliveryPayloads: fatalDeliveryPayload ? [fatalDeliveryPayload] : resolvedDeliveryPayloads, deliveryPayloadHasStructuredContent: fatalDeliveryPayload ? false : deliveryPayloadHasStructuredContent, hasFatalErrorPayload, hasFatalStructuredErrorPayload, embeddedRunError: structuredErrorText ? structuredErrorText : failureSignal ? formatCronFailureSignal(failureSignal) : runLevelError, pendingPresentationWarningError: hasPendingPresentationWarning ? lastErrorPayloadText : void 0 }; } //#endregion export { resolveHeartbeatAckMaxChars as a, resolveCronPayloadOutcome as i, pickLastNonEmptyTextFromPayloads as n, pickSummaryFromOutput as r, isHeartbeatOnlyResponse as t };