import type { SessionConfig, SessionResetConfig } from "../types.base.js" ;
import { DEFAULT_IDLE_MINUTES } from "./types.js" ;
export type SessionResetMode = "daily" | "idle" ;
export type SessionResetType = "direct" | "group" | "thread" ;
export type SessionResetPolicy = {
mode: SessionResetMode;
atHour: number;
idleMinutes?: number;
configured?: boolean ;
};
export type SessionFreshness = {
fresh: boolean ;
dailyResetAt?: number;
idleExpiresAt?: number;
};
export const DEFAULT_RESET_MODE: SessionResetMode = "daily" ;
export const DEFAULT_RESET_AT_HOUR = 4 ;
export function resolveDailyResetAtMs(now: number, atHour: number): number {
const normalizedAtHour = normalizeResetAtHour(atHour);
const resetAt = new Date(now);
resetAt.setHours(normalizedAtHour, 0 , 0 , 0 );
if (now < resetAt.getTime()) {
resetAt.setDate(resetAt.getDate() - 1 );
}
return resetAt.getTime();
}
export function resolveSessionResetPolicy(params: {
sessionCfg?: SessionConfig;
resetType: SessionResetType;
resetOverride?: SessionResetConfig;
}): SessionResetPolicy {
const sessionCfg = params.sessionCfg;
const baseReset = params.resetOverride ?? sessionCfg?.reset;
// Backward compat: accept legacy "dm" key as alias for "direct"
const typeReset = params.resetOverride
? undefined
: (sessionCfg?.resetByType?.[params.resetType] ??
(params.resetType === "direct"
? (sessionCfg?.resetByType as { dm?: SessionResetConfig } | undefined)?.dm
: undefined));
const hasExplicitReset = Boolean (baseReset || sessionCfg?.resetByType);
const legacyIdleMinutes = params.resetOverride ? undefined : sessionCfg?.idleMinutes;
const configured = Boolean (baseReset || typeReset || legacyIdleMinutes != null );
const mode =
typeReset?.mode ??
baseReset?.mode ??
(!hasExplicitReset && legacyIdleMinutes != null ? "idle" : DEFAULT_RESET_MODE);
const atHour = normalizeResetAtHour(
typeReset?.atHour ?? baseReset?.atHour ?? DEFAULT_RESET_AT_HOUR,
);
const idleMinutesRaw = typeReset?.idleMinutes ?? baseReset?.idleMinutes ?? legacyIdleMinutes;
let idleMinutes: number | undefined;
if (idleMinutesRaw != null ) {
const normalized = Math.floor(idleMinutesRaw);
if (Number.isFinite(normalized)) {
idleMinutes = Math.max(normalized, 0 );
}
} else if (mode === "idle" ) {
idleMinutes = DEFAULT_IDLE_MINUTES;
}
return { mode, atHour, idleMinutes, configured };
}
export function evaluateSessionFreshness(params: {
updatedAt: number;
now: number;
policy: SessionResetPolicy;
}): SessionFreshness {
const dailyResetAt =
params.policy.mode === "daily"
? resolveDailyResetAtMs(params.now, params.policy.atHour)
: undefined;
const idleExpiresAt =
params.policy.idleMinutes != null && params.policy.idleMinutes > 0
? params.updatedAt + params.policy.idleMinutes * 60 _000
: undefined;
const staleDaily = dailyResetAt != null && params.updatedAt < dailyResetAt;
const staleIdle = idleExpiresAt != null && params.now > idleExpiresAt;
return {
fresh: !(staleDaily || staleIdle),
dailyResetAt,
idleExpiresAt,
};
}
function normalizeResetAtHour(value: number | undefined): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
return DEFAULT_RESET_AT_HOUR;
}
const normalized = Math.floor(value);
if (!Number.isFinite(normalized)) {
return DEFAULT_RESET_AT_HOUR;
}
if (normalized < 0 ) {
return 0 ;
}
if (normalized > 23 ) {
return 23 ;
}
return normalized;
}
Messung V0.5 in Prozent C=100 H=100 G=100
¤ Dauer der Verarbeitung: 0.4 Sekunden
¤
*© Formatika GbR, Deutschland