From f086588599e86f8b5441e41e71adf409aeabb82a Mon Sep 17 00:00:00 2001 From: zeroaltitude Date: Wed, 18 Mar 2026 19:57:50 -0700 Subject: [PATCH] fix(channels): use caller normalizer in reverse map; skip disabled accounts Two fixes in response to review: 1. Use options.normalizeAccountId (caller-provided) instead of the imported normalizeAccountId when building the reverse map in everyAccountHasOwnTokens. Ensures the reverse map keys match the IDs returned by listConfiguredAccountIds() when a custom normalizer is used. 2. Filter disabled accounts before the everyAccountHasOwnTokens gate. Disabled accounts are skipped at channel startup; allowing them to block default injection would prevent the base account from starting in mixed enabled/disabled configs where all enabled accounts are independent. --- src/channels/plugins/account-helpers.ts | 28 ++++++++++++++++++------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/channels/plugins/account-helpers.ts b/src/channels/plugins/account-helpers.ts index 35403728c1a..f6b3bff47ca 100644 --- a/src/channels/plugins/account-helpers.ts +++ b/src/channels/plugins/account-helpers.ts @@ -63,26 +63,38 @@ export function createAccountListHelpers( ); if (baseTokenFields.length > 0) { const accounts = (base?.accounts ?? {}) as Record>; + // Use the caller-provided normalizer for the reverse map so it matches + // the IDs returned by listConfiguredAccountIds(). + const normalizeId = options?.normalizeAccountId ?? normalizeAccountId; // Build reverse map from normalized ID → raw config key so we can look // up the account object even when normalizeAccountId transforms the keys. const rawKeys = Object.keys(accounts); const normalizedToRaw = new Map(); for (const key of rawKeys) { - const normalized = normalizeAccountId(key); + const normalized = normalizeId(key); if (normalized && !normalizedToRaw.has(normalized)) { normalizedToRaw.set(normalized, key); } } - const everyAccountHasOwnTokens = ids.every((id) => { + // Only consider enabled accounts — disabled accounts are skipped at + // startup and must not prevent default injection for enabled ones. + const enabledIds = ids.filter((id) => { const rawKey = normalizedToRaw.get(id) ?? id; const acct = accounts[rawKey]; - if (!acct) { - return false; - } - // An account is only independent if it overrides *every* base token - // field. Partial overrides still inherit the remaining base tokens. - return baseTokenFields.every((f) => isTruthy(acct[f])); + return !acct || acct["enabled"] !== false; }); + const everyAccountHasOwnTokens = + enabledIds.length > 0 && + enabledIds.every((id) => { + const rawKey = normalizedToRaw.get(id) ?? id; + const acct = accounts[rawKey]; + if (!acct) { + return false; + } + // An account is only independent if it overrides *every* base token + // field. Partial overrides still inherit the remaining base tokens. + return baseTokenFields.every((f) => isTruthy(acct[f])); + }); if (everyAccountHasOwnTokens) { // Every named account has distinct credentials, so default won't // collide with any of them — safe to inject.