Reply: gate Slack directives behind capability

This commit is contained in:
Vincent Koc
2026-03-12 23:42:27 -04:00
parent af414e2e73
commit f16c14536c
4 changed files with 34 additions and 6 deletions

View File

@@ -18,6 +18,7 @@ export type NormalizeReplySkipReason = "empty" | "silent" | "heartbeat";
export type NormalizeReplyOptions = {
responsePrefix?: string;
enableSlackInteractiveReplies?: boolean;
/** Context for template variable interpolation in responsePrefix */
responsePrefixContext?: ResponsePrefixContext;
onHeartbeatStrip?: () => void;
@@ -107,7 +108,7 @@ export function normalizeReplyPayload(
}
enrichedPayload = { ...enrichedPayload, text };
if (text && hasSlackDirectives(text)) {
if (opts.enableSlackInteractiveReplies && text && hasSlackDirectives(text)) {
enrichedPayload = parseSlackDirectives(enrichedPayload);
}

View File

@@ -43,6 +43,7 @@ function getHumanDelay(config: HumanDelayConfig | undefined): number {
export type ReplyDispatcherOptions = {
deliver: ReplyDispatchDeliverer;
responsePrefix?: string;
enableSlackInteractiveReplies?: boolean;
/** Static context for response prefix template interpolation. */
responsePrefixContext?: ResponsePrefixContext;
/** Dynamic context provider for response prefix template interpolation.
@@ -84,7 +85,11 @@ export type ReplyDispatcher = {
type NormalizeReplyPayloadInternalOptions = Pick<
ReplyDispatcherOptions,
"responsePrefix" | "responsePrefixContext" | "responsePrefixContextProvider" | "onHeartbeatStrip"
| "responsePrefix"
| "enableSlackInteractiveReplies"
| "responsePrefixContext"
| "responsePrefixContextProvider"
| "onHeartbeatStrip"
> & {
onSkip?: (reason: NormalizeReplySkipReason) => void;
};
@@ -98,6 +103,7 @@ function normalizeReplyPayloadInternal(
return normalizeReplyPayload(payload, {
responsePrefix: opts.responsePrefix,
enableSlackInteractiveReplies: opts.enableSlackInteractiveReplies,
responsePrefixContext: prefixContext,
onHeartbeatStrip: opts.onHeartbeatStrip,
onSkip: opts.onSkip,

View File

@@ -12,6 +12,7 @@ import { resolveEffectiveMessagesConfig } from "../../agents/identity.js";
import { normalizeChannelId } from "../../channels/plugins/index.js";
import type { OpenClawConfig } from "../../config/config.js";
import { buildOutboundSessionContext } from "../../infra/outbound/session-context.js";
import { isSlackInteractiveRepliesEnabled } from "../../slack/interactive-replies.js";
import { INTERNAL_MESSAGE_CHANNEL, normalizeMessageChannel } from "../../utils/message-channel.js";
import type { OriginatingChannelType } from "../templating.js";
import type { ReplyPayload } from "../types.js";
@@ -94,6 +95,8 @@ export async function routeReply(params: RouteReplyParams): Promise<RouteReplyRe
: cfg.messages?.responsePrefix;
const normalized = normalizeReplyPayload(payload, {
responsePrefix,
enableSlackInteractiveReplies:
channel === "slack" ? isSlackInteractiveRepliesEnabled({ cfg, accountId }) : false,
});
if (!normalized) {
return { ok: true };

View File

@@ -5,19 +5,24 @@ import {
} from "../auto-reply/reply/response-prefix-template.js";
import type { GetReplyOptions } from "../auto-reply/types.js";
import type { OpenClawConfig } from "../config/config.js";
import { isSlackInteractiveRepliesEnabled } from "../slack/interactive-replies.js";
type ModelSelectionContext = Parameters<NonNullable<GetReplyOptions["onModelSelected"]>>[0];
export type ReplyPrefixContextBundle = {
prefixContext: ResponsePrefixContext;
responsePrefix?: string;
enableSlackInteractiveReplies?: boolean;
responsePrefixContextProvider: () => ResponsePrefixContext;
onModelSelected: (ctx: ModelSelectionContext) => void;
};
export type ReplyPrefixOptions = Pick<
ReplyPrefixContextBundle,
"responsePrefix" | "responsePrefixContextProvider" | "onModelSelected"
| "responsePrefix"
| "enableSlackInteractiveReplies"
| "responsePrefixContextProvider"
| "onModelSelected"
>;
export function createReplyPrefixContext(params: {
@@ -45,6 +50,10 @@ export function createReplyPrefixContext(params: {
channel: params.channel,
accountId: params.accountId,
}).responsePrefix,
enableSlackInteractiveReplies:
params.channel === "slack"
? isSlackInteractiveRepliesEnabled({ cfg, accountId: params.accountId })
: undefined,
responsePrefixContextProvider: () => prefixContext,
onModelSelected,
};
@@ -56,7 +65,16 @@ export function createReplyPrefixOptions(params: {
channel?: string;
accountId?: string;
}): ReplyPrefixOptions {
const { responsePrefix, responsePrefixContextProvider, onModelSelected } =
createReplyPrefixContext(params);
return { responsePrefix, responsePrefixContextProvider, onModelSelected };
const {
responsePrefix,
enableSlackInteractiveReplies,
responsePrefixContextProvider,
onModelSelected,
} = createReplyPrefixContext(params);
return {
responsePrefix,
enableSlackInteractiveReplies,
responsePrefixContextProvider,
onModelSelected,
};
}