matrix: prefer named default account

This commit is contained in:
Gustavo Madeira Santana
2026-04-14 17:01:16 -04:00
parent 70b67b0c68
commit 5bf30d258f
4 changed files with 48 additions and 6 deletions

View File

@@ -919,6 +919,7 @@ Entries without `account` stay shared across all Matrix accounts, and entries wi
Partial shared auth defaults do not create a separate implicit default account by themselves. OpenClaw only synthesizes the top-level `default` account when that default has fresh auth (`homeserver` plus `accessToken`, or `homeserver` plus `userId` and `password`); named accounts can still stay discoverable from `homeserver` plus `userId` when cached credentials satisfy auth later.
If Matrix already has exactly one named account, or `defaultAccount` points at an existing named account key, single-account-to-multi-account repair/setup promotion preserves that account instead of creating a fresh `accounts.default` entry. Only Matrix auth/bootstrap keys move into that promoted account; shared delivery-policy keys stay at the top level.
Set `defaultAccount` when you want OpenClaw to prefer one named Matrix account for implicit routing, probing, and CLI operations.
If multiple Matrix accounts are configured and one account id is `default`, OpenClaw uses that account implicitly even when `defaultAccount` is unset.
If you configure multiple named accounts, set `defaultAccount` or pass `--account <id>` for CLI commands that rely on implicit account selection.
Pass `--account <id>` to `openclaw matrix verify ...` and `openclaw matrix devices ...` when you want to override that implicit selection for one command.

View File

@@ -56,6 +56,22 @@ describe("matrix account selection", () => {
expect(requiresExplicitMatrixDefaultAccount(cfg)).toBe(true);
});
it('uses a named "default" Matrix account when defaultAccount is unset', () => {
const cfg: OpenClawConfig = {
channels: {
matrix: {
accounts: {
default: { homeserver: "https://matrix.example.org" },
ops: { homeserver: "https://matrix.example.org" },
},
},
},
};
expect(resolveMatrixDefaultOrOnlyAccountId(cfg)).toBe("default");
expect(requiresExplicitMatrixDefaultAccount(cfg)).toBe(false);
});
it("finds the raw Matrix account entry by normalized account id", () => {
const cfg: OpenClawConfig = {
channels: {
@@ -93,7 +109,7 @@ describe("matrix account selection", () => {
expect(requiresExplicitMatrixDefaultAccount(cfg, env)).toBe(false);
});
it("treats mixed default and named env-backed Matrix accounts as multi-account", () => {
it('uses the "default" Matrix account when mixed default and named env-backed accounts exist', () => {
const keys = getMatrixScopedEnvVarNames("team-ops");
const cfg: OpenClawConfig = {
channels: {
@@ -108,7 +124,8 @@ describe("matrix account selection", () => {
} satisfies NodeJS.ProcessEnv;
expect(resolveConfiguredMatrixAccountIds(cfg, env)).toEqual(["default", "team-ops"]);
expect(requiresExplicitMatrixDefaultAccount(cfg, env)).toBe(true);
expect(resolveMatrixDefaultOrOnlyAccountId(cfg, env)).toBe("default");
expect(requiresExplicitMatrixDefaultAccount(cfg, env)).toBe(false);
});
it("discovers default Matrix accounts backed only by global env vars", () => {

View File

@@ -213,6 +213,9 @@ export function requiresExplicitMatrixDefaultAccount(
if (configuredAccountIds.length <= 1) {
return false;
}
if (configuredAccountIds.includes(DEFAULT_ACCOUNT_ID)) {
return false;
}
const configuredDefault = normalizeOptionalAccountId(
typeof channel.defaultAccount === "string" ? channel.defaultAccount : undefined,
);

View File

@@ -403,6 +403,29 @@ describe("Matrix auth/config live surfaces", () => {
);
});
it('uses a named "default" account implicitly when multiple Matrix accounts exist', () => {
const cfg = {
channels: {
matrix: {
accounts: {
default: {
homeserver: "https://matrix.default.example.org",
accessToken: "default-token",
},
ops: {
homeserver: "https://matrix.ops.example.org",
accessToken: "ops-token",
},
},
},
},
} as CoreConfig;
expect(resolveMatrixAuthContext({ cfg, env: {} as NodeJS.ProcessEnv }).accountId).toBe(
"default",
);
});
it("does not materialize a default account from shared top-level defaults alone", () => {
const cfg = {
channels: {
@@ -439,7 +462,7 @@ describe("Matrix auth/config live surfaces", () => {
expect(resolveMatrixAuthContext({ cfg, env: {} as NodeJS.ProcessEnv }).accountId).toBe("ops");
});
it("honors injected env when implicit Matrix account selection becomes ambiguous", () => {
it('uses the injected env-backed "default" Matrix account when implicit selection is available', () => {
const cfg = {
channels: {
matrix: {},
@@ -452,9 +475,7 @@ describe("Matrix auth/config live surfaces", () => {
MATRIX_OPS_ACCESS_TOKEN: "ops-token",
} as NodeJS.ProcessEnv;
expect(() => resolveMatrixAuthContext({ cfg, env })).toThrow(
/channels\.matrix\.defaultAccount.*--account <id>/i,
);
expect(resolveMatrixAuthContext({ cfg, env }).accountId).toBe("default");
});
it("does not materialize a default env account from partial global auth fields", () => {