diff --git a/extensions/msteams/src/monitor.conversation-allowlist.lifecycle.test.ts b/extensions/msteams/src/monitor.conversation-allowlist.lifecycle.test.ts index bab588072f3f..2b6d5ee8891f 100644 --- a/extensions/msteams/src/monitor.conversation-allowlist.lifecycle.test.ts +++ b/extensions/msteams/src/monitor.conversation-allowlist.lifecycle.test.ts @@ -58,6 +58,17 @@ vi.mock("../runtime-api.js", async () => { keepHttpServerTaskAlive: keepHttpServerTaskAliveMock, mergeAllowlist: (params: { existing?: string[]; additions: string[] }) => Array.from(new Set([...(params.existing ?? []), ...params.additions])), + resolveChannelMediaMaxBytes: (params: { + cfg: OpenClawConfig; + resolveChannelLimitMb: (context: { cfg: OpenClawConfig }) => number | undefined; + }) => { + const mediaMaxMb = + params.resolveChannelLimitMb({ cfg: params.cfg }) ?? + params.cfg.agents?.defaults?.mediaMaxMb; + return typeof mediaMaxMb === "number" && mediaMaxMb > 0 + ? Math.floor(mediaMaxMb * 1024 * 1024) + : undefined; + }, summarizeMapping: vi.fn(), }; }); diff --git a/extensions/msteams/src/monitor.lifecycle.test.ts b/extensions/msteams/src/monitor.lifecycle.test.ts index a79450292a46..3504b226a4fd 100644 --- a/extensions/msteams/src/monitor.lifecycle.test.ts +++ b/extensions/msteams/src/monitor.lifecycle.test.ts @@ -243,6 +243,14 @@ function requireRegisteredMSTeamsConfig(): OpenClawConfig { return registered.cfg; } +function requireRegisteredMSTeamsMediaMaxBytes(): number { + const registered = registerMSTeamsHandlers.mock.calls[0]?.[1]; + if (!registered) { + throw new Error("expected registered MSTeams handler dependencies"); + } + return registered.mediaMaxBytes; +} + describe("monitorMSTeamsProvider lifecycle", () => { afterEach(() => { vi.clearAllMocks(); @@ -292,6 +300,49 @@ describe("monitorMSTeamsProvider lifecycle", () => { } }); + it("prefers the Teams media limit over the agent default", async () => { + const abort = new AbortController(); + const cfg = createConfig(0); + updateMSTeamsConfig(cfg, { mediaMaxMb: 12 }); + cfg.agents = { defaults: { mediaMaxMb: 3 } }; + + const task = monitorMSTeamsProvider({ + cfg, + runtime: createRuntime(), + abortSignal: abort.signal, + ...createStores(), + }); + + await waitForMSTeamsTestState(() => { + expect(registerMSTeamsHandlers).toHaveBeenCalledTimes(1); + }); + expect(requireRegisteredMSTeamsMediaMaxBytes()).toBe(12 * 1024 * 1024); + + abort.abort(); + await task; + }); + + it("falls back to the agent media limit when Teams has no override", async () => { + const abort = new AbortController(); + const cfg = createConfig(0); + cfg.agents = { defaults: { mediaMaxMb: 3 } }; + + const task = monitorMSTeamsProvider({ + cfg, + runtime: createRuntime(), + abortSignal: abort.signal, + ...createStores(), + }); + + await waitForMSTeamsTestState(() => { + expect(registerMSTeamsHandlers).toHaveBeenCalledTimes(1); + }); + expect(requireRegisteredMSTeamsMediaMaxBytes()).toBe(3 * 1024 * 1024); + + abort.abort(); + await task; + }); + it("rejects startup when the webhook port is already in use", async () => { const blocker = createServer(); await new Promise((resolve, reject) => { diff --git a/extensions/msteams/src/monitor.ts b/extensions/msteams/src/monitor.ts index 9131b118744e..3d26bef6e3a5 100644 --- a/extensions/msteams/src/monitor.ts +++ b/extensions/msteams/src/monitor.ts @@ -6,6 +6,7 @@ import { isDangerousNameMatchingEnabled, keepHttpServerTaskAlive, mergeAllowlist, + resolveChannelMediaMaxBytes, summarizeMapping, type OpenClawConfig, type RuntimeEnv, @@ -205,12 +206,11 @@ export async function monitorMSTeamsProvider( const port = msteamsCfg.webhook?.port ?? 3978; const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "msteams"); - const MB = 1024 * 1024; - const agentDefaults = cfg.agents?.defaults; const mediaMaxBytes = - typeof agentDefaults?.mediaMaxMb === "number" && agentDefaults.mediaMaxMb > 0 - ? Math.floor(agentDefaults.mediaMaxMb * MB) - : 8 * MB; + resolveChannelMediaMaxBytes({ + cfg, + resolveChannelLimitMb: ({ cfg: channelCfg }) => channelCfg.channels?.msteams?.mediaMaxMb, + }) ?? 8 * 1024 * 1024; const conversationStore = opts.conversationStore ?? createMSTeamsConversationStoreState(); const pollStore = opts.pollStore ?? createMSTeamsPollStoreState();