Files
openclaw/src/infra/session-maintenance-warning.ts
Gustavo Madeira Santana 21e8d88c1d build: fix ineffective dynamic imports with lazy boundaries (#33690)
Merged via squash.

Prepared head SHA: 38b3c23d6f
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-03 20:14:41 -05:00

123 lines
3.9 KiB
TypeScript

import type { OpenClawConfig } from "../config/config.js";
import type { SessionEntry, SessionMaintenanceWarning } from "../config/sessions.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { isDeliverableMessageChannel, normalizeMessageChannel } from "../utils/message-channel.js";
import { buildOutboundSessionContext } from "./outbound/session-context.js";
import { resolveSessionDeliveryTarget } from "./outbound/targets.js";
import { enqueueSystemEvent } from "./system-events.js";
type WarningParams = {
cfg: OpenClawConfig;
sessionKey: string;
entry: SessionEntry;
warning: SessionMaintenanceWarning;
};
const warnedContexts = new Map<string, string>();
const log = createSubsystemLogger("session-maintenance-warning");
let deliverRuntimePromise: Promise<typeof import("./outbound/deliver-runtime.js")> | null = null;
function loadDeliverRuntime() {
deliverRuntimePromise ??= import("./outbound/deliver-runtime.js");
return deliverRuntimePromise;
}
function shouldSendWarning(): boolean {
return !process.env.VITEST && process.env.NODE_ENV !== "test";
}
function buildWarningContext(params: WarningParams): string {
const { warning } = params;
return [
warning.activeSessionKey,
warning.pruneAfterMs,
warning.maxEntries,
warning.wouldPrune ? "prune" : "",
warning.wouldCap ? "cap" : "",
]
.filter(Boolean)
.join("|");
}
function formatDuration(ms: number): string {
if (ms >= 86_400_000) {
const days = Math.round(ms / 86_400_000);
return `${days} day${days === 1 ? "" : "s"}`;
}
if (ms >= 3_600_000) {
const hours = Math.round(ms / 3_600_000);
return `${hours} hour${hours === 1 ? "" : "s"}`;
}
if (ms >= 60_000) {
const mins = Math.round(ms / 60_000);
return `${mins} minute${mins === 1 ? "" : "s"}`;
}
const secs = Math.round(ms / 1000);
return `${secs} second${secs === 1 ? "" : "s"}`;
}
function buildWarningText(warning: SessionMaintenanceWarning): string {
const reasons: string[] = [];
if (warning.wouldPrune) {
reasons.push(`older than ${formatDuration(warning.pruneAfterMs)}`);
}
if (warning.wouldCap) {
reasons.push(`not in the most recent ${warning.maxEntries} sessions`);
}
const reasonText = reasons.length > 0 ? reasons.join(" and ") : "over maintenance limits";
return (
`⚠️ Session maintenance warning: this active session would be evicted (${reasonText}). ` +
`Maintenance is set to warn-only, so nothing was reset. ` +
`To enforce cleanup, set \`session.maintenance.mode: "enforce"\` or increase the limits.`
);
}
export async function deliverSessionMaintenanceWarning(params: WarningParams): Promise<void> {
if (!shouldSendWarning()) {
return;
}
const contextKey = buildWarningContext(params);
if (warnedContexts.get(params.sessionKey) === contextKey) {
return;
}
warnedContexts.set(params.sessionKey, contextKey);
const text = buildWarningText(params.warning);
const target = resolveSessionDeliveryTarget({
entry: params.entry,
requestedChannel: "last",
});
if (!target.channel || !target.to) {
enqueueSystemEvent(text, { sessionKey: params.sessionKey });
return;
}
const channel = normalizeMessageChannel(target.channel) ?? target.channel;
if (!isDeliverableMessageChannel(channel)) {
enqueueSystemEvent(text, { sessionKey: params.sessionKey });
return;
}
try {
const { deliverOutboundPayloads } = await loadDeliverRuntime();
const outboundSession = buildOutboundSessionContext({
cfg: params.cfg,
sessionKey: params.sessionKey,
});
await deliverOutboundPayloads({
cfg: params.cfg,
channel,
to: target.to,
accountId: target.accountId,
threadId: target.threadId,
payloads: [{ text }],
session: outboundSession,
});
} catch (err) {
log.warn(`Failed to deliver session maintenance warning: ${String(err)}`);
enqueueSystemEvent(text, { sessionKey: params.sessionKey });
}
}