mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 22:01:35 +00:00
fix(slack): thread channel ID through inbound context for reactions (#34831)
Slack reaction/thread context routing fixes via canonical synthesis of #34831. Co-authored-by: Tak <tak@users.noreply.github.com>
This commit is contained in:
@@ -65,6 +65,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Security/audit account handling: avoid prototype-chain account IDs in audit validation by using own-property checks for `accounts`. (#34982) Thanks @HOYALIM.
|
- Security/audit account handling: avoid prototype-chain account IDs in audit validation by using own-property checks for `accounts`. (#34982) Thanks @HOYALIM.
|
||||||
- Cron/restart catch-up semantics: replay interrupted recurring jobs and missed immediate cron slots on startup without replaying interrupted one-shot jobs, with guarded missed-slot probing to avoid malformed-schedule startup aborts and duplicate-trigger drift after restart. (from #34466, #34896, #34625, #33206) Thanks @dunamismax, @dsantoreis, @Octane0411, and @Sid-Qin.
|
- Cron/restart catch-up semantics: replay interrupted recurring jobs and missed immediate cron slots on startup without replaying interrupted one-shot jobs, with guarded missed-slot probing to avoid malformed-schedule startup aborts and duplicate-trigger drift after restart. (from #34466, #34896, #34625, #33206) Thanks @dunamismax, @dsantoreis, @Octane0411, and @Sid-Qin.
|
||||||
- Agents/session usage tracking: preserve accumulated usage metadata on embedded Pi runner error exits so failed turns still update session `totalTokens` from real usage instead of stale prior values. (#34275) thanks @RealKai42.
|
- Agents/session usage tracking: preserve accumulated usage metadata on embedded Pi runner error exits so failed turns still update session `totalTokens` from real usage instead of stale prior values. (#34275) thanks @RealKai42.
|
||||||
|
- Slack/reaction thread context routing: carry Slack native DM channel IDs through inbound context and threading tool resolution so reaction targets resolve consistently for DM `To=user:*` sessions (including `toolContext.currentChannelId` fallback behavior). (from #34831; overlaps #34440, #34502, #34483, #32754) Thanks @dunamismax.
|
||||||
- Nodes/system.run approval hardening: use explicit argv-mutation signaling when regenerating prepared `rawCommand`, and cover the `system.run.prepare -> system.run` handoff so direct PATH-based `nodes.run` commands no longer fail with `rawCommand does not match command`. (#33137) thanks @Sid-Qin.
|
- Nodes/system.run approval hardening: use explicit argv-mutation signaling when regenerating prepared `rawCommand`, and cover the `system.run.prepare -> system.run` handoff so direct PATH-based `nodes.run` commands no longer fail with `rawCommand does not match command`. (#33137) thanks @Sid-Qin.
|
||||||
- Models/custom provider headers: propagate `models.providers.<name>.headers` across inline, fallback, and registry-found model resolution so header-authenticated proxies consistently receive configured request headers. (#27490) thanks @Sid-Qin.
|
- Models/custom provider headers: propagate `models.providers.<name>.headers` across inline, fallback, and registry-found model resolution so header-authenticated proxies consistently receive configured request headers. (#27490) thanks @Sid-Qin.
|
||||||
- Ollama/custom provider headers: forward resolved model headers into native Ollama stream requests so header-authenticated Ollama proxies receive configured request headers. (#24337) thanks @echoVic.
|
- Ollama/custom provider headers: forward resolved model headers into native Ollama stream requests so header-authenticated Ollama proxies receive configured request headers. (#24337) thanks @echoVic.
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export function buildThreadingToolContext(params: {
|
|||||||
ReplyToId: sessionCtx.ReplyToId,
|
ReplyToId: sessionCtx.ReplyToId,
|
||||||
ThreadLabel: sessionCtx.ThreadLabel,
|
ThreadLabel: sessionCtx.ThreadLabel,
|
||||||
MessageThreadId: sessionCtx.MessageThreadId,
|
MessageThreadId: sessionCtx.MessageThreadId,
|
||||||
|
NativeChannelId: sessionCtx.NativeChannelId,
|
||||||
},
|
},
|
||||||
hasRepliedRef,
|
hasRepliedRef,
|
||||||
}) ?? {};
|
}) ?? {};
|
||||||
|
|||||||
@@ -142,6 +142,8 @@ export type MsgContext = {
|
|||||||
GatewayClientScopes?: string[];
|
GatewayClientScopes?: string[];
|
||||||
/** Thread identifier (Telegram topic id or Matrix thread event id). */
|
/** Thread identifier (Telegram topic id or Matrix thread event id). */
|
||||||
MessageThreadId?: string | number;
|
MessageThreadId?: string | number;
|
||||||
|
/** Platform-native channel/conversation id (e.g. Slack DM channel "D…" id). */
|
||||||
|
NativeChannelId?: string;
|
||||||
/** Telegram forum supergroup marker. */
|
/** Telegram forum supergroup marker. */
|
||||||
IsForum?: boolean;
|
IsForum?: boolean;
|
||||||
/** Warning: DM has topics enabled but this message is not in a topic. */
|
/** Warning: DM has topics enabled but this message is not in a topic. */
|
||||||
|
|||||||
@@ -257,6 +257,8 @@ export type ChannelThreadingContext = {
|
|||||||
ReplyToIdFull?: string;
|
ReplyToIdFull?: string;
|
||||||
ThreadLabel?: string;
|
ThreadLabel?: string;
|
||||||
MessageThreadId?: string | number;
|
MessageThreadId?: string | number;
|
||||||
|
/** Platform-native channel/conversation id (e.g. Slack DM channel "D…" id). */
|
||||||
|
NativeChannelId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ChannelThreadingToolContext = {
|
export type ChannelThreadingToolContext = {
|
||||||
|
|||||||
@@ -727,6 +727,7 @@ export async function prepareSlackMessage(params: {
|
|||||||
CommandAuthorized: commandAuthorized,
|
CommandAuthorized: commandAuthorized,
|
||||||
OriginatingChannel: "slack" as const,
|
OriginatingChannel: "slack" as const,
|
||||||
OriginatingTo: slackTo,
|
OriginatingTo: slackTo,
|
||||||
|
NativeChannelId: message.channel,
|
||||||
}) satisfies FinalizedMsgContext;
|
}) satisfies FinalizedMsgContext;
|
||||||
const pinnedMainDmOwner = isDirectMessage
|
const pinnedMainDmOwner = isDirectMessage
|
||||||
? resolvePinnedMainDmOwnerFromAllowlist({
|
? resolvePinnedMainDmOwnerFromAllowlist({
|
||||||
|
|||||||
@@ -144,4 +144,35 @@ describe("buildSlackThreadingToolContext", () => {
|
|||||||
});
|
});
|
||||||
expect(result.replyToMode).toBe("off");
|
expect(result.replyToMode).toBe("off");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("extracts currentChannelId from channel: prefixed To", () => {
|
||||||
|
const result = buildSlackThreadingToolContext({
|
||||||
|
cfg: emptyCfg,
|
||||||
|
accountId: null,
|
||||||
|
context: { ChatType: "channel", To: "channel:C1234ABC" },
|
||||||
|
});
|
||||||
|
expect(result.currentChannelId).toBe("C1234ABC");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses NativeChannelId for DM when To is user-prefixed", () => {
|
||||||
|
const result = buildSlackThreadingToolContext({
|
||||||
|
cfg: emptyCfg,
|
||||||
|
accountId: null,
|
||||||
|
context: {
|
||||||
|
ChatType: "direct",
|
||||||
|
To: "user:U8SUVSVGS",
|
||||||
|
NativeChannelId: "D8SRXRDNF",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(result.currentChannelId).toBe("D8SRXRDNF");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns undefined currentChannelId when neither channel: To nor NativeChannelId is set", () => {
|
||||||
|
const result = buildSlackThreadingToolContext({
|
||||||
|
cfg: emptyCfg,
|
||||||
|
accountId: null,
|
||||||
|
context: { ChatType: "direct", To: "user:U8SUVSVGS" },
|
||||||
|
});
|
||||||
|
expect(result.currentChannelId).toBeUndefined();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,10 +19,14 @@ export function buildSlackThreadingToolContext(params: {
|
|||||||
const hasExplicitThreadTarget = params.context.MessageThreadId != null;
|
const hasExplicitThreadTarget = params.context.MessageThreadId != null;
|
||||||
const effectiveReplyToMode = hasExplicitThreadTarget ? "all" : configuredReplyToMode;
|
const effectiveReplyToMode = hasExplicitThreadTarget ? "all" : configuredReplyToMode;
|
||||||
const threadId = params.context.MessageThreadId ?? params.context.ReplyToId;
|
const threadId = params.context.MessageThreadId ?? params.context.ReplyToId;
|
||||||
|
// For channel messages, To is "channel:C…" — extract the bare ID.
|
||||||
|
// For DMs, To is "user:U…" which can't be used for reactions; fall back
|
||||||
|
// to NativeChannelId (the raw Slack channel id, e.g. "D…").
|
||||||
|
const currentChannelId = params.context.To?.startsWith("channel:")
|
||||||
|
? params.context.To.slice("channel:".length)
|
||||||
|
: params.context.NativeChannelId?.trim() || undefined;
|
||||||
return {
|
return {
|
||||||
currentChannelId: params.context.To?.startsWith("channel:")
|
currentChannelId,
|
||||||
? params.context.To.slice("channel:".length)
|
|
||||||
: undefined,
|
|
||||||
currentThreadTs: threadId != null ? String(threadId) : undefined,
|
currentThreadTs: threadId != null ? String(threadId) : undefined,
|
||||||
replyToMode: effectiveReplyToMode,
|
replyToMode: effectiveReplyToMode,
|
||||||
hasRepliedRef: params.hasRepliedRef,
|
hasRepliedRef: params.hasRepliedRef,
|
||||||
|
|||||||
Reference in New Issue
Block a user