fix(channels): preserve ingress shutdown and named-account setup (#111449)

* fix(channels): repair plugin prerelease regressions

* chore: leave release notes to release automation

* fix(channels): update plugin SDK API baseline
This commit is contained in:
Peter Steinberger
2026-07-19 07:57:37 -07:00
committed by GitHub
parent 6dc6d5efee
commit 31423de908
8 changed files with 54 additions and 10 deletions
@@ -56,9 +56,9 @@ c97dd36cdf8f83c2893c33e9430a93cd131a03d855725783ca5b545de0cf84f8 module/channel
25230ecd4db8a49f94e29fffce985d40017227a778b1dd59b9c426b9ca627194 module/channel-location
159d034b431d113f3a6dc41ec0bcadba2d6664051f158330b0e3dd3da8b5d42f module/channel-logging
a693996a92b387ec26146daa7ba73b6b026cd8fc5e6d72874a6856d9c38b3f53 module/channel-mention-gating
7889f97bb1854541b17ec41fd7ab14423ac658d4584e023fd9bacde0437c0ff1 module/channel-message
7783f91ba5a95c2dfc1f42db956a468b94c3648d64b325504c233bf88a559d80 module/channel-message-runtime
f5bb3c54f11dd0049153203e9836e9d79d4856153813a52277d3e86f271df405 module/channel-outbound
5eccd03e85c032753e0d8931dace4cd952b83e4975d0acae272849fdabfbfe90 module/channel-message
1eeb53fdc4f526945c2cad5783e3b3184491d14c5286a5cca8145b5c97345e1d module/channel-message-runtime
e2b4d1923a19b927e912622576d43d20a6e188c124a7bc588fe567cd3c1c924e module/channel-outbound
50c61de5d522abadcae79841335aeac25e3791e6dbb0cfc3d64b2720a8c514ce module/channel-pairing
e3f8f0d0aaa3ea3a773ce2011d7af30f18b40c27cbbad0df2d53cd582a56b631 module/channel-pairing-paths
ee4292b069d4d48cce4fc2dc26df5b5c87eb1fa4769f1f6be9a10c3e1221e1a9 module/channel-plugin-common
@@ -3,7 +3,6 @@ import { EventEmitter } from "node:events";
import type { ChannelRuntimeSurface } from "openclaw/plugin-sdk/channel-contract";
import { createPluginRuntimeMock } from "openclaw/plugin-sdk/channel-test-helpers";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { RateLimitError } from "../internal/discord.js";
import {
@@ -18,8 +17,6 @@ import {
formatDiscordDeployErrorMessage,
} from "./provider.deploy-errors.js";
vi.mock("openclaw/plugin-sdk/runtime-env", { spy: true });
const {
clientConstructorOptionsMock,
clientDeployCommandsMock,
@@ -238,7 +235,6 @@ describe("monitorDiscordProvider", () => {
vi.doMock("../token.js", () => ({
normalizeDiscordToken: (value?: string) => value,
}));
vi.mocked(logVerbose).mockImplementation(() => undefined);
({ monitorDiscordProvider } = await import("./provider.js"));
({ discordProviderTestSupport: providerTesting } = await import("./provider.test-support.js"));
});
@@ -247,7 +243,6 @@ describe("monitorDiscordProvider", () => {
providerTesting.reset();
resetDiscordProviderMonitorMocks();
voiceAutoJoinMock.mockClear();
vi.mocked(logVerbose).mockClear();
providerTesting.setFetchDiscordApplicationId(async () => "app-1");
providerTesting.setCreateDiscordNativeCommand(((
...args: Parameters<typeof providerTesting.setCreateDiscordNativeCommand>[0] extends
@@ -321,7 +321,7 @@ describe("Google Chat durable ingress", () => {
releaseDelivery();
await stopping;
expect(stopped).toBe(true);
expect(await queue.listClaims()).toHaveLength(1);
expect(await queue.listClaims()).toHaveLength(0);
});
});
+1
View File
@@ -214,6 +214,7 @@ export async function startSignalIngressMonitor(params: {
return {
receive: async (event) => {
await monitor.admit(event);
await monitor.waitForPumpIdle();
},
stop: monitor.stop,
waitForIdle: monitor.waitForIdle,
@@ -206,6 +206,28 @@ describe("channel ingress monitor", () => {
});
});
it("can await claim startup without waiting for active delivery", async () => {
await withQueue(async (queue) => {
let releaseDelivery = () => {};
const deliveryGate = new Promise<void>((resolve) => {
releaseDelivery = resolve;
});
const deliver = vi.fn(async () => {
await deliveryGate;
});
const monitor = createMonitor(queue, deliver);
monitor.start();
await monitor.admit({ id: "event-started", lane: "a", text: "hello" });
await monitor.waitForPumpIdle();
expect(deliver).toHaveBeenCalledOnce();
releaseDelivery();
await monitor.waitForIdle();
await monitor.stop();
});
});
it("drains the next same-lane event after adoption while delivery remains active", async () => {
await withQueue(async (queue) => {
let releaseFirst: (() => void) | undefined;
+1
View File
@@ -641,6 +641,7 @@ export function createChannelIngressMonitor<TRaw, TBody, TStoredPayload, TMetada
}
}
},
waitForPumpIdle,
isRunning: () => running,
isStopped: () => stopped,
};
@@ -324,6 +324,29 @@ describe("moveSingleAccountChannelSectionToDefaultAccount", () => {
expect(next.channels?.matrix?.accessToken).toBeUndefined();
});
it("preserves explicit named-account values over promoted root defaults", () => {
const next = moveSingleAccountChannelSectionToDefaultAccount({
cfg: asConfig({
channels: {
zalouser: {
dmPolicy: "disabled",
accounts: {
work: {
dmPolicy: "allowlist",
},
},
},
},
}),
channelKey: "zalouser",
});
const channel = channelRecord(next, "zalouser");
const work = accountRecord(channel, "work");
expect(work.dmPolicy).toBe("allowlist");
expect(next.channels?.zalouser?.dmPolicy).toBeUndefined();
});
it("promotes legacy Matrix keys into an existing non-canonical default account key", () => {
const next = moveSingleAccountChannelSectionToDefaultAccount({
cfg: asConfig({
+3 -1
View File
@@ -425,7 +425,9 @@ function moveSingleAccountKeysIntoAccount(params: {
}): OpenClawConfig {
const nextAccount: Record<string, unknown> = { ...params.baseAccount };
for (const key of params.keysToMove) {
nextAccount[key] = cloneIfObject(params.channel[key]);
if (!(key in nextAccount)) {
nextAccount[key] = cloneIfObject(params.channel[key]);
}
}
const nextChannel: ChannelSectionRecord = { ...params.channel };
for (const key of params.keysToMove) {