fix: stabilize channel migration

This commit is contained in:
Peter Steinberger
2026-01-13 06:27:55 +00:00
parent 90342a4f3a
commit 993c1de361
6 changed files with 39 additions and 24 deletions

View File

@@ -3,8 +3,8 @@ import crypto from "node:crypto";
import { buildWorkspaceSkillSnapshot } from "../../agents/skills.js"; import { buildWorkspaceSkillSnapshot } from "../../agents/skills.js";
import type { ClawdbotConfig } from "../../config/config.js"; import type { ClawdbotConfig } from "../../config/config.js";
import { type SessionEntry, saveSessionStore } from "../../config/sessions.js"; import { type SessionEntry, saveSessionStore } from "../../config/sessions.js";
import { drainSystemEventEntries } from "../../infra/system-events.js";
import { buildChannelSummary } from "../../infra/channel-summary.js"; import { buildChannelSummary } from "../../infra/channel-summary.js";
import { drainSystemEventEntries } from "../../infra/system-events.js";
export async function prependSystemEvents(params: { export async function prependSystemEvents(params: {
cfg: ClawdbotConfig; cfg: ClawdbotConfig;

View File

@@ -2003,11 +2003,14 @@ describe("config preservation on validation failure", () => {
const { readConfigFileSnapshot } = await import("./config.js"); const { readConfigFileSnapshot } = await import("./config.js");
const snap = await readConfigFileSnapshot(); const snap = await readConfigFileSnapshot();
expect(snap.valid).toBe(false); expect(snap.valid).toBe(true);
expect(snap.legacyIssues.length).toBeGreaterThan(0); expect(snap.legacyIssues).toHaveLength(0);
expect((snap.config as Record<string, unknown>).customData).toEqual({ expect((snap.config as Record<string, unknown>).customData).toEqual({
preserved: true, preserved: true,
}); });
expect(snap.config.channels?.whatsapp?.allowFrom).toEqual([
"+15555550123",
]);
}); });
}); });
}); });

View File

@@ -352,11 +352,15 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
} }
const migrated = applyLegacyMigrations(resolved); const migrated = applyLegacyMigrations(resolved);
const resolvedConfig = migrated.next ?? resolved; const resolvedConfigRaw = migrated.next ?? resolved;
const legacyIssues = findLegacyConfigIssues(resolvedConfig); const legacyIssues = findLegacyConfigIssues(resolvedConfigRaw);
const validated = validateConfigObject(resolvedConfig); const validated = validateConfigObject(resolvedConfigRaw);
if (!validated.ok) { if (!validated.ok) {
const resolvedConfig =
typeof resolvedConfigRaw === "object" && resolvedConfigRaw !== null
? (resolvedConfigRaw as ClawdbotConfig)
: {};
return { return {
path: configPath, path: configPath,
exists: true, exists: true,

View File

@@ -4,9 +4,11 @@ import type { ClawdbotConfig } from "../../config/config.js";
import { runMessageAction } from "./message-action-runner.js"; import { runMessageAction } from "./message-action-runner.js";
const slackConfig = { const slackConfig = {
slack: { channels: {
botToken: "xoxb-test", slack: {
appToken: "xapp-test", botToken: "xoxb-test",
appToken: "xapp-test",
},
}, },
} as ClawdbotConfig; } as ClawdbotConfig;
@@ -16,7 +18,7 @@ describe("runMessageAction context isolation", () => {
cfg: slackConfig, cfg: slackConfig,
action: "send", action: "send",
params: { params: {
provider: "slack", channel: "slack",
to: "#C123", to: "#C123",
message: "hi", message: "hi",
}, },
@@ -33,7 +35,7 @@ describe("runMessageAction context isolation", () => {
cfg: slackConfig, cfg: slackConfig,
action: "send", action: "send",
params: { params: {
provider: "slack", channel: "slack",
to: "channel:C999", to: "channel:C999",
message: "hi", message: "hi",
}, },
@@ -49,7 +51,7 @@ describe("runMessageAction context isolation", () => {
cfg: slackConfig, cfg: slackConfig,
action: "thread-reply", action: "thread-reply",
params: { params: {
provider: "slack", channel: "slack",
channelId: "C999", channelId: "C999",
message: "hi", message: "hi",
}, },

View File

@@ -924,18 +924,20 @@ describe("createTelegramBot", () => {
replySpy.mockReset(); replySpy.mockReset();
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
telegram: { channels: {
accounts: { telegram: {
opie: { accounts: {
botToken: "tok-opie", opie: {
dmPolicy: "open", botToken: "tok-opie",
dmPolicy: "open",
},
}, },
}, },
}, },
bindings: [ bindings: [
{ {
agentId: "opie", agentId: "opie",
match: { provider: "telegram", accountId: "opie" }, match: { channel: "telegram", accountId: "opie" },
}, },
], ],
}); });
@@ -1851,9 +1853,11 @@ describe("createTelegramBot", () => {
replySpy.mockResolvedValue({ text: "response" }); replySpy.mockResolvedValue({ text: "response" });
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
telegram: { channels: {
groupPolicy: "open", telegram: {
groups: { "*": { requireMention: false } }, groupPolicy: "open",
groups: { "*": { requireMention: false } },
},
}, },
}); });

View File

@@ -159,14 +159,16 @@ describe("partial reply gating", () => {
const replyResolver = vi.fn().mockResolvedValue({ text: "final reply" }); const replyResolver = vi.fn().mockResolvedValue({ text: "final reply" });
const mockConfig: ClawdbotConfig = { const mockConfig: ClawdbotConfig = {
whatsapp: { channels: {
allowFrom: ["*"], whatsapp: {
allowFrom: ["*"],
},
}, },
}; };
setLoadConfigMock(mockConfig); setLoadConfigMock(mockConfig);
await monitorWebProvider( await monitorWebChannel(
false, false,
async ({ onMessage }) => { async ({ onMessage }) => {
await onMessage({ await onMessage({