fix(channels): skip default account injection when named accounts inherit base tokens

- Don't inject 'default' when all named accounts lack per-account
  auth overrides (they'd use the same base credentials, causing
  duplicate provider connections and event processing)
- Use normalizeAccountId for case-insensitive default detection
- Applies to Slack (botToken/appToken) and Discord (token) alike

Addresses review feedback from codex-connector on PR #30310.
This commit is contained in:
zeroaltitude 2026-03-03 09:45:57 -07:00
parent 4e183da984
commit 4b67092c2b
No known key found for this signature in database
GPG Key ID: 77592FB1C703882E
2 changed files with 59 additions and 8 deletions

View File

@ -82,7 +82,20 @@ describe("createAccountListHelpers", () => {
expect(listAccountIds(cfg({ z: {}, a: {}, m: {} }))).toEqual(["a", "m", "z"]);
});
it("includes default when base config has botToken and named accounts exist", () => {
it("includes default when base has tokens AND a named account has its own tokens", () => {
const config = {
channels: {
testchannel: {
botToken: "xoxb-base",
appToken: "xapp-base",
accounts: { tank: { botToken: "xoxb-tank" } },
},
},
} as unknown as OpenClawConfig;
expect(listAccountIds(config)).toEqual(["default", "tank"]);
});
it("does NOT inject default when named accounts inherit base tokens (avoids duplicates)", () => {
const config = {
channels: {
testchannel: {
@ -92,7 +105,31 @@ describe("createAccountListHelpers", () => {
},
},
} as unknown as OpenClawConfig;
expect(listAccountIds(config)).toEqual(["default", "tank"]);
expect(listAccountIds(config)).toEqual(["tank"]);
});
it("does NOT inject default when Discord named accounts inherit base token", () => {
const config = {
channels: {
testchannel: {
token: "discord-bot-token",
accounts: { teamA: {} },
},
},
} as unknown as OpenClawConfig;
expect(listAccountIds(config)).toEqual(["teamA"]);
});
it("does not duplicate default when already in accounts (case-insensitive)", () => {
const config = {
channels: {
testchannel: {
botToken: "xoxb-base",
accounts: { Default: {}, tank: {} },
},
},
} as unknown as OpenClawConfig;
expect(listAccountIds(config)).toEqual(["Default", "tank"]);
});
it("does not duplicate default when already in accounts", () => {

View File

@ -43,15 +43,29 @@ export function createAccountListHelpers(
if (ids.length === 0) {
return [DEFAULT_ACCOUNT_ID];
}
// Check whether any existing named account already normalizes to "default".
const normalizedIds = ids.map(normalizeAccountId);
if (normalizedIds.includes(DEFAULT_ACCOUNT_ID)) {
return ids.toSorted((a, b) => a.localeCompare(b));
}
// If the base channel config has its own tokens (botToken/appToken/token),
// include the default account alongside named accounts so both providers start.
if (!ids.includes(DEFAULT_ACCOUNT_ID)) {
const channel = cfg.channels?.[channelKey];
const base = channel as Record<string, unknown> | undefined;
const hasBaseTokens = Boolean(base?.botToken || base?.appToken || base?.token);
if (hasBaseTokens) {
// only inject a default account when at least one named account carries its
// own per-account auth. When every named account inherits the base tokens
// (i.e. has no per-account botToken/appToken/token override), injecting
// default would start a duplicate provider on the same credentials.
const channel = cfg.channels?.[channelKey];
const base = channel as Record<string, unknown> | undefined;
const hasBaseTokens = Boolean(base?.botToken || base?.appToken || base?.token);
if (hasBaseTokens) {
const accounts = (base?.accounts ?? {}) as Record<string, Record<string, unknown>>;
const someAccountHasOwnTokens = ids.some((id) => {
const acct = accounts[id];
return acct && Boolean(acct.botToken || acct.appToken || acct.token);
});
if (someAccountHasOwnTokens) {
return [DEFAULT_ACCOUNT_ID, ...ids].toSorted((a, b) => a.localeCompare(b));
}
// All named accounts inherit base tokens — don't inject default.
}
return ids.toSorted((a, b) => a.localeCompare(b));
}