fix: scope approval ambiguity to active accounts

This commit is contained in:
Gustavo Madeira Santana
2026-04-03 13:26:55 -04:00
parent 223e7a6eb8
commit ef3d2cc692
4 changed files with 128 additions and 2 deletions

View File

@@ -398,4 +398,60 @@ describe("matrix exec approvals", () => {
}),
).toBe(false);
});
it("allows unbound foreign-channel approvals when only one matrix account can handle them", () => {
const cfg = {
channels: {
matrix: {
accounts: {
default: {
homeserver: "https://matrix.example.org",
userId: "@bot-default:example.org",
accessToken: "tok-default",
execApprovals: {
enabled: true,
approvers: ["@owner:example.org"],
},
},
ops: {
homeserver: "https://matrix.example.org",
userId: "@bot-ops:example.org",
accessToken: "tok-ops",
execApprovals: {
enabled: false,
approvers: ["@owner:example.org"],
},
},
},
},
},
} as OpenClawConfig;
const request = {
id: "req-5",
request: {
command: "echo hi",
agentId: "ops-agent",
sessionKey: "agent:ops-agent:missing",
turnSourceChannel: "slack",
turnSourceTo: "channel:C123",
},
createdAtMs: 0,
expiresAtMs: 1000,
};
expect(
shouldHandleMatrixExecApprovalRequest({
cfg,
accountId: "default",
request,
}),
).toBe(true);
expect(
shouldHandleMatrixExecApprovalRequest({
cfg,
accountId: "ops",
request,
}),
).toBe(false);
});
});

View File

@@ -1,6 +1,7 @@
import {
createChannelExecApprovalProfile,
getExecApprovalReplyMetadata,
isChannelExecApprovalClientEnabledFromConfig,
isChannelExecApprovalTargetRecipient,
resolveApprovalRequestChannelAccountId,
resolveApprovalApprovers,
@@ -38,6 +39,15 @@ function resolveMatrixExecApprovalConfig(params: {
};
}
function countMatrixExecApprovalHandlerAccounts(cfg: OpenClawConfig): number {
return listMatrixAccountIds(cfg).filter((accountId) =>
isChannelExecApprovalClientEnabledFromConfig({
enabled: resolveMatrixExecApprovalConfig({ cfg, accountId }).enabled,
approverCount: getMatrixExecApprovalApprovers({ cfg, accountId }).length,
}),
).length;
}
function matchesMatrixRequestAccount(params: {
cfg: OpenClawConfig;
accountId?: string | null;
@@ -50,7 +60,7 @@ function matchesMatrixRequestAccount(params: {
channel: "matrix",
});
if (turnSourceChannel && turnSourceChannel !== "matrix" && !boundAccountId) {
return listMatrixAccountIds(params.cfg).length <= 1;
return countMatrixExecApprovalHandlerAccounts(params.cfg) <= 1;
}
return (
!boundAccountId ||

View File

@@ -241,6 +241,57 @@ describe("telegram exec approvals", () => {
).toBe(false);
});
it("allows unbound foreign-channel approvals when only one telegram account can handle them", () => {
const cfg = {
channels: {
telegram: {
accounts: {
default: {
botToken: "tok-default",
execApprovals: {
enabled: true,
approvers: ["123"],
},
},
ops: {
botToken: "tok-ops",
execApprovals: {
enabled: false,
approvers: ["123"],
},
},
},
},
},
} as OpenClawConfig;
const request = {
id: "req-4",
request: {
command: "echo hi",
sessionKey: "agent:ops:missing",
turnSourceChannel: "slack",
turnSourceTo: "channel:C123",
},
createdAtMs: 0,
expiresAtMs: 1000,
};
expect(
shouldHandleTelegramExecApprovalRequest({
cfg,
accountId: "default",
request,
}),
).toBe(true);
expect(
shouldHandleTelegramExecApprovalRequest({
cfg,
accountId: "ops",
request,
}),
).toBe(false);
});
it("only injects approval buttons on eligible telegram targets", () => {
const dmCfg = buildConfig({ enabled: true, approvers: ["123"], target: "dm" });
const channelCfg = buildConfig({ enabled: true, approvers: ["123"], target: "channel" });

View File

@@ -65,6 +65,15 @@ export function isTelegramExecApprovalTargetRecipient(params: {
});
}
function countTelegramExecApprovalHandlerAccounts(cfg: OpenClawConfig): number {
return listTelegramAccountIds(cfg).filter((accountId) =>
isChannelExecApprovalClientEnabledFromConfig({
enabled: resolveTelegramExecApprovalConfig({ cfg, accountId })?.enabled,
approverCount: getTelegramExecApprovalApprovers({ cfg, accountId }).length,
}),
).length;
}
function matchesTelegramRequestAccount(params: {
cfg: OpenClawConfig;
accountId?: string | null;
@@ -77,7 +86,7 @@ function matchesTelegramRequestAccount(params: {
channel: "telegram",
});
if (turnSourceChannel && turnSourceChannel !== "telegram" && !boundAccountId) {
return listTelegramAccountIds(params.cfg).length <= 1;
return countTelegramExecApprovalHandlerAccounts(params.cfg) <= 1;
}
return (
!boundAccountId ||