openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
278 lines (277 loc) • 11.5 kB
JavaScript
import { c as normalizeOptionalLowercaseString, l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { t as createDeferredCore } from "./deferred-D0La5CRk.js";
import { t as normalizeChatType } from "./chat-type-CG0X_HJM.js";
import { n as channelRouteDedupeKey } from "./channel-route--2in3Ckg.js";
import { n as getLoadedChannelPlugin } from "./registry-Cz6cv4VC.js";
import "./plugins-Cozj1Enf.js";
import { n as extractTextFromChatContent } from "./chat-content-DNdfeXZh.js";
import { a as logMessageQueuedWithBacklogPolicy } from "./diagnostic-runtime-Cq2Cubt3.js";
import { A as shouldSkipQueueItem, a as getExistingFollowupQueue, b as resetRecentQueuedMessageIdDedupe, f as completeFollowupRunLifecycle, h as markFollowupRunEnqueued, l as trimSummaryElisionsToCap, o as getFollowupQueue, p as isFollowupRunAborted, r as FOLLOWUP_QUEUES, t as resolveQueueSettingsCore, v as peekRecentQueueMessageId, w as countPendingQueueItems, x as applyQueueDropPolicy, y as recordRecentQueueMessageId } from "./settings-jmDsm5RI.js";
import { a as rememberFollowupDrainCallback, n as createOverflowSummaryRetrySource, o as resolveFollowupDeliveryContextKey, r as kickFollowupDrainIfIdle, s as resolveFollowupReplyAnchor, t as clearFollowupDrainCallback } from "./drain-Bt23d2Dt.js";
import "./cleanup-DKkilV3a.js";
//#region src/auto-reply/reply/queue/enqueue.ts
function followupRouteIdentityKey(run) {
return JSON.stringify([
channelRouteDedupeKey({
channel: run.originatingChannel,
to: run.originatingTo,
accountId: run.originatingAccountId,
threadId: run.originatingThreadId
}),
resolveFollowupReplyAnchor(run) ?? "",
run.originatingReplyToMode ?? "",
normalizeChatType(run.originatingChatType) ?? ""
]);
}
function followupMessageRouteIdentityKey(run) {
return JSON.stringify([channelRouteDedupeKey({
channel: run.originatingChannel,
to: run.originatingTo,
accountId: run.originatingAccountId,
threadId: run.originatingThreadId
}), normalizeChatType(run.originatingChatType) ?? ""]);
}
function buildRecentMessageIdKey(run, queueKey) {
const messageId = normalizeOptionalString(run.messageId);
if (!messageId) return;
return JSON.stringify([
"queue",
queueKey,
followupMessageRouteIdentityKey(run),
messageId
]);
}
function isRunAlreadyQueued(run, items, allowPromptFallback = false) {
const messageId = normalizeOptionalString(run.messageId);
if (messageId) {
const messageRouteKey = followupMessageRouteIdentityKey(run);
return items.some((item) => normalizeOptionalString(item.messageId) === messageId && followupMessageRouteIdentityKey(item) === messageRouteKey);
}
if (!allowPromptFallback) return false;
const routeKey = followupRouteIdentityKey(run);
return items.some((item) => item.prompt === run.prompt && followupRouteIdentityKey(item) === routeKey);
}
function appendQueueItem(params) {
params.queue.lastEnqueuedAt = Date.now();
params.queue.lastRun = params.run.run;
params.run.queueAbortSignal = params.queue.abortController.signal;
params.queue.items[params.front ? "unshift" : "push"](params.run);
if (params.recentMessageIdKey) recordRecentQueueMessageId(params.run, params.recentMessageIdKey);
if (params.runFollowup) rememberFollowupDrainCallback(params.key, params.runFollowup);
if (params.restartIfIdle && !params.queue.draining) kickFollowupDrainIfIdle(params.key);
}
function enqueueFollowupRun(key, run, settings, dedupeMode = "message-id", runFollowup, restartIfIdle = true, options = {}) {
if (isFollowupRunAborted(run)) return false;
if (options.position === "front") run.protectFromQueueOverflow = true;
if (options.steerCandidate) run.steerAnchor = true;
const recentMessageIdKey = dedupeMode !== "none" ? buildRecentMessageIdKey(run, key) : void 0;
if (recentMessageIdKey && peekRecentQueueMessageId(recentMessageIdKey)) return false;
const queue = getFollowupQueue(key, settings);
const dedupe = dedupeMode === "none" ? void 0 : (item, items) => isRunAlreadyQueued(item, items, dedupeMode === "prompt");
if (shouldSkipQueueItem({
item: run,
items: queue.items,
dedupe
})) return false;
if (options.steerCandidate) {
if (!markFollowupRunEnqueued(run)) return false;
const { promise: acceptance, resolve: settle } = createDeferredCore();
run.steerPending = {
predecessor: queue.steerAcceptanceTail,
settle
};
queue.steerAcceptanceTail = acceptance;
appendQueueItem({
key,
queue,
run,
recentMessageIdKey,
runFollowup,
restartIfIdle,
front: options.position === "front"
});
return true;
}
if (queue.items.some((item) => item.steerPending)) {
if (!markFollowupRunEnqueued(run)) return false;
appendQueueItem({
key,
queue,
run,
recentMessageIdKey,
runFollowup,
restartIfIdle,
front: false
});
return true;
}
const pendingCount = countPendingQueueItems(queue.items, queue.inFlight);
if (!options.steerCandidate && queue.dropPolicy === "new" && queue.cap > 0 && pendingCount >= queue.cap) {
run.onQueueDisposition?.("queue-cap-new");
completeFollowupRunLifecycle(run);
return false;
}
if (!markFollowupRunEnqueued(run)) return false;
const elidedSummaryLines = [];
const shouldEnqueue = applyQueueDropPolicy({
queue,
inFlight: queue.inFlight,
summarize: (item) => {
const approved = item.userTurnTranscriptRecorder?.getPendingInputMessage?.();
return approved ? extractTextFromChatContent(approved.content, {
normalizeText: (text) => text,
joinWith: "\n"
}) ?? "" : normalizeOptionalString(item.summaryLine) || item.prompt.trim();
},
onSummaryElide: (lines) => elidedSummaryLines.push(...lines),
onDrop: (dropped) => {
if (queue.dropPolicy === "summarize") {
queue.summarySources.push(...dropped);
return;
}
for (const item of dropped) {
item.onQueueDisposition?.("queue-cap-old");
completeFollowupRunLifecycle(item);
}
},
isProtected: (item) => item.protectFromQueueOverflow === true || item.steerAnchor === true
});
if (queue.dropPolicy === "summarize") {
const overflow = queue.summarySources.length - queue.summaryLines.length;
if (overflow > 0) {
const removed = queue.summarySources.splice(0, overflow);
for (const [index, item] of removed.entries()) {
const summaryLine = elidedSummaryLines[index];
if (summaryLine === void 0) throw new Error("followup queue summary source lost its elided line");
const contextKey = resolveFollowupDeliveryContextKey(item);
const lastElision = queue.summaryElisions.at(-1);
if (lastElision?.contextKey === contextKey) {
const compactSource = createOverflowSummaryRetrySource(item);
lastElision.count += 1;
lastElision.sources.push(compactSource);
lastElision.summaryLines.push(summaryLine);
lastElision.sourceRefs.set(item, compactSource);
if (queue.activeSummarySources.has(item)) queue.activeSummarySources.add(compactSource);
} else {
const compactSource = createOverflowSummaryRetrySource(item);
queue.summaryElisions.push({
contextKey,
count: 1,
sources: [compactSource],
summaryLines: [summaryLine],
sourceRefs: new WeakMap([[item, compactSource]])
});
if (queue.activeSummarySources.has(item)) queue.activeSummarySources.add(compactSource);
}
trimSummaryElisionsToCap(queue);
}
}
}
if (!shouldEnqueue) {
run.onQueueDisposition?.("queue-cap");
completeFollowupRunLifecycle(run);
return false;
}
appendQueueItem({
key,
queue,
run,
recentMessageIdKey,
runFollowup,
restartIfIdle,
front: options.position === "front"
});
return true;
}
function getFollowupQueueDepth(key) {
const queue = getExistingFollowupQueue(key);
if (!queue) return 0;
return countPendingQueueItems(queue.items, queue.inFlight);
}
function settleParkedSteerAcceptance(key, run, accepted) {
const queue = getExistingFollowupQueue(key);
const pending = run.steerPending;
if (!queue?.items.includes(run) || !pending) return false;
pending.settle(accepted);
if (!accepted) {
delete run.steerPending;
reapplyDeferredOverflow(key);
kickFollowupDrainIfIdle(key);
}
return true;
}
function isParkedFollowupRunOwned(key, run) {
return getExistingFollowupQueue(key)?.items.includes(run) === true;
}
function reapplyDeferredOverflow(key) {
const queue = getExistingFollowupQueue(key);
if (!queue || queue.items.some((item) => item.steerPending)) return;
const lastAnchor = queue.items.findLastIndex((item) => item.steerAnchor === true);
const suffix = queue.items.splice(lastAnchor + 1);
if (suffix.length === 0) return;
const originalCap = queue.cap;
const settings = {
mode: queue.mode,
debounceMs: queue.debounceMs,
cap: originalCap + lastAnchor + 1,
dropPolicy: queue.dropPolicy
};
for (const item of suffix) if (!enqueueFollowupRun(key, item, settings, "none", void 0, false)) completeFollowupRunLifecycle(item);
queue.cap = originalCap;
}
/** Remove an exactly committed steer while preserving every sibling's FIFO position. */
function consumeParkedFollowupRun(key, run, disposition) {
const queue = getExistingFollowupQueue(key);
const index = queue?.items.indexOf(run) ?? -1;
if (!queue || index < 0) return false;
queue.items.splice(index, 1);
run.steerPending?.settle(true);
delete run.steerPending;
delete run.protectFromQueueOverflow;
delete run.steerAnchor;
reapplyDeferredOverflow(key);
completeFollowupRunLifecycle(run, disposition);
if (!queue.draining && queue.items.length === 0 && queue.inFlight.size === 0 && queue.droppedCount === 0 && FOLLOWUP_QUEUES.get(key) === queue) {
FOLLOWUP_QUEUES.delete(key);
clearFollowupDrainCallback(key);
} else kickFollowupDrainIfIdle(key);
return true;
}
function parkSteerCandidate(key, run, settings, runFollowup) {
if (!enqueueFollowupRun(key, run, settings, "message-id", runFollowup, false, { steerCandidate: true })) return;
logMessageQueuedWithBacklogPolicy({
sessionId: run.run.sessionId,
sessionKey: key,
channel: run.originatingChannel ?? run.run.messageProvider,
source: "followup-queue-steer"
}, false);
return {
async admit() {
const predecessorAccepted = await run.steerPending?.predecessor ?? true;
if (isFollowupRunAborted(run) || !isParkedFollowupRunOwned(key, run)) return "cancelled";
return predecessorAccepted ? "steer" : "fallback";
},
accepted: (accepted) => settleParkedSteerAcceptance(key, run, accepted),
fallback: () => settleParkedSteerAcceptance(key, run, false),
consume: (disposition) => consumeParkedFollowupRun(key, run, disposition)
};
}
if (process.env.VITEST === "true" || false) globalThis[Symbol.for("openclaw.queueEnqueueTestApi")] = { resetRecentQueuedMessageIdDedupe };
//#endregion
//#region src/auto-reply/reply/queue/settings-runtime.ts
/** Resolves plugin-provided debounce defaults for a channel queue. */
function resolvePluginDebounce(channelKey) {
if (!channelKey) return;
const value = getLoadedChannelPlugin(channelKey)?.defaults?.queue?.debounceMs;
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : void 0;
}
/** Resolves queue settings with channel plugin defaults layered into core config. */
function resolveQueueSettings(params) {
const channelKey = normalizeOptionalLowercaseString(params.channel);
return resolveQueueSettingsCore({
...params,
pluginDebounceMs: params.pluginDebounceMs ?? resolvePluginDebounce(channelKey)
});
}
//#endregion
export { parkSteerCandidate as i, enqueueFollowupRun as n, getFollowupQueueDepth as r, resolveQueueSettings as t };