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:
@@ -436,3 +436,42 @@ describe("initSessionState reset policy", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("initSessionState channel reset overrides", () => {
|
||||
it("uses channel-specific reset policy when configured", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-channel-idle-"));
|
||||
const storePath = path.join(root, "sessions.json");
|
||||
const sessionKey = "agent:main:discord:dm:123";
|
||||
const sessionId = "session-override";
|
||||
const updatedAt = Date.now() - (10080 - 1) * 60_000;
|
||||
|
||||
await saveSessionStore(storePath, {
|
||||
[sessionKey]: {
|
||||
sessionId,
|
||||
updatedAt,
|
||||
},
|
||||
});
|
||||
|
||||
const cfg = {
|
||||
session: {
|
||||
store: storePath,
|
||||
idleMinutes: 60,
|
||||
resetByType: { dm: { mode: "idle", idleMinutes: 10 } },
|
||||
resetByChannel: { discord: { mode: "idle", idleMinutes: 10080 } },
|
||||
},
|
||||
} as ClawdbotConfig;
|
||||
|
||||
const result = await initSessionState({
|
||||
ctx: {
|
||||
Body: "Hello",
|
||||
SessionKey: sessionKey,
|
||||
Provider: "discord",
|
||||
},
|
||||
cfg,
|
||||
commandAuthorized: true,
|
||||
});
|
||||
|
||||
expect(result.isNewSession).toBe(false);
|
||||
expect(result.sessionEntry.sessionId).toBe(sessionId);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
evaluateSessionFreshness,
|
||||
type GroupKeyResolution,
|
||||
loadSessionStore,
|
||||
resolveChannelResetConfig,
|
||||
resolveThreadFlag,
|
||||
resolveSessionResetPolicy,
|
||||
resolveSessionResetType,
|
||||
@@ -106,6 +107,7 @@ export async function initSessionState(params: {
|
||||
sessionKey: sessionCtxForState.SessionKey,
|
||||
config: cfg,
|
||||
});
|
||||
const groupResolution = resolveGroupSessionKey(sessionCtxForState) ?? undefined;
|
||||
const resetTriggers = sessionCfg?.resetTriggers?.length
|
||||
? sessionCfg.resetTriggers
|
||||
: DEFAULT_RESET_TRIGGERS;
|
||||
@@ -129,7 +131,6 @@ export async function initSessionState(params: {
|
||||
let persistedModelOverride: string | undefined;
|
||||
let persistedProviderOverride: string | undefined;
|
||||
|
||||
const groupResolution = resolveGroupSessionKey(sessionCtxForState) ?? undefined;
|
||||
const normalizedChatType = normalizeChatType(ctx.ChatType);
|
||||
const isGroup =
|
||||
normalizedChatType != null && normalizedChatType !== "direct" ? true : Boolean(groupResolution);
|
||||
@@ -195,7 +196,19 @@ export async function initSessionState(params: {
|
||||
parentSessionKey: ctx.ParentSessionKey,
|
||||
});
|
||||
const resetType = resolveSessionResetType({ sessionKey, isGroup, isThread });
|
||||
const resetPolicy = resolveSessionResetPolicy({ sessionCfg, resetType });
|
||||
const channelReset = resolveChannelResetConfig({
|
||||
sessionCfg,
|
||||
channel:
|
||||
groupResolution?.channel ??
|
||||
(ctx.OriginatingChannel as string | undefined) ??
|
||||
ctx.Surface ??
|
||||
ctx.Provider,
|
||||
});
|
||||
const resetPolicy = resolveSessionResetPolicy({
|
||||
sessionCfg,
|
||||
resetType,
|
||||
resetOverride: channelReset,
|
||||
});
|
||||
const freshEntry = entry
|
||||
? evaluateSessionFreshness({ updatedAt: entry.updatedAt, now, policy: resetPolicy }).fresh
|
||||
: false;
|
||||
|
||||
Reference in New Issue
Block a user