feat(sessions): add channelIdleMinutes config for per-channel session idle durations (#1353)
* feat(sessions): add channelIdleMinutes config for per-channel session idle durations
Add new `channelIdleMinutes` config option to allow different session idle
timeouts per channel. For example, Discord sessions can now be configured
to last 7 days (10080 minutes) while other channels use shorter defaults.
Config example:
sessions:
channelIdleMinutes:
discord: 10080 # 7 days
The channel-specific idle is passed as idleMinutesOverride to the existing
resolveSessionResetPolicy, integrating cleanly with the new reset policy
architecture.
* fix
* feat: add per-channel session reset overrides (#1353) (thanks @cash-echo-bot)
---------
Co-authored-by: Cash Williams <cashwilliams@gmail.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -8,7 +8,7 @@ import { saveSessionStore } from "../../config/sessions.js";
|
||||
import { getSessionSnapshot } from "./session-snapshot.js";
|
||||
|
||||
describe("getSessionSnapshot", () => {
|
||||
it("uses heartbeat idle override while daily reset still applies", async () => {
|
||||
it("uses channel reset overrides when configured", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date(2026, 0, 18, 5, 0, 0));
|
||||
try {
|
||||
@@ -20,6 +20,7 @@ describe("getSessionSnapshot", () => {
|
||||
[sessionKey]: {
|
||||
sessionId: "snapshot-session",
|
||||
updatedAt: new Date(2026, 0, 18, 3, 30, 0).getTime(),
|
||||
lastChannel: "whatsapp",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -27,7 +28,9 @@ describe("getSessionSnapshot", () => {
|
||||
session: {
|
||||
store: storePath,
|
||||
reset: { mode: "daily", atHour: 4, idleMinutes: 240 },
|
||||
heartbeatIdleMinutes: 30,
|
||||
resetByChannel: {
|
||||
whatsapp: { mode: "idle", idleMinutes: 360 },
|
||||
},
|
||||
},
|
||||
} as Parameters<typeof getSessionSnapshot>[0];
|
||||
|
||||
@@ -35,9 +38,10 @@ describe("getSessionSnapshot", () => {
|
||||
sessionKey,
|
||||
});
|
||||
|
||||
expect(snapshot.resetPolicy.idleMinutes).toBe(30);
|
||||
expect(snapshot.fresh).toBe(false);
|
||||
expect(snapshot.dailyResetAt).toBe(new Date(2026, 0, 18, 4, 0, 0).getTime());
|
||||
expect(snapshot.resetPolicy.mode).toBe("idle");
|
||||
expect(snapshot.resetPolicy.idleMinutes).toBe(360);
|
||||
expect(snapshot.fresh).toBe(true);
|
||||
expect(snapshot.dailyResetAt).toBeUndefined();
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { loadConfig } from "../../config/config.js";
|
||||
import {
|
||||
evaluateSessionFreshness,
|
||||
loadSessionStore,
|
||||
resolveChannelResetConfig,
|
||||
resolveThreadFlag,
|
||||
resolveSessionResetPolicy,
|
||||
resolveSessionResetType,
|
||||
@@ -13,7 +14,7 @@ import { normalizeMainKey } from "../../routing/session-key.js";
|
||||
export function getSessionSnapshot(
|
||||
cfg: ReturnType<typeof loadConfig>,
|
||||
from: string,
|
||||
isHeartbeat = false,
|
||||
_isHeartbeat = false,
|
||||
ctx?: {
|
||||
sessionKey?: string | null;
|
||||
isGroup?: boolean;
|
||||
@@ -34,6 +35,7 @@ export function getSessionSnapshot(
|
||||
);
|
||||
const store = loadSessionStore(resolveStorePath(sessionCfg?.store));
|
||||
const entry = store[key];
|
||||
|
||||
const isThread = resolveThreadFlag({
|
||||
sessionKey: key,
|
||||
messageThreadId: ctx?.messageThreadId ?? null,
|
||||
@@ -42,11 +44,14 @@ export function getSessionSnapshot(
|
||||
parentSessionKey: ctx?.parentSessionKey ?? null,
|
||||
});
|
||||
const resetType = resolveSessionResetType({ sessionKey: key, isGroup: ctx?.isGroup, isThread });
|
||||
const idleMinutesOverride = isHeartbeat ? sessionCfg?.heartbeatIdleMinutes : undefined;
|
||||
const channelReset = resolveChannelResetConfig({
|
||||
sessionCfg,
|
||||
channel: entry?.lastChannel ?? entry?.channel,
|
||||
});
|
||||
const resetPolicy = resolveSessionResetPolicy({
|
||||
sessionCfg,
|
||||
resetType,
|
||||
idleMinutesOverride,
|
||||
resetOverride: channelReset,
|
||||
});
|
||||
const now = Date.now();
|
||||
const freshness = entry
|
||||
|
||||
Reference in New Issue
Block a user