diff --git a/src/channels/plugins/contracts/inbound.contract.test.ts b/src/channels/plugins/contracts/inbound.contract.test.ts index 9fa108bcb72..9b03c93ec77 100644 --- a/src/channels/plugins/contracts/inbound.contract.test.ts +++ b/src/channels/plugins/contracts/inbound.contract.test.ts @@ -1,5 +1,4 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { buildDiscordInboundAccessContext } from "../../../../extensions/discord/src/monitor/inbound-context.js"; import type { ResolvedSlackAccount } from "../../../../extensions/slack/src/accounts.js"; import type { SlackMessageEvent } from "../../../../extensions/slack/src/types.js"; import type { MsgContext } from "../../../auto-reply/templating.js"; @@ -73,14 +72,6 @@ vi.mock("../../../../extensions/whatsapp/src/auto-reply/deliver-reply.js", () => deliverWebReply: vi.fn(async () => {}), })); -const { finalizeInboundContext } = await import("../../../auto-reply/reply/inbound-context.js"); -const { prepareSlackMessage } = - await import("../../../../extensions/slack/src/monitor/message-handler/prepare.js"); -const { createInboundSlackTestContext } = - await import("../../../../extensions/slack/src/monitor/message-handler/prepare.test-helpers.js"); -const { buildTelegramMessageContextForTest } = - await import("../../../../extensions/telegram/src/bot-message-context.test-harness.js"); - function createSlackAccount(config: ResolvedSlackAccount["config"] = {}): ResolvedSlackAccount { return { accountId: "default", @@ -112,45 +103,25 @@ describe("channel inbound contract", () => { dispatchInboundMessageMock.mockClear(); }); - it("keeps Discord inbound context finalized", () => { - const { groupSystemPrompt, ownerAllowFrom, untrustedContext } = - buildDiscordInboundAccessContext({ - channelConfig: null, - guildInfo: null, - sender: { id: "U1", name: "Alice", tag: "alice" }, - isGuild: false, - }); - - const ctx = finalizeInboundContext({ - Body: "hi", - BodyForAgent: "hi", - RawBody: "hi", - CommandBody: "hi", - From: "discord:U1", - To: "user:U1", - SessionKey: "agent:main:discord:direct:u1", - AccountId: "default", - ChatType: "direct", - ConversationLabel: "Alice", - SenderName: "Alice", - SenderId: "U1", - SenderUsername: "alice", - GroupSystemPrompt: groupSystemPrompt, - OwnerAllowFrom: ownerAllowFrom, - UntrustedContext: untrustedContext, - Provider: "discord", - Surface: "discord", - WasMentioned: false, - MessageSid: "m1", - CommandAuthorized: true, - OriginatingChannel: "discord", - OriginatingTo: "user:U1", + it("keeps Discord inbound context finalized", async () => { + const { processDiscordMessage } = + await import("../../../../extensions/discord/src/monitor/message-handler.process.js"); + const { createBaseDiscordMessageContext, createDiscordDirectMessageContextOverrides } = + await import("../../../../extensions/discord/src/monitor/message-handler.test-harness.js"); + const messageCtx = await createBaseDiscordMessageContext({ + cfg: { messages: {} }, + ackReactionScope: "direct", + ...createDiscordDirectMessageContextOverrides(), }); - expectChannelInboundContextContract(ctx); + await processDiscordMessage(messageCtx); + + expect(inboundCtxCapture.ctx).toBeTruthy(); + expectChannelInboundContextContract(inboundCtxCapture.ctx!); }); it("keeps Signal inbound context finalized", async () => { + const { finalizeInboundContext } = await import("../../../auto-reply/reply/inbound-context.js"); const ctx = finalizeInboundContext({ Body: "Alice: hi", BodyForAgent: "hi", @@ -178,6 +149,10 @@ describe("channel inbound contract", () => { }); it("keeps Slack inbound context finalized", async () => { + const { prepareSlackMessage } = + await import("../../../../extensions/slack/src/monitor/message-handler/prepare.js"); + const { createInboundSlackTestContext } = + await import("../../../../extensions/slack/src/monitor/message-handler/prepare.test-helpers.js"); const ctx = createInboundSlackTestContext({ cfg: { channels: { slack: { enabled: true } }, @@ -198,6 +173,8 @@ describe("channel inbound contract", () => { }); it("keeps Telegram inbound context finalized", async () => { + const { buildTelegramMessageContextForTest } = + await import("../../../../extensions/telegram/src/bot-message-context.test-harness.js"); const context = await buildTelegramMessageContextForTest({ cfg: { agents: { @@ -232,6 +209,7 @@ describe("channel inbound contract", () => { }); it("keeps WhatsApp inbound context finalized", async () => { + const { finalizeInboundContext } = await import("../../../auto-reply/reply/inbound-context.js"); const ctx = finalizeInboundContext({ Body: "Alice: hi", BodyForAgent: "hi", diff --git a/src/channels/plugins/contracts/registry.ts b/src/channels/plugins/contracts/registry.ts index cf12d4f4355..1dddc70a62a 100644 --- a/src/channels/plugins/contracts/registry.ts +++ b/src/channels/plugins/contracts/registry.ts @@ -196,7 +196,8 @@ bundledChannelRuntimeSetters.setTelegramRuntime({ channel: { telegram: { messageActions: { - describeMessageTool: telegramDescribeMessageToolMock, + listActions: telegramListActionsMock, + getCapabilities: telegramGetCapabilitiesMock, }, }, }, @@ -206,7 +207,8 @@ bundledChannelRuntimeSetters.setDiscordRuntime({ channel: { discord: { messageActions: { - describeMessageTool: discordDescribeMessageToolMock, + listActions: discordListActionsMock, + getCapabilities: discordGetCapabilitiesMock, }, }, }, @@ -405,11 +407,10 @@ export const actionContractRegistry: ActionsContractEntry[] = [ expectedActions: ["send", "poll", "react"], expectedCapabilities: ["interactive", "buttons"], beforeTest: () => { - telegramDescribeMessageToolMock.mockReset(); - telegramDescribeMessageToolMock.mockReturnValue({ - actions: ["send", "poll", "react"], - capabilities: ["interactive", "buttons"], - }); + telegramListActionsMock.mockReset(); + telegramGetCapabilitiesMock.mockReset(); + telegramListActionsMock.mockReturnValue(["send", "poll", "react"]); + telegramGetCapabilitiesMock.mockReturnValue(["interactive", "buttons"]); }, }, ], @@ -424,11 +425,10 @@ export const actionContractRegistry: ActionsContractEntry[] = [ expectedActions: ["send", "react", "poll"], expectedCapabilities: ["interactive", "components"], beforeTest: () => { - discordDescribeMessageToolMock.mockReset(); - discordDescribeMessageToolMock.mockReturnValue({ - actions: ["send", "react", "poll"], - capabilities: ["interactive", "components"], - }); + discordListActionsMock.mockReset(); + discordGetCapabilitiesMock.mockReset(); + discordListActionsMock.mockReturnValue(["send", "react", "poll"]); + discordGetCapabilitiesMock.mockReturnValue(["interactive", "components"]); }, }, ], diff --git a/ui/src/ui/markdown.ts b/ui/src/ui/markdown.ts index 89788059de4..12e813c7204 100644 --- a/ui/src/ui/markdown.ts +++ b/ui/src/ui/markdown.ts @@ -186,7 +186,7 @@ htmlEscapeRenderer.code = ({ escaped?: boolean; }) => { const normalizedLang = lang?.trim().toLowerCase() ?? ""; - const langClass = lang ? ` class="language-${escapeHtml(lang)}"` : ""; + const langClass = normalizedLang ? ` class="language-${escapeHtml(normalizedLang)}"` : ""; const safeText = escaped ? text : escapeHtml(text); const codeBlock = `
${safeText}`;
const langLabel = lang ? `${escapeHtml(lang)}` : "";
diff --git a/ui/src/ui/mermaid.ts b/ui/src/ui/mermaid.ts
index 1271ce88667..7178a57ed2a 100644
--- a/ui/src/ui/mermaid.ts
+++ b/ui/src/ui/mermaid.ts
@@ -1,3 +1,5 @@
+import DOMPurify from "dompurify";
+
type MermaidApi = {
initialize: (config: Record