mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-03 16:41:10 +00:00
fix(slack): enable preview streaming in flat DMs (replyToMode: off) (#76330)
Summary: - The PR enables Slack draft preview streaming for flat DMs in all non-off modes, updates Slack streaming tests/docs/config metadata/changelog, and refreshes small guard baselines. - Reproducibility: yes. source-reproducible on current main: the helper returns false for `mode: "partial"` in ... current test asserts that disabled path. I did not run tests because this review was explicitly read-only. Automerge notes: - Ran the ClawSweeper repair loop before final review. - Included post-review commit in the final squash: fix(slack): enable preview streaming in flat DMs (replyToMode: off) - Included post-review commit in the final squash: fix(clawsweeper): address review for automerge-openclaw-openclaw-5654… Validation: - ClawSweeper review passed for head52e5d74ef9. - Required merge gates passed before the squash merge. Prepared head SHA:52e5d74ef9Review: https://github.com/openclaw/openclaw/pull/76330#issuecomment-4365017023 Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: bob <637186+HangGlidersRule@users.noreply.github.com>
This commit is contained in:
@@ -66,6 +66,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Gateway/status: add concrete service, config, listener-owner, and log collection next steps when gateway probes fail and Bonjour finds no local gateway, so frozen or port-conflict reports include the data needed for root-cause triage. Refs #49012. Thanks @vincentkoc.
|
||||
- Codex harness: forward OpenClaw workspace bootstrap files such as `SOUL.md` through native Codex config instructions while leaving `AGENTS.md` to Codex project-doc discovery. Fixes #76273. Thanks @zknicker.
|
||||
- Parallels/Windows update smoke: escape the stale post-swap import regex in the generated PowerShell script so expected `ERR_MODULE_NOT_FOUND` update handoffs continue to post-update health checks. (#75315)
|
||||
- Slack: allow draft preview streaming in top-level DMs when `replyToMode` is `off` while keeping Slack native streaming and assistant thread status gated on reply threads. Fixes #56480. (#56544) Thanks @HangGlidersRule.
|
||||
|
||||
## 2026.5.2
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
27444d825e0dda91c769267beb81b3826ca42fd9c2e16d4d6bedccbc5cbccba4 config-baseline.json
|
||||
bb58618ecdd5cb16d985eafa4a3a4634f366e6674f3db16c246336e6e02a3706 config-baseline.json
|
||||
b95cc4a3b15e688afa58cf700dac425af4e50fd092209e75903e73d5d2988717 config-baseline.core.json
|
||||
07005d122bda68081d530ae5cd455ffbc0817f7fb94bd462c4f25edc94a0546c config-baseline.channel.json
|
||||
f2a1aad257c570b497865680c331568a6775369528749826dfa35c1f644483fc config-baseline.channel.json
|
||||
fffe0e74eab92a88c3c57952a70bc932438ce3a7f5f9982688437f2cdaee0bcb config-baseline.plugin.json
|
||||
|
||||
@@ -648,8 +648,8 @@ Notes:
|
||||
`channels.slack.streaming.nativeTransport` controls Slack native text streaming when `channels.slack.streaming.mode` is `partial` (default: `true`).
|
||||
|
||||
- A reply thread must be available for native text streaming and Slack assistant thread status to appear. Thread selection still follows `replyToMode`.
|
||||
- Channel and group-chat roots can still use the normal draft preview when native streaming is unavailable.
|
||||
- Top-level Slack DMs stay off-thread by default, so they do not show the thread-style preview; use thread replies or `typingReaction` if you want visible progress there.
|
||||
- Channel, group-chat, and top-level DM roots can still use the normal draft preview when native streaming is unavailable or no reply thread exists.
|
||||
- Top-level Slack DMs stay off-thread by default, so they do not show Slack's thread-style native stream/status preview; OpenClaw posts and edits a draft preview in the DM instead.
|
||||
- Media and non-text payloads fall back to normal delivery.
|
||||
- Media/error finals cancel pending preview edits; eligible text/block finals flush only when they can edit the preview in place.
|
||||
- If streaming fails mid-reply, OpenClaw falls back to normal delivery for remaining payloads.
|
||||
|
||||
@@ -139,7 +139,7 @@ Modes:
|
||||
Slack-only:
|
||||
|
||||
- `channels.slack.streaming.nativeTransport` toggles Slack native streaming API calls when `channels.slack.streaming.mode="partial"` (default: `true`).
|
||||
- Slack native streaming and Slack assistant thread status require a reply thread target; top-level DMs do not show that thread-style preview.
|
||||
- Slack native streaming and Slack assistant thread status require a reply thread target. Top-level DMs do not show that thread-style preview, but they can still use Slack draft preview posts and edits.
|
||||
|
||||
Legacy key migration:
|
||||
|
||||
@@ -168,6 +168,7 @@ Slack:
|
||||
- `partial` can use Slack native streaming (`chat.startStream`/`append`/`stop`) when available.
|
||||
- `block` uses append-style draft previews.
|
||||
- `progress` uses status preview text, then final answer.
|
||||
- Top-level DMs without a reply thread use draft preview posts and edits instead of Slack native streaming.
|
||||
- Native and draft preview streaming suppress block replies for that turn, so a Slack reply is streamed by one delivery path only.
|
||||
- Final media/error payloads and progress finals do not create throwaway draft messages; only text/block finals that can edit the preview flush pending draft text.
|
||||
|
||||
|
||||
@@ -484,7 +484,7 @@ WhatsApp runs through the gateway's web channel (Baileys Web). It starts automat
|
||||
|
||||
**Thread session isolation:** `thread.historyScope` is per-thread (default) or shared across channel. `thread.inheritParent` copies parent channel transcript to new threads.
|
||||
|
||||
- Slack native streaming plus the Slack assistant-style "is typing..." thread status require a reply thread target. Top-level DMs stay off-thread by default, so they use `typingReaction` or normal delivery instead of the thread-style preview.
|
||||
- Slack native streaming plus the Slack assistant-style "is typing..." thread status require a reply thread target. Top-level DMs stay off-thread by default, so they can still stream through Slack draft post-and-edit previews instead of showing the thread-style native stream/status preview.
|
||||
- `typingReaction` adds a temporary reaction to the inbound Slack message while a reply is running, then removes it on completion. Use a Slack emoji shortcode such as `"hourglass_flowing_sand"`.
|
||||
- `channels.slack.execApprovals`: Slack-native exec approval delivery and approver authorization. Same schema as Discord: `enabled` (`true`/`false`/`"auto"`), `approvers` (Slack user IDs), `agentFilter`, `sessionFilter`, and `target` (`"dm"`, `"channel"`, or `"both"`).
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ export const slackChannelConfigUiHints = {
|
||||
},
|
||||
"streaming.nativeTransport": {
|
||||
label: "Slack Native Streaming",
|
||||
help: "Enable native Slack text streaming (chat.startStream/chat.appendStream/chat.stopStream) when channels.slack.streaming.mode is partial (default: true). Requires a reply thread target; top-level DMs stay on the non-thread fallback path.",
|
||||
help: "Enable native Slack text streaming (chat.startStream/chat.appendStream/chat.stopStream) when channels.slack.streaming.mode is partial (default: true). Native streaming and Slack assistant thread status require a reply thread target; top-level DMs can still use draft post-and-edit preview streaming.",
|
||||
},
|
||||
"streaming.preview.toolProgress": {
|
||||
label: "Slack Draft Tool Progress",
|
||||
|
||||
@@ -179,35 +179,44 @@ describe("slack native streaming thread hint", () => {
|
||||
});
|
||||
|
||||
describe("slack preview streaming eligibility", () => {
|
||||
it("stays on for room messages when streaming mode is enabled", () => {
|
||||
it("stays off when streaming mode is disabled", () => {
|
||||
expect(
|
||||
shouldEnableSlackPreviewStreaming({
|
||||
mode: "partial",
|
||||
isDirectMessage: false,
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("stays off for top-level DMs without a reply thread", () => {
|
||||
expect(
|
||||
shouldEnableSlackPreviewStreaming({
|
||||
mode: "partial",
|
||||
isDirectMessage: true,
|
||||
mode: "off",
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("allows DM preview when the reply is threaded", () => {
|
||||
it("stays on for room messages when streaming mode is enabled", () => {
|
||||
expect(
|
||||
shouldEnableSlackPreviewStreaming({
|
||||
mode: "partial",
|
||||
isDirectMessage: true,
|
||||
threadTs: "1000.1",
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps top-level DMs off even when replyToMode would create a reply thread", () => {
|
||||
it("allows top-level DM draft previews without a reply thread", () => {
|
||||
expect(
|
||||
shouldEnableSlackPreviewStreaming({
|
||||
mode: "partial",
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("allows non-partial draft preview modes", () => {
|
||||
expect(
|
||||
shouldEnableSlackPreviewStreaming({
|
||||
mode: "block",
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldEnableSlackPreviewStreaming({
|
||||
mode: "progress",
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps native streaming thread hints separate from draft preview eligibility", () => {
|
||||
const streamThreadHint = resolveSlackStreamingThreadHint({
|
||||
replyToMode: "all",
|
||||
incomingThreadTs: undefined,
|
||||
@@ -218,10 +227,8 @@ describe("slack preview streaming eligibility", () => {
|
||||
expect(
|
||||
shouldEnableSlackPreviewStreaming({
|
||||
mode: "partial",
|
||||
isDirectMessage: true,
|
||||
threadTs: undefined,
|
||||
}),
|
||||
).toBe(false);
|
||||
).toBe(true);
|
||||
expect(streamThreadHint).toBe("1000.4");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -117,16 +117,8 @@ export function isSlackStreamingEnabled(params: {
|
||||
|
||||
export function shouldEnableSlackPreviewStreaming(params: {
|
||||
mode: "off" | "partial" | "block" | "progress";
|
||||
isDirectMessage: boolean;
|
||||
threadTs?: string;
|
||||
}): boolean {
|
||||
if (params.mode === "off") {
|
||||
return false;
|
||||
}
|
||||
if (!params.isDirectMessage) {
|
||||
return true;
|
||||
}
|
||||
return Boolean(params.threadTs);
|
||||
return params.mode !== "off";
|
||||
}
|
||||
|
||||
export function shouldInitializeSlackDraftStream(params: {
|
||||
@@ -446,8 +438,6 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
!sourceRepliesAreToolOnly &&
|
||||
shouldEnableSlackPreviewStreaming({
|
||||
mode: slackStreaming.mode,
|
||||
isDirectMessage: prepared.isDirectMessage,
|
||||
threadTs: streamThreadHint,
|
||||
});
|
||||
const streamingEnabled =
|
||||
!sourceRepliesAreToolOnly &&
|
||||
|
||||
@@ -18,8 +18,8 @@ const allowedRawFetchCallsites = new Set([
|
||||
bundledPluginCallsite("bluebubbles", "src/types.ts", 204),
|
||||
bundledPluginCallsite("browser", "src/browser/cdp.helpers.ts", 268),
|
||||
bundledPluginCallsite("browser", "src/browser/client-fetch.ts", 192),
|
||||
bundledPluginCallsite("chutes", "models.ts", 535),
|
||||
bundledPluginCallsite("chutes", "models.ts", 542),
|
||||
bundledPluginCallsite("chutes", "models.ts", 536),
|
||||
bundledPluginCallsite("chutes", "models.ts", 543),
|
||||
bundledPluginCallsite("discord", "src/monitor/gateway-plugin.ts", 417),
|
||||
bundledPluginCallsite("discord", "src/monitor/gateway-plugin.ts", 483),
|
||||
bundledPluginCallsite("discord", "src/voice-message.ts", 298),
|
||||
@@ -30,7 +30,7 @@ const allowedRawFetchCallsites = new Set([
|
||||
bundledPluginCallsite("github-copilot", "login.ts", 69),
|
||||
bundledPluginCallsite("github-copilot", "login.ts", 101),
|
||||
bundledPluginCallsite("googlechat", "src/auth.ts", 83),
|
||||
bundledPluginCallsite("huggingface", "models.ts", 142),
|
||||
bundledPluginCallsite("huggingface", "models.ts", 143),
|
||||
bundledPluginCallsite("kilocode", "provider-models.ts", 130),
|
||||
bundledPluginCallsite("matrix", "src/matrix/sdk/transport.ts", 112),
|
||||
bundledPluginCallsite("microsoft-foundry", "onboard.ts", 479),
|
||||
|
||||
@@ -13313,7 +13313,7 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
||||
},
|
||||
"streaming.nativeTransport": {
|
||||
label: "Slack Native Streaming",
|
||||
help: "Enable native Slack text streaming (chat.startStream/chat.appendStream/chat.stopStream) when channels.slack.streaming.mode is partial (default: true). Requires a reply thread target; top-level DMs stay on the non-thread fallback path.",
|
||||
help: "Enable native Slack text streaming (chat.startStream/chat.appendStream/chat.stopStream) when channels.slack.streaming.mode is partial (default: true). Native streaming and Slack assistant thread status require a reply thread target; top-level DMs can still use draft post-and-edit preview streaming.",
|
||||
},
|
||||
"streaming.preview.toolProgress": {
|
||||
label: "Slack Draft Tool Progress",
|
||||
|
||||
@@ -25,6 +25,11 @@ const INDIRECT_RUNTIME_DEPENDENCIES = new Map<string, Set<string>>([
|
||||
// Baileys loads jimp as an optional peer when it needs media thumbnails.
|
||||
new Set(["jimp"]),
|
||||
],
|
||||
[
|
||||
"extensions/tlon",
|
||||
// The Tlon plugin manifest exposes the bundled skill from this package path.
|
||||
new Set(["@tloncorp/tlon-skill"]),
|
||||
],
|
||||
]);
|
||||
|
||||
type PackageManifest = {
|
||||
|
||||
Reference in New Issue
Block a user