fix(slack): preserve actual multipart delivery receipt kinds (#129089)

This commit is contained in:
Peter Steinberger
2026-08-25 00:21:23 -07:00
committed by GitHub
parent 0415c403a7
commit d2ac0e3b6f
3 changed files with 55 additions and 23 deletions
+13
View File
@@ -429,6 +429,19 @@ describe("sendMessageSlack blocks", () => {
expect(
delivered.some((result) => result.receipt.parts[0]?.kind === "card" && !result.meta),
).toBe(true);
expect(
aggregateResult.receipt.parts.map(({ platformMessageId, kind, index }) => ({
platformMessageId,
kind,
index,
})),
).toEqual(
delivered.map((result, index) => ({
platformMessageId: result.messageId,
kind: result.receipt.parts[0]?.kind,
index,
})),
);
const questionDelivery = delivered.find((delivery) => delivery.meta);
expect(questionDelivery?.messageId).not.toBe(aggregateResult.messageId);
expect(JSON.stringify(aggregateResult.meta)).toBe(
+19 -22
View File
@@ -339,6 +339,18 @@ function createSlackSendReceipt(params: {
});
}
function createSlackSendReceiptFromResults(
results: readonly SlackSendResult[],
threadTs?: string,
): MessageReceipt {
const receipt = createMessageReceiptFromOutboundResults({ results, threadId: threadTs });
const thread = threadTs ? { threadId: threadTs } : {};
for (const [index, part] of receipt.parts.entries()) {
Object.assign(part, { index, ...thread });
}
return Object.assign(receipt, thread);
}
function resolveToken(params: {
explicit?: string;
accountId: string;
@@ -1134,6 +1146,7 @@ async function sendMessageSlackQueuedInner(params: {
accountId: account.accountId,
token: delivery.credential,
});
const deliveredResults: SlackSendResult[] = [];
const reportDelivery = async (
result: SlackSendResult,
deliveredBlocks?: (Block | KnownBlock)[],
@@ -1154,6 +1167,7 @@ async function sendMessageSlackQueuedInner(params: {
},
}
: result;
deliveredResults.push(deliveryResult);
await opts.onDeliveryResult?.(deliveryResult);
return deliveryResult;
};
@@ -1213,7 +1227,6 @@ async function sendMessageSlackQueuedInner(params: {
orderedBlockDeliveryPlan?.skipOriginalBlocks
? orderedBlockDeliveryPlan
: undefined;
const sentMessageIds: string[] = [];
let lastMessageId = "";
let deliveredChannelId = channelId;
let canonicalDeliveredThreadTs: string | undefined;
@@ -1310,7 +1323,6 @@ async function sendMessageSlackQueuedInner(params: {
lastMessageId = response.ts;
deliveredChannelId = resolvePostedMessageChannelId(response, deliveredChannelId);
canonicalDeliveredThreadTs ??= resolvePostedMessageThreadTs(response);
sentMessageIds.push(response.ts);
const deliveredThreadTs =
resolvePostedMessageThreadTs(response) ?? normalizeSlackThreadTsCandidate(opts.threadTs);
const fallbackDelivery = await reportDelivery(
@@ -1331,11 +1343,10 @@ async function sendMessageSlackQueuedInner(params: {
questionDelivery = fallbackDelivery;
}
}
const messageId = lastMessageId;
const deliveredThreadTs =
canonicalDeliveredThreadTs ?? normalizeSlackThreadTsCandidate(opts.threadTs);
return {
messageId,
messageId: lastMessageId,
channelId: deliveredChannelId,
threadTs: deliveredThreadTs,
// Core replaces per-card progress with this aggregate; retain the
@@ -1348,12 +1359,7 @@ async function sendMessageSlackQueuedInner(params: {
},
}
: {}),
receipt: createSlackSendReceipt({
platformMessageIds: sentMessageIds,
channelId: deliveredChannelId,
kind: fallbackMessages.some((message) => message.blocks) ? "card" : "text",
threadTs: deliveredThreadTs,
}),
receipt: createSlackSendReceiptFromResults(deliveredResults, deliveredThreadTs),
};
}
}
@@ -1391,7 +1397,6 @@ async function sendMessageSlackQueuedInner(params: {
onPlatformSendDispatch: dispatchOnce,
...(delivery.upload ? { auditContext: delivery.upload.auditContext } : {}),
});
sentMessageIds.push(lastMessageId);
await reportDelivery({
messageId: lastMessageId,
channelId,
@@ -1409,8 +1414,7 @@ async function sendMessageSlackQueuedInner(params: {
}
for (const [partIndex, chunk] of chunksToPost.entries()) {
const carriesPrimaryMessageOptions =
partIndex === 0 && !opts.mediaUrl && sentMessageIds.length === 0;
const carriesPrimaryMessageOptions = partIndex === 0 && !opts.mediaUrl;
const baseMetadata = carriesPrimaryMessageOptions ? opts.metadata : undefined;
// Every post carries its index/count so reconciliation proves the complete
// logical text send and never mistakes a partial chunk fanout for success.
@@ -1442,7 +1446,6 @@ async function sendMessageSlackQueuedInner(params: {
lastMessageId = response.ts;
deliveredChannelId = resolvePostedMessageChannelId(response, deliveredChannelId);
canonicalDeliveredThreadTs ??= resolvePostedMessageThreadTs(response);
sentMessageIds.push(response.ts);
await reportDelivery({
messageId: response.ts,
channelId: deliveredChannelId,
@@ -1458,19 +1461,13 @@ async function sendMessageSlackQueuedInner(params: {
});
}
const messageId = lastMessageId;
const deliveredThreadTs =
canonicalDeliveredThreadTs ?? normalizeSlackThreadTsCandidate(opts.threadTs);
return {
messageId,
messageId: lastMessageId,
channelId: deliveredChannelId,
threadTs: deliveredThreadTs,
receipt: createSlackSendReceipt({
platformMessageIds: sentMessageIds,
channelId: deliveredChannelId,
kind: opts.mediaUrl ? "media" : "text",
threadTs: deliveredThreadTs,
}),
receipt: createSlackSendReceiptFromResults(deliveredResults, deliveredThreadTs),
};
}
/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */
+23 -1
View File
@@ -579,6 +579,7 @@ describe("sendMessageSlack file upload with user IDs", () => {
vi.stubEnv("NO_PROXY", "127.0.0.1,localhost");
vi.stubEnv("no_proxy", "127.0.0.1,localhost");
const alternateClient = createUploadTestClient(`${baseUrl}/api/`);
const onDeliveryResult = vi.fn();
mockUploadDestination(alternateClient, `${baseUrl}/upload/v1/capability`);
fetchWithSsrFGuard.mockImplementationOnce(async (params) => {
const mockedFetch = globalThis.fetch;
@@ -590,9 +591,30 @@ describe("sendMessageSlack file upload with user IDs", () => {
}
});
await sendUpload(alternateClient, { mediaUrl: "/tmp/alternate-root.png" });
const result = await sendUpload(alternateClient, {
mediaUrl: "/tmp/alternate-root.png",
message: "a".repeat(8_500),
threadTs: "171.222",
onDeliveryResult,
});
expectCompletedUpload({ client: alternateClient, expected: { channel_id: "C123CHAN" } });
expect(alternateClient.chat.postMessage).toHaveBeenCalledOnce();
expect(
onDeliveryResult.mock.calls.map(([delivery]) => delivery.receipt.parts[0]?.kind),
).toEqual(["media", "text"]);
expect(
result.receipt.parts.map(({ platformMessageId, kind, index, threadId }) => ({
platformMessageId,
kind,
index,
threadId,
})),
).toEqual([
{ platformMessageId: "F001", kind: "media", index: 0, threadId: "171.222" },
{ platformMessageId: "171234.567", kind: "text", index: 1, threadId: "171.222" },
]);
expect(result.receipt.threadId).toBe("171.222");
expect(cleanupUploadTimeout).toHaveBeenCalledOnce();
expect(uploadTimeoutControllers).toHaveLength(0);
},