From 3f27d76ea7dc31d75b818fb63691e594f054e76b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 25 Jul 2026 21:21:06 -0700 Subject: [PATCH] fix(slack): resolve thread freshness owner (#114009) --- .../message-handler/prepare-thread-context.ts | 3 +++ src/config/sessions/entry-freshness.test.ts | 26 +++++++++++++++++++ src/config/sessions/entry-freshness.ts | 3 ++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/extensions/slack/src/monitor/message-handler/prepare-thread-context.ts b/extensions/slack/src/monitor/message-handler/prepare-thread-context.ts index 23fd88542a70..ca7220122d52 100644 --- a/extensions/slack/src/monitor/message-handler/prepare-thread-context.ts +++ b/extensions/slack/src/monitor/message-handler/prepare-thread-context.ts @@ -1,4 +1,5 @@ // Slack plugin module implements prepare thread context behavior. +import { resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime"; import { formatInboundEnvelope, resolveInboundSupplementalSenderAllowed, @@ -49,6 +50,7 @@ type SlackSessionResetFreshness = { type SlackSessionFreshnessRuntime = { session?: { resolveEntryResetFreshness?: (params: { + defaultAgentId?: string; storePath?: string; sessionKey: string; sessionCfg?: OpenClawConfig["session"]; @@ -67,6 +69,7 @@ function resolveSlackThreadSessionFreshness(params: { // intentionally keeps non-context helpers untyped for external plugins. const runtime = params.ctx.channelRuntime as SlackSessionFreshnessRuntime | undefined; return runtime?.session?.resolveEntryResetFreshness?.({ + defaultAgentId: resolveDefaultAgentId(params.ctx.cfg), storePath: params.storePath, sessionKey: params.sessionKey, sessionCfg: params.ctx.cfg.session, diff --git a/src/config/sessions/entry-freshness.test.ts b/src/config/sessions/entry-freshness.test.ts index 88a898a5a766..5a31e7c3831b 100644 --- a/src/config/sessions/entry-freshness.test.ts +++ b/src/config/sessions/entry-freshness.test.ts @@ -43,6 +43,32 @@ describe("resolveSessionEntryResetFreshness", () => { }); }); + it("uses the configured default agent for an unqualified session key", async () => { + const sessionKey = "global"; + const now = new Date("2026-01-02T12:00:00Z").getTime(); + await upsertSessionEntry( + { agentId: "ops", defaultAgentId: "ops", sessionKey, storePath }, + { + sessionId: "session-global-ops", + updatedAt: now, + sessionStartedAt: now, + lastInteractionAt: now, + }, + ); + + const result = resolveSessionEntryResetFreshness({ + defaultAgentId: "ops", + sessionKey, + storePath, + sessionCfg: {}, + resetType: "direct", + now, + }); + + expect(result.state).toBe("fresh"); + expect(result.entry?.sessionId).toBe("session-global-ops"); + }); + it("resolves stale daily freshness from lifecycle timestamps instead of activity", async () => { const sessionKey = "agent:main:main:thread:100.000"; const now = new Date("2026-01-02T12:00:00Z").getTime(); diff --git a/src/config/sessions/entry-freshness.ts b/src/config/sessions/entry-freshness.ts index 5be995c5437e..5c62cd4747ab 100644 --- a/src/config/sessions/entry-freshness.ts +++ b/src/config/sessions/entry-freshness.ts @@ -54,7 +54,8 @@ export function hasProviderOwnedSession(entry: SessionEntry | undefined): boolea export function resolveSessionEntryResetFreshness( params: ResolveSessionEntryResetFreshnessParams, ): ResolvedSessionEntryResetFreshness { - const agentId = params.agentId ?? resolveAgentIdFromSessionKey(params.sessionKey); + const agentId = + params.agentId ?? resolveAgentIdFromSessionKey(params.sessionKey, params.defaultAgentId); const sessionCfg = params.sessionCfg; const storePath = params.storePath ??