fix(msteams): honor inbound channel media limit (#122315)

* fix(msteams): honor inbound channel media limit

* test(msteams): update lifecycle runtime mock

* style(msteams): avoid resolver config shadowing

* style(msteams): format lifecycle resolver mock

* refactor(msteams): inline media fallback

---------

Co-authored-by: DanielCardenas <djerez@lean-tech.io>
Co-authored-by: Jason (Json) <263060202+fuller-stack-dev@users.noreply.github.com>
This commit is contained in:
Daniel Cárdenas
2026-08-12 23:06:47 -03:00
committed by GitHub
parent 95bbd117ef
commit 326501fce3
3 changed files with 67 additions and 5 deletions
@@ -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(),
};
});
@@ -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<void>((resolve, reject) => {
+5 -5
View File
@@ -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();