diff --git a/config/assertion-safety-baseline.txt b/config/assertion-safety-baseline.txt index 9c81ddadcc35..28e149715c38 100644 --- a/config/assertion-safety-baseline.txt +++ b/config/assertion-safety-baseline.txt @@ -436,7 +436,6 @@ extensions/discord/src/send.shared.ts 4 extensions/discord/src/send.webhook.ts 1 extensions/discord/src/setup-account-state.ts 1 extensions/discord/src/setup-core.ts 1 -extensions/discord/src/shared-interactive.ts 1 extensions/discord/src/shared.ts 1 extensions/discord/src/status-issues.ts 1 extensions/discord/src/target-parsing.ts 1 diff --git a/extensions/discord/src/shared-interactive.test.ts b/extensions/discord/src/shared-interactive.test.ts index 642627a6d6df..5eed7ac26003 100644 --- a/extensions/discord/src/shared-interactive.test.ts +++ b/extensions/discord/src/shared-interactive.test.ts @@ -98,6 +98,59 @@ describe("buildDiscordInteractiveComponents", () => { }); }); + it.each([ + { + name: "approval", + action: { + type: "approval" as const, + approvalId: "approval-1", + approvalKind: "exec" as const, + decision: "allow-once" as const, + }, + expected: { + internalCustomId: "execapproval:kind=exec;id=approval-1;action=allow-once", + }, + }, + { + name: "callback", + action: { type: "callback" as const, value: "plugin:opaque|value" }, + expected: { callbackData: "plugin:opaque|value", callbackDataKind: "callback" }, + }, + { + name: "command", + action: { type: "command" as const, command: "/codex inspect" }, + expected: { callbackData: "/codex inspect", callbackDataKind: "command" }, + }, + { + name: "question", + action: { + type: "question" as const, + questionId: "ask_0123456789abcdef0123456789abcdef", + optionValue: "Production", + }, + expected: { internalCustomId: "ocq:id=ask_0123456789abcdef0123456789abcdef;i=1" }, + }, + ])("preserves typed $name authority through the legacy renderer", ({ action, expected }) => { + expect( + buildDiscordInteractiveComponents({ + blocks: [ + { + type: "buttons", + buttons: [ + { + label: "Unavailable", + action: { type: "web-app", widgetId: "invalid" }, + }, + { label: "Continue", action }, + ], + }, + ], + }), + ).toMatchObject({ + blocks: [{ type: "actions", buttons: [{ label: "Continue", ...expected }] }], + }); + }); + it.each(["url", "web-app"] as const)( "renders typed %s actions as Discord link buttons", (type) => { diff --git a/extensions/discord/src/shared-interactive.ts b/extensions/discord/src/shared-interactive.ts index a6ce85a64caa..d7fa5ba9fdb6 100644 --- a/extensions/discord/src/shared-interactive.ts +++ b/extensions/discord/src/shared-interactive.ts @@ -1,6 +1,6 @@ // Discord plugin module implements shared interactive behavior. import { - reduceLegacyInteractiveReply, + legacyInteractiveReplyToPresentation, resolveMessagePresentationActionValue, resolveMessagePresentationButtonAction, resolveMessagePresentationOptionAction, @@ -180,28 +180,9 @@ function appendDiscordSelectBlock( export function buildDiscordInteractiveComponents( interactive?: LegacyInteractiveReply, ): DiscordComponentMessageSpec | undefined { - const blocks = reduceLegacyInteractiveReply( - interactive, - [] as NonNullable, - (state, block) => { - if (block.type === "text") { - const text = block.text.trim(); - if (text) { - state.push({ type: "text", text }); - } - return state; - } - if (block.type === "buttons") { - appendDiscordButtonBlocks(state, block.buttons); - return state; - } - if (block.type === "select") { - appendDiscordSelectBlock(state, block); - } - return state; - }, + return buildDiscordPresentationComponents( + interactive ? legacyInteractiveReplyToPresentation(interactive) : undefined, ); - return blocks.length > 0 ? { blocks } : undefined; } export function buildDiscordPresentationComponents(