UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

766 lines (765 loc) 31.5 kB
import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js"; import { i as formatErrorMessage } from "./errors-BXgSefBE.js"; import { t as createLazyImportLoader } from "./lazy-promise-BONnzNfb.js"; import { i as isCronSessionKey, u as parseThreadSessionSuffix } from "./session-key-utils-Bx3apsJ3.js"; import { f as resolveAgentIdFromSessionKey } from "./session-key-B_NoIfpX.js"; import { y as shouldAttemptTtsPayload } from "./gateway-startup-plugin-ids-3X_G47Qt.js"; import { o as isAudioFileName } from "./mime-C8mVE2Bw.js"; import { i as resolveMainSessionKey, n as resolveAgentMainSessionKey, t as canonicalizeMainSessionAlias } from "./main-session-Eahn-btj.js"; import { h as stringifyRouteThreadId } from "./channel-route-D7X5briz.js"; import { u as resolveStorePath } from "./paths-NEwU8m3X.js"; import { o as resolveMirroredTranscriptText } from "./transcript-Cw-EfeYD.js"; import { c as stripLeadingSilentToken, l as stripSilentToken, n as SILENT_REPLY_TOKEN, o as isSilentReplyText, s as startsWithSilentToken } from "./tokens-U6o7_k27.js"; import { n as sleepWithAbort } from "./backoff-S1yazmy1.js"; import { a as hasReplyPayloadContent } from "./payload-CZlThETZ.js"; import { u as retireSessionMcpRuntime } from "./agent-bundle-mcp-runtime-Bf36JLIh.js"; import "./agent-bundle-mcp-tools-tGjdbeEf.js"; import { a as normalizeTargetForProvider } from "./target-normalization-BUpmwo0F.js"; import { s as projectOutboundPayloadPlanForMirror, t as createOutboundPayloadPlan } from "./payloads-B4DTFTLz.js"; import "./inbound.runtime-Osgy9aR1.js"; import { n as pickLastNonEmptyTextFromPayloads, r as pickSummaryFromOutput } from "./helpers-CYQZyDV5.js"; import { l as hasScheduledNextRunAtMs, y as resolveDeliveryTarget } from "./jobs-CRtvC4Q4.js"; import { t as createCronExecutionId } from "./run-id-kGde0n7U.js"; import { n as isLikelyInterimCronMessage, t as expectsSubagentFollowup } from "./subagent-followup-hints-WJCMG0pw.js"; //#region src/cron/isolated-agent/delivery-dispatch.ts /** Dispatches isolated cron output to direct delivery, mirrors, and follow-up queues. */ function normalizeDeliveryTarget(channel, to) { const toTrimmed = to.trim(); return normalizeTargetForProvider(channel, toTrimmed) ?? toTrimmed; } function normalizeSilentReplyText(text) { if (!text) return { text, strippedTrailingSilentToken: false }; if (isSilentReplyText(text, "NO_REPLY")) return { text: void 0, strippedTrailingSilentToken: false }; let next = text; const hasLeadingSilentToken = startsWithSilentToken(next, SILENT_REPLY_TOKEN); if (hasLeadingSilentToken) next = stripLeadingSilentToken(next, SILENT_REPLY_TOKEN); let strippedTrailingSilentToken = false; if (hasLeadingSilentToken || next.toLowerCase().includes("NO_REPLY".toLowerCase())) { const trimmedBefore = next.trim(); const stripped = stripSilentToken(next, SILENT_REPLY_TOKEN); strippedTrailingSilentToken = stripped !== trimmedBefore; next = stripped; } if (!next.trim() || isSilentReplyText(next, "NO_REPLY")) return { text: void 0, strippedTrailingSilentToken }; return { text: next, strippedTrailingSilentToken }; } /** Returns whether cron delivery should tolerate per-payload send failures. */ function resolveCronDeliveryBestEffort(job) { return job.delivery?.bestEffort === true; } const TRANSIENT_DIRECT_CRON_DELIVERY_ERROR_PATTERNS = [ /\berrorcode=unavailable\b/i, /\bstatus\s*[:=]\s*"?unavailable\b/i, /\bUNAVAILABLE\b/, /no active .* listener/i, /gateway not connected/i, /gateway closed \(1006/i, /gateway timeout/i, /\b(econnreset|econnrefused|etimedout|enotfound|ehostunreach|network error)\b/i ]; const PERMANENT_DIRECT_CRON_DELIVERY_ERROR_PATTERNS = [ /unsupported channel/i, /unknown channel/i, /chat not found/i, /user not found/i, /bot.*not.*member/i, /bot was blocked by the user/i, /forbidden: bot was kicked/i, /recipient is not a valid/i, /outbound not configured for channel/i ]; const STALE_CRON_DELIVERY_MAX_START_DELAY_MS = 180 * 6e4; const gatewayCallRuntimeLoader = createLazyImportLoader(() => import("./call.runtime.js")); const deliveryOutboundRuntimeLoader = createLazyImportLoader(() => import("./delivery-outbound.runtime.js")); const outboundSessionRuntimeLoader = createLazyImportLoader(() => import("./outbound-session-DL-mc08T.js")); const transcriptRuntimeLoader = createLazyImportLoader(() => import("./transcript.runtime.js")); const deliverySubagentRegistryRuntimeLoader = createLazyImportLoader(() => import("./delivery-subagent-registry.runtime.js")); const deliveryLoggerRuntimeLoader = createLazyImportLoader(() => import("./delivery-logger.runtime.js")); const subagentFollowupRuntimeLoader = createLazyImportLoader(() => import("./subagent-followup.runtime.js")); const ttsRuntimeLoader = createLazyImportLoader(() => import("./tts.runtime.js")); const COMPLETED_DIRECT_CRON_DELIVERIES = /* @__PURE__ */ new Map(); async function loadGatewayCallRuntime() { return await gatewayCallRuntimeLoader.load(); } async function loadDeliveryOutboundRuntime() { return await deliveryOutboundRuntimeLoader.load(); } async function loadOutboundSessionRuntime() { return await outboundSessionRuntimeLoader.load(); } async function loadTranscriptRuntime() { return await transcriptRuntimeLoader.load(); } async function loadDeliverySubagentRegistryRuntime() { return await deliverySubagentRegistryRuntimeLoader.load(); } async function loadDeliveryLoggerRuntime() { return await deliveryLoggerRuntimeLoader.load(); } async function loadSubagentFollowupRuntime() { return await subagentFollowupRuntimeLoader.load(); } async function loadTtsRuntime() { return await ttsRuntimeLoader.load(); } async function logCronDeliveryWarn(message) { const { logWarn } = await loadDeliveryLoggerRuntime(); logWarn(message); } async function logCronDeliveryError(message) { const { logError } = await loadDeliveryLoggerRuntime(); logError(message); } /** Deletes or retires ephemeral direct-delivery cron sessions for delete-after-run jobs. */ async function cleanupDirectCronSession(params) { if (!params.job.deleteAfterRun) return; if (!isCronSessionKey(params.agentSessionKey)) return; try { const { callGateway } = await loadGatewayCallRuntime(); await callGateway({ method: "sessions.delete", params: { key: params.agentSessionKey, deleteTranscript: true, emitLifecycleHooks: false }, timeoutMs: 1e4 }); } catch { await retireSessionMcpRuntime({ sessionId: params.sessionId, reason: params.retireReason }); } } function logCronDeliveryErrorDeferred(message) { loadDeliveryLoggerRuntime().then(({ logError }) => { logError(message); }); } function cloneDeliveryResults(results) { return results.map((result) => ({ ...result, ...result.meta ? { meta: { ...result.meta } } : {} })); } function pruneCompletedDirectCronDeliveries(now) { const ttlMs = process.env.OPENCLAW_TEST_FAST === "1" ? 6e4 : 1440 * 60 * 1e3; for (const [key, entry] of COMPLETED_DIRECT_CRON_DELIVERIES) if (now - entry.ts >= ttlMs) COMPLETED_DIRECT_CRON_DELIVERIES.delete(key); const maxEntries = 2e3; if (COMPLETED_DIRECT_CRON_DELIVERIES.size <= maxEntries) return; const entries = [...COMPLETED_DIRECT_CRON_DELIVERIES.entries()].toSorted((a, b) => a[1].ts - b[1].ts); const toDelete = COMPLETED_DIRECT_CRON_DELIVERIES.size - maxEntries; for (let i = 0; i < toDelete; i += 1) { const oldest = entries[i]; if (!oldest) break; COMPLETED_DIRECT_CRON_DELIVERIES.delete(oldest[0]); } } function resolveCronDeliveryScheduledAtMs(params) { const scheduledAt = params.job.state?.nextRunAtMs; return hasScheduledNextRunAtMs(scheduledAt) ? scheduledAt : params.runStartedAt; } function resolveCronDeliveryStartDelayMs(params) { return params.runStartedAt - resolveCronDeliveryScheduledAtMs(params); } function isStaleCronDelivery(params) { return resolveCronDeliveryStartDelayMs(params) > STALE_CRON_DELIVERY_MAX_START_DELAY_MS; } function rememberCompletedDirectCronDelivery(idempotencyKey, results) { const now = Date.now(); COMPLETED_DIRECT_CRON_DELIVERIES.set(idempotencyKey, { ts: now, results: cloneDeliveryResults(results) }); pruneCompletedDirectCronDeliveries(now); } function getCompletedDirectCronDelivery(idempotencyKey) { pruneCompletedDirectCronDeliveries(Date.now()); const cached = COMPLETED_DIRECT_CRON_DELIVERIES.get(idempotencyKey); if (!cached) return; return cloneDeliveryResults(cached.results); } async function maybeApplyTtsToCronPayloads(params) { if (!shouldAttemptTtsPayload({ cfg: params.cfg, ttsAuto: params.ttsAuto, agentId: params.agentId, channelId: params.delivery.channel, accountId: params.delivery.accountId })) return params.payloads; const { maybeApplyTtsToPayload } = await loadTtsRuntime(); return await Promise.all(params.payloads.map((payload) => maybeApplyTtsToPayload({ payload, cfg: params.cfg, channel: params.delivery.channel, kind: "final", ttsAuto: params.ttsAuto, agentId: params.agentId, accountId: params.delivery.accountId }))); } function buildDirectCronDeliveryIdempotencyKey(params) { const executionId = createCronExecutionId(params.jobId, params.runStartedAt); const threadId = params.delivery.threadId == null || params.delivery.threadId === "" ? "" : stringifyRouteThreadId(params.delivery.threadId) ?? ""; const accountId = params.delivery.accountId?.trim() ?? ""; const normalizedTo = normalizeDeliveryTarget(params.delivery.channel, params.delivery.to); return `cron-direct-delivery:v1:${executionId}:${params.delivery.channel}:${accountId}:${normalizedTo}:${threadId}`; } function shouldQueueCronAwareness(params) { return params.job.sessionTarget === "isolated" && !params.deliveryBestEffort && params.delivery.mode === "explicit"; } function resolveCronAwarenessMainSessionKey(params) { return params.cfg.session?.scope === "global" ? resolveMainSessionKey(params.cfg) : resolveAgentMainSessionKey({ cfg: params.cfg, agentId: params.agentId }); } function isSameSessionKey(left, right) { const normalizedLeft = normalizeOptionalString(left); const normalizedRight = normalizeOptionalString(right); return normalizedLeft != null && normalizedLeft === normalizedRight; } function resolveCronAwarenessText(params) { if (params.outboundPayloads?.length) { const projectedText = resolveDirectCronTranscriptMirrorText(projectDeliveredDirectCronPayloadsForMirror(params.outboundPayloads)); if (projectedText) return projectedText; } return params.deliveryPayloads ? pickLastNonEmptyTextFromPayloads(params.deliveryPayloads) : normalizeOptionalString(params.outputText) ?? normalizeOptionalString(params.synthesizedText); } async function queueCronAwarenessSystemEvent(params) { const text = resolveCronAwarenessText(params); if (!text) return; try { const { enqueueSystemEvent } = await loadDeliveryOutboundRuntime(); enqueueSystemEvent(text, { sessionKey: resolveCronAwarenessMainSessionKey({ cfg: params.cfg, agentId: params.agentId }), contextKey: params.deliveryIdempotencyKey }); } catch (err) { await logCronDeliveryWarn(`[cron:${params.jobId}] failed to queue isolated cron awareness for the main session: ${formatErrorMessage(err)}`); } } function isCustomCronSessionTarget(sessionTarget) { return typeof sessionTarget === "string" && sessionTarget.startsWith("session:"); } function buildDirectCronTranscriptMirrorPayloads(payloads) { return payloads.map((payload) => { const spokenText = normalizeOptionalString(payload.spokenText); if (!spokenText) return payload; const mediaUrls = [payload.mediaUrl, ...payload.mediaUrls ?? []].filter((url) => Boolean(url) && !isAudioFileName(url)); const { mediaUrl: _mediaUrl, mediaUrls: _mediaUrls, audioAsVoice: _audioAsVoice, spokenText: _spokenText, ...rest } = payload; return { ...rest, text: spokenText, ...mediaUrls.length ? { mediaUrls } : {} }; }); } function resolveDirectCronTranscriptMirrorText(params) { const text = normalizeOptionalString(params.text); const mediaText = resolveMirroredTranscriptText({ mediaUrls: params.mediaUrls }) ?? void 0; if (text && mediaText) return `${text}\n${mediaText}`; if (text || mediaText) return text ?? mediaText; } function pickDirectCronMirrorPayloadText(payload) { return normalizeOptionalString(payload.hookContent) ?? normalizeOptionalString(payload.text); } function isTtsAudioMirrorOnly(params) { return (params.payload.audioAsVoice === true || Boolean(params.payload.hookContent)) && isAudioFileName(params.mediaUrl); } function projectDeliveredDirectCronPayloadsForMirror(payloads) { const textParts = []; const mediaUrls = []; for (const payload of payloads) { const text = pickDirectCronMirrorPayloadText(payload); if (text) textParts.push(text); for (const mediaUrl of payload.mediaUrls) { if (isTtsAudioMirrorOnly({ payload, mediaUrl })) continue; mediaUrls.push(mediaUrl); } } return { text: textParts.join("\n"), mediaUrls }; } function canonicalizeDirectCronRouteSessionKey(params) { const sessionKey = params.sessionKey.trim(); const canonical = canonicalizeMainSessionAlias({ cfg: params.cfg, agentId: params.agentId, sessionKey }); if (canonical !== sessionKey) return canonical; const thread = parseThreadSessionSuffix(sessionKey); if (!thread.baseSessionKey || !thread.threadId) return sessionKey; const canonicalBase = canonicalizeMainSessionAlias({ cfg: params.cfg, agentId: params.agentId, sessionKey: thread.baseSessionKey }); if (canonicalBase === thread.baseSessionKey || canonicalBase === "global") return sessionKey; return `${canonicalBase}:thread:${thread.threadId}`; } async function resolveDirectCronDeliverySessionKey(params) { if (isCustomCronSessionTarget(params.job.sessionTarget)) return params.agentSessionKey; try { const { resolveOutboundSessionRoute, ensureOutboundSessionEntry } = await loadOutboundSessionRuntime(); const route = await resolveOutboundSessionRoute({ cfg: params.cfg, channel: params.delivery.channel, agentId: params.agentId, accountId: params.delivery.accountId, target: params.delivery.to, currentSessionKey: params.agentSessionKey, threadId: params.delivery.threadId }); const routeSessionKey = route?.sessionKey?.trim(); if (!route || !routeSessionKey) return params.agentSessionKey; const canonicalRouteSessionKey = canonicalizeDirectCronRouteSessionKey({ cfg: params.cfg, agentId: params.agentId, sessionKey: routeSessionKey }); const canonicalRouteBaseSessionKey = canonicalizeDirectCronRouteSessionKey({ cfg: params.cfg, agentId: params.agentId, sessionKey: route.baseSessionKey }); const canonicalRoute = canonicalRouteSessionKey === route.sessionKey && canonicalRouteBaseSessionKey === route.baseSessionKey ? route : { ...route, sessionKey: canonicalRouteSessionKey, baseSessionKey: canonicalRouteBaseSessionKey }; await ensureOutboundSessionEntry({ cfg: params.cfg, channel: params.delivery.channel, accountId: params.delivery.accountId, route: canonicalRoute }); return canonicalRouteSessionKey; } catch (err) { await logCronDeliveryWarn(`[cron:${params.job.id}] failed to resolve destination session for direct delivery mirror: ${formatErrorMessage(err)}`); return params.agentSessionKey; } } async function appendDirectCronDeliveryTranscriptMirror(params) { if (!params.mirror.text && !params.mirror.mediaUrls?.length) return; try { const { appendAssistantMessageToSessionTranscript } = await loadTranscriptRuntime(); const result = await appendAssistantMessageToSessionTranscript(params.mirror); if (!result.ok) await logCronDeliveryWarn(`[cron:${params.job.id}] failed to mirror direct delivery into session transcript: ${result.reason}`); } catch (err) { await logCronDeliveryWarn(`[cron:${params.job.id}] failed to mirror direct delivery into session transcript: ${formatErrorMessage(err)}`); } } function summarizeDirectCronDeliveryError(error) { if (error instanceof Error) return error.message || "error"; if (typeof error === "string") return error; try { return JSON.stringify(error) || String(error); } catch { return String(error); } } function isTransientDirectCronDeliveryError(error) { const message = summarizeDirectCronDeliveryError(error); if (!message) return false; if (PERMANENT_DIRECT_CRON_DELIVERY_ERROR_PATTERNS.some((re) => re.test(message))) return false; return TRANSIENT_DIRECT_CRON_DELIVERY_ERROR_PATTERNS.some((re) => re.test(message)); } function resolveDirectCronRetryDelaysMs() { return [ 5e3, 1e4, 2e4 ]; } async function retryTransientDirectCronDelivery(params) { const retryDelaysMs = resolveDirectCronRetryDelaysMs(); for (const [retryIndex, delayMs] of retryDelaysMs.entries()) { if (params.signal?.aborted) throw new Error("cron delivery aborted"); try { return await params.run(); } catch (err) { if (!isTransientDirectCronDeliveryError(err) || params.signal?.aborted) throw err; const nextAttempt = retryIndex + 2; const maxAttempts = retryDelaysMs.length + 1; await logCronDeliveryWarn(`[cron:${params.jobId}] transient direct announce delivery failure, retrying ${nextAttempt}/${maxAttempts} in ${Math.round(delayMs / 1e3)}s: ${summarizeDirectCronDeliveryError(err)}`); await sleepWithAbort(delayMs, params.signal); } } if (params.signal?.aborted) throw new Error("cron delivery aborted"); return await params.run(); } /** Dispatches cron run output through verified message-tool or direct delivery paths. */ async function dispatchCronDelivery(params) { const sourceDeliverySatisfied = params.sourceDeliveryOutcome.satisfiesSourceDelivery; const verifiedMessageToolDelivery = params.sourceDeliveryOutcome.verifiedMessageToolDelivery; let summary = params.summary; let outputText = params.outputText; let synthesizedText = params.synthesizedText; let deliveryPayloads = params.deliveryPayloads; let delivered = verifiedMessageToolDelivery; let deliveryAttempted = verifiedMessageToolDelivery; let directCronSessionDeleted = false; const formatDeliveryTargetError = (error) => params.sourceDeliveryOutcome.unverifiedMessageToolDelivery ? `${error}; the agent used the message tool, but OpenClaw could not verify that message matched the cron delivery target` : error; const failDeliveryTarget = (error) => params.withRunSession({ status: "error", error: formatDeliveryTargetError(error), errorKind: "delivery-target", summary, outputText, deliveryAttempted, ...params.telemetry }); const cleanupDirectCronSessionIfNeeded = async () => { if (directCronSessionDeleted) return; directCronSessionDeleted = true; await cleanupDirectCronSession({ job: params.job, agentSessionKey: params.agentSessionKey, sessionId: params.sessionId, retireReason: "cron-delete-after-run-fallback" }); }; const finishSilentReplyDelivery = async () => { deliveryAttempted = true; await cleanupDirectCronSessionIfNeeded(); return params.withRunSession({ status: "ok", summary, outputText, delivered: false, deliveryAttempted: true, ...params.telemetry }); }; const deliverViaDirect = async (delivery, options) => { const { buildOutboundSessionContext, createOutboundSendDeps, resolveAgentOutboundIdentity, sendDurableMessageBatch } = await loadDeliveryOutboundRuntime(); const identity = resolveAgentOutboundIdentity(params.cfgWithAgentDefaults, params.agentId); const deliveryIdempotencyKey = buildDirectCronDeliveryIdempotencyKey({ jobId: params.job.id, runStartedAt: params.runStartedAt, delivery }); try { const normalizedPayloads = (deliveryPayloads.length > 0 ? deliveryPayloads : synthesizedText ? [{ text: synthesizedText }] : []).map((p) => { if (!p.text) return p; const normalized = normalizeSilentReplyText(p.text); return Object.assign({}, p, { text: normalized.strippedTrailingSilentToken ? void 0 : normalized.text }); }).filter((p) => hasReplyPayloadContent(p, { trimText: true })); if (normalizedPayloads.length === 0) return await finishSilentReplyDelivery(); if (params.isAborted()) return params.withRunSession({ status: "error", error: params.abortReason(), deliveryAttempted, ...params.telemetry }); if (params.deliveryRequested && isStaleCronDelivery({ job: params.job, runStartedAt: params.runStartedAt })) { deliveryAttempted = true; const nowMs = Date.now(); const scheduledAtMs = resolveCronDeliveryScheduledAtMs({ job: params.job, runStartedAt: params.runStartedAt }); const startDelayMs = resolveCronDeliveryStartDelayMs({ job: params.job, runStartedAt: params.runStartedAt }); await logCronDeliveryWarn(`[cron:${params.job.id}] skipping stale delivery scheduled at ${new Date(scheduledAtMs).toISOString()}, started ${Math.round(startDelayMs / 6e4)}m late, current age ${Math.round((nowMs - scheduledAtMs) / 6e4)}m`); return params.withRunSession({ status: "ok", summary, outputText, deliveryAttempted, delivered: false, ...params.telemetry }); } const payloadsForDelivery = (await maybeApplyTtsToCronPayloads({ cfg: params.cfgWithAgentDefaults, payloads: normalizedPayloads, delivery, agentId: params.agentId, ttsAuto: params.ttsAuto })).filter((p) => hasReplyPayloadContent(p, { trimText: true })); if (payloadsForDelivery.length === 0) return await finishSilentReplyDelivery(); deliveryAttempted = true; if (getCompletedDirectCronDelivery(deliveryIdempotencyKey)) { delivered = true; return null; } const deliverySessionKey = await resolveDirectCronDeliverySessionKey({ cfg: params.cfgWithAgentDefaults, job: params.job, agentId: params.agentId, agentSessionKey: params.agentSessionKey, delivery }); const deliverySession = buildOutboundSessionContext({ cfg: params.cfgWithAgentDefaults, agentId: params.agentId, sessionKey: deliverySessionKey }); const mirrorTargetsAwarenessMainSession = isSameSessionKey(deliverySessionKey, resolveCronAwarenessMainSessionKey({ cfg: params.cfgWithAgentDefaults, agentId: params.agentId })); let hadPartialFailure = false; const attemptedPayloadsForMirror = []; const onError = params.deliveryBestEffort ? (err, _payload) => { hadPartialFailure = true; logCronDeliveryErrorDeferred(`[cron:${params.job.id}] delivery payload failed (bestEffort): ${formatErrorMessage(err)}`); } : void 0; const runDelivery = async () => { attemptedPayloadsForMirror.length = 0; const send = await sendDurableMessageBatch({ cfg: params.cfgWithAgentDefaults, channel: delivery.channel, to: delivery.to, accountId: delivery.accountId, threadId: delivery.threadId, payloads: payloadsForDelivery, session: deliverySession, identity, bestEffort: params.deliveryBestEffort, durability: params.deliveryBestEffort ? "best_effort" : "required", deps: createOutboundSendDeps(params.deps), signal: params.abortSignal, onError, onPayload: (payload) => { attemptedPayloadsForMirror.push(payload); }, skipQueue: true }); if (send.status === "failed" || !params.deliveryBestEffort && send.status === "partial_failed") throw send.error; if (send.status === "partial_failed") hadPartialFailure = true; return send.status === "sent" || send.status === "partial_failed" ? send.results : []; }; const deliveryResults = options?.retryTransient ? await retryTransientDirectCronDelivery({ jobId: params.job.id, signal: params.abortSignal, run: runDelivery }) : await runDelivery(); delivered = deliveryResults.length > 0 && !hadPartialFailure; const shouldQueueAwarenessForDelivery = shouldQueueCronAwareness({ job: params.job, delivery, deliveryBestEffort: params.deliveryBestEffort }); const awarenessText = shouldQueueAwarenessForDelivery ? resolveCronAwarenessText({ outputText, synthesizedText, deliveryPayloads: payloadsForDelivery, outboundPayloads: attemptedPayloadsForMirror }) : void 0; const deliveryWillReachAwarenessMainSession = mirrorTargetsAwarenessMainSession && shouldQueueAwarenessForDelivery && Boolean(awarenessText); const mirrorWouldBypassIsolatedAwarenessPolicy = mirrorTargetsAwarenessMainSession && params.job.sessionTarget === "isolated" && delivery.mode !== "explicit"; if (delivered && !deliveryWillReachAwarenessMainSession && !mirrorWouldBypassIsolatedAwarenessPolicy) { const mirrorText = resolveDirectCronTranscriptMirrorText(attemptedPayloadsForMirror.length > 0 ? projectDeliveredDirectCronPayloadsForMirror(attemptedPayloadsForMirror) : projectOutboundPayloadPlanForMirror(createOutboundPayloadPlan(buildDirectCronTranscriptMirrorPayloads(payloadsForDelivery), { cfg: params.cfgWithAgentDefaults, sessionKey: deliverySessionKey, surface: delivery.channel }))); const transcriptMirror = { sessionKey: deliverySessionKey, agentId: params.agentId, text: mirrorText, mediaUrls: void 0, storePath: resolveStorePath(params.cfgWithAgentDefaults.session?.store, { agentId: resolveAgentIdFromSessionKey(deliverySessionKey) }), idempotencyKey: deliveryIdempotencyKey, config: params.cfgWithAgentDefaults }; await appendDirectCronDeliveryTranscriptMirror({ job: params.job, mirror: transcriptMirror }); } if (delivered && shouldQueueAwarenessForDelivery) await queueCronAwarenessSystemEvent({ cfg: params.cfgWithAgentDefaults, jobId: params.job.id, agentId: params.agentId, deliveryIdempotencyKey, outputText, synthesizedText, deliveryPayloads: payloadsForDelivery, outboundPayloads: attemptedPayloadsForMirror }); if (delivered) rememberCompletedDirectCronDelivery(deliveryIdempotencyKey, deliveryResults); return null; } catch (err) { if (!params.deliveryBestEffort) return params.withRunSession({ status: "error", summary, outputText, error: String(err), deliveryAttempted, ...params.telemetry }); await logCronDeliveryError(`[cron:${params.job.id}] delivery failed (bestEffort): ${formatErrorMessage(err)}`); return null; } }; const deliverViaDirectAndCleanup = async (delivery, options) => { try { return await deliverViaDirect(delivery, options); } finally { await cleanupDirectCronSessionIfNeeded(); } }; const finalizeTextDelivery = async (delivery) => { if (!synthesizedText) return null; const initialSynthesizedText = synthesizedText.trim(); const expectedSubagentFollowup = expectsSubagentFollowup(initialSynthesizedText); const subagentRegistryRuntime = await loadDeliverySubagentRegistryRuntime(); const subagentFollowupSessionKey = params.runSessionKey; let activeSubagentRuns = subagentRegistryRuntime.countActiveDescendantRuns(subagentFollowupSessionKey); const shouldCheckCompletedDescendants = activeSubagentRuns === 0 && isLikelyInterimCronMessage(initialSynthesizedText); const subagentFollowupRuntime = shouldCheckCompletedDescendants || activeSubagentRuns > 0 || expectedSubagentFollowup ? await loadSubagentFollowupRuntime() : void 0; const completedDescendantReply = shouldCheckCompletedDescendants ? await subagentFollowupRuntime?.readDescendantSubagentFallbackReply({ sessionKey: subagentFollowupSessionKey, runStartedAt: params.runStartedAt }) : void 0; const hadDescendants = activeSubagentRuns > 0 || Boolean(completedDescendantReply); if (!params.deliveryBestEffort && (activeSubagentRuns > 0 || expectedSubagentFollowup)) { let finalReply = await subagentFollowupRuntime?.waitForDescendantSubagentSummary({ sessionKey: subagentFollowupSessionKey, initialReply: initialSynthesizedText, timeoutMs: params.timeoutMs, observedActiveDescendants: activeSubagentRuns > 0 || expectedSubagentFollowup }); activeSubagentRuns = subagentRegistryRuntime.countActiveDescendantRuns(subagentFollowupSessionKey); if (!finalReply && activeSubagentRuns === 0) finalReply = await subagentFollowupRuntime?.readDescendantSubagentFallbackReply({ sessionKey: subagentFollowupSessionKey, runStartedAt: params.runStartedAt }); if (finalReply && activeSubagentRuns === 0) { outputText = finalReply; summary = pickSummaryFromOutput(finalReply) ?? summary; synthesizedText = finalReply; deliveryPayloads = [{ text: finalReply }]; } } else if (completedDescendantReply) { outputText = completedDescendantReply; summary = pickSummaryFromOutput(completedDescendantReply) ?? summary; synthesizedText = completedDescendantReply; deliveryPayloads = [{ text: completedDescendantReply }]; } if (!params.deliveryBestEffort && activeSubagentRuns > 0) { deliveryAttempted = true; return params.withRunSession({ status: "ok", summary, outputText, deliveryAttempted, ...params.telemetry }); } if (hadDescendants && synthesizedText.trim() === initialSynthesizedText && isLikelyInterimCronMessage(initialSynthesizedText) && !isSilentReplyText(initialSynthesizedText, "NO_REPLY")) { deliveryAttempted = true; return params.withRunSession({ status: "ok", summary, outputText, deliveryAttempted, ...params.telemetry }); } const normalizedSynthesizedText = normalizeSilentReplyText(synthesizedText); if (normalizedSynthesizedText.text === void 0 || normalizedSynthesizedText.strippedTrailingSilentToken) return await finishSilentReplyDelivery(); synthesizedText = normalizedSynthesizedText.text; outputText = synthesizedText; if (params.isAborted()) return params.withRunSession({ status: "error", error: params.abortReason(), deliveryAttempted, ...params.telemetry }); return await deliverViaDirectAndCleanup(delivery, { retryTransient: true }); }; if (params.deliveryRequested && !params.skipHeartbeatDelivery && !sourceDeliverySatisfied) { if (!params.resolvedDelivery.ok) { if (!params.deliveryBestEffort) return { result: failDeliveryTarget(params.resolvedDelivery.error.message), delivered, deliveryAttempted, summary, outputText, synthesizedText, deliveryPayloads }; await logCronDeliveryWarn(`[cron:${params.job.id}] ${params.resolvedDelivery.error.message}`); return { result: params.withRunSession({ status: "ok", summary, outputText, deliveryAttempted, ...params.telemetry }), delivered, deliveryAttempted, summary, outputText, synthesizedText, deliveryPayloads }; } if (params.deliveryPayloadHasStructuredContent || params.resolvedDelivery.threadId != null) { const directResult = await deliverViaDirectAndCleanup(params.resolvedDelivery); if (directResult) return { result: directResult, delivered, deliveryAttempted, summary, outputText, synthesizedText, deliveryPayloads }; } else { const finalizedTextResult = await finalizeTextDelivery(params.resolvedDelivery); if (finalizedTextResult) return { result: finalizedTextResult, delivered, deliveryAttempted, summary, outputText, synthesizedText, deliveryPayloads }; } } return { delivered, deliveryAttempted, summary, outputText, synthesizedText, deliveryPayloads }; } //#endregion export { cleanupDirectCronSession, dispatchCronDelivery, resolveCronDeliveryBestEffort, resolveDeliveryTarget };