* feat(slack): populate thread session with existing thread history When a new session is created for a Slack thread, fetch and inject the full thread history as context. This preserves conversation continuity so the bot knows what it previously said in the thread. - Add resolveSlackThreadHistory() to fetch all thread messages - Add ThreadHistoryBody to context payload - Use thread history instead of just thread starter for new sessions Fixes #4470 * chore: remove redundant comments * fix: use threadContextNote in queue body * fix(slack): address Greptile review feedback - P0: Use thread session key (not base session key) for new-session check This ensures thread history is injected when the thread session is new, even if the base channel session already exists. - P1: Fetch up to 200 messages and take the most recent N Slack API returns messages in chronological order (oldest first). Previously we took the first N, now we take the last N for relevant context. - P1: Batch resolve user names with Promise.all Avoid N sequential API calls when resolving user names in thread history. - P2: Include file-only messages in thread history Messages with attachments but no text are now included with a placeholder like '[attached: image.png, document.pdf]'. - P2: Add documentation about intentional 200-message fetch limit Clarifies that we intentionally don't paginate; 200 covers most threads. * style: add braces for curly lint rule * feat(slack): add thread.initialHistoryLimit config option Allow users to configure the maximum number of thread messages to fetch when starting a new thread session. Defaults to 20. Set to 0 to disable thread history fetching entirely. This addresses the optional configuration request from #2608. * chore: trigger CI * fix(slack): ensure isNewSession=true on first thread turn recordInboundSession() in prepare.ts creates the thread session entry before session.ts reads the store, causing isNewSession to be false on the very first user message in a thread. This prevented thread context (history/starter) from being injected. Add IsFirstThreadTurn flag to message context, set when readSessionUpdatedAt() returns undefined for the thread session key. session.ts uses this flag to force isNewSession=true. * style: format prepare.ts for oxfmt * fix: suppress InboundHistory/ThreadStarterBody when ThreadHistoryBody present (#13912) When ThreadHistoryBody is fetched from the Slack API (conversations.replies), it already contains pending messages and the thread starter. Passing both InboundHistory and ThreadStarterBody alongside ThreadHistoryBody caused duplicate content in the LLM context on new thread sessions. Suppress InboundHistory and ThreadStarterBody when ThreadHistoryBody is present, since it is a strict superset of both. * remove verbose comment * fix(slack): paginate thread history context fetch * fix(slack): wire session file path options after main merge --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
155 lines
6.0 KiB
TypeScript
155 lines
6.0 KiB
TypeScript
import type {
|
|
BlockStreamingCoalesceConfig,
|
|
DmPolicy,
|
|
GroupPolicy,
|
|
MarkdownConfig,
|
|
ReplyToMode,
|
|
} from "./types.base.js";
|
|
import type { ChannelHeartbeatVisibilityConfig } from "./types.channels.js";
|
|
import type { DmConfig, ProviderCommandsConfig } from "./types.messages.js";
|
|
import type { GroupToolPolicyBySenderConfig, GroupToolPolicyConfig } from "./types.tools.js";
|
|
|
|
export type SlackDmConfig = {
|
|
/** If false, ignore all incoming Slack DMs. Default: true. */
|
|
enabled?: boolean;
|
|
/** Direct message access policy (default: pairing). */
|
|
policy?: DmPolicy;
|
|
/** Allowlist for DM senders (ids). */
|
|
allowFrom?: Array<string | number>;
|
|
/** If true, allow group DMs (default: false). */
|
|
groupEnabled?: boolean;
|
|
/** Optional allowlist for group DM channels (ids or slugs). */
|
|
groupChannels?: Array<string | number>;
|
|
/** @deprecated Prefer channels.slack.replyToModeByChatType.direct. */
|
|
replyToMode?: ReplyToMode;
|
|
};
|
|
|
|
export type SlackChannelConfig = {
|
|
/** If false, disable the bot in this channel. (Alias for allow: false.) */
|
|
enabled?: boolean;
|
|
/** Legacy channel allow toggle; prefer enabled. */
|
|
allow?: boolean;
|
|
/** Require mentioning the bot to trigger replies. */
|
|
requireMention?: boolean;
|
|
/** Optional tool policy overrides for this channel. */
|
|
tools?: GroupToolPolicyConfig;
|
|
toolsBySender?: GroupToolPolicyBySenderConfig;
|
|
/** Allow bot-authored messages to trigger replies (default: false). */
|
|
allowBots?: boolean;
|
|
/** Allowlist of users that can invoke the bot in this channel. */
|
|
users?: Array<string | number>;
|
|
/** Optional skill filter for this channel. */
|
|
skills?: string[];
|
|
/** Optional system prompt for this channel. */
|
|
systemPrompt?: string;
|
|
};
|
|
|
|
export type SlackReactionNotificationMode = "off" | "own" | "all" | "allowlist";
|
|
|
|
export type SlackActionConfig = {
|
|
reactions?: boolean;
|
|
messages?: boolean;
|
|
pins?: boolean;
|
|
search?: boolean;
|
|
permissions?: boolean;
|
|
memberInfo?: boolean;
|
|
channelInfo?: boolean;
|
|
emojiList?: boolean;
|
|
};
|
|
|
|
export type SlackSlashCommandConfig = {
|
|
/** Enable handling for the configured slash command (default: false). */
|
|
enabled?: boolean;
|
|
/** Slash command name (default: "openclaw"). */
|
|
name?: string;
|
|
/** Session key prefix for slash commands (default: "slack:slash"). */
|
|
sessionPrefix?: string;
|
|
/** Reply ephemerally (default: true). */
|
|
ephemeral?: boolean;
|
|
};
|
|
|
|
export type SlackThreadConfig = {
|
|
/** Scope for thread history context (thread|channel). Default: thread. */
|
|
historyScope?: "thread" | "channel";
|
|
/** If true, thread sessions inherit the parent channel transcript. Default: false. */
|
|
inheritParent?: boolean;
|
|
/** Maximum number of thread messages to fetch as context when starting a new thread session (default: 20). Set to 0 to disable thread history fetching. */
|
|
initialHistoryLimit?: number;
|
|
};
|
|
|
|
export type SlackAccountConfig = {
|
|
/** Optional display name for this account (used in CLI/UI lists). */
|
|
name?: string;
|
|
/** Slack connection mode (socket|http). Default: socket. */
|
|
mode?: "socket" | "http";
|
|
/** Slack signing secret (required for HTTP mode). */
|
|
signingSecret?: string;
|
|
/** Slack Events API webhook path (default: /slack/events). */
|
|
webhookPath?: string;
|
|
/** Optional provider capability tags used for agent/runtime guidance. */
|
|
capabilities?: string[];
|
|
/** Markdown formatting overrides (tables). */
|
|
markdown?: MarkdownConfig;
|
|
/** Override native command registration for Slack (bool or "auto"). */
|
|
commands?: ProviderCommandsConfig;
|
|
/** Allow channel-initiated config writes (default: true). */
|
|
configWrites?: boolean;
|
|
/** If false, do not start this Slack account. Default: true. */
|
|
enabled?: boolean;
|
|
botToken?: string;
|
|
appToken?: string;
|
|
userToken?: string;
|
|
/** If true, restrict user token to read operations only. Default: true. */
|
|
userTokenReadOnly?: boolean;
|
|
/** Allow bot-authored messages to trigger replies (default: false). */
|
|
allowBots?: boolean;
|
|
/** Default mention requirement for channel messages (default: true). */
|
|
requireMention?: boolean;
|
|
/**
|
|
* Controls how channel messages are handled:
|
|
* - "open": channels bypass allowlists; mention-gating applies
|
|
* - "disabled": block all channel messages
|
|
* - "allowlist": only allow channels present in channels.slack.channels
|
|
*/
|
|
groupPolicy?: GroupPolicy;
|
|
/** Max channel messages to keep as history context (0 disables). */
|
|
historyLimit?: number;
|
|
/** Max DM turns to keep as history context. */
|
|
dmHistoryLimit?: number;
|
|
/** Per-DM config overrides keyed by user ID. */
|
|
dms?: Record<string, DmConfig>;
|
|
textChunkLimit?: number;
|
|
/** Chunking mode: "length" (default) splits by size; "newline" splits on every newline. */
|
|
chunkMode?: "length" | "newline";
|
|
blockStreaming?: boolean;
|
|
/** Merge streamed block replies before sending. */
|
|
blockStreamingCoalesce?: BlockStreamingCoalesceConfig;
|
|
mediaMaxMb?: number;
|
|
/** Reaction notification mode (off|own|all|allowlist). Default: own. */
|
|
reactionNotifications?: SlackReactionNotificationMode;
|
|
/** Allowlist for reaction notifications when mode is allowlist. */
|
|
reactionAllowlist?: Array<string | number>;
|
|
/** Control reply threading when reply tags are present (off|first|all). */
|
|
replyToMode?: ReplyToMode;
|
|
/**
|
|
* Optional per-chat-type reply threading overrides.
|
|
* Example: { direct: "all", group: "first", channel: "off" }.
|
|
*/
|
|
replyToModeByChatType?: Partial<Record<"direct" | "group" | "channel", ReplyToMode>>;
|
|
/** Thread session behavior. */
|
|
thread?: SlackThreadConfig;
|
|
actions?: SlackActionConfig;
|
|
slashCommand?: SlackSlashCommandConfig;
|
|
dm?: SlackDmConfig;
|
|
channels?: Record<string, SlackChannelConfig>;
|
|
/** Heartbeat visibility settings for this channel. */
|
|
heartbeat?: ChannelHeartbeatVisibilityConfig;
|
|
/** Outbound response prefix override for this channel/account. */
|
|
responsePrefix?: string;
|
|
};
|
|
|
|
export type SlackConfig = {
|
|
/** Optional per-account Slack configuration (multi-account). */
|
|
accounts?: Record<string, SlackAccountConfig>;
|
|
} & SlackAccountConfig;
|