Files
openclaw/src/infra/session-maintenance-warning.ts
Mr. Guy e927fd1e35 fix: allow agent workspace directories in media local roots (#17136)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 7545ef1e1901a5bfd33aaa55a2320e003ea39126
Co-authored-by: MisterGuy420 <255743668+MisterGuy420@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-02-15 10:53:45 -05:00

111 lines
3.5 KiB
TypeScript

import type { OpenClawConfig } from "../config/config.js";
import type { SessionEntry, SessionMaintenanceWarning } from "../config/sessions.js";
import { resolveSessionAgentId } from "../agents/agent-scope.js";
import { isDeliverableMessageChannel, normalizeMessageChannel } from "../utils/message-channel.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>();
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 import("./outbound/deliver.js");
await deliverOutboundPayloads({
cfg: params.cfg,
channel,
to: target.to,
accountId: target.accountId,
threadId: target.threadId,
payloads: [{ text }],
agentId: resolveSessionAgentId({ sessionKey: params.sessionKey, config: params.cfg }),
});
} catch (err) {
console.warn(`Failed to deliver session maintenance warning: ${String(err)}`);
enqueueSystemEvent(text, { sessionKey: params.sessionKey });
}
}