diff --git a/src/secrets/runtime-config-collectors-channels.ts b/src/secrets/runtime-config-collectors-channels.ts index 46d946cb9d4..9d6a351e355 100644 --- a/src/secrets/runtime-config-collectors-channels.ts +++ b/src/secrets/runtime-config-collectors-channels.ts @@ -1,6 +1,7 @@ import type { OpenClawConfig } from "../config/config.js"; import { coerceSecretRef, resolveSecretInputRef } from "../config/types.secrets.js"; import { getMatrixScopedEnvVarNames } from "../plugin-sdk/matrix.js"; +import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/account-id.js"; import { collectTtsApiKeyAssignments } from "./runtime-config-collectors-tts.js"; import { collectSecretInputAssignment, @@ -625,6 +626,11 @@ function collectMatrixAssignments(params: { normalizeSecretStringValue( params.context.env[getMatrixScopedEnvVarNames("default").accessToken], ).length > 0; + const defaultAccountAccessTokenConfigured = surface.accounts.some( + ({ accountId, account }) => + normalizeAccountId(accountId) === DEFAULT_ACCOUNT_ID && + hasConfiguredSecretInputValue(account.accessToken, params.defaults), + ); const baseAccessTokenConfigured = hasConfiguredSecretInputValue( matrix.accessToken, params.defaults, @@ -652,9 +658,11 @@ function collectMatrixAssignments(params: { !( baseAccessTokenConfigured || envAccessTokenConfigured || - defaultScopedAccessTokenConfigured + defaultScopedAccessTokenConfigured || + defaultAccountAccessTokenConfigured ), - inactiveReason: "Matrix channel is disabled or a top-level accessToken is configured.", + inactiveReason: + "Matrix channel is disabled or access-token auth is configured for the default Matrix account.", apply: (value) => { matrix.password = value; }, @@ -689,7 +697,8 @@ function collectMatrixAssignments(params: { params.context.env[getMatrixScopedEnvVarNames(accountId).accessToken], ).length > 0; const inheritedDefaultAccountAccessTokenConfigured = - accountId === "default" && (baseAccessTokenConfigured || envAccessTokenConfigured); + normalizeAccountId(accountId) === DEFAULT_ACCOUNT_ID && + (baseAccessTokenConfigured || envAccessTokenConfigured); collectSecretInputAssignment({ value: account.password, path: `channels.matrix.accounts.${accountId}.password`, diff --git a/src/secrets/runtime.test.ts b/src/secrets/runtime.test.ts index b5b9b816c1b..149ef92026e 100644 --- a/src/secrets/runtime.test.ts +++ b/src/secrets/runtime.test.ts @@ -396,6 +396,91 @@ describe("secrets runtime snapshot", () => { ); }); + it.each([ + { + name: "channels.matrix.accounts.default.accessToken config", + config: { + channels: { + matrix: { + password: { + source: "env", + provider: "default", + id: "MATRIX_PASSWORD", + }, + accounts: { + default: { + accessToken: "default-token", + }, + }, + }, + }, + }, + env: {}, + }, + { + name: "channels.matrix.accounts.default.accessToken SecretRef config", + config: { + channels: { + matrix: { + password: { + source: "env", + provider: "default", + id: "MATRIX_PASSWORD", + }, + accounts: { + default: { + accessToken: { + source: "env", + provider: "default", + id: "MATRIX_DEFAULT_ACCESS_TOKEN_REF", + }, + }, + }, + }, + }, + }, + env: { + MATRIX_DEFAULT_ACCESS_TOKEN_REF: "default-token", + }, + }, + { + name: "MATRIX_DEFAULT_ACCESS_TOKEN env auth", + config: { + channels: { + matrix: { + password: { + source: "env", + provider: "default", + id: "MATRIX_PASSWORD", + }, + }, + }, + }, + env: { + MATRIX_DEFAULT_ACCESS_TOKEN: "default-token", + }, + }, + ])("ignores top-level Matrix password refs shadowed by $name", async ({ config, env }) => { + const snapshot = await prepareSecretsRuntimeSnapshot({ + config: asConfig(config), + env, + agentDirs: ["/tmp/openclaw-agent-main"], + loadAuthStore: () => ({ version: 1, profiles: {} }), + }); + + expect(snapshot.config.channels?.matrix?.password).toEqual({ + source: "env", + provider: "default", + id: "MATRIX_PASSWORD", + }); + expect(snapshot.warnings).toContainEqual( + expect.objectContaining({ + code: "SECRETS_REF_IGNORED_INACTIVE_SURFACE", + path: "channels.matrix.password", + }), + ); + }); + it.each([ { name: "top-level Matrix accessToken config",