























11// Slack plugin module implements prepare thread context behavior.
22import { formatInboundEnvelope } from "openclaw/plugin-sdk/channel-inbound";
33import { runTasksWithConcurrency } from "openclaw/plugin-sdk/concurrency-runtime";
4-import type { ContextVisibilityMode } from "openclaw/plugin-sdk/config-contracts";
4+import type { ContextVisibilityMode, OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
55import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
66import {
77filterSupplementalContextItems,
@@ -10,7 +10,7 @@ import {
1010import type { ResolvedSlackAccount } from "../../accounts.js";
1111import type { SlackMessageEvent } from "../../types.js";
1212import { resolveSlackAllowListMatch } from "../allow-list.js";
13-import { readSessionUpdatedAt } from "../config.runtime.js";
13+import { readSessionUpdatedAt, resolveChannelResetConfig } from "../config.runtime.js";
1414import type { SlackMonitorContext } from "../context.js";
1515import type { SlackMediaResult } from "../media-types.js";
1616import { resolveSlackThreadHistory, type SlackThreadStarter } from "../thread.js";
@@ -35,13 +35,49 @@ function loadSlackMediaModule(): Promise<SlackMediaModule> {
3535type SlackThreadContextData = {
3636threadStarterBody: string | undefined;
3737threadHistoryBody: string | undefined;
38-threadSessionPreviousTimestamp: number | undefined;
38+shouldSeedInitialThreadContext: boolean;
3939threadLabel: string | undefined;
4040threadStarterMedia: SlackMediaResult[] | null;
4141};
42424343const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
444445+type SlackSessionResetFreshness = {
46+state: "missing" | "fresh" | "stale";
47+};
48+49+type SlackSessionFreshnessRuntime = {
50+session?: {
51+resolveEntryResetFreshness?: (params: {
52+storePath?: string;
53+sessionKey: string;
54+sessionCfg?: OpenClawConfig["session"];
55+resetType: "thread";
56+resetOverride?: ReturnType<typeof resolveChannelResetConfig>;
57+}) => SlackSessionResetFreshness;
58+};
59+};
60+61+function resolveSlackThreadSessionFreshness(params: {
62+ctx: SlackMonitorContext;
63+storePath: string;
64+sessionKey: string;
65+}): SlackSessionResetFreshness | undefined {
66+// Gateway startup supplies the full channel runtime, but the public surface
67+// intentionally keeps non-context helpers untyped for external plugins.
68+const runtime = params.ctx.channelRuntime as SlackSessionFreshnessRuntime | undefined;
69+return runtime?.session?.resolveEntryResetFreshness?.({
70+storePath: params.storePath,
71+sessionKey: params.sessionKey,
72+sessionCfg: params.ctx.cfg.session,
73+resetType: "thread",
74+resetOverride: resolveChannelResetConfig({
75+sessionCfg: params.ctx.cfg.session,
76+channel: "slack",
77+}),
78+});
79+}
80+4581function isSlackThreadContextSenderAllowed(params: {
4682allowFromLower: string[];
4783allowNameMatching: boolean;
@@ -125,19 +161,36 @@ export async function resolveSlackThreadContextData(params: {
125161let threadHistoryBody: string | undefined;
126162let threadLabel: string | undefined;
127163let threadStarterMedia: SlackMediaResult[] | null = null;
128-const threadSessionPreviousTimestamp =
164+const threadSessionFreshness =
129165params.isThreadReply && params.threadTs
166+ ? resolveSlackThreadSessionFreshness({
167+ctx: params.ctx,
168+storePath: params.storePath,
169+sessionKey: params.sessionKey,
170+})
171+ : undefined;
172+const threadSessionPreviousTimestamp =
173+params.isThreadReply && params.threadTs && !threadSessionFreshness
130174 ? readSessionUpdatedAt({
131175storePath: params.storePath,
132176sessionKey: params.sessionKey,
133177})
134178 : undefined;
179+const shouldSeedInitialThreadContext = Boolean(
180+params.isThreadReply &&
181+params.threadTs &&
182+(threadSessionFreshness
183+ ? threadSessionFreshness.state !== "fresh"
184+ : threadSessionPreviousTimestamp === undefined),
185+);
186+const shouldLoadInitialThreadHistory =
187+shouldSeedInitialThreadContext || params.forceInitialHistory === true;
135188136189if (!params.isThreadReply || !params.threadTs) {
137190return {
138191 threadStarterBody,
139192 threadHistoryBody,
140-threadSessionPreviousTimestamp,
193+shouldSeedInitialThreadContext,
141194 threadLabel,
142195 threadStarterMedia,
143196};
@@ -195,10 +248,9 @@ export async function resolveSlackThreadContextData(params: {
195248threadLabel = `Slack thread ${params.roomLabel}`;
196249}
197250198-const isNewThreadSession = !threadSessionPreviousTimestamp;
199251const includeBotStarterAsRootContext = shouldIncludeBotThreadStarterContext({
200252 starterIsCurrentBot,
201- isNewThreadSession,
253+isNewThreadSession: shouldSeedInitialThreadContext,
202254hasStarterText: Boolean(starter?.text),
203255});
204256@@ -218,10 +270,7 @@ export async function resolveSlackThreadContextData(params: {
218270219271const threadInitialHistoryLimit = params.account.config?.thread?.initialHistoryLimit ?? 20;
220272221-if (
222-threadInitialHistoryLimit > 0 &&
223-(!threadSessionPreviousTimestamp || params.forceInitialHistory)
224-) {
273+if (threadInitialHistoryLimit > 0 && shouldLoadInitialThreadHistory) {
225274const currentBotRootTs = starter?.ts ?? params.threadTs;
226275const threadHistory = await resolveSlackThreadHistory({
227276channelId: params.message.channel,
@@ -333,7 +382,7 @@ export async function resolveSlackThreadContextData(params: {
333382return {
334383 threadStarterBody,
335384 threadHistoryBody,
336-threadSessionPreviousTimestamp,
385+shouldSeedInitialThreadContext,
337386 threadLabel,
338387 threadStarterMedia,
339388};
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。