feat(slack): add configurable stream modes

This commit is contained in:
Colin
2026-02-16 16:56:41 -05:00
committed by Peter Steinberger
parent 087edec93f
commit 89ce1460e1
7 changed files with 196 additions and 0 deletions

View File

@@ -11,6 +11,11 @@ import { resolveStorePath, updateLastRoute } from "../../../config/sessions.js";
import { danger, logVerbose, shouldLogVerbose } from "../../../globals.js";
import { removeSlackReaction } from "../../actions.js";
import { createSlackDraftStream } from "../../draft-stream.js";
import {
applyAppendOnlyStreamUpdate,
buildStatusFinalPreviewText,
resolveSlackStreamMode,
} from "../../stream-mode.js";
import { resolveSlackThreadTargets } from "../../threading.js";
import { createSlackReplyDeliveryPlan, deliverReplies } from "../replies.js";
@@ -112,6 +117,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
const draftChannelId = draftStream?.channelId();
const finalText = payload.text;
const canFinalizeViaPreviewEdit =
streamMode !== "status_final" &&
mediaCount === 0 &&
!payload.isError &&
typeof finalText === "string" &&
@@ -134,6 +140,21 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
`slack: preview final edit failed; falling back to standard send (${String(err)})`,
);
}
} else if (streamMode === "status_final" && hasStreamedMessage) {
try {
const statusChannelId = draftStream?.channelId();
const statusMessageId = draftStream?.messageId();
if (statusChannelId && statusMessageId) {
await ctx.app.client.chat.update({
token: ctx.botToken,
channel: statusChannelId,
ts: statusMessageId,
text: "Status: complete. Final answer posted below.",
});
}
} catch (err) {
logVerbose(`slack: status_final completion update failed (${String(err)})`);
}
} else if (mediaCount > 0) {
await draftStream?.clear();
hasStreamedMessage = false;
@@ -170,11 +191,42 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
warn: logVerbose,
});
let hasStreamedMessage = false;
const streamMode = resolveSlackStreamMode(account.config.streamMode);
let appendRenderedText = "";
let appendSourceText = "";
let statusUpdateCount = 0;
const updateDraftFromPartial = (text?: string) => {
const trimmed = text?.trimEnd();
if (!trimmed) {
return;
}
if (streamMode === "append") {
const next = applyAppendOnlyStreamUpdate({
incoming: trimmed,
rendered: appendRenderedText,
source: appendSourceText,
});
appendRenderedText = next.rendered;
appendSourceText = next.source;
if (!next.changed) {
return;
}
draftStream.update(next.rendered);
hasStreamedMessage = true;
return;
}
if (streamMode === "status_final") {
statusUpdateCount += 1;
if (statusUpdateCount > 1 && statusUpdateCount % 4 !== 0) {
return;
}
draftStream.update(buildStatusFinalPreviewText(statusUpdateCount));
hasStreamedMessage = true;
return;
}
draftStream.update(trimmed);
hasStreamedMessage = true;
};
@@ -199,12 +251,18 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
if (hasStreamedMessage) {
draftStream.forceNewMessage();
hasStreamedMessage = false;
appendRenderedText = "";
appendSourceText = "";
statusUpdateCount = 0;
}
},
onReasoningEnd: async () => {
if (hasStreamedMessage) {
draftStream.forceNewMessage();
hasStreamedMessage = false;
appendRenderedText = "";
appendSourceText = "";
statusUpdateCount = 0;
}
},
},